5. 回测基础与可视化

5.回测基础与可视化

策略开发到这个阶段,手里应该已经有一个能跑起来的交易策略了。前面几章把环境搭好、数据导入、买卖逻辑写完,现在终于来到最激动人心的环节——看看策略在历史数据上到底表现如何。回测就像给策略做一次全面体检,各项指标会告诉我们它是个潜力股还是绣花枕头。

Jesse 提供了两种回测模式:图形界面点击模式和编程模式。前者适合快速验证想法,后者适合批量处理和深度研究。两种模式底层用的是同一套引擎,结果完全一致,选择哪种取决于使用场景。

执行回测模式

图形界面回测

打开 Jesse 仪表板,在左侧菜单找到 Backtest 页面,整个流程非常直观。顶部选择交易路由,也就是指定用哪个策略、交易什么品种、在什么时间周期上跑。配置好起止日期,点击 Start 按钮,引擎就开始逐根 K 线模拟交易。

回测过程中,界面会实时显示进度条和当前模拟到的日期。跑完后自动跳转到结果页面,这里集中展示了所有关键信息。对于刚入门的用户,图形界面是最友好的起点,不需要写任何额外代码,策略文件准备好就能跑。

界面下方有几个重要选项需要注意。Export Charts 会生成传统图表文件,虽然现在标记为 Legacy,但依然很有用。Export Tradingview 选项能生成 Pine Script,方便在 TradingView 上可视化买卖点。Export CSV 和 Export JSON 则把成交记录导出为标准格式,方便用 Excel 或 Python 做进一步分析。

编程模式回测

当需要批量测试、参数优化或集成到研究流程时,图形界面就显得不够用了。Jesse 提供了 backtest() 函数,可以在 Python 脚本或 Jupyter Notebook 中直接调用。这个函数设计为纯函数,所有输入通过参数传递,不依赖全局状态,因此特别适合多进程并行计算。

from jesse.research import backtest

result = backtest(
    config=config,
    routes=routes,
    extra_routes=[],
    candles=candles,
    warmup_candles=warmup_candles,
    generate_charts=True,
    generate_equity_curve=True,
    generate_csv=True,
    generate_json=True,
    generate_logs=True,
    fast_mode=True
)

函数参数看起来很多,但结构清晰。config 字典定义账户初始资金、手续费、杠杆等交易配置。routes 列表指定策略路由,和图形界面的选择对应。candleswarmup_candles 是核心数据,前者是回测期的 K 线,后者是指标计算需要的预热数据。

fast_mode 参数值得特别关注。这个优化模式能大幅提升回测速度,量级级的提升。它通过跳过一些非必要的计算和日志记录实现加速,行为已经和图形界面默认模式一致。目前限制是不能用于多路由场景,但单策略回测时强烈建议开启。

返回结果是个字典,包含 metricschartslogs 等键。metrics 是各项指标的字典,charts 是生成的图表文件路径,logs 记录了详细日志。这种结构化数据非常适合程序化分析,比如自动筛选表现好的参数组合。

预热数据的重要性

很多技术指标需要一定长度的历史数据才能正确计算。比如 50 日均线,至少需要 50 根 K 线。Jesse 的 warmup_candles 机制就是解决这个问题的。在回测时,引擎会先加载预热数据计算指标,但交易模拟从正式回测期开始。

# 获取数据时指定预热长度
warmup_candles, trading_candles = get_candles(
    exchange_name, symbol, timeframe, 
    start_timestamp, finish_timestamp,
    warmup_candles=200,  # 预热 200 根 K 线
    caching=True
)

这样既能保证指标值正确,又不会把预热期的交易计入结果。如果策略用了多个时间周期的指标,每个周期都需要相应的预热数据。

分析回测指标

回测完成后,面对密密麻麻的数字,该关注哪些?Jesse 的指标面板设计得很专业,涵盖了策略评估的方方面面。理解这些指标的含义,是提升策略开发能力的关键。

核心绩效指标

净收益率(Net Profit Percentage) 是最直观的指标,表示账户资金的增长百分比。但单独看这个指标没有意义,必须结合风险指标一起看。一个收益 200% 但回撤 80% 的策略,远不如收益 50% 回撤 5% 的策略稳健。

最大回撤(Max Drawdown) 衡量策略在最不利情况下的亏损幅度。计算方式是从历史最高权益到后续最低点的跌幅。这个指标直接决定了账户的生存能力。一般来说,回撤超过 30% 就很难被接受,10% 以内属于优秀水平。

