Order Flow Imbalance: The Cont-Kukanov-Stoikov Event Decomposition
معظم الإشارات المستخرجة من دفتر الأوامر في هذه المدونة هي لقطات: مقدار الحجم المستقر على كل جانب الآن. اختلال تدفق الأوامر كيان مختلف. إنه تفكيك سلسلة التحديثات بين لقطتين إلى كمية موقعة واحدة — كمية تعد إلغاء أمر شراء بالطريقة نفسها التي تعد بها بيعًا سوقيًا.
ذلك التفكيك هو موضوع هذا المقال. وتحديدًا: مؤشرات الأحداث لدى Cont-Kukanov-Stoikov (2014) التي تعرّفه، وامتداد Xu-Gould-Howison (2019) متعدد المستويات واختزاله بالمكونات الرئيسية، وقواعد Lee-Ready وBulk Volume Classification اللازمة عندما لا يضع مصدر البيانات علامة على اتجاه التداول.
ملاحظة تأطير في البداية، لأنها تحكم طريقة قراءة كل رقم أدناه. إن قيمة R-squared الشهيرة البالغة 50-65% في ورقة CKS هي انحدار متزامن: تغير السعر خلال فترة منحدر على تدفق الأوامر خلال الفترة نفسها. إنها تفكيك لتحركات السعر، وليست تنبؤًا بها. مقارنتها بنسبة 1-2% من التباين التي يفسرها نموذج عائد أسهم يومي هي مقارنة غير متكافئة، وقد جادلت هذه المدونة مطولًا — في DeepLOB والفجوة بين التنبؤ والربح وفي النتيجة السلبية الصادقة — بأن الخلط بين الاثنين هو ما يحول الدروس إلى أفخاخ. انحدار OFI المتأخر زمنيًا بالكامل رقم مشروع وأصغر بكثير. هذا المقال لا يعرضه.
لماذا تدفق الأوامر وليس التداولات

يحدث تغير السعر في دفتر أوامر حدّي مستمر عبر أربع آليات بالضبط: شراء سوقي يستهلك طلبات البيع المستقرة؛ بيع سوقي يستهلك عروض الشراء المستقرة؛ إلغاء على جانب الشراء يزيل الدعم ويسمح بانخفاض العرض؛ وأمر حد جديد على جانب البيع يضيف مقاومة. الآليتان الأوليان فقط هما تداولات. الآخرتان غير مرئيتين لأي مقياس قائم على الحجم — VWAP، والحجم المتوازن، وتدفق التداولات الموقعة — وفي معظم الأسواق تتجاوز نسبة الأوامر إلى التداولات 10:1، ولذلك فالجزء غير المرئي هو الأكبر. تدور فائدة OFI كلها حول تسعير الآليات الأربع على المقياس نفسه. (للاطلاع على دفتر الأوامر كبنية بيانات ومتجه السمات القياسي للّقطة، راجع DeepLOB.)
نموذج OFI لدى Cont-Kukanov-Stoikov

