4、随机对照试验:RCT原理、投资中的A/B测试、随机化方法、样本量计算、常见陷阱
各位好,今天我们来聊聊随机对照试验。说实话,这玩意儿在医学界已经用了上百年,但在投资领域,很多人还把它当成实验室里的东西。我个人觉得,RCT是因果推断的“黄金标准”,没有之一。你想想看,如果连随机化都做不好,那后面算出来的因果效应,基本就是自欺欺人。
4.1 RCT的基本原理
RCT的核心逻辑其实很简单:把样本随机分成两组,一组给处理(比如推一个新策略),另一组不给(保持原样)。然后比较两组的差异。这个差异,就是处理效应。
为什么随机化这么重要?因为随机化能消除混杂因素。我举个例子,假设你想测试一个选股模型,如果不随机化,你可能会把表现好的股票都分到实验组,那结果肯定有偏。随机化之后,理论上两组在所有特征上都是均衡的——年龄、市值、波动率、行业分布,全都差不多。这样,两组之间的差异就只能归因于处理本身。
嗯,这里要注意:RCT的假设是“可忽略性”,也就是处理分配独立于潜在结果。随机化能保证这一点,但前提是随机化真的做到了“随机”。
核心公式:
ATE = E[Y(1) - Y(0)] = E[Y | T=1] - E[Y | T=0]
其中Y(1)是处理组的潜在结果,Y(0)是对照组的潜在结果,T是处理指示变量。
4.2 投资中的A/B测试
在投资领域,A/B测试其实就是RCT的实践版。我见过很多团队,一上来就搞全量上线,结果亏得底裤都不剩。正确的做法是:先做小范围的A/B测试,验证策略有效,再逐步放量。
投资中的A/B测试,常见场景包括:
- 策略参数调优:比如均线策略的周期参数,是20天好还是30天好?
- 信号生成方式:用LSTM还是用XGBoost?
- 风控规则:止损线设5%还是8%?
- 交易执行算法:TWAP还是VWAP?
我在项目中遇到过一件事:有个团队测试一个高频策略,实验组和对照组各分配了100只股票。结果实验组跑赢了5%,大家都很兴奋。但我一看数据,发现实验组里科技股占比明显偏高。这就是随机化没做好,导致行业分布不均衡。后来重新随机化,效果就消失了。
我的建议:做A/B测试时,一定要做“分层随机化”。比如按市值、行业、波动率分层,然后在每一层内随机分配。这样能保证两组在关键特征上均衡。
4.3 随机化方法
随机化不是随便扔硬币。常用的方法有几种:
| 方法 | 描述 | 适用场景 |
|---|---|---|
| 简单随机化 | 每个样本独立地以概率p分配到处理组 | 样本量大,且无分层需求 |
| 分层随机化 | 先按关键特征分层,再在层内随机分配 | 样本量中等,有已知混杂因素 |
| 区组随机化 | 将样本分成大小相等的区组,每个区组内随机分配 | 样本量小,需要保证组间均衡 |
| 自适应随机化 | 根据已有结果动态调整分配概率 | 伦理考量,或希望更多样本分配到更优处理 |
我个人习惯用分层随机化。原因很简单:投资数据里,行业和市值的影响太大了。如果不分层,随机化很容易失衡。举个例子,你随机分两组,每组100只股票,结果实验组里金融股占了40只,对照组只有20只。那实验组的收益差异,到底是策略带来的,还是金融板块涨跌带来的?说不清楚。
下面是一个简单的分层随机化代码示例:
import numpy as np
import pandas as pd
def stratified_randomization(df, strata_cols, treat_prob=0.5):
"""
分层随机化
df: 数据框
strata_cols: 分层变量列表
treat_prob: 分配到处理组的概率
"""
df['treatment'] = 0
for _, group in df.groupby(strata_cols):
n = len(group)
n_treat = int(n * treat_prob)
indices = group.index.tolist()
np.random.shuffle(indices)
treat_indices = indices[:n_treat]
df.loc[treat_indices, 'treatment'] = 1
return df
# 示例:按行业和市值分层
df = pd.DataFrame({
'stock': ['A', 'B', 'C', 'D', 'E', 'F'],
'industry': ['tech', 'tech', 'finance', 'finance', 'health', 'health'],
'market_cap': ['large', 'small', 'large', 'small', 'large', 'small']
})
df = stratified_randomization(df, ['industry', 'market_cap'])
print(df)
4.4 样本量计算
样本量不够,结果就是白做。我见过太多人,拿几十个样本就跑A/B测试,然后得出一个“显著”的结果。说白了,那都是噪声。
样本量计算的核心公式是:
n = (Z_α/2 + Z_β)² × 2σ² / δ²
其中:
- Z_α/2:显著性水平对应的Z值(通常α=0.05,Z=1.96)
- Z_β:检验功效对应的Z值(通常β=0.2,Z=0.84)
- σ²:结果的方差
- δ:最小可检测效应量
举个例子:你想检测一个策略能否带来0.1%的额外收益,假设日收益的方差是0.01,那么需要的样本量是:
import math
alpha = 0.05
beta = 0.2
Z_alpha = 1.96
Z_beta = 0.84
sigma_sq = 0.01
delta = 0.001
n = (Z_alpha + Z_beta)**2 * 2 * sigma_sq / delta**2
print(f"需要的样本量: {math.ceil(n)}")
算出来大概是...嗯,你自己跑一下看看。你会发现,效应量越小,需要的样本量越大。很多策略的效应量只有几个基点,那需要的样本量可能是几万甚至几十万。所以,别指望拿几百个样本就能验证一个微弱的策略。
避坑指南:我曾经犯过一个错误——用历史回测的数据来估算样本量。历史回测的方差往往比实盘小很多,因为回测里没有交易成本、滑点、流动性冲击。结果算出来的样本量偏小,实盘测试时效果就不显著了。后来我学乖了,用实盘小样本的方差来估算。
4.5 常见陷阱
做RCT和A/B测试,陷阱太多了。我列几个最常见的:
- 污染效应:实验组和对照组之间互相影响。比如你在同一个市场里测试两个策略,它们可能会互相抢流动性。解决办法:用独立的市场或时间段。
- 霍桑效应:被测试者知道自己被观察,行为发生改变。在投资中,交易员知道自己被监控,可能会改变交易习惯。解决办法:双盲设计,但投资中很难做到。
- 多重比较问题:同时测试多个假设,总会有几个“显著”的结果。我见过有人同时测试50个参数组合,然后挑出3个显著的,说策略有效。这纯粹是数据挖掘。解决办法:Bonferroni校正或FDR控制。
- 提前停止:看到结果“显著”就提前停止测试。这会导致假阳性率飙升。解决办法:预先设定样本量,不要中途偷看。
- 样本选择偏差:只选了表现好的股票或时间段做测试。比如只测试牛市期间的策略,那结果肯定有偏。解决办法:确保样本覆盖完整的市场周期。
核心要点:随机对照试验是因果推断的基石,但前提是设计要严谨。随机化、样本量、分层、避免污染,每一步都不能马虎。投资中的A/B测试,说白了就是拿真金白银做实验,容错率很低。
最后,我给大家画了一张本章的知识体系图,方便理解各个概念之间的关系:
好了,这一章的内容就到这里。记住,RCT不是万能的,但没有RCT是万万不能的。在投资中,每一次A/B测试都是一次因果推断的实践。设计得越严谨,结论就越可靠。
公众号:蓝海资料掘金营,微信deep3321