← กลับไปยังบทความ
August 17, 2026
อ่าน 5 นาที

Order Flow Imbalance: The Cont-Kukanov-Stoikov Event Decomposition

Order Flow Imbalance: The Cont-Kukanov-Stoikov Event Decomposition
#microstructure
#order-flow
#imbalance
#prediction
#HFT

สัญญาณส่วนใหญ่ที่ได้จากสมุดคำสั่งในบล็อกนี้เป็น ภาพสแนปชอต: ตอนนี้มีปริมาณคำสั่งค้างอยู่แต่ละฝั่งเท่าใด Order flow imbalance เป็นสิ่งที่แตกต่างออกไป มันคือการแยก สตรีมการอัปเดต ระหว่างสองภาพสแนปชอตออกเป็นค่าที่มีเครื่องหมายค่าเดียว — โดยนับการยกเลิกคำสั่งซื้อที่ bid แบบเดียวกับการขายแบบ market

การแยกนี้คือหัวข้อของบทความนี้ โดยเฉพาะอย่างยิ่ง: ตัวบ่งชี้เหตุการณ์ของ Cont-Kukanov-Stoikov (2014) ที่นิยามมัน ส่วนขยายหลายระดับของ Xu-Gould-Howison (2019) และการลดมิติด้วย principal component รวมถึงกฎ Lee-Ready และ Bulk Volume Classification ที่ต้องใช้เมื่อฟีดข้อมูลไม่ได้ระบุทิศทางการซื้อขาย

หมายเหตุเรื่องกรอบแนวคิดตั้งแต่ต้น เพราะมันกำหนดวิธีอ่านตัวเลขทุกตัวด้านล่าง ค่า R-squared 50-65% อันโด่งดังจากงาน CKS เป็นการถดถอยแบบ ร่วมเวลา: การเปลี่ยนแปลงราคาตลอดช่วงเวลาหนึ่งถดถอยบน order flow ในช่วงเดียวกัน นี่คือการแยกองค์ประกอบการเคลื่อนไหวของราคา ไม่ใช่การพยากรณ์มัน การนำไปเทียบกับความแปรปรวน 1-2% ที่โมเดลผลตอบแทนหุ้นรายวันอธิบายได้เป็นการเทียบสิ่งที่ไม่เหมือนกัน และบล็อกนี้ได้โต้แย้งไว้อย่างละเอียด — ใน DeepLOB กับช่องว่างระหว่างการพยากรณ์และกำไร และ ผลลัพธ์เชิงลบอย่างซื่อสัตย์ — ว่าการปนสองสิ่งนี้เข้าด้วยกันคือสาเหตุที่บทเรียนกลายเป็นกับดัก การถดถอย OFI ที่หน่วงเวลาอย่างเคร่งครัดเป็นตัวเลขที่ถูกต้องและเล็กกว่ามาก บทความนี้ไม่ได้รายงานตัวเลขนั้น

ทำไมต้องเป็น Order Flow ไม่ใช่การซื้อขาย

กลไกสี่แบบของสมุดคำสั่งมาบรรจบเป็นสัญญาณ flow ที่มีเครื่องหมายเดียว

การเปลี่ยนแปลงราคาในสมุดคำสั่งแบบ limit ที่ต่อเนื่องเกิดผ่านกลไกสี่แบบพอดี: market buy กิน ask ที่ค้างอยู่; market sell กิน bid ที่ค้างอยู่; การยกเลิกที่ bid ลบแรงพยุงและทำให้ bid ลดลง; limit order ใหม่ที่ ask เพิ่มแรงต้าน มีเพียงสองแบบแรกเท่านั้นที่เป็นการซื้อขาย สองแบบหลังมองไม่เห็นด้วยมาตรวัดที่อิงปริมาณใด ๆ — VWAP, on-balance volume, signed trade flow — และในตลาดส่วนใหญ่สัดส่วน order-to-trade สูงกว่า 10:1 ดังนั้นส่วนที่มองไม่เห็นจึงเป็นส่วนใหญ่ทั้งหมดของ OFI ที่อ้างว่ามีประโยชน์คือการกำหนดราคากลไกทั้งสี่บนสเกลเดียวกัน (สำหรับสมุดคำสั่งในฐานะโครงสร้างข้อมูลและเวกเตอร์ฟีเจอร์สแนปชอตมาตรฐาน 4L4L ดู DeepLOB)

โมเดล OFI ของ Cont-Kukanov-Stoikov

การเปลี่ยนแปลง bid และ ask ในการแยกเหตุการณ์ CKS

โมเดลพื้นฐานมาจากงานปี 2014 ของ Cont, Kukanov และ Stoikov เรื่อง "The Price Impact of Order Book Events" (Journal of Financial Econometrics)

การนิยาม Order Flow Imbalance

พิจารณาราคา bid ที่ดีที่สุด PtbP^b_t ราคา ask ที่ดีที่สุด PtaP^a_t และขนาดของทั้งสองฝั่ง QtbQ^b_t และ QtaQ^a_t ระหว่างการสังเกตต่อเนื่องที่ tn1t_{n-1} และ tnt_n:

OFIn=enBenSOFI_n = e_n^B - e_n^S

โดยส่วนสนับสนุนของเหตุการณ์ฝั่งซื้อและฝั่งขายคือ:

enB=1{Pnb>Pn1b}Qnb1{Pnb<Pn1b}Qn1b+1{Pnb=Pn1b}(QnbQn1b)e_n^B = \mathbb{1}_{\{P^b_n > P^b_{n-1}\}} Q^b_n - \mathbb{1}_{\{P^b_n < P^b_{n-1}\}} Q^b_{n-1} + \mathbb{1}_{\{P^b_n = P^b_{n-1}\}} (Q^b_n - Q^b_{n-1}) enS=1{Pna<Pn1a}Qna1{Pna>Pn1a}Qn1a+1{Pna=Pn1a}(QnaQn1a)e_n^S = \mathbb{1}_{\{P^a_n < P^a_{n-1}\}} Q^a_n - \mathbb{1}_{\{P^a_n > P^a_{n-1}\}} Q^a_{n-1} + \mathbb{1}_{\{P^a_n = P^a_{n-1}\}} (Q^a_n - Q^a_{n-1})

อธิบายง่าย ๆ: หากราคา bid ที่ดีที่สุดสูงขึ้น แสดงว่าความสนใจซื้อใหม่เข้ามา — นับขนาดเต็มเป็นค่าบวก หากราคาลดลง แสดงว่าความสนใจซื้อหายไป — ลบขนาดเดิม หากราคาไม่เปลี่ยน ให้นับเฉพาะการเปลี่ยนแปลงของขนาด ฝั่ง ask เป็นภาพสะท้อนกัน

ความงดงามคือกิ่งของ indicator ทั้งสามครอบคลุมการเปลี่ยนแปลงที่เป็นไปได้ทุกแบบของส่วนบนสุดของสมุดคำสั่ง ดังนั้นทุกการอัปเดตจึงแปลงเป็นตัวเลขที่มีเครื่องหมายหนึ่งค่าโดยพอดี ไม่ต้องจัดประเภทการซื้อขาย ไม่ต้องมีป้ายกำกับฝั่ง และไม่ต้องมีฟีดระดับข้อความ — สแนปชอตต่อเนื่องสองภาพก็เพียงพอ

การรวมในช่วงเวลา

สำหรับช่วง [T1,T2][T_1, T_2] ที่มีการอัปเดตสมุดคำสั่ง NN ครั้ง:

OFI[T1,T2]=n=1NOFIn=n=1N(enBenS)OFI_{[T_1,T_2]} = \sum_{n=1}^{N} OFI_n = \sum_{n=1}^{N} (e_n^B - e_n^S)

โมเดลผลกระทบของราคาเชิงเส้น

ΔP[T1,T2]=α+βOFI[T1,T2]+ε\Delta P_{[T_1,T_2]} = \alpha + \beta \cdot OFI_{[T_1,T_2]} + \varepsilon

โดย ΔP\Delta P คือการเปลี่ยนแปลงของ mid-price, β\beta คือค่าสัมประสิทธิ์ผลกระทบของราคา และ ε\varepsilon คือ noise ของ residual ในหุ้นสหรัฐฯ ที่ช่วงเวลา 10 วินาทีถึง 1 นาที CKS รายงานค่า R2R^2 ร่วมเวลา 50-65% ต่อหุ้น ย้ำอีกครั้ง: เป็นช่วงเวลาเดียวกัน ไม่ใช่การมองไปข้างหน้า

การปรับสเกลข้ามหน้าตัด

CKS ยังแสดงให้เห็นว่าสัมประสิทธิ์ผลกระทบแปรผกผันกับ depth:

β1Dˉ\beta \propto \frac{1}{\bar{D}}

โดย Dˉ\bar{D} คือปริมาณค้างเฉลี่ยที่ bid และ ask ที่ดีที่สุด flow เดียวกันจะผลักราคาได้ไกลกว่าในสมุดคำสั่งที่บาง นี่คือส่วนเดียวของโมเดลที่ถ่ายโอนข้ามตลาดได้โดยไม่ต้อง fit ระดับใหม่ เพราะมันทำนาย ความสัมพันธ์ ไม่ใช่ตัวเลข และทดสอบได้โดยตรงกับคู่คริปโตที่มี depth ต่างกัน

Multi-Level Order Flow Imbalance (MLOFI)

ความลึกหลายระดับของสมุดคำสั่งถูกบีบอัดเป็นสัญญาณหลัก

โมเดลเดิมใช้เฉพาะส่วนบนของสมุดคำสั่ง Xu, Gould และ Howison (2019) ขยายไปยัง LL ระดับ

นิยาม

MLOFIn=(OFIn(1)OFIn(2)OFIn(L))\mathbf{MLOFI}_n = \begin{pmatrix} OFI_n^{(1)} \\ OFI_n^{(2)} \\ \vdots \\ OFI_n^{(L)} \end{pmatrix}

โดย OFIn()OFI_n^{(\ell)} ใช้สูตรสามกิ่งเดียวกันกับคู่ bid/ask ที่ระดับ \ell

ผลกระทบราคาหลายระดับ

ΔP=α+βTMLOFI+ε=α+=1LβOFI()+ε\Delta P = \alpha + \boldsymbol{\beta}^T \cdot \mathbf{MLOFI} + \varepsilon = \alpha + \sum_{\ell=1}^{L} \beta_\ell \cdot OFI^{(\ell)} + \varepsilon

ผลที่ตีพิมพ์ในหุ้น Nasdaq ระบุว่าแต่ละระดับที่เพิ่มเข้ามาเพิ่ม R2R^2 นอกตัวอย่าง — ราว 10-15 จุดเปอร์เซ็นต์เมื่อเพิ่มจากหนึ่งระดับเป็นห้า และยังมีผลเพิ่มเล็กน้อยที่สิบระดับ ค่าสัมประสิทธิ์ลดลงอย่างเป็นลำดับ β1>β2>>βL|\beta_1| > |\beta_2| > \cdots > |\beta_L|: ส่วนบนของสมุดคำสั่งมีอิทธิพลหลัก แต่ระดับที่ลึกกว่ายังมีสัญญาณเพิ่มเติมที่ไม่ใช่ศูนย์

การที่ผลนี้จะอยู่รอดบนสมุดคริปโตหรือไม่คือคำถามเปิดที่น่าสนใจที่สุดของบทความนี้ สมุดคริปโตบางกว่าและมีการ churn ที่ depth สูงกว่ามาก และเป็นไปได้อย่างสมเหตุสมผลว่าระดับ 2-5 ไม่ได้เพิ่มอะไรเมื่อออกจากสมุดคำสั่ง Nasdaq

การลดด้วย Principal Component

OFI ในระดับติดกันมีความสัมพันธ์กันสูง (มากกว่า 0.8 ในผลหุ้นที่ตีพิมพ์) ดังนั้นการแยก principal component จึงเป็นธรรมชาติ องค์ประกอบแรกที่รายงานจับความแปรปรวนรวมได้มากกว่า 89% และทำหน้าที่เป็นสัญญาณรวมค่าเดียว:

OFIPC1=wTMLOFIOFI^{PC1} = \mathbf{w}^T \cdot \mathbf{MLOFI}

โดย w\mathbf{w} คือ eigenvector ชั้นนำของ covariance matrix ของ OFI ประโยชน์ในทางปฏิบัติคือการยุบ regression ที่มี collinearity ให้เหลือ scalar เดียวที่มี conditioning ดี — ควรทำแม้สัดส่วนความแปรปรวนในข้อมูลของคุณจะต่ำกว่านี้

การจัดประเภทการซื้อขาย: เริ่มโดยซื้อหรือขาย

เหตุการณ์การซื้อขายแยกเป็นสตรีมการเริ่มซื้อและเริ่มขาย

