回测常见陷阱
1)前视偏差:使用未来数据;2)幸存者偏差:剔除退市股票;3)过拟合:过度优化参数;4)忽略交易成本;5)样本数据不足。
项目一:多因子选股策略
策略回测是验证多因子选股策略有效性的关键环节。本节将详细介绍回测的完整流程和注意事项。
加载历史数据、因子数据,进行数据清洗
配置回测参数:起始日期、初始资金、手续费等
初始化策略,设置调仓频率、选股数量等
执行策略,记录每笔交易
计算各项绩效指标,生成分析报告
样本外测试,验证策略稳健性
class FixedCommission(bt.Commission):
"""
固定手续费模型
"""
params = (
('commission', 0.0003), # 万三手续费
)
def _getcommission(self, size, price, pseudoexec):
"""计算手续费"""
return abs(size) * price * self.p.commission
class PercentageSlippage(bt.Slipper):
"""
百分比滑点模型
"""
params = (
('perc', 0.001), # 千分之一滑点
)
def _getprice(self, price, pseudoexec):
"""计算滑点"""
if pseudoexec:
return price
# 买入价上浮,卖出价下浮
return price * (1 + self.p.perc)
def complete_backtest_example():
"""
完整回测示例
"""
# 1. 加载数据
start_date = '2015-01-01'
end_date = '2023-12-31'
# 加载因子数据
factor_data = load_factor_data(start_date, end_date)
# 加载收益率数据
returns_data = load_returns_data(start_date, end_date)
# 2. 回测参数配置
backtest_params = {
'initial_cash': 10000000, # 初始资金1000万
'commission': 0.0003, # 手续费万三
'slippage': 0.001, # 滑点千一
'lookback': 20, # 因子回看期20天
'top_n': 50, # 选股50只
'rebalance_freq': 20, # 每月调仓
}
# 3. 初始化回测引擎
cerebro = bt.Cerebro()
# 添加策略
cerebro.addstrategy(
MultiFactorStrategy,
lookback=backtest_params['lookback'],
top_n=backtest_params['top_n'],
rebalance_freq=backtest_params['rebalance_freq']
)
# 加载数据
for ticker in factor_data.columns:
data = prepare_data_feed(factor_data, returns_data, ticker)
cerebro.adddata(data)
# 设置初始资金
cerebro.broker.setcash(backtest_params['initial_cash'])
# 设置手续费
cerebro.broker.setcommission(
commission=backtest_params['commission']
)
# 设置滑点
cerebro.broker.set_slippage_perc(perc=backtest_params['slippage'])
# 添加分析器
cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='sharpe')
cerebro.addanalyzer(bt.analyzers.DrawDown, _name='drawdown')
cerebro.addanalyzer(bt.analyzers.Returns, _name='returns')
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name='trades')
# 4. 运行回测
print(f'初始资金: {cerebro.broker.getvalue():,.2f}')
results = cerebro.run()
# 5. 分析结果
strat = results[0]
final_value = cerebro.broker.getvalue()
print(f'期末资金: {final_value:,.2f}')
print(f'总收益率: {(final_value/backtest_params["initial_cash"]-1)*100:.2f}%')
# 获取绩效指标
sharpe = strat.analyzers.sharpe.get_analysis()
drawdown = strat.analyzers.drawdown.get_analysis()
returns = strat.analyzers.returns.get_analysis()
trades = strat.analyzers.trades.get_analysis()
print(f"夏普比率: {sharpe.get('sharperatio', 0):.2f}")
print(f"最大回撤: {drawdown.get('max', {}).get('drawdown', 0):.2f}%")
print(f"年化收益: {returns.get('rnorm100', 0):.2f}%")
# 6. 可视化
cerebro.plot(style='candle', barup='red', bardown='green')
return results
# 运行回测
results = complete_backtest_example()
def out_of_sample_test():
"""
样本外测试
"""
# 训练集:2015-2020
train_start = '2015-01-01'
train_end = '2020-12-31'
# 测试集:2021-2023
test_start = '2021-01-01'
test_end = '2023-12-31'
# 1. 训练集回测
print("========== 训练集回测 ==========")
train_results = run_backtest(train_start, train_end)
# 2. 测试集回测
print("\n========== 测试集回测 ==========")
test_results = run_backtest(test_start, test_end)
# 3. 对比分析
print("\n========== 对比分析 ==========")
print(f"训练集年化收益: {train_results['annual_return']:.2%}")
print(f"测试集年化收益: {test_results['annual_return']:.2%}")
print(f"训练集夏普比率: {train_results['sharpe_ratio']:.2f}")
print(f"测试集夏普比率: {test_results['sharpe_ratio']:.2f}")
print(f"训练集最大回撤: {train_results['max_drawdown']:.2%}")
print(f"测试集最大回撤: {test_results['max_drawdown']:.2%}")
# 判断是否存在过拟合
performance_drop = (train_results['annual_return'] -
test_results['annual_return'])
if performance_drop > 0.05: # 下降超过5%
print("\n⚠️ 警告: 策略可能存在过拟合")
else:
print("\n✅ 策略表现稳定")
return train_results, test_results
1)前视偏差:使用未来数据;2)幸存者偏差:剔除退市股票;3)过拟合:过度优化参数;4)忽略交易成本;5)样本数据不足。