Synthetic Control Methods for Evaluating Trading Strategies
Loạt bài này đã dành nhiều thời gian cho một con đường dẫn đến edge giả: selection. Deflated Sharpe Ratio định giá người thắng trong một cuộc tìm kiếm. Probability of Backtest Overfitting định giá chính cuộc tìm kiếm. Kết quả âm trung thực là điều xảy ra khi bạn áp dụng cả hai vào chiến lược thực sự muốn giao dịch. Bộ công cụ đó trả lời tốt một câu hỏi: tôi tìm thấy điều này vì đã nhìn quá nhiều lần sao?
Nó không chạm đến một con đường hoàn toàn khác: confounding. Bạn chỉ thay đổi một thứ — triển khai thuật toán execution mới, đổi tham số, phản ứng với một regime — và hiệu suất cải thiện. Không có cuộc tìm kiếm. Không có multiple-testing problem. Thế nhưng cải thiện đó vẫn có thể chẳng liên quan gì đến bạn, vì volatility đã tăng gấp đôi trong tuần triển khai. Không có deflation nào bắt được điều này: deflation hiệu chỉnh số lần thử, còn bạn chỉ chạy một lần.
Synthetic Control Method (SCM), do Abadie và Gardeazabal (2003) phát triển, rồi được Abadie, Diamond và Hainmueller (2010, 2015) hoàn thiện, tấn công trực tiếp vào confounding. Nó xây dựng một counterfactual — tổ hợp có trọng số của những instrument bạn không đụng tới, được chọn để bám sát instrument được xử lý trước can thiệp. Độ phân kỳ sau can thiệp giữa hai bên là ước lượng effect. Quan trọng nhất, SCM có tiêu chí falsification cho biết khi nào nó không thể tạo counterfactual đáng tin, cùng p-value dựa trên permutation không cần bạn tin vào câu chuyện tiệm cận nào.
Vì sao các phương án hiển nhiên thất bại

So sánh trước-sau trộn can thiệp với mọi thứ khác thay đổi trong ngày đó. So sánh với một benchmark duy nhất thất bại vì không asset đơn lẻ nào theo dõi asset của bạn — đây là Single Instrument Trap nhìn từ hướng ngược lại: nếu cấu trúc riêng của một instrument có thể giả tạo edge, cấu trúc riêng của một instrument cũng có thể giả tạo control. Donor pool của SCM là câu trả lời hình thức cho phản đối này: thay vì chọn một asset tương đồng, để dữ liệu pre-period chọn một basket có trọng số.
Difference-in-differences xứng đáng hơn một câu vì nó là phương án suýt đúng. DiD so sánh thay đổi ở unit được xử lý với thay đổi ở control group, và chỉ nhận diện effect dưới parallel trends: nếu không có can thiệp, treated và control sẽ di chuyển cùng nhau. Trong thị trường tài chính, giả định đó gần như không thể bảo vệ — các asset khác beta, volatility regime, liquidity tier và đường đi tương đối của chúng lang thang. SCM thay giả định bằng một object được fit: không giả định control di chuyển song song, nó xây dựng một control rồi cho thấy nó thực sự bám sát đến đâu. Nếu bám kém, nó yêu cầu bạn dừng lại.
Phương pháp