يأتي النموذج التأسيسي من ورقة Cont وKukanov وStoikov لعام 2014 بعنوان "The Price Impact of Order Book Events" (Journal of Financial Econometrics).
تعريف اختلال تدفق الأوامر
لننظر إلى أفضل سعر شراء ، وأفضل سعر بيع ، وحجميهما و. بين الملاحظتين المتتاليتين عند و:
حيث تكون مساهمتا أحداث جانب الشراء والبيع:
بلغة بسيطة: إذا ارتفع أفضل سعر شراء، فقد ظهر اهتمام شراء جديد — فاحسب حجمه كاملًا موجبًا. إذا انخفض، فقد اختفى اهتمام الشراء — اطرح الحجم القديم. وإذا لم يتغير السعر، فاحسب تغير الحجم فقط. جانب البيع هو الصورة المرآتية.
تكمن أناقة الصيغة في أن فروع المؤشرات الثلاثة تغطي كل انتقال ممكن في قمة الدفتر، ولذلك يتحول كل تحديث إلى رقم موقّع واحد بالضبط. لا حاجة إلى تصنيف التداول، أو علامات الجانب، أو مصدر على مستوى الرسالة — تكفي لقطتان متتاليتان.
التجميع عبر الفترات
لفترة تحتوي على تحديثات للدفتر:
نموذج الأثر السعري الخطي
حيث إن هو تغير السعر المتوسط، و معامل الأثر السعري، و ضجيج البواقي. في الأسهم الأمريكية خلال فترات من 10 ثوانٍ إلى دقيقة واحدة، أبلغت CKS عن متزامن قدره 50-65% لكل سهم. مرة أخرى: الفترة نفسها، وليس تنبؤًا مستقبليًا.
القياس المقطعي
أظهرت CKS أيضًا أن معامل الأثر يتناسب عكسيًا مع العمق:
حيث إن هو متوسط الحجم المستقر عند أفضل عرض وأفضل طلب. التدفق نفسه يدفع السعر أكثر في دفتر رقيق. هذا هو الجزء الوحيد من النموذج الذي ينتقل بين الأسواق دون إعادة ملاءمة المستوى، لأنه يتنبأ بعلاقة لا برقم — ويمكن اختباره مباشرة على أزواج العملات المشفرة ذات الأعماق المختلفة.
اختلال تدفق الأوامر متعدد المستويات (MLOFI)

يستخدم النموذج الأصلي قمة الدفتر فقط. وقد وسّعه Xu وGould وHowison (2019) إلى مستويات.
التعريف
حيث يطبق الصيغة نفسها ذات الفروع الثلاثة على زوج الشراء/البيع عند المستوى .
الأثر السعري متعدد المستويات
تقول النتيجة المنشورة، على أسهم Nasdaq، إن كل مستوى إضافي يضيف إلى خارج العينة — نحو 10-15 نقطة مئوية عند الانتقال من مستوى واحد إلى خمسة، مع استمرار مكاسب هامشية عند عشرة مستويات. تتناقص المعاملات رتيبًا، : فقمة الدفتر هي المهيمنة، لكن المستويات الأعمق تحمل إشارة إضافية غير تافهة.
أما بقاء ذلك على دفتر عملات مشفرة فهو السؤال المفتوح الأكثر إثارة للاهتمام في هذا المقال. دفاتر العملات المشفرة أرق وأكثر تعرضًا للتبدل عند العمق، ومن المعقول تمامًا أن تضيف المستويات 2-5 لا شيئًا بمجرد مغادرة دفتر أوامر Nasdaq.
الاختزال بالمكونات الرئيسية
ترتبط قيم OFI في المستويات المتجاورة ارتباطًا عاليًا (أعلى من 0.8 في نتائج الأسهم المنشورة)، لذا فإن تفكيك المكونات الرئيسية طبيعي. يلتقط المكون الأول المبلغ عنه أكثر من 89% من التباين الكلي ويعمل كإشارة تجميعية واحدة:
حيث إن هو المتجه الذاتي الرائد لمصفوفة تغاير OFI. تكمن الفائدة العملية في أنه يختزل انحدارًا متعدد الخطية إلى عدد قياسي واحد حسن التكييف — وهو أمر يستحق التنفيذ حتى إن خرجت حصة التباين في بياناتك أقل.
تصنيف التداولات: مبادرة شراء أم بيع

