Skip to content

About

Order Flow Imbalance from Cont-Kukanov-Stoikov 2014 - OFI vs TFI head-to-head shows R^2 3261x advantage. Companion to as-market-maker + almgren-chriss

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ofi-signal

Python implementation of Order Flow Imbalance from Cont, Kukanov & Stoikov (2014), the most-cited empirical microstructure paper of the 2010s. Includes a synthetic LOB simulator with embedded latent alpha, the OFI vs TFI head-to-head regression, and a bucket-size sensitivity sweep that shows OFI dominates trade-flow across all sub-second timeframes.

Cont, R., Kukanov, A. & Stoikov, S. (2014). The price impact of order book events. Journal of Financial Econometrics 12(1): 47-88.

$ pytest tests/                          ->  13 passed in 0.49s

$ python examples/run_regression.py

  delta_mid = +0.0020 + +0.00005 * OFI      R^2 = 0.974   t(beta) = +86.13
  delta_mid = +0.0092 + -0.00001 * TFI      R^2 = 0.000   t(beta) =  -0.24

  R^2 ratio (OFI / TFI) = 3261x

Companion repo to as-market-maker and almgren-chriss: the trio covers theoretical optimal control (passive + aggressive) and the empirical signal that drives both.


Why OFI?

In market microstructure, the question for decades was: what moves the mid? The pre-CKS answer was trade flow (Hasbrouck 1991, Kyle 1985): buys lift, sells press down, net signed volume regresses well on mid-changes.

CKS 2014 made a surprising claim: trades are downstream of order book events. The real signal lives in how the resting book changes - deeper bids appearing, asks retreating, levels improving. They define Order Flow Imbalance to capture this, then show empirically that on NASDAQ:

  • OFI explains 60-75% of contemporaneous mid-price variance
  • TFI (trade flow) explains 5-15%
  • The OFI advantage persists from millisecond to minute scales

This repo provides a clean Python implementation of the OFI definition, plus a controlled synthetic LOB where you can vary parameters to understand what drives the gap.


The OFI formula (CKS 2014 eq. 2)

For each pair of consecutive top-of-book snapshots (prev, curr), the OFI increment e_n = e_n^bid + e_n^ask decomposes as:

            +bid_qty_n            if bid_px_n > bid_px_{n-1}    (better bid)
e_n^bid =   +Δbid_qty             if bid_px_n = bid_px_{n-1}    (size update)
            -bid_qty_{n-1}        if bid_px_n < bid_px_{n-1}    (retreat)

            -ask_qty_n            if ask_px_n < ask_px_{n-1}    (better ask)
e_n^ask =   -Δask_qty             if ask_px_n = ask_px_{n-1}    (size update)
            +ask_qty_{n-1}        if ask_px_n > ask_px_{n-1}    (retreat)

Sign convention: positive OFI = buying pressure. A better bid, a thicker bid, or a retreating ask all add positively. The mirrored condition on asks holds for selling pressure.

Per-bucket OFI is just the sum of increments over the events that fall inside the bucket. Cumulative OFI predicts cumulative mid change in that bucket - linear regression recovers the coefficient.


The synthetic LOB

Real CKS data is paywalled NASDAQ ITCH. We build a deterministic in-process generator (ofi/simulator.py) that mirrors the empirical structure:

  1. A latent alpha_t follows an AR(1): alpha_{t+1} = phi * alpha_t + eps_t
  2. alpha_t > 0 raises the probability of:
    • improving the bid, thickening the bid, retreating the ask
    • buy-initiated market orders
  3. Each LOB action emits a fresh snapshot; each market order also.
  4. Trades fill the top of book; if size exceeds resting qty the price level moves (impact).

This means OFI and TFI are both partially driven by alpha, but the LOB-event side picks up the signal earlier in the chain. The OFI regression therefore wins, mirroring the paper.

