Interactive appendix2026Published3 min
HFT feature explorer: one recorded 15-minute SOL market
Fifteen minutes of Binance and Polymarket data from my HFT recorder, with feed freshness and derived features on one clock. Run the same kind of inspection that sat behind the HFT datasets.
- Market
- SOL 15-minute Up/Down on Polymarket (sol_1771810200)
- Recorded
- 23 Feb 2026, 01:30–01:45 UTC
- Data
- 58,613 rows × 57 columns; a 12,000-row sample is embedded
- Format
- Single HTML file (Parquet export + Plotly.js)
- Polymarket
- Plotly
- Time series
- Feature engineering
- Data quality
- Market microstructure
Interactive
Fifteen minutes of one market, on the recorder's clock
The explorer opens on the pair behind the HFT thesis that Polymarket might lag its reference market: the Binance SOL ask (binance_ask, left axis) and the Polymarket bid for Up (up_bid, right axis). The x axis is local receipt time, when my machine got each update: the clock a live strategy actually has.
Recorded L1 explorer for Polymarket slug sol_1771810200
Open full screen
Loads about 6 MB of data and Plotly.js from its CDN. Best on a desktop screen.
Guide
Five checks to run before trusting a feature
I used the explorer for these questions during the HFT work. Control names match the explorer's labels.
Do the two markets move together?
Series A binance_ask · Series B up_bidIf Polymarket lagged Binance, a move in the ask would reach the Up bid a moment later. Drag across the chart to zoom in; Reset Plotly zoom undoes it.
How stale is each quote?
Series B poly_age_ms · Series C binance_age_msEvery row records how old the latest update from each feed was. That turns misalignment into data, and it let training rows be filtered by latency thresholds such as 200–500 ms.
Does smoothing help or hurt?
Hampel → EWMA → rolling medianLoad a noisy series such as
order_book_imbalanceand add the filters one at a time; they always run in this order. Hampel and EWMA only look back, as a live model must. The rolling median is centered and also uses later rows: fine for the eye, not for a live feature.Do two features share a shape?
Normalize (z-score) · Force single left Y axisNormalize turns each series into z-scores over the visible window, so prices, sizes and returns can share one axis. Try
order_book_imbalanceagainstreturn_1s: one HFT hypothesis was that imbalance and short lag features carried signal.Does the last minute behave differently?
Full X range · Last 10% of X · Last 60sThe view opens 10 s in; Full X range shows all 900 s. Last 60s isolates the final minute, often trivial or structurally unrealistic in a 15-minute market, and a source of fake signal if it leaks into a backtest.
Legend
Quotes, clocks, features and one label: the 57 columns
The dropdowns sort the columns alphabetically. By role, they fall into six groups.
- Binance reference market (9)
- SOL top of book:
binance_bid,binance_ask, their_sizecolumns,binance_spread,binance_spread_bpsandlog_binance_ask_size, plusunderlying_priceand thebinance_connectedflag. - Polymarket book, Up and Down (17)
- Bid, ask, mid, spread and sizes for both outcomes (
up_*,dn_*,log_up_ask_size), the VWAP of a $1,000 order on each side (vwap_*_1000usd) andslippage_ask_1000usd_bps. The Down book largely mirrors Up (dn_ask≈ 1 −up_bid), so read them as one signal, not two. - Clocks and freshness (8)
- When my machine received the row (
local_receipt_ts_ms,ts_datetime), each feed's own timestamp (binance_ts_ms,up_ts_ms,dn_ts_ms), and how old or delayed each update was (binance_age_ms,poly_age_ms,network_latency_ms). Staleness is a column, not an assumption. - Returns, volatility, imbalance (10)
return_100ms,return_1s,return_5sand z-scored versions of the first two; volatility over 30 s, 5 min and 15 min (vol_30s,vol_5m,vol_15m);order_book_imbalanceandjump_flag.- Contract and time to expiry (12)
strike_at_open,strike_proxy,distance_to_strike,moneynessand two variants,seconds_left,ms_left,log_time,nonlinear_time,interval_start_tsandfair_prob_up, a fair-value probability for Up.- Hindsight (1)
close_at_15mis the price at the end of the window, repeated on every row. It is only known at expiry, so it can label a row but must never feed a feature.
Context
Where this slug sits in the HFT pipeline
The HFT work was not just about training LightGBM on a prepared table. A big part of it was checking recorded data like this.
01
Record
Rust hot path
- Binance and Polymarket WebSockets
- local_receipt_ts_ms on every tick
02
Store
Python ETL
- JSONL archive
- Incremental conversion to Parquet
03
Prepare
prepared_l1 and prepared_l2
- Train, val and test sets
- Latency filters such as 200–500 ms
- Leakage checks
04
Model
LightGBM
- Targets such as hft_1s, maker_1s and adverse_bid_1s
- adverse_bid_1s: about 0.921 test accuracy, still negative maker-style PnL
The explorer reads one Parquet L1 slug from the Store stage.
Related
Latency and infrastructure
HFT on Polymarket: Rust, timestamps and the 98% hit rate that wasn't
Second-scale Polymarket research built recorder-first, with Rust on the hot path and LightGBM on top, and a 98% hit rate that fell to about 55% once latency was simulated.
Backtest realism
The backtest that lies, and the rules that stop it
The rules that keep short-horizon backtests honest: stop, take-profit, entry inside every bar, reaction latency, slippage, and payout and leakage audits.

Market systems2025–2026Stopped
Polymarket trading systems
Prediction-market research: a dataset builder, a 15-minute bot and a Rust + Python HFT recorder.
Get in touch
Working on something similar? I am happy to compare notes.