夏普比率(Sharpe Ratio) 把收益和风险结合起来,计算每单位总风险带来的超额回报。数值大于 1 算不错,大于 2 就很好,大于 3 属于顶级策略。但要注意,夏普比率假设收益分布是正态的,对极端行情不敏感。

卡玛比率(Calmar Ratio) 用年化收益率除以最大回撤,更直接地反映风险调整后收益。这个比率越高越好,一般要求至少大于 1,意味着收益要能覆盖最大回撤。

交易统计指标

胜率(Win Rate) 是盈利交易的比例。很多人过度追求高胜率,其实胜率 40% 的策略如果盈亏比够高,依然可以赚钱。关键是胜率和盈亏比的配合。

盈亏比(Ratio Avg Win/Loss) 表示平均盈利与平均亏损的比值。2 以上的盈亏比比较健康,意味着每次盈利能覆盖两次亏损。

期望值(Expectancy) 综合了胜率和盈亏比,计算每笔交易的平均收益。公式是 (胜率 × 平均盈利) - (败率 × 平均亏损)。正值表示策略长期能赚钱,负值则必亏无疑。

持仓周期(Holding Period) 反映策略的交易频率。高频策略可能平均持仓几小时,低频策略可能持仓数周。这个指标要和手续费、滑点结合起来看,高频策略对成本更敏感。

策略 API 中的指标访问

在策略代码中,可以通过 self.metrics 属性访问这些指标。这在编写动态调整逻辑时很有用,比如根据近期表现调整仓位。

def before(self):
    # 获取当前策略的指标
    metrics = self.metrics
    
    if metrics is not None:
        # 计算凯利准则
        win_rate = metrics['win_rate'] / 100
        avg_win = metrics['average_win']
        avg_loss = metrics['average_loss']
        
        kelly = (win_rate * avg_win - (1 - win_rate) * avg_loss) / avg_win
        
        # 根据凯利值调整风险
        self.risk_per_trade = min(kelly * 0.5, 0.02)  # 半凯利,上限 2%

需要注意的是,如果没有发生交易,metrics 会返回 None。使用前要做空值检查,避免程序报错。

绘制权益曲线

数字指标虽然精确,但缺乏直观感受。权益曲线把账户价值随时间的变化画成图表,一眼就能看出策略的表现特征。好的权益曲线应该平滑向上,回撤小且恢复快。

基础权益曲线

回测完成后,Jesse 会自动生成权益曲线图。横轴是时间,纵轴是账户价值。曲线整体趋势向上说明策略赚钱,中间的凹陷就是回撤期。通过观察曲线的形状,能判断策略在不同市场环境下的适应性。

在图形界面中,可以启用基准对比功能。这个会把买入持有策略的曲线叠加显示,用来判断策略是否跑赢简单持有。如果权益曲线长期低于基准,说明策略可能过度交易,还不如不动。

交互式图表

Jesse 的交互式图表功能非常强大,能在 K 线图上直接标注买卖点,还能叠加指标线。在回测前勾选 Interactive Charts 选项,完成后点击按钮就能打开。

def after(self):
    # 在蜡烛图上叠加 SuperTrend 指标
    self.add_line_to_candle_chart(
        'supertrend', 
        ta.supertrend(self.candles).trend
    )
    
    # 添加 EMA 线
    self.add_line_to_candle_chart(
        'ema50', 
        ta.ema(self.candles, 50)
    )
    
    # 画支撑阻力线
    self.add_horizontal_line_to_candle_chart(
        'resistance', 
        45000, 
        color='red'
    )

add_line_to_candle_chart 函数把指标值画在主图上,适合均线、趋势线等与价格同量纲的指标。add_horizontal_line_to_candle_chart 画水平线,用来标记关键价位。这些标注会出现在交互式图表中,方便复盘时理解策略逻辑。

独立指标图

有些指标的值范围和价格差异很大,比如 RSI 在 0-100 之间,MACD 可能是个位数。把这些画在价格图上会压缩得看不清。Jesse 提供了独立图表功能。

def after(self):
    # 在独立图表中显示 RSI
    rsi = ta.rsi(self.candles, 14)
    self.add_extra_line_chart(
        'rsi_chart',  # 图表名称
        'rsi',        # 线名称
        rsi
    )
    
    # 在同个图表中添加另一条线
    self.add_extra_line_chart(
        'rsi_chart',
        'rsi_ma',
        ta.sma(rsi, 7)
    )

