Дисбаланс потока заказов: декомпозиция событий Конта-Куканова-Стойкова
Большинство сигналов, полученных из книг в этом блоге, представляют собой моментальные снимки: какой объем сейчас находится на каждой стороне. Дисбаланс потока заказов — это другой объект. Это разложение потока обновлений между двумя снимками в одно число со знаком, которое учитывает отмену заявки так же, как и рыночную продажу.
Это разложение и является темой данной статьи. В частности: определяющие его индикаторы событий Cont-Kukanov-Stoikov (2014), многоуровневое расширение Xu-Gould-Howison (2019) и его сокращение основных компонентов, а также правила Lee-Ready и Bulk Volume Classification, которые вам нужны, когда ваш канал не обозначает направление торговли.
Одно общее примечание заранее, потому что оно определяет, как вам следует читать каждое число ниже. Знаменитый R-квадрат 50-65% из статьи CKS представляет собой одновременную регрессию: изменение цены за определенный интервал регрессирует в зависимости от потока заказов за тот же интервал. Это разложение ценовых движений, а не их прогноз. Сравнивая это с дисперсией в 1-2%, которую объясняет модель ежедневной доходности акций, это яблоки и апельсины, и в этом блоге подробно обсуждаются - в DeepLOB и разрыв между прогнозированием и прибылью и в честный отрицательный результат — объединение этих двух понятий превращает учебные пособия в ловушки. Регрессия OFI со строгим лагом является вполне обоснованным и гораздо меньшим числом. В этой статье не сообщается ни об одном.
Почему поток ордеров, а не сделок

Изменение цены в книге непрерывных лимитных ордеров происходит посредством ровно четырех механизмов: рыночная покупка поглощает оставшийся спрос; рыночная продажа поглощает остающиеся заявки; отмена ставки лишает поддержки и позволяет ставке упасть; новый лимитный ордер на аск добавляет сопротивления. Только первые два являются сделками. Последние два невидимы для любого измерения объема — VWAP, балансовый объем, подписанный торговый поток — и на большинстве площадок соотношение заказов к сделкам превышает 10:1, поэтому невидимая часть — это большая часть. Вся претензия OFI на полезность заключается в том, что она оценивает все четыре механизма по одной и той же шкале. (Для книги как структуры данных и стандарта вектор объекта моментального снимка, см. ДипЛОБ.)
Модель OFI Конта-Куканова-Стойкова

Основополагающая модель взята из статьи Конта, Куканова и Стойкова 2014 года "Влияние на цену событий в книге заказов" (Журнал финансовой эконометрики).
Определение дисбаланса потока заказов
Учитывайте лучшую цену предложения , лучшая цена предложения и их размеры и . Между последовательными наблюдениями в и :
где вклады событий со стороны покупателя и продавца составляют:
Говоря простым языком: если лучшая цена предложения выросла, появился новый интерес к покупкам — считайте его полный размер положительным. Если он упадет, интерес к покупке исчез — вычтите старый размер. Если цена не изменилась, учитывайте только изменение размера. Сторона спроса является зеркальным отражением.
Элегантность в том, что эти три ветки индикатора охватывают все возможные переходы начала книги, поэтому каждое обновление соответствует ровно одному числу со знаком. Никакой торговой классификации, боковых меток или ленты сообщений не требуется — достаточно двух последовательных снимков.
Агрегация по интервалам
На интервал содержащий обновления книг:
Модель линейного воздействия на цену
где это изменение средней цены, – коэффициент влияния на цену, а это остаточный шум. Об акциях США с интервалом от 10 секунд до 1 минуты сообщает CKS. 50-65% на акцию. Опять же: одноинтервальный, а не перспективный.
Поперечное масштабирование
CKS также показал, что коэффициент воздействия обратно пропорционален глубине:
где средний объем покоя при лучшем спросе и предложении. Тот же поток толкает цену дальше в тонкой книге. Это та часть модели, которая передается между площадками без перенастройки уровня, поскольку она предсказывает отношения, а не числа — и ее можно напрямую протестировать на криптопарах разной глубины.
Многоуровневый дисбаланс потока заказов (MLOFI)

