Order Flow Imbalance: The Cont-Kukanov-Stoikov Event Decomposition
La maggior parte dei segnali derivati dai libri su questo blog sono istantanee: quanto volume è appoggiato su ciascun lato in questo momento. Lo squilibrio del flusso degli ordini è un oggetto diverso. Si tratta di una scomposizione del flusso di aggiornamenti tra due istantanee in un'unica quantità firmata, che conta una cancellazione dell'offerta nello stesso modo in cui conta una vendita sul mercato.
Tale scomposizione è l'oggetto di questo articolo. Nello specifico: gli indicatori di evento Cont-Kukanov-Stoikov (2014) che lo definiscono, l'estensione multi-livello di Xu-Gould-Howison (2019) e la sua riduzione delle componenti principali, e le regole di classificazione Lee-Ready e Bulk Volume di cui hai bisogno quando il tuo feed non etichetta la direzione commerciale.
Una nota di inquadramento in anticipo, perché governa il modo in cui dovresti leggere ogni numero di seguito. Il famoso 50-65% R quadrato del documento CKS è una regressione contemporanea: la variazione di prezzo su un intervallo regredisce sul flusso degli ordini sullo stesso intervallo. Si tratta di una scomposizione dei movimenti dei prezzi, non di una loro previsione. Confrontandolo con l'1-2% della varianza che un modello di rendimento azionario giornaliero spiega è "mele contro arance", e questo blog ha sostenuto a lungo - in DeepLOB e il divario previsione-vs-profitto e in Il risultato negativo onesto - che fondendo i due è il modo in cui i tutorial diventano trappole. Una regressione AIF rigorosamente ritardata è un numero legittimo e molto più piccolo. Questo articolo non ne riporta uno.
Perché il flusso degli ordini, non le transazioni

Una variazione di prezzo in un book di ordini limite continuo avviene attraverso esattamente quattro meccanismi: un acquisto di mercato consuma domande rimanenti; una vendita di mercato consuma offerte rimanenti; un annullamento dell'offerta rimuove il supporto e lascia cadere l'offerta; un nuovo ordine limite sulla domanda aggiunge resistenza. Solo i primi due sono scambi. Gli ultimi due sono invisibili a qualsiasi misura basata sul volume – VWAP, volume in saldo, flusso commerciale firmato – e nella maggior parte delle sedi il rapporto ordini/negoziazioni supera 10:1, quindi la parte invisibile è la parte più grande. L'intera pretesa di utilità di OFI è che valuta tutti e quattro i meccanismi sulla stessa scala. (Per il libro come struttura dati e il vettore di funzionalità snapshot standard , vedere DeepLOB.)
Il modello OFI di Cont-Kukanov-Stoikov

Il modello fondamentale deriva dal documento di Cont, Kukanov e Stoikov del 2014 "The Price Impact of Order Book Events" (Journal of Financial Econometrics).
Definizione dello squilibrio del flusso degli ordini
Considera il miglior prezzo bid , il miglior prezzo ask e le loro dimensioni e . Tra osservazioni consecutive a e :
dove i contributi degli eventi buy-side e sell-side sono:
In parole povere: se il miglior prezzo di offerta aumenta, appare un nuovo interesse di acquisto: considera positiva la sua dimensione intera. Se diminuisce, l'interesse all'acquisto è scomparso: sottrai la vecchia dimensione. Se il prezzo rimane invariato conta solo il cambio taglia. Il lato della domanda è l'immagine speculare.
L'eleganza è che questi tre rami indicatori coprono ogni possibile transizione della parte superiore del libro, quindi ogni aggiornamento corrisponde esattamente a un numero firmato. Nessuna classificazione commerciale, nessuna etichetta laterale, nessun feed a livello di messaggio richiesto: sono sufficienti due istantanee consecutive.
Aggregazione su intervalli
Per un intervallo contenente gli aggiornamenti del libro :
Il modello dell'impatto lineare del prezzo
dove è la variazione del prezzo medio, è il coefficiente di impatto del prezzo e è il rumore residuo. Sulle azioni statunitensi con intervalli da 10 secondi a 1 minuto, CKS riporta un contemporaneo del 50-65% per azione. Ancora una volta: stesso intervallo, non lungimirante.
Ridimensionamento trasversale
CKS ha anche mostrato che il coefficiente di impatto scala inversamente alla profondità:
dove è il volume residuo medio al miglior bid e ask. Lo stesso flusso spinge ulteriormente il prezzo in un libro sottile. Questa è l'unica parte del modello che si trasferisce da una sede all'altra senza modificare il livello, perché prevede una relazione anziché un numero ed è direttamente verificabile su coppie crittografiche di diversa profondità.
Squilibrio del flusso degli ordini multilivello (MLOFI)

