Backtesting.py多策略回测指标最优值自动对比实现方案
Backtesting.py多策略回测指标自动对比最优方案
Backtesting.py回测框架运行完成后,会返回backtesting._stats._Stats类型的回测统计结果对象。本次测试分别运行三类策略得到独立统计实例:
- RSI策略:
stats_RSI - 4移动平均线策略:
stats_Sma4 - scikit-optimize(skopt)参数优化策略:
stats_skopt
需求说明
需要提取三类策略的5项核心绩效指标做横向对比,自动输出每个指标的最高值及对应生成策略,方便使用者根据自身投资策略的核心考核维度选择适配策略,待对比指标如下:
- 年化收益率(
Return (Ann.) [%]) - Sortino比率
- SQN系统质量数
- 胜率(
Win Rate [%]) - 最终权益(
Equity Final [$])
原有实现的缺陷
手动打印对比版本
最初采用逐策略打印指标、人工比对的方式,实现代码如下:
#Comparison of all methods print('\033[1m' + 'Method 1: RSI' + '\033[0m''\nAnnual Return: ',round(stats_RSI["Return (Ann.) [%]"],2), '\nSortino Ratio: ',round(stats_RSI["Sortino Ratio"],2), '\nSQN: ', round(stats_RSI["SQN"],2),'\nWin Rate: ',round(stats_RSI["Win Rate [%]"],2), '\nFinal Equity: ',round(stats_RSI["Equity Final [$]"],2)) print('\033[1m' + '\nMethod 2: Moving Averages'+ '\033[0m''\nAnnual Return: ',round(stats_Sma4["Return (Ann.) [%]"],2), '\nSortino Ratio: ',round(stats_Sma4["Sortino Ratio"],2), '\nSQN: ', round(stats_Sma4["SQN"],2),'\nWin Rate: ',round(stats_Sma4["Win Rate [%]"],2), '\nFinal Equity: ',round(stats_Sma4["Equity Final [$]"],2)) print('\033[1m' + '\nMethod 3: Scikit Optimize'+ '\033[0m''\nAnnual Return: ',round(stats_skopt["Return (Ann.) [%]"],2), '\nSortino Ratio: ',round(stats_skopt["Sortino Ratio"],2), '\nSQN: ', round(stats_skopt["SQN"],2),'\nWin Rate: ',round(stats_skopt["Win Rate [%]"],2), '\nFinal Equity: ',round(stats_skopt["Equity Final [$]"],2))
该版本完全依赖人工比对数值,效率极低,且容易看错结果。
初版循环判断版本
后续编写了循环遍历逐指标判断最大值的版本,实现代码如下:
methods = [stats_RSI, stats_Sma4, stats_skopt] annual_return = 0 sortino_ratio = 0 sqn = 0 win_rate = 0 final_equity = 0 for method in methods: if method["Return (Ann.) [%]"] > annual_return: annual_return = method["Return (Ann.) [%]"] print("Annual Return:\n", annual_return, "\n", method._strategy, "\n") if method["Sortino Ratio"] > sortino_ratio: sortino_ratio = method["Sortino Ratio"] print("Sortino Ratio:\n", sortino_ratio, "\n", method._strategy, "\n") if method["SQN"] > sqn: sqn = method["SQN"] print("SQN:\n", sqn, "\n", method._strategy, "\n") if method["Win Rate [%]"] > win_rate: win_rate = method["Win Rate [%]"] print("Win Rate:\n", win_rate, "\n", method._strategy, "\n") if method["Equity Final [$]"] > final_equity: final_equity = method["Equity Final [$]"] print("Final Equity:\n", final_equity, "\n", method._strategy, "\n")
该版本虽然能输出正确结果,但代码冗余度高,存在大量重复的变量初始化、if判断、print逻辑,还容易出现缩进错误;Python层分支判断多导致执行效率低,实测耗时15.5ms,远高于社区优化方案的863μs。新增策略或者新增对比指标时,需要修改多处代码,可维护性差。
高性能简洁优化方案
核心优化思路是用结构化配置替代硬编码逻辑,调用Python内置C实现的max()函数直接取最优值,减少Python层多余的分支判断和赋值操作,兼顾代码简洁度、可维护性和执行效率,实现代码如下:
# 绑定策略实例和对应展示名称,新增策略仅需在此处添加键值对 strategy_group = { "RSI策略": stats_RSI, "4移动平均线策略": stats_Sma4, "skopt参数优化策略": stats_skopt } # 配置待对比的指标:(Stats对象中的key, 指标展示名称),新增指标仅需在此处添加元组 compare_metrics = [ ("Return (Ann.) [%]", "年化收益率(%)"), ("Sortino Ratio", "Sortino比率"), ("SQN", "SQN系统质量数"), ("Win Rate [%]", "胜率(%)"), ("Equity Final [$]", "最终权益($)") ] # 逐指标取最优值,无多余冗余逻辑 for metric_key, metric_show_name in compare_metrics: best_name, best_result = max(strategy_group.items(), key=lambda item: item[1][metric_key]) print(f"=== {metric_show_name} 最优结果 ===") print(f"数值:{round(best_result[metric_key], 2)}") print(f"对应策略:{best_name}\n")
方案优势
- 代码量比初版循环版本减少60%以上,结构清晰
- 扩展性极强,新增策略、新增对比指标都只需要修改对应配置项,不需要改动核心对比逻辑
- 内置
max()函数执行效率远高于手写Python层if判断,实测耗时稳定在800~900μs区间,达到社区最优性能水平 - 输出格式统一,不会出现手写print导致的格式错乱、字段遗漏问题
内容的提问来源于stack exchange,提问作者Zen4ttitude
相关产品推荐
相关产品推荐

