Order Flow Imbalance: The Cont-Kukanov-Stoikov Event Decomposition
Kebanyakan isyarat yang diperoleh daripada book di blog ini ialah snapshot: berapa banyak volume yang sedang menunggu di setiap sisi sekarang. Ketidakseimbangan aliran pesanan ialah objek yang berbeza. Ia menguraikan strim kemas kini antara dua snapshot menjadi satu kuantiti bertanda — pembatalan pada bid dikira dengan cara yang sama seperti market sell.
Penguraian itu ialah subjek artikel ini. Secara khusus: event indicator Cont-Kukanov-Stoikov (2014) yang mentakrifkannya, lanjutan multi-level Xu-Gould-Howison (2019) dan pengurangan principal-componentnya, serta peraturan Lee-Ready dan Bulk Volume Classification yang diperlukan apabila feed anda tidak melabel arah trade.
Satu nota framing di awal, kerana ia menentukan cara anda membaca setiap nombor di bawah. R-squared 50-65% yang terkenal daripada paper CKS ialah regresi contemporaneous: perubahan harga dalam sesuatu interval diregresikan terhadap order flow dalam interval yang sama. Ia ialah penguraian pergerakan harga, bukan ramalan. Membandingkannya dengan 1-2% varians yang diterangkan oleh model pulangan ekuiti harian ialah perbandingan epal dengan oren, dan blog ini telah menghujahkan panjang lebar — dalam DeepLOB dan jurang prediction-vs-profit serta keputusan negatif yang jujur — bahawa mencampurkan kedua-duanya menjadikan tutorial sebagai perangkap. Regresi OFI yang benar-benar lagged ialah nombor yang sah dan jauh lebih kecil. Artikel ini tidak melaporkannya.
Mengapa Order Flow, Bukan Trade

Perubahan harga dalam limit order book berterusan berlaku melalui tepat empat mekanisme: market buy menggunakan ask yang sedang menunggu; market sell menggunakan bid yang sedang menunggu; pembatalan pada bid membuang sokongan dan membolehkan bid jatuh; limit order baharu pada ask menambah rintangan. Hanya dua yang pertama ialah trade. Dua yang terakhir tidak kelihatan pada mana-mana ukuran berasaskan volume — VWAP, on-balance volume, signed trade flow — dan di kebanyakan venue, nisbah order-to-trade melebihi 10:1, jadi bahagian tidak kelihatan itu sebenarnya bahagian terbesar. Seluruh dakwaan kegunaan OFI ialah ia menilai keempat-empat mekanisme pada skala yang sama. (Untuk book sebagai struktur data dan vector feature snapshot standard , lihat DeepLOB.)
Model OFI Cont-Kukanov-Stoikov

Model asas datang daripada paper Cont, Kukanov dan Stoikov pada 2014, "The Price Impact of Order Book Events" (Journal of Financial Econometrics).
Mentakrifkan Order Flow Imbalance
Pertimbangkan harga bid terbaik , harga ask terbaik , serta saiznya dan . Antara pemerhatian berturutan pada dan :
di mana sumbangan peristiwa buy-side dan sell-side ialah:
Dalam bahasa mudah: jika harga bid terbaik meningkat, minat beli baharu muncul — kira keseluruhan saiznya sebagai positif. Jika ia jatuh, minat beli hilang — tolak saiz lama. Jika harga tidak berubah, kira perubahan saiz sahaja. Sisi ask ialah imej cerminnya.
Keanggunannya ialah tiga cabang indicator ini meliputi setiap peralihan yang mungkin pada top of book, maka setiap update dipetakan kepada tepat satu nombor bertanda. Tiada trade classification, tiada side label, tiada feed peringkat message diperlukan — dua snapshot berturutan sudah mencukupi.
Aggregation Merentasi Interval
Untuk interval yang mengandungi kemas kini book:
Model Linear Price Impact
di mana ialah perubahan mid-price, ialah price impact coefficient, dan ialah residual noise. Pada ekuiti AS dalam interval 10 saat hingga 1 minit, CKS melaporkan contemporaneous sebanyak 50-65% bagi setiap saham. Sekali lagi: interval yang sama, bukan forward-looking.
Cross-Sectional Scaling
CKS turut menunjukkan bahawa impact coefficient berskala songsang dengan depth:
di mana ialah purata resting volume pada bid dan ask terbaik. Flow yang sama menolak harga lebih jauh dalam book yang nipis. Ini satu-satunya bahagian model yang boleh dipindahkan merentasi venue tanpa refit level, kerana ia meramalkan hubungan dan bukannya nombor — serta boleh diuji terus pada pasangan crypto dengan depth berbeza.
Multi-Level Order Flow Imbalance (MLOFI)