لا يحتاج OFI نفسه إلى تصنيف التداولات. لكن إذا أردت مقارنته بمقاييس قائمة على التداول، أو لم تكن لديك إلا أشرطة مجمعة، فعليك استنتاج الاتجاه.
قاعدة السعر المعروض
قارن سعر التداول بنقطة المنتصف السائدة :
التداول فوق نقطة المنتصف يرجح أن يكون مشتريًا يرفع العرض؛ وتحته يرجح أن يكون بائعًا يضرب الطلب.
خوارزمية Lee-Ready (1991) هي قاعدة السعر المعروض مع احتياط قاعدة الإشارة السعرية للتداولات الواقعة تمامًا عند نقطة المنتصف، حيث تكون قاعدة السعر غير حاسمة. قاعدة الإشارة — إشارة آخر تغير في السعر، مع حمل الجانب السابق عند تغير صفري — مشتقة ومطبقة بالفعل في ما وراء أشرطة الوقت، الذي يوفر _tick_sign() عاملًا. تتراوح دقة تصنيف Lee-Ready المبلغ عنها بين 72-85% بحسب السوق والفترة.
تصنيف الحجم الإجمالي (BVC)
عندما يتعذر إسناد التداولات الفردية — الأشرطة المجمعة ومعظم واجهات الشموع العامة — يقدّر Bulk Volume Classification لدى Easley وLopez de Prado وO'Hara جزء شراء حجم الشريط من تغير السعر المعياري:
حيث إن هو دالة التوزيع التراكمي الطبيعية القياسية، و مقدر من تغيرات السعر الأخيرة. دقته أقل من التصنيف على مستوى التداول، لكنه يعمل على OHLCV.
ما ليسه OFI

تختلط ثلاث كميات مجاورة مع OFI. كل واحدة منها مغطاة بشكل صحيح في مكان آخر من هذه المدونة؛ والفروق هي ما يهم هنا.
اختلال دفتر الأوامر الساكن (OBI) هو نسخة اللقطة — حجم الشراء المستقر مقابل حجم البيع المستقر، دون تتبع الأحداث. راجع سمات LOB التقليدية في DeepLOB للصيغة ونسختها متعددة المستويات. المقارنة هي جوهر OFI: يخبرك OBI بحالة الدفتر، بينما يخبرك OFI بكيفية وصوله إليها.
اختلال التداول (TI) ونسخه الموزونة بالحجم هي تدفق تداول موقّع، معرف ومطبق كسمات متحركة في نمذجة السبريد بالتعلم الآلي. هناك نتيجة منشورة عن العلاقة تحمل الحجة هنا: عندما يدخل OFI وTI معًا كمتغيرين في انحدار تغيرات السعر المتوسط، يصبح TI غير دال إحصائيًا. لقد استوعب OFI مضمونه. هذه هي الحجة التجريبية لتتبع أحداث الدفتر بدل التداولات — فالأحداث التي كادت تصبح تداولات تحمل المعلومات نفسها التي تحملها الأحداث التي أصبحت تداولات.
المتوسط الموزون بالحجم مقدر للقيمة العادلة، وليس مقياس اختلال، وهو مغطى في DeepLOB — بما في ذلك التحفظ على التسمية بأنه ليس السعر الجزئي لـ Stoikov، الذي هو مقدر معدل بمارتينجال، بُني تحديدًا لأن المتوسط الموزون الساذج متحيز.
تطبيق 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
التجميع في نوافذ
@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 خلال النافذة نفسها. هذا هو الانحدار المتزامن. وللحصول على نسخة تنبؤية، اربط mlofi للنافذة مع 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 كحد انحراف تنبؤي في القيمة العادلة، فوق انحراف المخزون المشتق والمبرمج بالفعل في صانع السوق Avellaneda-Stoikov: . يجيب الحدّان عن سؤالين مختلفين — حد التدفق يقول إلى أين يتجه السعر، وحد المخزون يقول ما الذي يمكنك تحمل الاحتفاظ به — والمقال المنشور يغطي الثاني، بما في ذلك سبب دفع المخزون الموجب لكلا السعرين إلى الأسفل. كما أن قيمة الكبيرة محفز معقول لاختيار ردود الفعل الدفاعية (توسيع السبريد، تقليص الحجم، سحب جانب واحد) المفهرسة في البصمات الرقمية لتعريف المتداول وكشف الشذوذ.
التنفيذ. OFI مدخل إلى تقدير احتمال التنفيذ في طبقة التكتيكات، وليس متحكمًا منفصلًا في الاستعجال. قرار الإرسال مقابل العبور هو حساب تعادل صريح — مع — ويعيش في تكتيكات تنفيذ الأوامر الجزئية، التي تجادل أيضًا بأن طبقة التكتيكات يجب ألا تعيد اشتقاق الاستعجال من رؤيتها الخاصة للسوق، لأن ذلك ينشئ متحكمين يختلفان.
اعتبارات عملية