所有名称相同的调用会画在同一张图上,不同名称则创建新图表。这样在复盘时能同时观察多个指标的状态,分析策略的决策依据。

图表导出与分享

交互式图表只能在 Jesse 界面中查看,如果要分享或做报告,需要静态图片。勾选 Export Charts 后,回测结束会生成 PNG 文件,包含主图和指标图。

对于习惯 TradingView 的用户,可以导出 Pine Script。把生成的文本粘贴到 TradingView 的 Pine Editor,就能在熟悉的界面上看买卖点。要注意 TradingView 只显示最近约 30 笔交易,而且它的回测计算方式和 Jesse 不同,数值会有差异,仅供参考。

导出结果数据

图表和指标面板的信息虽然丰富,但有时候需要更灵活的分析方式。Jesse 支持多种数据导出格式,方便用外部工具深度挖掘。

CSV 格式导出

CSV 是最通用的格式,可以用 Excel、Google Sheets 或 Pandas 打开。导出文件包含每笔交易的详细信息:入场时间、价格、出场时间、盈亏、持仓时长等。

# 在 backtest() 调用中设置
result = backtest(
    # ... 其他参数
    generate_csv=True
)

# 或者在图形界面勾选 Export CSV

CSV 文件的路径会在回测日志中打印出来。用这个文件可以计算自定义指标,比如统计每周的盈亏分布,分析策略在特定时段的表现。也可以导入到 Tableau 等 BI 工具做可视化。

JSON 格式导出

JSON 格式保留了更多结构化信息,包括完整的指标字典、权益曲线数据等。适合用 Python 做自动化分析。

result = backtest(
    # ... 其他参数
    generate_json=True
)

# 结果已经包含在 result 字典中
metrics = result['metrics']
trades = result['trades']
equity_curve = result['equity_curve']

社区开发了专门的分析工具,比如 jesse-trades-info,能解析 JSON 文件生成更详细的报告。这类工具通常提供表格和图表界面,比原生界面更灵活。

HTML 报告生成

从 0.21.3 版本开始,Jesse 支持生成完整的 HTML 报告。用 --full-reports 命令行标志启用,会得到包含所有指标和图表的网页文件。这个报告可以离线打开,方便分享给不懂技术的合作伙伴。

报告包含权益曲线、回撤图、月度收益表、交易统计等模块,格式专业,直接可用于展示。对于需要定期向投资人汇报的量化团队,这个功能节省了大量手工制作报告的时间。

数据导出的最佳实践

建议每次重要回测都同时开启 CSV 和 JSON 导出。CSV 方便快速查看和简单分析,JSON 适合深度研究。把导出文件按策略名称和参数命名,建立自己的测试结果数据库。

import json
from datetime import datetime

# 保存完整结果
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f"backtest_{strategy_name}_{timestamp}.json"

with open(filename, 'w') as f:
    json.dump(result, f, indent=2)

这样长期积累下来,可以分析策略的稳定性变化,观察不同参数对结果的系统性影响。量化交易是数据驱动的行业,建立自己的数据库是专业化的必经之路。

回测的注意事项

拿到漂亮的回测结果固然开心,但要保持清醒。回测只是历史模拟,不代表未来表现。有几个常见陷阱需要警惕。

过拟合风险

策略参数太多,或者规则过于复杂,很容易拟合历史数据的噪音而非真实规律。表现就是在回测期业绩惊人,一到实盘就萎靡不振。蒙特卡洛分析是检测过拟合的有效工具,通过打乱交易顺序或修改市场数据,观察策略表现是否稳定。这个主题在第 11 章会深入讲解。

数据质量

回测的准确性完全依赖数据质量。要确保导入的 K 线完整,没有缺失或错误。特别是低频数据,一根 K 线的错误可能影响长期结果。Jesse 的数据导入有校验机制,但人工检查依然必要。

成本假设

手续费和滑点的设置要合理。加密货币市场波动大,滑点可能比想象更严重。建议设置保守一些,比如手续费 0.1%,滑点 0.05%。如果策略在这些成本下依然盈利,实盘才更有把握。

样本外测试

不要把所有数据都用于回测优化。应该划分样本内和样本外数据,先在样本内开发和调参,然后在样本外验证。这是防止过拟合的基本纪律。第 11 章会介绍三段式验证方法。

回测是策略开发的核心环节,但不是终点。它只是告诉我们策略在历史上是否有效,未来表现如何还需要更多验证。下一章将介绍高级策略模式,包括多点进出、动态仓位等更复杂的交易逻辑,让策略能应对更多市场情况。