Il modello originale utilizza solo la parte superiore del libro. Xu, Gould e Howison (2019) lo hanno esteso ai livelli .
Definizione
dove applica la formula identica a tre rami alla -esima coppia bid/ask.
Impatto sui prezzi a più livelli
La scoperta pubblicata, sulle azioni del Nasdaq, è che ogni livello aggiuntivo aggiunge fuori campione: circa 10-15 punti percentuali che vanno da un livello a cinque, con guadagni marginali ancora presenti a dieci. I coefficienti decadono in modo monotono, : la parte superiore del libro domina, ma i livelli più profondi portano un segnale incrementale non banale.
Se questo sopravviva su un libro crittografico è la domanda aperta più interessante in questo articolo. I libri di criptovaluta sono più sottili e molto più agitati in profondità, ed è del tutto plausibile che i livelli 2-5 non aggiungano nulla una volta che si lascia un libro degli ordini del Nasdaq.
Riduzione della componente principale
Gli AIF di livello adiacente sono altamente correlati (oltre 0,8 nei risultati azionari pubblicati), quindi una scomposizione delle componenti principali è naturale. La prima componente riportata cattura oltre l'89% della varianza totale e funge da unico segnale aggregato:
con l'autovettore principale della matrice di covarianza OFI. L'aspetto pratico è che comprime una regressione collineare in uno scalare ben condizionato: vale la pena farlo anche se la quota di varianza sui dati risulta inferiore.
Classificazione delle operazioni: acquisto e vendita avviate

Lo stesso OFI non necessita di classificazione commerciale. Ma se vuoi confrontarlo con misure basate sul commercio, o se disponi solo di barre aggregate, devi dedurre la direzione.
Regola delle virgolette
Confronta il prezzo commerciale con il punto medio prevalente :
Uno scambio al di sopra del punto medio era probabilmente un acquirente che alzava l'offerta; sotto di esso, un venditore che ha centrato l'offerta.
L'algoritmo Lee-Ready (1991) è la regola della quotazione con un fallback della regola del tick per le operazioni esattamente nel punto medio, dove la regola della quotazione è indeterminata. La regola del tick, ovvero il segno dell'ultima variazione di prezzo, che riporta il lato precedente su un tick pari a zero, è già derivata e implementata in Beyond time bars, che fornisce un _tick_sign() funzionante. La precisione della classificazione Lee-Ready riportata è del 72-85% a seconda del mercato e del periodo.
Classificazione del volume di massa (BVC)
Quando le singole operazioni non possono essere assegnate (barre aggregate, la maggior parte delle API di candele pubbliche), la classificazione del volume di massa di Easley, Lopez de Prado e O'Hara stima la frazione di acquisto del volume di una barra dalla sua variazione di prezzo normalizzata:
con il CDF normale standard e stimato dalle recenti variazioni di prezzo. Meno accurata della classificazione a livello di tick, ma funziona su OHLCV.
Cosa non è OFI

Tre quantità vicine vengono confuse con OFI. Ciascuno è trattato adeguatamente altrove su questo blog; le distinzioni sono ciò che conta qui.
Squilibrio del registro ordini statico (OBI) è la versione istantanea: volume delle offerte rimanenti rispetto al volume delle domande rimanenti, nessun monitoraggio degli eventi. Consulta le funzionalità LOB tradizionali di DeepLOB per la formula e il relativo modulo multilivello. Il contrasto è il punto centrale di OFI: OBI ti dice lo stato del libro, OFI ti dice come è arrivato lì.
Lo squilibrio commerciale (TI) e le sue varianti ponderate per il volume sono flussi commerciali con segno, definiti e implementati come funzionalità a rotazione in modellazione degli spread con machine learning. Un risultato pubblicato sulla relazione è fondamentale in questo caso: quando OFI e TI vengono inseriti insieme come regressori sulle variazioni dei prezzi medi, TI diventa statisticamente insignificante. Il suo contenuto è sussunto. Questo è il caso empirico del monitoraggio degli eventi contabili invece che delle operazioni: gli eventi che quasi sono diventati operazioni contengono le stesse informazioni di quelli che lo sono stati.
Il valore medio ponderato per il volume è uno stimatore del valore equo, non una misura di squilibrio, ed è trattato in DeepLOB - inclusa l'avvertenza sulla denominazione che non è il micro-prezzo di Stoikov, che è uno stimatore aggiustato con martingala costruito proprio perché il valore medio ponderato ingenuo è distorto.
Implementazione Python

Un'implementazione diretta della scomposizione CKS, generalizzata a livelli .
Calcolo degli AIF fondamentali
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
Aggregazione in Windows
@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
Nota cosa emette l'aggregatore: mlofi e delta_mid nella stessa finestra. Questa è la regressione contemporanea. Per ottenere uno predittivo, accoppia mlofi di window con delta_mid di window e aspettati un adattamento sostanzialmente peggiore.
Classificazione commerciale (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
Normalizzazione del segnale
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}
Dove si collega OFI

