Machine Learning · Forecasting · Financial Markets

Order-Book Imbalance as a One-Second Predictor

Abstract

A single fully-reproducible LOBSTER trading day (AAPL, 2012-06-21) used to ask one execution question end-to-end: does top-five book imbalance — together with spread and prior-second signed trade imbalance — predict the next-second mid-price direction strongly enough to survive a taker execution model.

Session timeline — AAPL on 2012-06-21 (chronological 70/30 split) 577 580 583 586 589 split @ 12:28:15 09:3010:3011:3012:3013:3014:3015:3016:00 Midprice (USD) Time on 2012-06-21 Train (70%) Test (30%)
Figure 1. Session timeline — AAPL on 2012-06-21 (chronological 70/30 split). Mid-price drifts from ~$588 to ~$577 with no large gaps. The training window is 09:30–12:28; the held-out test slice is 12:28–16:00 and is the source of every AUC, calibration, and cost number on this page.

Key results

Held-out AUC
0.567 (100 ms)
Test slice
57,213 snapshots
Sample
AAPL · 2012-06-21
Net EV / action
-$0.167 @ p* = 0.55

Headline

On a real LOBSTER AAPL trading day sampled at 100 ms, the full logistic specification picks up genuine one-second-ahead directional information — but the lift sits well below the venue cost it would have to clear to be deployable as a taker strategy. The numbers reported throughout this page come from the chronological 70/30 held-out slice (test ≥ 12:28:16, 57,213 observations across eight 30-minute buckets, 36 distinct spread tick values).

Held-out ROC AUC is 0.567 with a Brier score of 0.2467, and the result is essentially scale-invariant: AUC moves by less than 0.003 across 50, 100, and 250 ms snapshots. The decision rule at the primary 0.55 probability threshold and 1.0× modeled costs delivers a net expected value of −$0.048 per snapshot (−$0.167 per action across 16,584 actions, 71% no-trade share). Every cell of the cost cube reports negative_after_cost, so the signal does not survive realistic transaction costs in this single-day sample.

Methodology

The pipeline reconstructs event-time top-five book states from raw LOBSTER message + orderbook CSVs into a venue-agnostic canonical schema, samples backward on a fixed cadence grid with a 1 s staleness guard, and labels the next-second mid-price direction without any forward leakage.

Data. LOBSTER AAPL for 2012-06-21 (event file + 10-level orderbook CSVs). Features. Signed top-five depth imbalance, top-of-book spread in ticks, prior-one-second signed trade imbalance, and a 30-minute time-of-day bucket. Label. y = 1 if the mid one second ahead is strictly above the current mid; no-change ties are dropped and counted. Model. ℓ₂-regularized logistic regression (L-BFGS-B), median-imputed, compared against constant base-rate and imbalance-only baselines. Split. Chronological 70/30 (train ≤ 12:28:15, test ≥ 12:28:16). Cost overlay. Full spread crossing (α = 1) plus a 1 bp taker fee, swept over thresholds {0.50, 0.55, 0.60} and cost multipliers {0.5, 1.0, 1.5}×. The full pipeline is reproducible from raw CSVs via python -m src.cli run-all --source raw.

Data and reconstruction

The raw inputs join one event row to one orderbook row by position; the orderbook file is authoritative for top-five state after each event. The cadence sampler chooses the most recent event-time snapshot ≤ each grid timestamp via a backward as-of merge with a one-second staleness tolerance, so no feature value uses information from after t.

The session spans 09:30:00.004 – 15:59:59.913 (6.5 hours), 400,391 raw events, and 34,990 extracted trades — every one of which carries an explicit aggressor flag, so no tick-rule fallback was needed. After sampling at 100 ms, 114,426 valid one-second labels remain (49.4% positive, 50.6% negative); a further 95,902 no-change ties (45.6% of would-be labels) are dropped by construction. Quality status is pass: no crossed books, no non-monotonic levels, no non-positive sizes, and the test-slice spread distribution covers 36 distinct tick values with a 13-tick median and a 36-tick maximum.

Features and model

Three features enter the full model: top-five signed depth imbalance It(5) in [−1, +1], top-of-book spread in ticks, and a prior-one-second signed trade imbalance Tt(1s). Mid-price and spread are defined from the inside quotes, and the two imbalance features are constructed as size-normalized signed sums.

mt = 12 ( a1,t + b1,t ) ,   sprt = a1,t b1,t
(1)
It(5) = Σℓ=1..5 qbℓ,t Σℓ=1..5 qaℓ,tΣℓ=1..5 qbℓ,t + Σℓ=1..5 qaℓ,t [ 1 , +1 ]
(2)
Tt(1s) = Σi : t−1 ≤ τit si · viΣi : t−1 ≤ τit | vi | ,   yt = 𝟙{ mt+1s mt > 0 }
(3)

Standardized coefficients