В оригинальной модели используется только верхняя часть книги. Сюй, Гулд и Хоуисон (2019) расширили его до уровни.
Определение
где применяет идентичную формулу трех ветвей к -я пара бид/аск.
Многоуровневое влияние на цену
Опубликованные данные по акциям Nasdaq показывают, что каждый дополнительный уровень добавляет значения, выходящие за пределы выборки. — примерно 10–15 процентных пунктов при переходе от одного уровня к пятому, при этом незначительный прирост все еще присутствует на уровне десяти. Коэффициенты монотонно затухают: : верхняя часть книги доминирует, но более глубокие уровни несут нетривиальный дополнительный сигнал.
Сохранится ли это в криптокниге — это единственный наиболее интересный открытый вопрос в этой статье. Крипто-книги тоньше и гораздо более запутаны по глубине, и вполне вероятно, что уровни 2-5 ничего не добавляют, когда вы покидаете книгу заказов Nasdaq.
Сокращение основных компонентов
OFI смежных уровней сильно коррелируют (выше 0,8 в опубликованных результатах акций), поэтому разложение по основным компонентам является естественным. Сообщаемый первый компонент охватывает более 89% общей дисперсии и служит единым совокупным сигналом:
с ведущий собственный вектор ковариационной матрицы OFI. Практическая привлекательность заключается в том, что он сжимает коллинеарную регрессию в один хорошо обусловленный скаляр — это стоит сделать, даже если доля дисперсии ваших данных окажется ниже.
Классификация сделок: покупка и продажа.

OFI сам по себе не нуждается в торговой классификации. Но если вы хотите сравнить его с показателями, основанными на торговле, или у вас есть только агрегированные столбцы, вам необходимо определить направление.
Правило цитаты
Сравните торговую цену к преобладающей средней точке :
Сделка выше средней точки, скорее всего, означает, что покупатель снимет предложение; под ним — продавец, сделавший ставку.
Алгоритм Lee-Ready (1991 г.) представляет собой правило котировки с откатом по тиковому правилу для сделок точно в средней точке, где правило котировки является неопределенным. Правило тика — признак последнего изменения цены, переносящий предыдущую сторону вперед на нулевой тик — уже выведено и реализовано в За рамками времени, который отправляет рабочий _tick_sign(). Заявленная точность классификации Lee-Ready составляет 72–85 % в зависимости от рынка и периода.
Классификация по объему (BVC)
Когда отдельные сделки не могут быть назначены — агрегированные бары, большинство общедоступных API свечей — Исли, Лопес де Прадо и классификация объемов О'Хары оценивают долю покупок в объеме бара на основе его нормализованного изменения цены:
с стандартный нормальный CDF и рассчитано на основе недавних изменений цен. Менее точная, чем классификация на уровне тиков, но она работает для OHLCV.
Чем OFI не является

Три соседние величины путают с OFI. Каждый из них подробно описан в других разделах этого блога; здесь важны различия.
Статический дисбаланс в книге заказов (OBI) — это версия моментального снимка: объем спроса в покое относительно объема спроса, отслеживание событий отсутствует. Видеть Традиционные бизнес-функции DeepLOB для формулы и ее многоуровневой формы. В контрасте и заключается весь смысл OFI: OBI сообщает вам о состоянии книги, OFI рассказывает вам, как она туда попала.
Торговый дисбаланс (TI) и его варианты, взвешенные по объему, представляют собой подписанный торговый поток, определяемый и реализуемый как скользящие характеристики в моделирование распространения с помощью машинного обучения. Один из опубликованных результатов об этой взаимосвязи здесь имеет решающее значение: когда OFI и TI вводятся вместе в качестве регрессоров изменений средних цен, TI становится статистически незначимым. Его содержание включено в категорию. Это эмпирический случай отслеживания книжных событий, а не сделок: события, которые почти стали сделками, несут ту же информацию, что и те, которые ею стали.
Средний показатель, взвешенный по объему, представляет собой оценку справедливой стоимости, а не показатель дисбаланса, и он рассматривается в разделе ДипЛОБ - включая оговорку в названии, что это не микроцена Стойкова, которая представляет собой корректировку по мартингейлу. оценщик построен именно потому, что наивная взвешенная середина смещена.
Реализация Python

Прямая реализация разложения CKS, обобщенная до уровни.
Базовый расчет OFI
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
Агрегация в 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
Обратите внимание, что выдает агрегатор: mlofi и delta_mid над то же окном. Это современный регресс. Чтобы получить прогнозируемое окно, спарьте его 's mlofi с окном 's delta_mid и ожидайте существенно худшего соответствия.
Торговая классификация (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
Нормализация сигнала
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}
Где подключается OFI

Маркетинг. OFI вводится как прогнозируемый коэффициент отклонения справедливой стоимости поверх уже полученного и закодированного отклонения запасов. маркет-мейкер Авельянеда-Стойков: . Эти два термина отвечают на разные вопросы: термин потока говорит, куда движется цена, термин запаса говорит, что вы можете себе позволить держать, а опубликованная статья охватывает второй вопрос, в том числе, почему положительные запасы толкают обе котировки вниз. Большой также является разумным триггером неблагоприятного отбора для защитных реакций (расширение, сжатие, оттягивание одной стороны), перечисленных в цифровые отпечатки пальцев идентификации трейдера и обнаружение аномалий.
Выполнение. OFI – это входные данные для оценки вероятности заполнения тактического слоя. , а не отдельный контроллер срочности. Решение "пост-крест" представляет собой явную арифметику безубыточности: с — и живет в тактика исполнения дочернего заказа, в котором также утверждается, что уровень тактики никогда не должен основывать срочность на своем собственном взгляде на рынок, потому что это создает два несогласных контролера.
Практические соображения

