Skip to content

Co-integration and Error Correction — Lý Thuyết Đồng Tích Hợp

Tác giả: Robert F. Engle, Clive W. J. Granger (1987) Nguồn: Econometrica, Vol. 55, No. 2 (Giải Nobel 2003) Tag: mới:2026-05-16 #cointegration #pairs-trading #mean-reversion

Nội dung chính (Core Concept)

Trước Engle-Granger, giới econometrics đối mặt nghịch lý: nhiều chuỗi kinh tế (GDP, tiền cung, giá cổ phiếu) là non-stationary I(1) — random-walk-like — nên hồi quy chúng trực tiếp cho ra spurious regression với R² cao giả tạo. Engle & Granger định nghĩa cointegration: hai chuỗi $X_t, Y_t \sim I(1)$ được gọi là cointegrated nếu tồn tại $\beta$ sao cho $Z_t = Y_t - \beta X_t \sim I(0)$ — tức tổ hợp tuyến tính là stationary, có mean và variance hữu hạn.

Ý nghĩa kinh tế: hai chuỗi có thể đi lang thang vô tận, nhưng nếu cointegrated thì chênh lệch giữa chúng bị buộc về một equilibrium dài hạn. Engle-Granger chứng minh Representation Theorem: nếu $X_t, Y_t$ cointegrated, tồn tại biểu diễn Error Correction Model (ECM):

$$\Delta Y_t = \alpha (Y_{t-1} - \beta X_{t-1}) + \gamma \Delta X_t + \epsilon_t$$

trong đó $\alpha < 0$ là tốc độ điều chỉnh về equilibrium. Khi $Z_{t-1}$ (residual) dương, $\Delta Y_t$ âm — Y bị kéo xuống. Đây là cơ chế mean-reversion về spread.

Hai bước test Engle-Granger: (1) hồi quy OLS $Y_t = a + bX_t + u_t$; (2) chạy ADF test trên residuals $\hat{u}_t$ — nếu reject unit root, cặp cointegrated.

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

Đây là nền tảng toán học của pairs trading và stat-arb:

  • Cointegration vs Correlation: Hai cổ phiếu có thể correlation = 0.9 nhưng spread phình ra vô tận (không cointegrated) — pairs trading sẽ chết. Ngược lại, cointegrated nghĩa là spread mean-revert chắc chắn trong dài hạn.
  • Hedge ratio $\beta$ ≠ 1: Long 1 cổ phiếu A short β cổ phiếu B (không phải 1:1). Hedge ratio đến từ regression coefficient.
  • Half-life của ECM: $\alpha$ cho biết tốc độ về equilibrium. Half-life = $-\log(2)/\log(1+\alpha)$. Half-life ngắn → trade nhiều; quá dài (> 60 ngày) → spread tay đôi không profitable sau costs.
  • Hạn chế: hệ số $\beta$ có thể drift theo thời gian — cần re-estimate rolling, hoặc dùng Kalman filter (xem [[kalman-pairs-spread]]) để cập nhật adaptive.

Ứng dụng trên VN30F

VN30F là futures chỉ một sản phẩm — không pairs trade trực tiếp, nhưng có thể áp dụng cointegration:

  1. VN30F vs spot VN30 index: Basis = $F - S \cdot e^{(r-q)\tau}$ thường mean-reverting; nếu cointegrated, basis arbitrage khi |basis z-score| > 2.
  2. VN30F vs ETF E1VFVN30: ETF tracking VN30, có thể có premium/discount intra-day.
  3. VN30F vs rổ cổ phiếu trọng số lớn (VCB, VIC, HPG, VHM): test cointegration giữa VN30F và linear combination top-10 components → giao dịch divergence khi index futures lệch rổ.

Quy trình:

  • Lấy log-prices, run Engle-Granger 2-step trên window 252 ngày rolling.
  • Nếu ADF p < 0.05 → cặp valid; tính z-score $\hat{u}_t$; entry $|z|>2$, exit $|z|<0.5$, stop $|z|>4$.
  • Re-test cointegration hàng tuần — nếu break, đóng vị thế ngay.

Code minh họa (Python)

python
import numpy as np
import pandas as pd
import statsmodels.api as sm
from statsmodels.tsa.stattools import adfuller, coint

def engle_granger_test(y: pd.Series, x: pd.Series):
    """Step 1: OLS regression. Step 2: ADF on residuals."""
    X = sm.add_constant(x)
    model = sm.OLS(y, X).fit()
    beta = model.params.iloc[1]
    residuals = model.resid
    adf_stat, p_value, *_ = adfuller(residuals, maxlag=1, autolag=None)
    return {
        'beta': beta,
        'adf_stat': adf_stat,
        'p_value': p_value,
        'cointegrated': p_value < 0.05,
        'residuals': residuals,
    }

def half_life_ou(residuals: pd.Series) -> float:
    """Half-life of mean reversion via OU fit on residuals."""
    z = residuals.dropna()
    z_lag = z.shift(1).dropna()
    delta_z = z.diff().dropna()
    z_lag, delta_z = z_lag.align(delta_z, join='inner')
    alpha = sm.OLS(delta_z, sm.add_constant(z_lag)).fit().params.iloc[1]
    return -np.log(2) / alpha if alpha < 0 else np.inf

def pairs_signal(y: pd.Series, x: pd.Series, lookback=252, entry=2.0, exit=0.5):
    rolling_beta = []
    z_scores = []
    for t in range(lookback, len(y)):
        ys, xs = y.iloc[t-lookback:t], x.iloc[t-lookback:t]
        res = engle_granger_test(ys, xs)
        if not res['cointegrated']:
            rolling_beta.append(np.nan); z_scores.append(np.nan); continue
        beta = res['beta']
        spread = y.iloc[t] - beta * x.iloc[t]
        mu, sigma = res['residuals'].mean(), res['residuals'].std()
        z = (spread - mu) / sigma
        rolling_beta.append(beta); z_scores.append(z)
    return pd.Series(z_scores, index=y.index[lookback:])

Tài liệu tham khảo

  • Engle, R. F., & Granger, C. W. J. (1987). Co-integration and error correction: Representation, estimation, and testing. Econometrica, 55(2), 251-276.
  • Johansen, S. (1991). Estimation and hypothesis testing of cointegration vectors in Gaussian vector autoregressive models. Econometrica, 59(6), 1551-1580.
  • Vidyamurthy, G. (2004). Pairs Trading: Quantitative Methods and Analysis. Wiley.

Powered by dautu.tech