Model asal hanya menggunakan top of book. Xu, Gould dan Howison (2019) mengembangkannya kepada level.
Definisi
di mana menggunakan formula tiga cabang yang sama pada pasangan bid/ask ke-.
Multi-Level Price Impact
Penemuan yang diterbitkan pada ekuiti Nasdaq ialah setiap level tambahan menambah out-of-sample — kira-kira 10-15 mata peratusan apabila bergerak dari satu level ke lima, dan masih ada keuntungan marginal pada sepuluh. Coefficient merosot secara monoton, : top of book mendominasi, tetapi level lebih dalam membawa signal tambahan yang tidak remeh.
Sama ada ini kekal pada crypto book ialah satu-satunya soalan terbuka paling menarik dalam artikel ini. Crypto book lebih nipis dan jauh lebih banyak churn pada depth, dan sangat munasabah jika level 2-5 tidak menambah apa-apa selepas meninggalkan order book Nasdaq.
Principal Component Reduction
OFI pada level bersebelahan sangat berkorelasi (melebihi 0.8 dalam hasil ekuiti yang diterbitkan), jadi principal component decomposition adalah semula jadi. Komponen pertama yang dilaporkan menangkap lebih 89% jumlah varians dan berfungsi sebagai satu aggregate signal:
dengan ialah leading eigenvector bagi covariance matrix OFI. Daya tarikan praktikalnya ialah ia meruntuhkan regresi collinear kepada satu scalar yang well-conditioned — tetap wajar dilakukan walaupun bahagian varians pada data anda lebih rendah.
Mengklasifikasikan Trade: Buy berbanding Sell Initiated

OFI sendiri tidak memerlukan trade classification. Tetapi jika anda mahu membandingkannya dengan ukuran berasaskan trade, atau hanya mempunyai aggregated bar, anda perlu membuat inferens arah.
Quote Rule
Compare the trade price to the prevailing midpoint :
Trade di atas midpoint berkemungkinan ialah buyer yang mengangkat offer; di bawahnya ialah seller yang mengena bid.
Lee-Ready algorithm (1991) ialah quote rule dengan tick-rule fallback bagi trade tepat pada midpoint, apabila quote rule tidak dapat ditentukan. Tick rule — tanda perubahan harga terakhir, dengan side sebelumnya dibawa ke hadapan pada zero tick — telah diterbitkan dan dilaksanakan dalam Beyond time bars, yang menyediakan _tick_sign() berfungsi. Ketepatan klasifikasi Lee-Ready yang dilaporkan ialah 72-85% bergantung pada pasaran dan tempoh.
Bulk Volume Classification (BVC)
Apabila trade individu tidak boleh ditetapkan — aggregated bar dan kebanyakan public candle API — Bulk Volume Classification oleh Easley, Lopez de Prado dan O'Hara menganggar pecahan buy bagi volume bar daripada perubahan harga yang dinormalkan:
dengan ialah standard normal CDF dan dianggar daripada perubahan harga terkini. Kurang tepat berbanding klasifikasi tick-level, tetapi ia berfungsi pada OHLCV.
Apa yang Bukan OFI