نصف عمر الإشارة وإعادة المعايرة المتحركة. هذا هو الجزء الخاص بـ OFI. يتلاشى المحتوى التنبؤي لـ OFI على مقياس من ميلي ثانية إلى ثوانٍ في الأدوات السائلة، ما يعني أن نافذة التجميع ليست معاملًا مجانيًا — إنها رهان على الأفق. و ليس ثابتًا: فهو يتحرك خلال اليوم، ومع العمق، وحول الأحداث المجدولة، لذلك تعيد أي ملاءمة إنتاجية تقديره على نوافذ متحركة بدل تثبيت قيمة من اختبار خلفي.
كل ما عدا ذلك هنا مغطى في مكان آخر. ميزانيات التأخير وسلّم التجميع المشترك / FPGA / تجاوز النواة: قسم الإنتاج في DeepLOB. نمط السيولة اليومي على شكل U وإحصاءات التطبيع المنجرفة: نمذجة السبريد. إعادة المعايرة لكل سوق وسبب عدم انتقال نموذج ملائم على Nasdaq إلى زوج عملات مشفرة دون تغيير: DeepLOB مرة أخرى، مع جانب التجزؤ في التوجيه الذكي للأوامر.
التلاعب. هناك نتيجة خاصة بهذا النموذج تستحق التصريح بها بوضوح: تفكيك CKS يزن كل حدث بالحجم وحده، دون مفهوم النية أو الاستمرارية. لذلك فإن واضع الأوامر الوهمية الذي يضع حجمًا ثم يلغيه يحقن مباشرة في الإشارة، بالوزن الكامل، بحكم البناء. توجد إرشادات الكشف — معدلات الإلغاء، أعمار الأوامر، سلوك الجدران مع اقتراب السعر — في تحليل موقع الطابور وجدار دفتر الأوامر؛ والسيولة الوهمية عبر الأسواق في التوجيه الذكي للأوامر.
الخلاصات الرئيسية

-
OFI تفكيك للأحداث، وليس لقطة. تحول صيغة المؤشر ذات الفروع الثلاثة كل انتقال في قمة الدفتر — ارتفاع السعر، انخفاضه، تغير الحجم — إلى رقم موقّع واحد، انطلاقًا من لقطتين متتاليتين ولا شيء آخر.
-
قيمة R-squared الرئيسية متزامنة. تفكك قيمة CKS البالغة 50-65% تحركات السعر في الفترة نفسها؛ إنها ليست تنبؤًا، ولا ينبغي مقارنتها بنماذج العائد المستقبلية.
-
يضيف OFI متعدد المستويات مستويات، ثم تضغطها PCA. نتائج الزيادة التدريجية في و89% من التباين منشورة لأسهم Nasdaq. لم يُختبر هنا ما إذا كان العمق لا يزال مفيدًا على دفتر عملات مشفرة.
-
يستوعب OFI اختلال التداول. عند إدخالهما معًا، يصبح TI غير دال — الإلغاءات التي يراها OFI ولا يستطيع TI رؤيتها هي ما يصنع الفرق.
-
النموذج أعمى عن النية. الوزن المتساوي حسب الحجم هو بالضبط ما يجعله قابلًا للتلاعب.
-
لا شيء في هذا المقال مقاس. كل رقم مقتبس يأتي من أدبيات الأسهم. قبل أن تلامس هذه الإشارة رأس المال، تحتاج إلى ملاءمة على بيانات دفترك، ومواصفة متأخرة معروضة منفصلة، وفحص ما إذا كانت الميزة تصمد أمام الرسوم ونصف السبريد.
قراءة إضافية

- 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.
Authors
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.