Skip to content

Illiquidity and Stock Returns — Phần Bù Thanh Khoản Trong Cross-Section

Tác giả: Yakov Amihud (2002) Nguồn: Journal of Financial Markets, Vol. 5, No. 1 Tag: mới:2026-05-16 #liquidity #microstructure #factor

Nội dung chính (Core Concept)

Trước Amihud, đo lường thanh khoản đòi hỏi dữ liệu intra-day order book — đắt và khó tiếp cận. Amihud đề xuất một proxy cực kỳ đơn giản chỉ cần daily OHLCV:

$$\text{ILLIQ}{i,t} = \frac{1}{D{i,t}} \sum_{d=1}^{D_{i,t}} \frac{|r_{i,d}|}{V_{i,d}}$$

trong đó $r_{i,d}$ là daily return và $V_{i,d}$ là daily dollar volume. Trực giác: tỉ số $|r|/V$ đo price impact per dollar traded — bao nhiêu phần trăm giá thay đổi cho mỗi đồng giao dịch. ILLIQ càng cao → cổ phiếu càng kém thanh khoản (1 đồng order đẩy giá mạnh).

Amihud chạy Fama-MacBeth cross-section regression trên NYSE 1964–1997, kết quả:

  1. Hệ số ILLIQ dương và significant ngay cả sau khi kiểm soát beta, size, book-to-market, momentum. Cổ phiếu kém thanh khoản kiếm illiquidity premium ~0.5-1% năm mỗi đơn vị ILLIQ.
  2. ILLIQ time-series có power giải thích market-wide expected returns: khi ILLIQ tăng (market dries up), expected stock returns tăng — phù hợp lý thuyết Pastor-Stambaugh.
  3. Negative contemporaneous effect: shock thanh khoản (ILLIQ tăng đột biến) đi kèm với giá giảm — flight to liquidity.

Đây là một trong những factor đơn giản nhất mà robust nhất — chỉ cần returnvolume.

Ý tưởng chính cho giao dịch (Key Trading Insight)

Bài này có 3 implication trực tiếp cho quant:

  • Cross-sectional liquidity premium: Long bottom-decile ILLIQ (kém thanh khoản), short top-decile (thanh khoản tốt). Strategy này hoạt động vì investors đòi premium ôm risk thanh khoản — đặc biệt mạnh trên small/mid-cap.
  • Liquidity timing: Khi market-wide ILLIQ tăng (panic), expected returns tới sẽ cao → vào long market. Khi ILLIQ thấp (bull complacency), expected returns thấp → giảm leverage.
  • Filter rủi ro thanh khoản: Tránh trade cổ phiếu có ILLIQ > P95 historical — slippage sẽ ăn hết alpha. Đây là execution filter, không phải alpha signal.

Kết hợp với [[hasbrouck-1991]] (information content of trades) và [[kyle-1985]] (Kyle's λ), ILLIQ là proxy "nghèo người" cho Kyle's lambda.

Ứng dụng trên VN30F

VN30F là một sản phẩm duy nhất nên không có cross-section, nhưng ILLIQ áp dụng được dạng time-series:

  1. Liquidity regime trên futures: Tính ILLIQ rolling 20 ngày trên VN30F. Khi ILLIQ tăng > 1.5× median 6 tháng → giảm position size (slippage cao).
  2. Spot equity universe (VN30 components): Áp dụng cross-sectional ranking ILLIQ cho 30 cổ phiếu trong rổ. Top illiquid (HVN, BVH thường) có thể có illiquidity premium nhưng cũng có execution risk.
  3. Macro liquidity timing: Market-wide ILLIQ aggregate (trung bình VN30 components) làm regime indicator — khi spike, giảm gross exposure VN30F.

Code minh họa (Python)

python
import numpy as np
import pandas as pd

def amihud_illiq(returns: pd.Series, dollar_volume: pd.Series,
                 window: int = 21) -> pd.Series:
    """
    ILLIQ = mean(|r| / V_dollar) rolling.
    returns: daily simple returns.
    dollar_volume: daily volume × VWAP (USD or VND).
    Scale: nhân 10^6 cho equity USD để dễ đọc; 10^9 cho VND.
    """
    daily_impact = returns.abs() / dollar_volume.replace(0, np.nan)
    return daily_impact.rolling(window).mean() * 1e9

def illiq_zscore_regime(illiq: pd.Series, lookback: int = 126) -> pd.Series:
    """Z-score of ILLIQ vs 6-month history for liquidity timing."""
    return (illiq - illiq.rolling(lookback).mean()) / illiq.rolling(lookback).std()

def position_size_liquidity_adjusted(target_size: float, illiq_z: float,
                                      max_z: float = 2.0) -> float:
    """Reduce size when ILLIQ z-score is high (market is illiquid)."""
    if illiq_z > max_z:
        return target_size * 0.3
    elif illiq_z > 1.0:
        return target_size * (1 - 0.5 * (illiq_z - 1.0))
    return target_size

# Cross-sectional ranking
def cs_illiq_signal(panel_returns: pd.DataFrame,
                    panel_dollar_volume: pd.DataFrame) -> pd.DataFrame:
    """
    panel_*: index=date, columns=tickers.
    Output: rank-based long illiquid / short liquid.
    """
    illiq = (panel_returns.abs() / panel_dollar_volume.replace(0, np.nan))
    illiq_ranks = illiq.rolling(21).mean().rank(axis=1, pct=True)
    signal = 2 * illiq_ranks - 1  # range [-1, 1]; +1 = top illiquid
    return signal

Tài liệu tham khảo

  • Amihud, Y. (2002). Illiquidity and stock returns: cross-section and time-series effects. J. of Financial Markets, 5(1), 31-56.
  • Pastor, L., & Stambaugh, R. F. (2003). Liquidity risk and expected stock returns. J. of Political Economy, 111(3), 642-685.
  • Acharya, V. V., & Pedersen, L. H. (2005). Asset pricing with liquidity risk. J. of Financial Economics, 77(2), 375-410.

Powered by dautu.tech