策略回测

策略写好先别急着上实盘——用历史数据跑一遍回测,看看它在过去能不能赚钱、最大回撤能不能扛得住。本章讲 PTrade 回测怎么配、怎么调、结果怎么看。

新建回测

在客户端选中策略后新建回测,需设定几项要素:

参数说明
开始时间 / 结束时间建议覆盖一段完整的牛熊周期,至少 2~3 年
回测资金初始资金(context.capital_base),如 100 万
回测基准默认沪深 300(000300.SS),用于对比超额收益
回测频率日线或分钟

设定后点保存、再点回测即可。跑完会展示:收益曲线、评价指标(年化、最大回撤、夏普、胜率等)、逐笔交易明细、运行日志。

回测频率

频率handle_data 触发注意
日线每个交易日 15:00信号基于当日收盘,撮合在当日开盘或收盘价
分钟每根分钟 K 线结束9:3111:30、13:0115:00;run_daily 的 time 须落在交易时段

tick 级别不支持回测,只能在交易模块运行。回测中 tick_datarun_intervalorder_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 优秀
胜率 / 盈亏比盈利交易占比 / 平均盈亏高胜率低盈亏比或反之都可能赚钱
交易次数信号总数太少不显著,太多可能过拟合

期望收益 = 胜率 × 平均盈利 − 败率 × 平均亏损,正期望才有戏。

九、常见陷阱

  1. 未来函数get_stock_blocks / get_industry_stocks 取的是当下数据,回测拿不到真实匹配日期的成分,信号会泄漏。财务数据用默认 merge_type(首次发布)规避。
  2. 过拟合:参数和历史贴合太好,换个区间就失效。做样本外检验——用一段没参与调参的时间再测。
  3. 忽略成本:不设佣金/滑点/印花税,频繁交易的策略会被成本吃光利润。
  4. target 系列回测与实盘差异:回测持仓瞬时更新,实盘有 6 秒延迟——回测好看不代表实盘安全。
  5. 停牌处理get_history 停牌日用前值填充、量为 0,要按 volume == 0 过滤。
  6. 成交价假设:日回测撮合在开盘/收盘价,实盘不一定能以该价成交。

十、对账与导出

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

小结

回测的关键是让模拟贴近真实:设好基准、佣金、滑点、成交比例;低频策略可关成交量限制;用底仓测调仓。警惕未来函数和过拟合,一组好结果 + 合理逻辑 + 样本外稳健才值得小仓位试水。下一步直接抄实战示例:策略示例