共计 2943 个字符,预计需要花费 8 分钟才能阅读完成。
为什么需要量化回测
量化投资的核心在于通过历史数据验证策略的有效性。A 股市场由于 T + 1 交易、涨跌停限制等特殊规则,直接套用海外框架往往会出现水土不服。一个好的回测系统能帮我们:

- 验证策略逻辑是否具备统计显著性
- 评估策略在不同市场环境下的稳定性
- 优化参数组合避免过度拟合
主流框架对比
| 框架 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Backtrader | 事件驱动架构,支持多品种多时间帧 | 需要自行处理 A 股特有规则 | 中低频策略研发 |
| Zipline | 自带基本面数据,社区生态完善 | 对 A 股支持较弱,学习曲线陡峭 | 美股市场研究 |
| PyAlgoTrade | 代码简洁,易于上手 | 功能较为基础,扩展性有限 | 教育演示和小型策略测试 |
Backtrader 实战示例
1. 数据接口封装
import backtrader as bt
import pandas as pd
class AStockData(bt.feeds.PandasData):
params = (('datetime', None), # 使用索引列作为时间
('open', 'open'),
('high', 'high'),
('low', 'low'),
('close', 'close'),
('volume', 'volume'),
('openinterest', -1), # A 股无持仓量概念
('preday_close', 'pre_close'), # 前收盘价(处理涨跌停)
('is_trading', 'is_trading'), # 当日是否可交易(处理停牌)
)
def __init__(self, df):
# 确保包含涨跌停标志和停牌标志
assert 'limit_up' in df.columns and 'limit_down' in df.columns
super().__init__(dataname=df)
2. 均线策略实现
class MaCrossStrategy(bt.Strategy):
params = (('fast_period', 5), # 快线周期
('slow_period', 20), # 慢线周期
('printlog', False), # 是否打印交易日志
)
def __init__(self):
# 计算双均线
self.ma_fast = bt.indicators.SMA(self.data.close, period=self.p.fast_period)
self.ma_slow = bt.indicators.SMA(self.data.close, period=self.p.slow_period)
# 跟踪订单状态
self.order = None
def next(self):
# 检查是否有未完成订单
if self.order:
return
# 检查是否可交易(处理停牌)
if not self.data.is_trading[0]:
return
# 金叉信号且未持仓
if not self.position and \
self.ma_fast[0] > self.ma_slow[0] and \
self.ma_fast[-1] <= self.ma_slow[-1]:
# 处理涨跌停无法买入的情况
if self.data.limit_up[0]:
return
# 计算可买数量(处理 A 股 100 股整数倍)
size = int(self.broker.getcash() / self.data.close[0] // 100) * 100
if size > 0:
self.order = self.buy(size=size)
# 死叉信号且持有仓位
elif self.position and \
self.ma_fast[0] < self.ma_slow[0] and \
self.ma_fast[-1] >= self.ma_slow[-1]:
# 处理跌停无法卖出的情况
if self.data.limit_down[0]:
return
self.order = self.close()
def log(self, txt, dt=None):
if self.p.printlog:
dt = dt or self.datas[0].datetime.date(0)
print(f'{dt.isoformat()}, {txt}')
3. 绩效评估模块
def analyze_results(cerebro):
# 获取回测结果
strat = cerebro.runstrats[0][0]
# 打印最终组合价值
portvalue = cerebro.broker.getvalue()
print(f'最终资产: {portvalue:.2f}')
# 计算年化收益率
ret = (portvalue / cerebro.startingcash - 1)
years = len(cerebro) / 252
annual_ret = (1 + ret) ** (1/years) - 1
# 使用 PyFolio 生成专业分析报告
import pyfolio as pf
returns, positions, transactions = pf.utils.extract_rets_pos_txn_from_backtrader(cerebro)
pf.create_returns_tear_sheet(returns)
A 股特有难题解决方案
涨跌停板处理
- 买入控制 :当
limit_up=True时跳过交易信号 - 卖出控制:持仓股票触发跌停时,次日优先挂单
- 价格修正 :使用
preday_close字段计算合理价格区间
停复牌数据清洗
# 示例:标记停牌日
def clean_suspension(df):
"""
处理停牌数据:1. 将停牌日的 OHLC 设为前收盘价
2. 成交量设为 0
3. 添加 is_trading 标志
"""suspend_days = df[df['volume'] == 0].index
df.loc[suspend_days, ['open','high','low','close']] = df['pre_close']
df['is_trading'] = ~df.index.isin(suspend_days)
return df
滑点与手续费建模
# 在 Cerebro 引擎中设置
cerebro.broker.set_slippage_fixed(0.01) # 固定滑点 1 分钱
cerebro.broker.setcommission(
commission=0.0003, # 万 3 佣金
stamp=0.001, # 千 1 印花税(仅卖出)
margin=None, # 不使用杠杆
mult=1.0 # 价格乘数
)
生产环境建议
- 避免未来函数:
- 禁用
.shift(-1)等超前索引操作 -
使用
datetime.now()替代预测时间 -
处理幸存者偏差:
- 包含已退市股票数据
-
使用全市场历史成分股测试
-
多时间帧验证:
# 添加周线数据作为参考 weekly_data = cerebro.resampledata( daily_data, timeframe=bt.TimeFrame.Weeks, compression=1 )
下一步改进方向
尝试给示例策略添加以下增强功能:
- 加入动态止盈止损逻辑
- 结合成交量过滤虚假突破
- 测试不同参数组合的稳定性
推荐学习资源:
– 书籍:《量化交易如何构建自己的算法交易业务》
– 社区:JoinQuant、Ricequant
– 进阶框架:vn.py (适合高频交易)
记住:回测只是量化投资的第一步,实盘前务必进行模拟交易验证!
正文完
发表至: 量化投资
近一天内