Market making. OFI entra come termine di distorsione predittiva sul valore equo, in aggiunta alla distorsione dell'inventario già derivata e codificata nel market maker di Avellaneda-Stoikov: . I due termini rispondono a domande diverse: il termine di flusso dice dove sta andando il prezzo, il termine di inventario dice cosa puoi permetterti di detenere - e l'articolo pubblicato copre il secondo, compreso il motivo per cui l'inventario positivo spinge entrambe le quotazioni verso il basso. Un ampio è anche un ragionevole trigger di selezione avversa per le risposte difensive (allargare, restringere, tirare da un lato) catalogate in impronte digitali di identificazione del trader e rilevamento di anomalie.
Esecuzione. OFI è un input per la stima della probabilità di riempimento del livello tattico , non un controller di urgenza separato. La decisione post-contro-incrocio è un'esplicita aritmetica di pareggio - con - e vive in [tattiche di esecuzione degli ordini secondari] (/en/blog/post/child-order-execution-tactics), che sostiene anche che il livello tattico non dovrebbe mai ridisegnare l'urgenza dalla propria visione del mercato, perché ciò crea due controllori in disaccordo.
Considerazioni pratiche

Emivita del segnale e ricalibrazione continua. Questa è la parte specifica di OFI. Il contenuto predittivo dell’OFI decade su una scala temporale che va dai millisecondi ai secondi negli strumenti liquidi, il che significa che la finestra di aggregazione non è un parametro libero: è una scommessa all’orizzonte. E non è una costante: si muove intraday, con profondità e attorno a eventi pianificati, quindi qualsiasi adattamento di produzione lo rivaluta su finestre mobili anziché fissare un valore da un backtest.
Tutto il resto qui è trattato altrove. Budget di latenza e ladder di co-ubicazione/FPGA/bypass del kernel: sezione di produzione di DeepLOB. Il modello di liquidità intraday a forma di U e le statistiche di normalizzazione alla deriva: modellazione degli spread. Ricalibrazione per sede e perché un modello montato sul Nasdaq non si trasferirà intatto su una coppia crittografica: di nuovo DeepLOB, con il lato della frammentazione in instradamento intelligente degli ordini.
Manipolazione. Una conseguenza è specifica di questo modello e vale la pena dirla chiaramente: la scomposizione del CKS pesa ogni evento solo in base alla dimensione, senza nozione di intento o persistenza. Uno spoofer che posiziona e cancella le dimensioni si inserisce quindi direttamente nel segnale, a pieno peso, per costruzione. Le euristiche di rilevamento (tassi di annullamento, durata degli ordini, comportamento del wall all'avvicinarsi del prezzo) sono in posizione della coda e analisi del wall del book degli ordini; la liquidità fantasma tra sedi è in instradamento intelligente degli ordini.
Punti chiave

-
OFI è una scomposizione di eventi, non un'istantanea. La formula dell'indicatore a tre rami mappa ogni transizione top-of-book (aumento del prezzo, riduzione del prezzo, cambio di dimensione) su un numero con segno, da due istantanee consecutive e nient'altro.
-
Il titolo R al quadrato è contemporaneo. La cifra CKS 50-65% scompone i movimenti di prezzo nello stesso intervallo; non è una previsione e non deve essere confrontata con modelli di rendimento lungimiranti.
-
L'OFI multilivello aggiunge livelli e PCA li comprime. I risultati incrementali- e la varianza dell'89% pubblicati provengono dalle azioni Nasdaq. Non è stato testato qui se la profondità sia ancora d'aiuto su un libro crittografico.
-
OFI comprende lo squilibrio commerciale. Entrati congiuntamente, TI diventa insignificante: le cancellazioni che OFI vede e TI non può sono ciò che fa la differenza.
-
Il modello è cieco alle intenzioni. La parità di peso in base alle dimensioni è esattamente ciò che lo rende falsificabile.
-
Niente in questo articolo viene misurato. Ogni numero citato proviene dalla letteratura azionaria. Prima che questo segnale raggiunga il capitale, è necessario un adattamento dei dati contabili, una specifica ritardata riportata separatamente e un controllo per verificare se il vantaggio sopravvive alle commissioni e allo spread.
Ulteriori letture

- Cont, R., Kukanov, A., & Stoikov, S. (2014). "L'impatto sui prezzi degli eventi del portafoglio ordini." Journal of Financial Econometrics, 12(1), 47-88.
- Xu, K., Gould, M. e Howison, S. (2019). "Squilibrio del flusso degli ordini a più livelli in un libro degli ordini con limite." arXiv:1907.06230.
- Kolm, P., Turiel, J., & Westray, N. (2023). "Squilibrio del flusso di ordini profondi: estrazione di alfa su più orizzonti dal registro degli ordini limite." Finanza matematica, 33(4).
- Lee, C. e Ready, M. (1991). "Deduzione della direzione commerciale dai dati intraday." Giornale delle Finanze, 46(2), 733-746.
- Easley, D., Lopez de Prado, M., & O'Hara, M. (2012). "Tossicità del flusso e liquidità in un mondo ad alta frequenza". Rassegna di studi finanziari, 25(5), 1457-1493.
Autori
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.