Tiga kuantiti bersebelahan sering dikelirukan dengan OFI. Setiap satunya dibincangkan dengan betul di tempat lain dalam blog ini; perbezaannya yang penting di sini.
Static order book imbalance (OBI) ialah versi snapshot — resting bid volume berbanding resting ask volume, tanpa event tracking. Lihat traditional LOB features DeepLOB untuk formula dan bentuk multi-levelnya. Kontras inilah keseluruhan tujuan OFI: OBI memberitahu keadaan book, OFI memberitahu bagaimana book sampai ke keadaan itu.
Trade imbalance (TI) dan varian volume-weightednya ialah signed trade flow, ditakrifkan dan dilaksanakan sebagai rolling feature dalam spread modeling dengan machine learning. Satu hasil yang diterbitkan tentang hubungan ini penting di sini: apabila OFI dan TI dimasukkan bersama sebagai regressor bagi perubahan mid-price, TI menjadi tidak signifikan secara statistik. Kandungannya telah diserap. Itulah kes empirikal untuk menjejak peristiwa book dan bukannya trade — peristiwa yang hampir menjadi trade membawa maklumat yang sama dengan peristiwa yang benar-benar menjadi trade.
Volume-weighted mid ialah estimator fair-value, bukan ukuran imbalance, dan dibincangkan dalam DeepLOB — termasuk caveat penamaan bahawa ia bukan micro-price Stoikov, yang merupakan estimator terlaras martingale, dibina tepat kerana weighted mid naif adalah bias.
Implementasi Python