Setup và ký hiệu
Xét unit (asset, strategy hoặc portfolio) được quan sát trong period. Unit 1 là treated unit — asset hoặc strategy nơi bạn can thiệp. Units tạo thành donor pool của các unit chưa xử lý có thể so sánh.
Gọi là outcome của treated unit ở thời điểm khi có can thiệp, và là outcome nếu không có can thiệp. Treatment effect tại là:
Ta quan sát trực tiếp . Toàn bộ vấn đề là ước lượng .
Xây dựng Synthetic Control
SCM ước lượng như trung bình có trọng số của outcome donor:
trong đó vector trọng số thỏa với mọi và .
Hai ràng buộc này thực sự có tác dụng. Chúng buộc synthetic control là convex combination của donor, ngăn extrapolation ra ngoài support của dữ liệu. Hồi quy không ràng buộc có thể đưa cho bạn counterfactual tạo từ vị thế long 3.4x trên một coin đối đầu short 2.4x trên coin khác — fit pre-period đẹp về số học nhưng counterfactual không tương ứng với portfolio nào có thể nắm giữ. Ràng buộc convexity khiến counterfactual dễ diễn giải và trọng số sparse: thường chỉ một số ít donor có trọng số khác không, và bạn có thể đọc chúng.
Chọn trọng số tối ưu
Trọng số được chọn để tối thiểu hóa khoảng cách giữa treated unit và synthetic control trong pre-intervention period:
trong đó là vector của các đặc trưng pre-intervention cho treated unit, là matrix của các đặc trưng tương tự cho donor pool, còn là matrix đường chéo positive semi-definite gán tầm quan trọng cho từng predictor.
được chọn bằng cross-validation: chọn sao cho tạo ra tối thiểu mean squared prediction error (MSPE) của outcome trong pre-intervention period. Đây là nested optimization — outer loop trên , inner constrained solve cho .
Predictor cho ứng dụng trading
Trong khi Abadie et al. dùng GDP bình quân đầu người và cơ cấu ngành, ứng dụng trading cần:
- Đặc trưng return: mean return, volatility, skewness, kurtosis trong pre-period
- Đo liquidity: spread trung bình, volume hàng ngày, Amihud illiquidity ratio
- Đặc trưng microstructure: order flow imbalance, trade arrival rate, quote-to-trade ratio
- Factor exposure: beta với market, momentum, value và volatility factor
- Outcome trễ: giá trị PnL tích lũy tại một số ngày pre-intervention
Outcome trễ quan trọng nhất. Abadie et al. (2010) cho thấy nếu synthetic control khớp treated unit trên outcome trễ trong pre-intervention window dài, nó ngầm kiểm soát confounder không quan sát được dưới linear factor model — đó là lý do phương pháp đáng công sức. Bạn không cần gọi tên confounder đã tạo PnL. Bạn cần tìm donor cũng phơi nhiễm với nó.
Ứng dụng ở đâu

Kịch bản 1: Đánh giá thuật toán execution mới
Bạn triển khai lịch TWAP mới trên một instrument và muốn biết effect lên implementation shortfall. Cả metric và họ thuật toán đều được trình bày ở nơi khác — xem implementation shortfall và TCA cho phân rã cost và thuật toán TWAP/VWAP/POV cho chính scheduler. Đóng góp của SCM là lớp evaluation: donor pool là những instrument vẫn chạy scheduler cũ trong cùng window, nên thay đổi spread hay volume toàn venue tác động lên treated và donor như nhau.
Pre-intervention period: 60 trading day trước khi chuyển. Post-intervention period: 30 trading day sau đó. Ước lượng effect là gap:
Nếu scheduler mới thực sự giảm cost, sẽ bền vững sau — và quan trọng hơn, treated gap nằm ngoài cloud của donor gap.
Kịch bản 2: Tác động của sự kiện thị trường
Thay đổi fee schedule, thử nghiệm tick-size, transaction tax hoặc circuit-breaker rule tác động lên một venue. Treated unit: strategy của bạn trên venue bị ảnh hưởng. Donor pool: cùng pair trên những venue không bị thay đổi chạm tới. Effect của fee structure lên execution được xử lý riêng trong maker-taker fee và rebate; SCM biến "fill của chúng ta tệ hơn sau thay đổi" thành ước lượng có null đi kèm.
Kịch bản 3: Chuyển strategy theo regime
Bạn chuyển từ mean-reversion sang momentum trên một asset khi regime-detection signal phát tín hiệu. Chuyển đổi có tốt hơn đứng yên không? Donor pool: các asset tương tự nơi signal phát nhưng bạn không chuyển. Lưu ý bẫy — nếu regime signal phát trên toàn thị trường, donor cũng bị treated và design sụp đổ. Xem spillover rule bên dưới.
Placebo Test và Inference