The trade-sign feature carries the largest standardized weight, and it points the opposite direction from a naive momentum reading — recent aggressor buying is followed, on average, by a small reversal of the mid one second later, consistent with temporary price impact and partial liquidity replenishment. Depth imbalance contributes roughly half the magnitude with the expected positive sign (heavier bid depth predicts up), and spread enters with a small positive coefficient that says widest-spread snapshots weakly precede up moves.

Under feature ablation, depth imbalance alone clears chance by less than two percentage points; adding spread and trade-sign imbalance lifts AUC by another five points. The trade-sign coefficient is therefore not redundant with depth — it is doing real, decoupled work.

FeatureStd. coef.Reading
trade_sign_imbalance_1s -0.154 Largest magnitude; negative ⇒ short-horizon mean reversion.
imbalance_top5 +0.077 Half the magnitude of trade-sign; heavier bid depth predicts up.
spread_ticks +0.042 Small positive contribution; widest spreads weakly precede up moves.
intercept -0.005 Effectively zero — training class share is 49.4% positive.
Table 1.
Held-out AUC by model (100 ms) 0.450.480.510.540.570.60 AUC = 0.50 0.500 0.513 0.567 constantimbalance_onlyfull AUC
Figure 2. Held-out AUC by model (100 ms). Depth imbalance alone clears chance by less than two percentage points. Adding spread and trade-sign imbalance lifts AUC by another five points. The trade-sign feature does real, decoupled work — it is not redundant with depth imbalance.

Discrimination

The ROC curve shows weak but real discrimination across the entire operating range, not a sharp regime. Calibration is close to the diagonal — predicted probabilities cluster tightly between 0.42 and 0.57, which is the expected shape for a small-AUC classifier on a balanced-class problem. The reliability curve at the top end (n ≈ 5,722 in the highest predicted-probability bin) hits a 62.5% realized positive rate against a 56.7% predicted rate, which is the most useful corner of the score distribution.

ROC curve at 100 ms on the held-out test slice 0.0 0.2 0.4 0.6 0.8 1.0 0.00.20.40.60.81.0 True positive rate False positive rate chance (AUC = 0.50) imbalance only (AUC = 0.513) full model (AUC = 0.567)
Figure 3. ROC curve at 100 ms on the held-out test slice. Full model (teal) sits modestly above the imbalance-only baseline (amber), which itself sits just above chance. Shape is consistent with AUC ≈ 0.57 — weak but real discrimination across the full operating range.
Reliability curve at 100 ms (10 quantile bins, ~5.7k obs per bin) 0.35 0.40 0.45 0.50 0.55 0.60 0.65 0.350.400.450.500.550.600.65 Observed positive rate Predicted probability (bin mean)
Figure 4. Reliability curve at 100 ms (10 quantile bins, ~5.7k obs per bin). Predicted probabilities cluster tightly around the base rate (range 0.42–0.57); the model rarely makes confident predictions away from 0.5, which is the expected shape for a small-AUC classifier. The top bin (n = 5,722) reaches a 62.5% realized positive rate at a 56.7% predicted rate.
Predicted-probability histogram at 100 ms (stacked by realized label) 0 4,000 8,000 12,000 16,000 0.350.400.450.500.550.60 Test observations Predicted probability of up move Realized up Realized down
Figure 5. Predicted-probability histogram at 100 ms (stacked by realized label). Both classes are present at every probability level; the model separates them in expectation but not categorically. The mass concentrates between 0.40 and 0.58, which is the visual signature of a small-AUC signal in a balanced-class problem.

Stratified discrimination

On this single-day sample the predictive discrimination is mildly heterogeneous: AUC varies from 0.508 (12:00, just inside the test window) to 0.597 (15:30, the close half-hour), and is U-shaped in spread regime with Q3 (mid spreads, 11–14 ticks) dominating at 0.580. The widest-spread quintile Q5 is unremarkable, so the stratified result is more nuanced than a simple "wider spreads imply stronger predictability" story.

AUC by 30-minute time-of-day bucket (held-out only, 100 ms) 0.460.500.540.580.62 AUC = 0.50 (no-skill) n=576 n=6,797 n=6,546 n=7,182 n=7,759 n=7,847 n=8,680 n=11,817 12:0012:3013:0013:3014:0014:3015:0015:30 AUC Time-of-day bucket (held-out)
Figure 6. AUC by 30-minute time-of-day bucket (held-out only, 100 ms). Test buckets only — the chronological split sends the morning into training. AUC rises into the close: 0.508 at 12:00, 0.597 at 15:30. The 11.8k-observation 15:30 bucket carries the strongest evidence.
AUC by spread quintile at 100 ms (test slice, per-venue boundaries) 0.460.500.540.580.62 AUC = 0.50 0.538 0.567 0.580 0.574 0.568 Q1[1–8 ticks]Q2[8–11 ticks]Q3[11–14 ticks]Q4[14–18 ticks]Q5[18–36 ticks] AUC Spread quintile
Figure 7. AUC by spread quintile at 100 ms (test slice, per-venue boundaries). AUC peaks in Q3 (11–14 ticks) at 0.580 and is lowest in Q1 (tight spreads, 1–8 ticks) at 0.538. The widest-spread quintile Q5 sits in the middle at 0.568 — predictability does not concentrate in the loosest book.
Spread distribution in the test slice (log y-axis, 57,213 obs) 1 10 100 10^3 10^4 05101520253035 Count (log scale) Spread (ticks; 1 tick = $0.01)
Figure 8. Spread distribution in the test slice (log y-axis, 57,213 obs). 36 distinct tick values populate a heavy right tail; the per-venue quintile boundaries (dotted) cleanly partition the distribution. The stratified analysis therefore rests on a genuinely varied spread environment rather than a few discrete values.