Implementasi langsung penguraian CKS, digeneralisasikan kepada level.
Pengiraan OFI Teras
import numpy as np
import pandas as pd
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class OrderBookSnapshot:
timestamp: float
bid_prices: np.ndarray # best bid at index 0, descending
ask_prices: np.ndarray # best ask at index 0, ascending
bid_sizes: np.ndarray
ask_sizes: np.ndarray
@dataclass
class OFICalculator:
"""
Computes Order Flow Imbalance from consecutive order book snapshots.
Supports multi-level OFI (MLOFI) up to `n_levels` deep.
"""
n_levels: int = 5
prev_snapshot: Optional[OrderBookSnapshot] = field(default=None, init=False)
def compute_level_ofi(
self,
prev_price: float, curr_price: float,
prev_size: float, curr_size: float,
side: str
) -> float:
"""Compute single-level OFI contribution for bid or ask side."""
if side == "bid":
if curr_price > prev_price:
return curr_size # new level appeared above
elif curr_price < prev_price:
return -prev_size # old level disappeared
else:
return curr_size - prev_size # same level, size changed
else: # ask side
if curr_price < prev_price:
return curr_size # new level appeared below
elif curr_price > prev_price:
return -prev_size # old level disappeared
else:
return curr_size - prev_size # same level, size changed
def update(self, snapshot: OrderBookSnapshot) -> Optional[np.ndarray]:
"""
Process new snapshot, return MLOFI vector of shape (n_levels,).
Returns None on first call (no previous snapshot to compare).
"""
if self.prev_snapshot is None:
self.prev_snapshot = snapshot
return None
prev = self.prev_snapshot
n = min(self.n_levels, len(snapshot.bid_prices), len(prev.bid_prices))
ofi = np.zeros(n)
for level in range(n):
e_buy = self.compute_level_ofi(
prev.bid_prices[level], snapshot.bid_prices[level],
prev.bid_sizes[level], snapshot.bid_sizes[level],
side="bid"
)
e_sell = self.compute_level_ofi(
prev.ask_prices[level], snapshot.ask_prices[level],
prev.ask_sizes[level], snapshot.ask_sizes[level],
side="ask"
)
ofi[level] = e_buy - e_sell
self.prev_snapshot = snapshot
return ofi
Aggregation ke dalam Window
@dataclass
class OFIAggregator:
"""
Aggregates raw OFI updates into fixed time windows.
Emits the regression inputs (aggregated MLOFI) and target (delta mid).
"""
window_seconds: float = 10.0
n_levels: int = 5
calculator: OFICalculator = field(init=False)
buffer: list = field(default_factory=list, init=False)
window_start: float = 0.0
def __post_init__(self):
self.calculator = OFICalculator(n_levels=self.n_levels)
def on_snapshot(self, snapshot: OrderBookSnapshot) -> Optional[dict]:
"""
Feed a new order book snapshot.
Returns aggregated window dict when a window completes, else None.
"""
ofi_vec = self.calculator.update(snapshot)
if ofi_vec is None:
self.window_start = snapshot.timestamp
return None
if not self.buffer:
self.window_start = snapshot.timestamp
self.buffer.append({
"timestamp": snapshot.timestamp,
"ofi": ofi_vec.copy(),
"mid": (snapshot.bid_prices[0] + snapshot.ask_prices[0]) / 2,
})
elapsed = snapshot.timestamp - self.window_start
if elapsed >= self.window_seconds:
return self._flush()
return None
def _flush(self) -> dict:
"""Aggregate buffered OFI updates into a single window record."""
ofi_matrix = np.array([b["ofi"] for b in self.buffer])
agg_ofi = ofi_matrix.sum(axis=0) # shape: (n_levels,)
result = {
"window_start": self.window_start,
"window_end": self.buffer[-1]["timestamp"],
"n_updates": len(self.buffer),
"mid_open": self.buffer[0]["mid"],
"mid_close": self.buffer[-1]["mid"],
"delta_mid": self.buffer[-1]["mid"] - self.buffer[0]["mid"],
"ofi_level1": agg_ofi[0],
"ofi_total": agg_ofi.sum(),
"mlofi": agg_ofi,
}
self.buffer.clear()
return result
Perhatikan output aggregator: mlofi dan delta_mid sepanjang window yang sama. Itulah regresi contemporaneous. Untuk mendapatkan regresi predictive, pasangkan mlofi window dengan delta_mid window dan jangkakan fit yang jauh lebih buruk.
Trade Classification (Lee-Ready)
def classify_trades_lee_ready(
trades: pd.DataFrame,
quotes: pd.DataFrame
) -> pd.DataFrame:
"""
Classify trades as buy (+1) or sell (-1) using Lee-Ready:
quote rule first, tick rule as fallback at the midpoint.
Parameters
----------
trades : DataFrame with columns ['timestamp', 'price', 'size']
quotes : DataFrame with columns ['timestamp', 'bid', 'ask']
Returns
-------
trades with added 'side' column
"""
trades = trades.sort_values("timestamp").copy()
quotes = quotes.sort_values("timestamp")
trades = pd.merge_asof(
trades, quotes,
on="timestamp",
direction="backward"
)
trades["mid"] = (trades["bid"] + trades["ask"]) / 2
trades["side"] = np.where(
trades["price"] > trades["mid"], 1,
np.where(trades["price"] < trades["mid"], -1, 0)
)
trades["price_diff"] = trades["price"].diff()
tick_sign = np.sign(trades["price_diff"])
tick_sign = tick_sign.replace(0, np.nan).ffill().fillna(1)
midpoint_mask = trades["side"] == 0
trades.loc[midpoint_mask, "side"] = tick_sign[midpoint_mask].astype(int)
return trades
Menormalkan Signal
class OFISignal:
"""
Rolling z-score normalization of aggregated OFI.
Deliberately does NOT convert OFI into a predicted return: that
requires a fitted beta, and beta is venue-, pair- and regime-specific.
Fit it on your own data before wiring this into anything.
"""
def __init__(self, window_seconds: float = 10.0, n_levels: int = 5,
lookback: int = 100):
self.aggregator = OFIAggregator(
window_seconds=window_seconds, n_levels=n_levels,
)
self.lookback = lookback
self.ofi_history: list[float] = []
def process(self, snapshot: OrderBookSnapshot) -> Optional[dict]:
agg = self.aggregator.on_snapshot(snapshot)
if agg is None:
return None
ofi = agg["ofi_level1"]
self.ofi_history.append(ofi)
if len(self.ofi_history) > self.lookback:
self.ofi_history.pop(0)
if len(self.ofi_history) >= 20:
arr = np.array(self.ofi_history)
mu, sigma = arr.mean(), arr.std()
zscore = (ofi - mu) / max(sigma, 1e-10)
else:
zscore = 0.0
return {**agg, "ofi_zscore": zscore}
Di Mana OFI Disambungkan

Market making. OFI masuk sebagai predictive skew term pada fair value, di atas inventory skew yang telah diterbitkan dan dikodkan dalam Avellaneda-Stoikov market maker: . Dua term menjawab soalan berbeza — flow term menyatakan ke mana harga pergi, inventory term menyatakan apa yang mampu anda pegang — dan artikel yang diterbitkan meliputi term kedua, termasuk sebab inventory positif menolak kedua-dua quote ke bawah. yang besar juga merupakan trigger adverse-selection yang munasabah bagi tindak balas pertahanan (widen, shrink, pull satu sisi) yang disenaraikan dalam digital fingerprints of trader identification dan anomaly detection.
Execution. OFI ialah input kepada fill-probability estimate tactics layer, bukan urgency controller berasingan. Keputusan post-versus-cross ialah matematik break-even yang jelas — dengan — dan berada dalam child order execution tactics, yang turut berhujah bahawa tactics layer tidak boleh mengira semula urgency daripada pandangan sendiri terhadap pasaran, kerana itu menghasilkan dua controller yang tidak sependapat.
Pertimbangan Praktikal

Separuh hayat signal dan rolling recalibration. Ini ialah bahagian khusus OFI. Kandungan predictive OFI merosot pada skala masa milisaat hingga saat bagi instrument cair, bermakna aggregation window bukan parameter percuma — ia pertaruhan pada horizon. Dan bukan pemalar: ia berubah sepanjang hari, mengikut depth dan sekitar peristiwa berjadual, maka mana-mana production fit perlu menganggar semula pada rolling window dan bukannya menetapkan nilai daripada backtest.
Selebihnya telah diliputi di tempat lain. Bajet latency dan tangga co-location / FPGA / kernel-bypass: bahagian production DeepLOB. Corak liquidity intraday berbentuk U dan statistik normalization yang hanyut: spread modeling. Recalibration per venue dan sebab model yang difit pada Nasdaq tidak boleh dipindahkan terus ke pasangan crypto: sekali lagi DeepLOB, dengan sisi fragmentation dalam smart order routing.
Manipulasi. Satu kesan khusus model ini wajar dinyatakan dengan jelas: penguraian CKS menimbang setiap peristiwa berdasarkan size sahaja, tanpa konsep intent atau persistence. Maka spoofer yang meletakkan dan membatalkan size memasukkannya terus ke dalam signal dengan full weight, secara binaan. Heuristik pengesanan — cancel rate, jangka hayat order, tingkah laku wall apabila harga menghampiri — ada dalam analisis queue position dan wall order book; liquidity phantom merentas venue ada dalam smart order routing.
Kesimpulan Utama

-
OFI ialah penguraian peristiwa, bukan snapshot. Formula indicator tiga cabang memetakan setiap peralihan top-of-book — harga naik, harga turun, perubahan size — kepada satu nombor bertanda daripada dua snapshot berturutan dan tiada apa-apa lagi.
-
R-squared utama adalah contemporaneous. Angka 50-65% CKS menguraikan pergerakan harga dalam interval yang sama; ia bukan forecast dan tidak wajar dibandingkan dengan model pulangan forward-looking.
-
Multi-level OFI menambah level dan PCA meruntuhkannya. Hasil incremental- dan 89%-variance yang diterbitkan datang daripada ekuiti Nasdaq. Sama ada depth masih membantu pada crypto book belum diuji di sini.
-
OFI merangkumi trade imbalance. Apabila dimasukkan bersama, TI menjadi tidak signifikan — pembatalan yang dilihat OFI tetapi tidak dapat dilihat TI itulah yang membuat perbezaan.
-
Model ini buta terhadap intent. Pemberian weight sama rata mengikut size tepat menjadikannya boleh dispoof.
-
Tiada apa-apa dalam artikel ini yang diukur. Setiap nombor yang dipetik datang daripada literatur ekuiti. Sebelum signal ini menyentuh modal, ia memerlukan fit pada data book anda sendiri, spesifikasi lagged yang dilaporkan secara berasingan, dan pemeriksaan sama ada edge bertahan selepas fee dan spread.
Bacaan Lanjutan

- Cont, R., Kukanov, A., & Stoikov, S. (2014). "The Price Impact of Order Book Events." Journal of Financial Econometrics, 12(1), 47-88.
- Xu, K., Gould, M., & Howison, S. (2019). "Multi-Level Order-Flow Imbalance in a Limit Order Book." arXiv:1907.06230.
- Kolm, P., Turiel, J., & Westray, N. (2023). "Deep Order Flow Imbalance: Extracting Alpha at Multiple Horizons from the Limit Order Book." Mathematical Finance, 33(4).
- Lee, C., & Ready, M. (1991). "Inferring Trade Direction from Intraday Data." Journal of Finance, 46(2), 733-746.
- Easley, D., Lopez de Prado, M., & O'Hara, M. (2012). "Flow Toxicity and Liquidity in a High-Frequency World." Review of Financial Studies, 25(5), 1457-1493.
Pengarang
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.