Inference của SCM dựa trên permutation. Lý do chung để xây empirical null bằng resampling thay vì tin distribution dạng đóng được trình bày trong Monte Carlo và bootstrap cho backtest; phần sau là construction đặc thù của SCM.
Placebo theo thời gian
Dời intervention date lùi vào pre-period — chẳng hạn một nửa — rồi chạy lại. Nếu SCM tìm "treatment effect" tại ngày không có gì xảy ra, nó đang bắt divergence có từ trước chứ không phải intervention của bạn. Synthetic control đáng tin không cho thấy effect tại false intervention date.
Placebo theo không gian (Permutation Test)
Áp dụng toàn bộ SCM procedure cho từng donor, lần lượt giả định mỗi donor là treated unit:
- Với mỗi donor , xây synthetic control từ các donor còn lại cộng với treated unit ban đầu.
- Tính post-intervention gap .
- So sánh gap của treated unit với distribution của placebo gap.
P-value là một rank:
trong đó
Ratio, thay vì raw post-period gap, là statistic đúng: donor chưa từng fit tốt sẽ cho post gap lớn vì lý do không liên quan intervention nào, và chia cho pre-period MSPE phạt chính điều đó.
Đây là cùng inferential move mà Deflated Sharpe Ratio thực hiện — xếp candidate của bạn trong reference distribution tạo bằng empirical, không bao giờ so với zero. DSR xếp winner với best-of-N null trên trial; SCM xếp treated unit với null trên unit. Estimator khác nhau nhưng kỷ luật giống nhau, cùng cảnh báo: với donor, p-value nhỏ nhất đạt được là . Permutation test không thể vượt qua granularity của chính nó, vì vậy donor pool nhỏ giới hạn mức significance bạn có quyền tuyên bố, dù effect trông lớn đến đâu.
Lọc thực tế
Abadie et al. khuyến nghị loại placebo unit có pre-intervention fit kém — pre-MSPE lớn hơn lần treated unit, với thường từ 2 đến 20. Placebo fit kém có denominator nhỏ và tạo MSPE ratio khổng lồ, làm nhiễm reference distribution và khiến treated unit trông bình thường. Hãy report ratio tại nhiều threshold; nếu p-value chỉ significant ở một cụ thể, bạn đã tìm thấy tuning parameter chứ chưa tìm thấy effect.
Implementasi