(Caveat: synthetic R² are unrealistically high - real data has ~60-75% for OFI. The qualitative ranking OFI >> TFI is what's faithful.)


Results

Head-to-head (200 buckets over 20k LOB events)

Regressor Slope beta R^2 t(beta)
OFI +5.0e-5 0.97 +86.1
TFI -1.0e-5 0.0003 -0.24

The OFI regression slope is 86 standard errors from zero; the TFI regression is statistically zero on this realization. R² ratio 3261×.

Bucket sensitivity (25 ms to 2000 ms)

buckets   bucket size   R^2(OFI)   R^2(TFI)
   20       2000.0 ms     0.987      0.174
   50        800.0 ms     0.979      0.058
  100        400.0 ms     0.973      0.007
  200        200.0 ms     0.977      0.016
  400        100.0 ms     0.974      0.011
  800         50.0 ms     0.970      0.017
 1600         25.0 ms     0.961      0.009

OFI's R² is essentially flat across two decades of timescale; TFI is noise at sub-second scales and only becomes mildly predictive at the two-second bucket. The paper finds the same qualitative pattern on NASDAQ.


Tests (13/13 passing)

pytest tests/ covers:

  • Six branches of the OFI increment formula (CKS eq. 2):
    • bid: improves / same-price size delta / retreats (3 branches × pos/neg)
    • ask: with mirrored sign convention
  • Two-sided event: both sides change simultaneously - increments add
  • Aggregation: events bucket into correct intervals; mid changes use forward-filled last-of-bucket prices to handle empty intervals
  • TFI: signed trade quantities aggregate correctly
  • OLS: perfect-fit recovery, zero-slope noise case

Run it

pip install -r requirements.txt
pytest tests/                              # 13 passed
python examples/run_regression.py          # the OFI vs TFI head-to-head
python examples/bucket_sensitivity.py      # R^2 across timescales

Layout

ofi/
  events.py        - BookSnapshot, Trade dataclasses
  compute.py       - e_increment, compute_ofi/tfi, aggregate_buckets
  regression.py    - lightweight OLS with R^2 + t-statistic
  simulator.py     - synthetic LOB with embedded alpha
tests/
  test_ofi.py      - 13 unit tests (formula + aggregation + regression)
examples/
  run_regression.py       - head-to-head plot
  bucket_sensitivity.py   - R^2 vs bucket size curve

Roadmap

  • v0.2.0 - L2 OFI. CKS show in their 2014 paper that adding deeper book levels (L2: top-3 or top-5) materially raises R² on real data. Extend BookSnapshot to carry depth, generalize the increment formula to weighted levels.
  • v0.2.0 alt - Real data ingestion. ITCH parser + Nasdaq sample data; demonstrate on real (not synthetic) prices.
  • v0.3.0 - Cross-impact. Cont-Kukanov 2017 multi-asset OFI: level-1 events in correlated stocks contain information about each other.
  • v0.3.0 alt - Permanent vs transient. Hasbrouck-style decomposition of mid-price impact into permanent (information) and transient (liquidity) components.

References

  • Cont, R., Kukanov, A. & Stoikov, S. (2014). The price impact of order book events. Journal of Financial Econometrics 12(1): 47-88.
  • Cont, R. & Kukanov, A. (2017). Optimal order placement in limit order markets. Quantitative Finance 17(1).
  • Hasbrouck, J. (1991). Measuring the information content of stock trades. Journal of Finance 46(1): 179-207. (the trade-flow benchmark that OFI dethrones)
  • Eisler, Z., Bouchaud, J.-P. & Kockelkoren, J. (2012). The price impact of order book events: market orders, limit orders, and cancellations. Quantitative Finance 12(9): 1395-1419. (independent derivation of the same idea)

About

Order Flow Imbalance from Cont-Kukanov-Stoikov 2014 - OFI vs TFI head-to-head shows R^2 3261x advantage. Companion to as-market-maker + almgren-chriss

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages