Skip to content

Signal 16 — Microprice (Stoikov Fair Price)

Cảnh báo

Microprice là fair-price estimator cho HFT/market making chứ không phải tín hiệu entry/exit độc lập cho swing trading. Nó nên được dùng để (i) định giá limit order, (ii) chuẩn hóa các tín hiệu mean-reversion về fair price, (iii) phát hiện stale midprice.

Thuộc tínhGiá trị
Phân loạiMicrostructure / Fair Price Estimator
Khung thời gianTick / 1s / 1m
Paper gốcStoikov (2018), QF Journal
Loại dữ liệuLevel-1 (bid/ask + size) hoặc Level-2
Hướng giao dịchReference price (không generate trade trực tiếp)
CapacityCao (đặc biệt cho HFT/MM)

Ý tưởng (Concept)

Midprice $m_t = (b_t + a_t)/2$ là estimator naive cho giá thực, nhưng có vấn đề: khi size mất cân đối (vd. bid size = 1000 lots, ask size = 100), giá thực sự kỳ vọng lệch về phía có ít size hơn — bởi vì side mỏng dễ bị "ăn sạch" hơn. Stoikov chứng minh micro-price:

$$p_{\text{micro}} = a_t \cdot \frac{Q_b}{Q_b + Q_a} + b_t \cdot \frac{Q_a}{Q_b + Q_a}$$

là estimator không biased về mean midprice tương lai trong nhiều mô hình order book, vượt trội midprice và weighted-midprice naive. Trực giác: nếu bid size dồi dào → áp lực mua mạnh → giá tới gần ask. Công thức cho weight ngược size để bắt asymmetry này.

Bản gốc Stoikov (2018) còn extend sang adjusted microprice dùng Markov chain trên (imbalance, spread) để dự đoán $E[m_{t+\delta}]$ thay vì chỉ điểm hiện tại — đây là full version cho HFT.

Công thức (Formula)

Basic microprice:

$$p_{\text{micro}} = \frac{a_t Q_b + b_t Q_a}{Q_b + Q_a}$$

trong đó $b_t$ = best bid, $a_t$ = best ask, $Q_b$ = size at best bid, $Q_a$ = size at best ask.

Imbalance ratio (định nghĩa convenience):

$$I_t = \frac{Q_b - Q_a}{Q_b + Q_a} \in [-1, 1]$$

khi đó: $p_{\text{micro}} = m_t + \frac{\text{spread}_t}{2} \cdot I_t$

Microprice z-score (cho mean-reversion):

$$z_t = \frac{p_{\text{micro}, t} - m_t}{\sigma_{\text{micro-mid}}^{(\text{rolling})}}$$

Cách giao dịch (Entry/Exit Rules)

Use case 1 — Reference price cho limit orders:

  • Post bid tại $p_{\text{micro}} - k\sigma$, post ask tại $p_{\text{micro}} + k\sigma$ (k = 0.5–1.5).
  • Tránh post symmetric quanh midprice khi imbalance lớn (sẽ bị adverse fill ở phía dồi dào).

Use case 2 — Tín hiệu mean-reversion micro vs mid:

  • Entry: khi $|z_t| > 2$ và $|I_t| > 0.6$ — microprice đẩy mạnh khỏi mid, kỳ vọng mid sẽ "đuổi theo" microprice trong 5-30 giây.
  • Direction: long nếu $p_{\text{micro}} > m_t$ (imbalance bid).
  • Exit: khi $|z_t| < 0.3$ hoặc 30 giây timeout.
  • Stop loss: $\pm 1$ tick từ entry.

Use case 3 — Stale-quote detector:

  • Nếu microprice di chuyển nhưng midprice không cập nhật trong N tick → quote stale, không trade execution.

Timeframe khuyến nghị: tick-level cho HFT; resample 1-second hoặc 5-second cho intraday momentum.

Code Python (Python Implementation)

