策略回测
策略写好先别急着上实盘——用历史数据跑一遍回测,看看它在过去能不能赚钱、最大回撤能不能扛得住。本章讲 PTrade 回测怎么配、怎么调、结果怎么看。
新建回测
在客户端选中策略后新建回测,需设定几项要素:
| 参数 | 说明 |
|---|---|
| 开始时间 / 结束时间 | 建议覆盖一段完整的牛熊周期,至少 2~3 年 |
| 回测资金 | 初始资金(context.capital_base),如 100 万 |
| 回测基准 | 默认沪深 300(000300.SS),用于对比超额收益 |
| 回测频率 | 日线或分钟 |
设定后点保存、再点回测即可。跑完会展示:收益曲线、评价指标(年化、最大回撤、夏普、胜率等)、逐笔交易明细、运行日志。
回测频率
| 频率 | handle_data 触发 | 注意 |
|---|---|---|
| 日线 | 每个交易日 15:00 | 信号基于当日收盘,撮合在当日开盘或收盘价 |
| 分钟 | 每根分钟 K 线结束 | 9:31run_daily 的 time 须落在交易时段 |
tick 级别不支持回测,只能在交易模块运行。回测中
tick_data、run_interval、order_tick等函数不可用。
一、设置基准:set_benchmark
set_benchmark(sids)
只能在 initialize 中调用。不设默认沪深 300。前端展示的超额收益、Beta 等指标都基于此基准。
def initialize(context):
set_benchmark('000016.SS') # 改用上证 50 做基准
二、设置佣金:set_commission
set_commission(commission_ratio=0.0003, min_commission=5.0, type='STOCK')
commission_ratio:佣金费率,默认股票万三、ETF/LOF 万八。min_commission:单笔最低佣金,默认 5 元。type:'STOCK'/'ETF'/'LOF'。
回测总手续费 = 佣金费 + 经手费(万 0.487)+ 印花税(千 1,仅卖出)。想贴近实盘就要如实设置,否则结果偏乐观。
三、设置滑点
两种方式,都仅在回测可用:
# 固定滑点:价差为固定值,成交价 = 委托价 ± fixedslippage/2
set_fixed_slippage(fixedslippage=0.2)
# 比例滑点:成交价 = 委托价 ± 委托价 × slippage/2
set_slippage(slippage=0.002)
滑点若小于品种最小价差(如股票 0.01、IF 期货 0.2)则不生效。
四、设置成交比例:set_volume_ratio
set_volume_ratio(volume_ratio=0.25)
限制单笔委托最多成交本周期市场总量的比例,默认 0.25(四分之一)。模拟大单对市场的冲击,让回测更接近真实。
五、不限制成交量:set_limit_mode
set_limit_mode('UNLIMITED') # 不做成交量限制
set_limit_mode('LIMIT') # 限制(默认)
月度调仓等低频策略对流动性不敏感,设为 'UNLIMITED' 可让回测更便捷——实际撮合量可超过该周期市场真实成交量。实盘不受此设置影响。
def initialize(context):
if not is_trade():
set_limit_mode('UNLIMITED') # 仅回测时关闭限制
六、设置底仓:set_yesterday_position
让策略启动时就带着持仓,用于测试调仓或卖出逻辑:
set_yesterday_position(poslist)
poslist 是字典列表,每项含 sid / amount / enable_amount / cost_basis:
def initialize(context):
pos = {
'sid': '600570.SS',
'amount': '1000',
'enable_amount': '600',
'cost_basis': '55',
}
set_yesterday_position([pos])
也可用 convert_position_from_csv('Poslist.csv') 从 CSV 文件读入底仓,CSV 表头需含 sid, amount, enable_amount, cost_basis。
七、期货回测专属设置
| 函数 | 含义 |
|---|---|
set_margin_rate(transaction_code, margin_rate) | 设置保证金比例,如 set_margin_rate('IF', 0.08) |
set_future_commission(transaction_code, commission) | 设置手续费(股指按金额比例、国债按每手金额) |
def initialize(context):
g.security = 'IF2312.CCFX'
set_universe(g.security)
set_margin_rate('IF', 0.15)
set_future_commission('IF', 0.00004) # 0.4/万
八、结果指标解读
| 指标 | 含义 | 怎么看 |
|---|---|---|
| 总收益 / 年化收益 | 绝对与年化回报 | 要和基准比,跑赢才有意义 |
| 最大回撤 | 净值从高点的最大跌幅 | 关系能否拿住,30% 以上很难扛 |
| 夏普比率 | 单位风险的超额回报 | >1 尚可,>2 优秀 |
| 胜率 / 盈亏比 | 盈利交易占比 / 平均盈亏 | 高胜率低盈亏比或反之都可能赚钱 |
| 交易次数 | 信号总数 | 太少不显著,太多可能过拟合 |
期望收益 = 胜率 × 平均盈利 − 败率 × 平均亏损,正期望才有戏。
九、常见陷阱
- 未来函数:
get_stock_blocks/get_industry_stocks取的是当下数据,回测拿不到真实匹配日期的成分,信号会泄漏。财务数据用默认merge_type(首次发布)规避。 - 过拟合:参数和历史贴合太好,换个区间就失效。做样本外检验——用一段没参与调参的时间再测。
- 忽略成本:不设佣金/滑点/印花税,频繁交易的策略会被成本吃光利润。
- target 系列回测与实盘差异:回测持仓瞬时更新,实盘有 6 秒延迟——回测好看不代表实盘安全。
- 停牌处理:
get_history停牌日用前值填充、量为 0,要按volume == 0过滤。 - 成交价假设:日回测撮合在开盘/收盘价,实盘不一定能以该价成交。
十、对账与导出
get_trades_file(save_path=None) # 仅回测,after_trading_end 调用,导出 csv 逐笔对账
导出表头:order_id, trading_id, entrust_id, security_code, order_type, volume, price, total_money, trading_fee, trade_time。
小结
回测的关键是让模拟贴近真实:设好基准、佣金、滑点、成交比例;低频策略可关成交量限制;用底仓测调仓。警惕未来函数和过拟合,一组好结果 + 合理逻辑 + 样本外稳健才值得小仓位试水。下一步直接抄实战示例:策略示例。