OFI เองไม่ต้องจัดประเภทการซื้อขาย แต่ถ้าต้องการเทียบกับมาตรวัดจาก trade หรือมีเพียงแท่งข้อมูลที่ aggregate แล้ว ต้องอนุมานทิศทาง

Quote Rule

เปรียบเทียบราคาซื้อขาย PtP_t กับ midpoint ที่ใช้อยู่ Mt=Ptask+Ptbid2M_t = \frac{P^{ask}_t + P^{bid}_t}{2}:

side={+1 (buy)if Pt>Mt1 (sell)if Pt<Mt\text{side} = \begin{cases} +1 \text{ (buy)} & \text{if } P_t > M_t \\ -1 \text{ (sell)} & \text{if } P_t < M_t \end{cases}

การซื้อขายเหนือ midpoint น่าจะเป็นผู้ซื้อที่ยก offer; ใต้ midpoint น่าจะเป็นผู้ขายที่ชน bid

อัลกอริทึม Lee-Ready (1991) คือ quote rule ที่มี tick-rule เป็นทางเลือกสำรองสำหรับการซื้อขายที่อยู่ตรง midpoint ซึ่ง quote rule ตัดสินไม่ได้ tick rule — เครื่องหมายของการเปลี่ยนแปลงราคาครั้งล่าสุด โดยส่งต่อฝั่งเดิมเมื่อ tick เป็นศูนย์ — ได้รับการอนุมานและ implement แล้วใน Beyond time bars ซึ่งมี _tick_sign() ที่ใช้งานได้ ความแม่นยำของ Lee-Ready ที่รายงานอยู่ที่ 72-85% ขึ้นกับตลาดและช่วงเวลา

Bulk Volume Classification (BVC)

เมื่อไม่สามารถกำหนดฝั่งให้ trade รายการเดียวได้ — แท่ง aggregate และ candle API สาธารณะส่วนใหญ่ — Bulk Volume Classification ของ Easley, Lopez de Prado และ O'Hara จะประเมินสัดส่วน buy ของ volume ในแท่งจากการเปลี่ยนแปลงราคาที่ normalize:

Vtbuy=VtΦ(ΔPtσΔP)V^{buy}_t = V_t \cdot \Phi\left(\frac{\Delta P_t}{\sigma_{\Delta P}}\right)

โดย Φ\Phi คือ standard normal CDF และ σΔP\sigma_{\Delta P} ประเมินจากการเปลี่ยนแปลงราคาล่าสุด แม่นยำน้อยกว่าการจัดประเภทระดับ tick แต่ใช้กับ OHLCV ได้

สิ่งที่ OFI ไม่ใช่

การแยกแบบร่วมเวลาถูกแยกออกจากการพยากรณ์จริง

มีปริมาณใกล้เคียงสามอย่างที่มักสับสนกับ OFI แต่ละอย่างอธิบายไว้อย่างถูกต้องที่อื่นในบล็อกนี้ ความแตกต่างต่างหากที่สำคัญที่นี่

Static order book imbalance (OBI) คือเวอร์ชันสแนปชอต — volume bid ที่ค้างอยู่เทียบกับ volume ask ที่ค้างอยู่ โดยไม่ติดตามเหตุการณ์ ดู ฟีเจอร์ LOB แบบดั้งเดิมของ DeepLOB สำหรับสูตรและรูปแบบหลายระดับ ความแตกต่างนี้คือประเด็นทั้งหมดของ OFI: OBI บอกสถานะของสมุดคำสั่ง ส่วน OFI บอกว่ามันมาถึงสถานะนั้นได้อย่างไร

Trade imbalance (TI) และรูปแบบที่ถ่วงน้ำหนักด้วย volume คือ signed trade flow ซึ่งนิยามและ implement เป็น rolling feature ใน การทำโมเดล spread ด้วย machine learning ผลตีพิมพ์หนึ่งเกี่ยวกับความสัมพันธ์นี้สำคัญมาก: เมื่อใส่ OFI และ TI ร่วมกันเป็น regressors ของ mid-price change, TI จะไม่มีนัยสำคัญทางสถิติ เนื้อหาของมันถูก OFI กลืนไปแล้ว นี่คือหลักฐานเชิงประจักษ์สำหรับการติดตามเหตุการณ์ของสมุดคำสั่งแทน trade — เหตุการณ์ที่ เกือบ กลายเป็น trade มีข้อมูลเดียวกับเหตุการณ์ที่กลายเป็น trade จริง

Volume-weighted mid เป็นตัวประมาณ fair value ไม่ใช่มาตรวัด imbalance และครอบคลุมใน DeepLOB — รวมถึงข้อควรระวังเรื่องชื่อที่ว่าไม่ใช่ micro-price ของ Stoikov ซึ่งเป็นตัวประมาณ Mt+g(I,S)M_t + g(I,S) ที่ปรับด้วย martingale และสร้างขึ้นโดยเฉพาะเพราะ weighted mid แบบ naive มี bias

การ Implement ด้วย Python

สตรีมเหตุการณ์สมุดคำสั่งไหลผ่าน engine คำนวณ flow

การ implement โดยตรงของ CKS decomposition ซึ่งขยายให้รองรับ LL ระดับ

การคำนวณ 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

โปรดสังเกตว่า aggregator ส่งออก mlofi และ delta_mid ใน หน้าต่างเดียวกัน นี่คือ contemporaneous regression หากต้องการเวอร์ชันเชิงพยากรณ์ ให้จับคู่ mlofi ของหน้าต่าง kk กับ delta_mid ของหน้าต่าง k+1k+1 และคาดหวังว่า fit จะแย่ลงอย่างมาก

การจัดประเภท trade (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

การ normalize สัญญาณ

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 เชื่อมต่อกับส่วนใด

สัญญาณ order flow เชื่อมเข้าสู่ pipeline การเทรดแบบโมดูลาร์

Market making. OFI เข้ามาเป็นเทอม skew เชิงพยากรณ์ของ fair value บวกกับ inventory skew ที่อนุมานและเขียนโค้ดไว้แล้วใน Avellaneda-Stoikov market maker: Pfair=Pmid+γflowOFIzγinvqP^{fair} = P^{mid} + \gamma_{flow} \cdot OFI_{z} - \gamma_{inv} \cdot q. สองเทอมนี้ตอบคนละคำถาม — flow term บอกว่าราคากำลังจะไปทางไหน inventory term บอกว่าคุณรับการถือครองได้เท่าใด — และบทความที่เผยแพร่ครอบคลุมเทอมที่สอง รวมถึงเหตุผลที่ inventory เป็นบวกผลัก quote ทั้งสองลง ค่า OFIz|OFI_z| ที่มากยังเป็น trigger ที่เหมาะสมสำหรับ response เชิงป้องกัน (ขยาย spread, ลดขนาด, ถอนหนึ่งฝั่ง) ที่จัดหมวดหมู่ไว้ใน digital fingerprints of trader identification และ การตรวจจับความผิดปกติ

Execution. OFI เป็น input ของการประเมิน fill probability pp ใน tactics layer ไม่ใช่ urgency controller แยกต่างหาก การตัดสินใจ post-versus-cross คือ break-even arithmetic ที่ชัดเจน — p=δ/(Π+δ)p^* = \delta/(\Pi + \delta) โดย Π=s+ftfm\Pi = s + f_t - f_m — และอยู่ใน child order execution tactics ซึ่งยังอธิบายว่า tactics layer ไม่ควร derive urgency ใหม่จากมุมมองตลาดของตัวเอง เพราะจะสร้าง controller สองตัวที่ไม่เห็นตรงกัน

ข้อพิจารณาเชิงปฏิบัติ

สัญญาณตลาดที่ทนทานเคลื่อนผ่านภูมิประเทศ microstructure ที่มี noise

Half-life ของสัญญาณและการ recalibrate แบบ rolling. นี่คือส่วนเฉพาะของ OFI เนื้อหาเชิงพยากรณ์ของ OFI ลดลงในระดับมิลลิวินาทีถึงวินาทีสำหรับ instrument ที่มีสภาพคล่องสูง ซึ่งหมายความว่า aggregation window ไม่ใช่พารามิเตอร์ฟรี — มันคือการเดิมพันกับ horizon และ β\beta ไม่ใช่ค่าคงที่: มันเปลี่ยนระหว่างวัน ตาม depth และรอบเหตุการณ์ที่กำหนดไว้ ดังนั้น production fit ใด ๆ จึงต้อง re-estimate บน rolling windows แทนการตรึงค่าจาก backtest

เรื่องอื่นทั้งหมดครอบคลุมไว้ที่อื่นแล้ว งบ latency และบันได co-location / FPGA / kernel-bypass: ส่วน production ของ DeepLOB รูปแบบ liquidity ระหว่างวันที่เป็นรูปตัว U และสถิติ normalization ที่ drift: spread modeling การ recalibrate แยกตาม venue และเหตุผลที่โมเดลซึ่ง fit กับ Nasdaq ไม่โอนไปยังคู่คริปโตโดยไม่เปลี่ยนแปลง: DeepLOB อีกครั้ง โดยมีด้าน fragmentation ใน smart order routing

การบิดเบือนตลาด. ผลที่เฉพาะกับโมเดลนี้และควรกล่าวอย่างตรงไปตรงมาคือ CKS decomposition ให้น้ำหนักทุกเหตุการณ์ตามขนาดเพียงอย่างเดียว โดยไม่มีแนวคิดเรื่องเจตนาหรือความต่อเนื่อง ผู้วาง spoof ที่ใส่ขนาดแล้ว cancel จึง inject เข้า signal โดยตรงด้วยน้ำหนักเต็มตาม construction heuristic สำหรับตรวจจับ — cancel rate, อายุ order, พฤติกรรม wall เมื่อราคาเข้าใกล้ — อยู่ใน queue position และการวิเคราะห์ order book wall; phantom liquidity ข้าม venue อยู่ใน smart order routing

ประเด็นสำคัญ

ข้อค้นพบหลักของ order flow มาบรรจบเป็นสัญญาณชัดเจนหนึ่งเดียว

  1. OFI คือ event decomposition ไม่ใช่ snapshot. สูตร indicator สามกิ่ง map การเปลี่ยนแปลงทุกแบบที่ส่วนบนของสมุดคำสั่ง — ราคาขึ้น ราคาลง ขนาดเปลี่ยน — เป็นตัวเลขที่มีเครื่องหมายหนึ่งค่า จากสแนปชอตต่อเนื่องสองภาพและไม่มีอย่างอื่น

  2. R-squared หลักเป็นค่าร่วมเวลา. ตัวเลข 50-65% ของ CKS แยกการเคลื่อนไหวราคาในช่วงเวลาเดียวกัน ไม่ใช่การพยากรณ์ และไม่ควรนำไปเทียบกับโมเดลผลตอบแทนที่มองไปข้างหน้า

  3. OFI หลายระดับเพิ่มระดับ และ PCA ยุบระดับเหล่านั้น. ผล incremental-R2R^2 และความแปรปรวน 89% มาจากหุ้น Nasdaq การที่ depth ยังช่วยในสมุดคริปโตหรือไม่ยังไม่ได้ทดสอบที่นี่

  4. OFI กลืน trade imbalance. เมื่อใส่ร่วมกัน TI จะไม่มีนัยสำคัญ — cancel ที่ OFI มองเห็นแต่ TI มองไม่เห็นคือสิ่งที่สร้างความแตกต่าง

  5. โมเดลมองไม่เห็นเจตนา. การให้น้ำหนักเท่ากันตามขนาดคือสิ่งที่ทำให้มัน spoof ได้

  6. ไม่มีสิ่งใดในบทความนี้ถูกวัด. ตัวเลขทุกตัวที่ยกมาจากวรรณกรรมหุ้น ก่อนให้สัญญาณนี้แตะเงินทุน ต้อง fit บนข้อมูลสมุดคำสั่งของคุณเอง รายงาน specification แบบ lagged แยกต่างหาก และตรวจว่า edge อยู่รอดหลังค่าธรรมเนียมและ spread หรือไม่

อ่านเพิ่มเติม

เส้นทางวิจัย microstructure ที่เชื่อมต่อกันรอบแนวคิด OFI

  • 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.
ข้อจำกัดความรับผิดชอบ: ข้อมูลที่ให้ไว้ในบทความนี้มีไว้เพื่อการศึกษาและให้ข้อมูลเท่านั้น และไม่ถือเป็นคำแนะนำทางการเงิน การลงทุน หรือการเทรด การเทรดสกุลเงินดิจิทัลมีความเสี่ยงสูงที่จะขาดทุน

ผู้เขียน

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

ก้าวนำหน้าตลาด

สมัครรับจดหมายข่าวของเราเพื่อรับข้อมูลเชิงลึกการเทรดด้วย AI เฉพาะ การวิเคราะห์ตลาด และการอัปเดตแพลตฟอร์ม

เราเคารพความเป็นส่วนตัวของคุณ ยกเลิกการสมัครได้ทุกเมื่อ