python
import numpy as np
import pandas as pd

def microprice(bid: pd.Series, ask: pd.Series,
               bid_size: pd.Series, ask_size: pd.Series) -> pd.Series:
    """Stoikov microprice from Level-1 quotes."""
    denom = bid_size + ask_size
    return (ask * bid_size + bid * ask_size) / denom.replace(0, np.nan)

def imbalance(bid_size: pd.Series, ask_size: pd.Series) -> pd.Series:
    """Order book imbalance in [-1, 1]; +1 = all bid, -1 = all ask."""
    total = bid_size + ask_size
    return (bid_size - ask_size) / total.replace(0, np.nan)

def micro_minus_mid_z(bid: pd.Series, ask: pd.Series,
                      bid_size: pd.Series, ask_size: pd.Series,
                      lookback: int = 600) -> pd.Series:
    """Z-score of (microprice - midprice). Lookback in observations."""
    mp = microprice(bid, ask, bid_size, ask_size)
    mid = (bid + ask) / 2
    diff = mp - mid
    return (diff - diff.rolling(lookback).mean()) / diff.rolling(lookback).std()

def microprice_signal(quotes: pd.DataFrame,
                      z_entry: float = 2.0, z_exit: float = 0.3,
                      imb_min: float = 0.6) -> pd.Series:
    """
    quotes: DataFrame có cột [bid, ask, bid_size, ask_size].
    Output: -1 / 0 / +1 position state.
    """
    z = micro_minus_mid_z(quotes['bid'], quotes['ask'],
                          quotes['bid_size'], quotes['ask_size'])
    imb = imbalance(quotes['bid_size'], quotes['ask_size'])
    position = pd.Series(0, index=quotes.index, dtype=int)
    in_pos = 0
    for t in range(1, len(quotes)):
        if in_pos == 0:
            if z.iloc[t] > z_entry and imb.iloc[t] > imb_min:
                in_pos = 1
            elif z.iloc[t] < -z_entry and imb.iloc[t] < -imb_min:
                in_pos = -1
        else:
            if abs(z.iloc[t]) < z_exit:
                in_pos = 0
        position.iloc[t] = in_pos
    return position

# Ví dụ Level-1 VN30F
# df cols: time, bid, ask, bid_size, ask_size
# df = pd.read_parquet('vn30f_l1.parquet').set_index('time')
# df['micro'] = microprice(df.bid, df.ask, df.bid_size, df.ask_size)
# df['signal'] = microprice_signal(df[['bid','ask','bid_size','ask_size']])

Ưu nhược điểm

Ưu điểm:

  • Tính toán cực nhanh, chỉ cần Level-1 quotes — phù hợp HFT/low-latency.
  • Cải thiện nhiều so với midprice khi book asymmetric (rất hay xảy ra ở VN30F gần expiry).
  • reference price chuẩn cho execution algos (TWAP, VWAP, POV) và market making.
  • Đã có nền tảng lý thuyết chắc (Stoikov 2018, Cartea-Jaimungal book).

Nhược điểm:

  • Cần data Level-1 real-time hoặc gần real-time; daily OHLCV không dùng được.
  • Khi spread = 0 (tradethrough) hoặc one-sided book (locked/crossed), công thức không xác định — cần handler edge cases.
  • Tín hiệu trade trực tiếp (use case 2) có rất ít alpha sau costs — chủ yếu hữu ích cho market makers, không phải directional traders.
  • Phụ thuộc microstructure venue: VN30F (HOSE futures) có tick size lớn → microprice ít rõ rệt hơn US futures tick nhỏ.

Liên kết

  • Kết hợp với [[ofi]] (Order Flow Imbalance) để strengthen microstructure signal.
  • Bổ sung [[avellaneda-stoikov-2008]] — Stoikov tự author cả hai bài.
  • Lý thuyết nền: [[glosten-milgrom-1985]] (adverse selection drives imbalance).

Powered by dautu.tech