Kode di bawah hanya menggunakan numpy dan scipy agar mekanismenya terlihat. Alternatif production — SparseSC (Microsoft Research), pysyncon, SyntheticControlMethods — menangani edge case dan berskala lebih baik.
Membangun Matrix Predictor
import numpy as np
import pandas as pd
from scipy.optimize import minimize
def build_predictors(series: pd.Series, T0: int) -> np.ndarray:
"""
Extract predictor vector from pre-intervention data.
Features: mean return, volatility, skewness, kurtosis,
plus lagged outcome values at regular intervals.
"""
pre = series.iloc[:T0]
daily_ret = pre.diff().dropna()
stats = [
daily_ret.mean(),
daily_ret.std(),
daily_ret.skew(),
daily_ret.kurtosis(),
]
lag_indices = np.linspace(0, T0 - 1, 5, dtype=int)
lags = pre.iloc[lag_indices].values.tolist()
return np.array(stats + lags)
df di sini adalah panel cumulative return — treated unit di column 0, donor di kolom lain, satu baris per bar. Gunakan cumulative return, bukan price level.
X1 = build_predictors(df["Treated"], T0).reshape(-1, 1) # (k x 1)
X0 = np.column_stack([ # (k x J)
build_predictors(df[col], T0) for col in df.columns[1:]
])
Optimisasi Bobot
def solve_weights(
X1: np.ndarray,
X0: np.ndarray,
Y1_pre: np.ndarray,
Y0_pre: np.ndarray,
) -> np.ndarray:
"""
Solve for optimal synthetic control weights.
Nested optimization:
Outer: optimize V (predictor importance)
Inner: optimize w (donor weights) given V
"""
k = X1.shape[0]
J = X0.shape[1]
def solve_w_given_V(V_diag: np.ndarray) -> np.ndarray:
"""Inner: find w minimizing weighted predictor distance."""
V = np.diag(V_diag)
def objective(w):
gap = X1.flatten() - X0 @ w
return gap @ V @ gap
constraints = {"type": "eq", "fun": lambda w: np.sum(w) - 1.0}
bounds = [(0, 1)] * J
w0 = np.ones(J) / J
result = minimize(objective, w0, method="SLSQP",
bounds=bounds, constraints=constraints,
options={"maxiter": 1000, "ftol": 1e-12})
return result.x
def outer_objective(V_diag: np.ndarray) -> float:
"""Outer: minimize pre-period MSPE given V."""
w = solve_w_given_V(np.abs(V_diag))
return np.mean((Y1_pre - Y0_pre @ w) ** 2)
V0 = np.ones(k)
result = minimize(outer_objective, V0, method="Nelder-Mead",
options={"maxiter": 5000, "xatol": 1e-8})
return solve_w_given_V(np.abs(result.x))
Y1_pre = df["Treated"].iloc[:T0].values
Y0_pre = df.iloc[:T0, 1:].values
weights = solve_weights(X1, X0, Y1_pre, Y0_pre)
for col, w in zip(df.columns[1:], weights):
if w > 0.01:
print(f" {col}: {w:.4f}")
Gap series kemudian adalah df["Treated"].values - df.iloc[:, 1:].values @ weights, diplot terhadap intervention date bersama donor gap dari placebo run di bawah.
Placebo Test
def run_placebo_tests(df: pd.DataFrame, T0: int) -> pd.DataFrame:
"""In-space placebo: treat each unit in turn as if intervened upon."""
results = []
all_columns = df.columns.tolist()
for placebo_col in all_columns:
donor_cols = [c for c in all_columns if c != placebo_col]
donor_df = df[donor_cols]
X1_p = build_predictors(df[placebo_col], T0).reshape(-1, 1)
X0_p = np.column_stack([
build_predictors(donor_df[c], T0) for c in donor_cols
])
w_p = solve_weights(
X1_p, X0_p,
df[placebo_col].iloc[:T0].values,
donor_df.iloc[:T0].values,
)
gap_p = df[placebo_col].values - donor_df.values @ w_p
mspe_pre = np.mean(gap_p[:T0] ** 2)
mspe_post = np.mean(gap_p[T0:] ** 2)
results.append({
"unit": placebo_col,
"mspe_pre": mspe_pre,
"mspe_post": mspe_post,
"mspe_ratio": mspe_post / mspe_pre if mspe_pre > 1e-10 else np.inf,
"gap_series": gap_p,
"is_treated": placebo_col == "Treated",
})
return pd.DataFrame(results)
placebo_results = run_placebo_tests(df, T0)
treated_pre = placebo_results.loc[placebo_results["is_treated"], "mspe_pre"].values[0]
filtered = placebo_results[placebo_results["mspe_pre"] <= 5 * treated_pre]
treated_ratio = filtered.loc[filtered["is_treated"], "mspe_ratio"].values[0]
p_value = (filtered["mspe_ratio"] >= treated_ratio).sum() / len(filtered)
print(f"MSPE ratio (treated): {treated_ratio:.2f} p = {p_value:.3f}")
Hiệu chỉnh Estimator
Có một demonstration đáng chạy và một demonstration đáng từ chối. Tạo synthetic panel data, inject effect +0.3%/day, recover khoảng +0.3%/day rồi trình bày như evidence chỉ chứng minh SLSQP hội tụ. Nó circular, và blog này không publish điều đó.
Synthetic data thực sự hữu ích cho calibration — cùng standard được áp dụng trong bài DSR và PBO, nơi known null làm statistic đáng tin. Hai kiểm tra, đều chạy trên data sinh từ linear factor model với donor count và pre-period length thực tế:
- Power tại effect size đã biết. Inject effect có magnitude đã biết. Estimated post-period gap có recover được nó không và sai số bao nhiêu? Sweep effect size giảm dần đến khi recovery thất bại — floor đó là effect nhỏ nhất panel geometry phát hiện được, và result thực dưới nó là noise bất kể p-value.
- Size dưới true null. Inject nothing. Chạy toàn bộ placebo pipeline. P-value phải gần uniform trên , nên nó phải dưới 0.10 khoảng 10% số lần trong repeated draw. Nếu thường xuyên hơn, predictor set hoặc filter threshold của bạn đang chế tạo significance và pipeline hỏng trước khi chạm real data.
Kiểm tra 2 là thứ mọi người bỏ qua nhưng lại quan trọng. Hãy chạy nó trước.
Cân nhắc thực tế