Период полураспада сигнала и повторная калибровка. Это часть, специфичная для OFI. Прогнозируемое содержание OFI затухает во времени от миллисекунд до секунд в ликвидных инструментах, а это означает, что окно агрегации не является свободным параметром — это ставка на горизонт. И не является константой: он перемещается в течение дня, с глубиной и вокруг запланированных событий, поэтому при любом производственном подходе он переоценивается в скользящих окнах, а не закрепляется значение из бэктеста.
Все остальное здесь описано в другом месте. Бюджеты задержки и лестница совместного размещения/FPGA/обхода ядра: Производственный отдел DeepLOB. U-образная модель внутридневной ликвидности и дрейфующая статистика нормализации: моделирование распространения. Перекалибровка для каждой площадки и почему модель, установленная на Nasdaq, не будет переведена в криптовалютную пару без изменений: ДипЛОБ опять же, со стороны фрагментации в интеллектуальная маршрутизация заказов.
Манипуляция. Одно последствие является специфичным для этой модели, и его стоит сказать прямо: декомпозиция CKS взвешивает каждое событие только по размеру, без понятия намерения или постоянства. Таким образом, спуфер, размещающий и удаляющий размер, по своей конструкции вводит непосредственно в сигнал полный вес. Эвристика обнаружения — коэффициенты отмены, срок действия ордеров, поведение стены при приближении цены — находятся в стадии разработки. анализ позиции в очереди и стены книги заказов; межплатформенная фантомная ликвидность находится в интеллектуальная маршрутизация заказов.
Ключевые выводы

-
OFI — это декомпозиция событий, а не снимок. Формула индикатора с тремя ветвями отображает каждый переход к началу книги — цена вверх, цена вниз, изменение размера — в одно число со знаком, полученное из двух последовательных снимков, и ничего больше.
-
Заголовок R-квадрата относится к тому времени. Показатель CKS 50-65% разлагает ценовые движения в одном и том же интервале; это не прогноз, и его не следует сравнивать с прогнозными моделями доходности.
-
Многоуровневый OFI добавляет уровни, а PCA сворачивает их. Опубликованный инкрементный и результаты с отклонением 89% получены по акциям Nasdaq. Здесь еще не проверено, помогает ли глубина написанию книги о криптографии.
-
OFI включает в себя торговый дисбаланс. При совместном вводе TI становится незначительным — отмены, которые OFI видит, а TI не может, имеют значение.
-
Модель слепа к намерениям. Равный вес по размеру — именно то, что делает ее подделываемой.
-
Ничто в этой статье не измеряется. Все приведенные цифры взяты из литературы по акциям. Прежде чем этот сигнал коснется капитала, он должен соответствовать вашим собственным балансовым данным, отдельной спецификации, сообщаемой отдельно, и проверке того, выдерживает ли преимущество комиссию и спред.
Дальнейшее чтение

- Конт Р., Куканов А. и Стойков С. (2014). "Влияние на цену событий в книге заказов". Журнал финансовой эконометрики, 12(1), 47-88.
- Сюй К., Гулд М. и Ховисон С. (2019). "Многоуровневый дисбаланс потоков ордеров в книге лимитных ордеров". arXiv:1907.06230.
- Колм П., Туриэль Дж. и Вестрей Н. (2023). "Глубокий дисбаланс потока ордеров: извлечение альфы на нескольких горизонтах из книги лимитных ордеров". Математические финансы, 33(4).
- Ли, К., и Риди, М. (1991). "Определение направления торговли на основе внутридневных данных". Журнал финансов, 46(2), 733-746.
- Исли Д., Лопес де Прадо М. и О'Хара М. (2012). "Токсичность потоков и ликвидность в мире высоких частот". Обзор финансовых исследований, 25(5), 1457-1493.
Авторы
Инженер торговых систем
Разработка торговых ботов с 2017 года: межбиржевой арбитраж (подключал до 30 бирж), парный арбитраж на коинтеграции между спотом и фьючерсами, скальпинг, фронтраннинг, торговля по новостям, сентиментный анализ, трендовые алгоритмы, а также алгоритмы управления и балансировки портфелей. Делает выставление ордеров до 1 мс, warehouse для big data, бэктестинг-движки, AI-агентов и интерфейсы для ботов (в т.ч. open-source profitmaker.cc). Стек: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, архитектура.