Cadence robustness

The signal is essentially scale-invariant on this day: AUC moves by less than 0.003 across 50, 100, and 250 ms snapshots and Brier by less than 0.0003. At 50 ms (114,232 obs) the model reports AUC 0.568 and Brier 0.2466; at the 100 ms primary cadence (57,213 obs) it reports AUC 0.567 and Brier 0.2467; at 250 ms (22,971 obs) it reports AUC 0.570 and Brier 0.2464.

This is a real robustness check rather than a sub-second artifact — at 50 ms many snapshots reuse the same event-book state because LOBSTER updates do not occur every 50 ms (the sampler back-fills), while at 250 ms each snapshot reflects more information accumulated since the previous sample. Either way, the result weakens the case for sub-100 ms snapshotting in production for this signal.

Cost-after-execution

The decision rule converts predicted probabilities into trade actions dt ∈ {−1, 0, +1} via a symmetric threshold p★. Per-action taker cost is the full spread crossed at α = 1 plus a 1 bp taker fee on notional, scaled by a stress multiplier κ ∈ {0.5, 1.0, 1.5}×. The net result rt is the realized directional return on executed snapshots minus that cost.

At the primary 0.55 / 1.0× cell, the gross edge is roughly $0.003 per action while the average per-action transaction cost is $0.177 — decomposed into ~$0.129 of spread (the dominant term, mean 12.9 ticks) and ~$0.058 of fee (1 bp on a $582 mean mid). Spread dominates fees by roughly 2.2×. To clear cost as a pure taker, the model would need a ~60× improvement in per-action edge; alternatively, a maker-rebate execution model that recovers most of the spread would change the conclusion without changing the prediction itself.

dt = +1 if p̂t ≥ p★,   1 if p̂t ≤ 1 p★0 otherwise
(4)
ct = sprt · α + f · mt ,   rt = dt · Δhmt ct · 𝟙{ dt ≠ 0 }
(5)
Gross vs. net EV per action by spread quintile (threshold 0.55, 1.0× costs) -0.30 -0.25 -0.20 -0.15 -0.10 -0.05 0.00 -0.104 -0.141 -0.169 -0.200 -0.259 Q1[$0.01–0.08]Q2[$0.08–0.11]Q3[$0.11–0.14]Q4[$0.14–0.18]Q5[$0.18–0.34] Dollars per action Spread quintile and dollar range Gross EV / action Net EV / action
Figure 9. Gross vs. net EV per action by spread quintile (threshold 0.55, 1.0× costs). Gross EV (teal) is positive but tiny and roughly flat across quintiles. Net EV (orange) is uniformly negative and scales linearly with spread: -$0.104/action in Q1 widening to -$0.259/action in Q5.
Thr. Mult. Actions No-trade Net EV / obs Net EV / action
0.50 0.5× 57,213 0% -$0.0895 -$0.0895
0.50 1.0× 57,213 0% -$0.1831 -$0.1831
0.50 1.5× 57,213 0% -$0.2768 -$0.2768
0.55 0.5× 16,584 71% -$0.0227 -$0.0782
0.55 1.0× 16,584 71% -$0.0483 -$0.1668
0.55 1.5× 16,584 71% -$0.0740 -$0.2553
0.60 0.5× 341 99% -$0.0003 -$0.0504
0.60 1.0× 341 99% -$0.0007 -$0.1188
0.60 1.5× 341 99% -$0.0011 -$0.1872
Table 2.

Discussion and limitations

The full specification picks up genuine one-second-ahead directional information — AUC clears chance, calibration tracks the diagonal across the [0.42, 0.57] predicted-probability range, and the coefficient ranking is robust under feature ablation. It does not survive a taker execution model: every cell of the cost cube is negative, and the widest-spread-quintile hypothesis is not borne out, with Q3 (mid spreads) dominating instead.

The result is also scoped tightly. It speaks to a single instrument (AAPL) on a single day (2012-06-21), so no cross-day or cross-sectional variance is measured. The 45.6% tie share means the 1-second forward mid often does not change at all in 2012 AAPL, and ties are dropped by construction of the binary label. The cost overlay is taker-only — there is no maker, partial-fill, or queue-rebate model, so the negative number is specifically a taker result. Depth aggregates rather than own-order rank, which means no queue-position feature enters the panel. And the trade-sign feature leans on LOBSTER's explicit aggressor flag (100% coverage on this sample); on venues where that flag is missing, a tick-rule fallback would introduce additional noise into the dominant feature.

Related research