Chọn Donor Pool
Donor phải có thể so sánh hợp lý với treated unit và không bị ảnh hưởng bởi intervention. Với execution: cùng algorithm trên instrument tương tự — cùng sector, market-cap decile và liquidity tier. Taxonomy trong multi-symbol validation có thể dùng lại trực tiếp ở đây; các property khiến instrument là robustness test công bằng cũng khiến chúng là donor pool công bằng. Với event theo venue: cùng instrument trên những venue mà thay đổi không chạm tới.
Loại unit có spillover. Nếu thay đổi algorithm trên một instrument làm quoting của bạn trên instrument tương quan thay đổi, các instrument đó bị treated một phần. Để chúng trong pool sẽ bias synthetic control về post-period path của treated unit và thu nhỏ estimate về zero — design flaw dễ tạo null result như một control tệ tạo positive result.
Pre-Intervention Fit là toàn bộ lập luận
Độ tin cậy của SCM hoàn toàn dựa vào pre-period fit. Report pre-intervention MSPE và cho thấy fit. Pre-fit kém làm mất giá trị mọi thứ sau .
Abadie et al. (2015) nói rõ quy tắc: nếu không convex combination donor nào tái tạo trajectory pre-intervention của treated unit, đừng dùng method. Đây là tiêu chí falsification trung thực và hiếm hơn mức nên có — hầu hết estimator trả về một con số dù bạn đưa gì vào. SCM cho biết khi nào nó không tạo được counterfactual đáng tin, và phản ứng đúng là publish failure, không phải mở donor pool đến khi fit đẹp. Mở pool để đuổi theo pre-fit chính là overfitting tương tự mà phần còn lại của series bàn tới, chỉ chuyển sang bước xây control.
Overfitting trong Panel Ngắn
Đại lượng đặc thù SCM cần theo dõi là tỷ lệ donor trên pre-period observation . Với trọng số tự do fit trên điểm, perfect pre-fit trở nên có thể đạt được do construction khi tiến gần — và perfect pre-fit theo cách đó không mang thông tin. Giữ lớn hơn đáng kể ; nếu strategy chạy 30 ngày trước thay đổi và có 20 donor candidate, bạn không có SCM problem mà có interpolation. Mitigation:
- Regularization: SparseSC thêm penalty và vào weight optimization.
- Giới hạn donor pool: ít donor được biện minh tốt hơn nhiều donor — đổi lại permutation grid thô hơn vì cũng đặt minimum p-value. Căng thẳng này là thực và không có lời giải sạch.
- Augmented SCM: Ben-Michael, Feller và Rothstein (2021) dùng outcome model để bias-correct khi perfect pre-fit không đạt được.
Non-Stationarity
Chạy SCM trên cumulative return, không bao giờ trên price level — level mời gọi spurious fit. Xác nhận pre-intervention gap mean-zero và stationary trước khi tin post-period divergence; machinery ADF và Engle-Granger cho việc này, gồm corrected critical value khi test fitted residual thay vì observed series, được trình bày trong statistical arbitrage và pairs trading.
Nhiều Treated Unit
Triển khai trên nhiều asset cùng lúc phá vỡ design single-treated-unit. Extension: pooled SCM (average effect trên treated unit) và design staggered adoption (Ben-Michael et al., 2022) cho unit được treated ở thời điểm khác nhau.
Khi không nên dùng SCM

Phù hợp với: intervention rời rạc, định nghĩa rõ tại ngày đã biết; donor pool hợp lý gồm unit chưa xử lý có thể so sánh; intervention không được anticipation trước, vì anticipation di chuyển treated unit trước và vi phạm identification.
Không phù hợp với: continuous treatment, như parameter drift trong nhiều tuần; setting không có untreated comparable thật sự; evaluation tần suất cao khi signal-to-noise ratio đánh bại việc ước lượng trọng số.
Kết luận

Selection và confounding là failure mode khác nhau, cần instrument khác nhau. DSR và PBO định giá search. SCM định giá counterfactual, và là tool duy nhất trong series này xử lý trường hợp bạn chạy đúng một experiment nhưng vẫn không thể tin result.
Lý do chọn nó thay ad-hoc benchmark không phải vì nó tạo effect lớn hơn — thường xuyên nó tạo effect nhỏ hơn — mà vì nó có thể từ chối. Nếu không convex combination donor nào theo dõi instrument trước intervention, SCM nói vậy và bạn dừng. Nếu treated gap nằm trong donor cloud, placebo p-value nói vậy và bạn report null. Hai lối thoát đó là giá trị của method, và bài viết SCM không report pre-period MSPE cùng placebo p-value đã bỏ qua hai phần duy nhất khiến nó hơn một chart của hai đường phân kỳ.
Đọc thêm
- Abadie, A., Diamond, A., & Hainmueller, J. (2010). "Synthetic Control Methods for Comparative Case Studies." Journal of the American Statistical Association, 105(490), 493-505.
- Abadie, A. (2021). "Using Synthetic Controls: Feasibility, Data Requirements, and Methodological Aspects." Journal of Economic Literature, 59(2), 391-425.
- Ben-Michael, E., Feller, A., & Rothstein, J. (2021). "The Augmented Synthetic Control Method." Journal of the American Statistical Association, 116(536), 1789-1803.
- Cunningham, S. (2021). Causal Inference: The Mixtape. Chapter 10: Synthetic Control. Available at mixtape.scunning.com.
- Microsoft Research. SparseSC: Sparse Synthetic Controls. github.com/microsoft/SparseSC.
Tác Giả
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.