问题描述
解决方案
在聚宽(JoinQuant)平台中,如果策略回测运行缓慢,最直接且官方推荐的方法是使用 性能分析函数 enable_profile()。
这个函数可以帮助你精确地定位每一行代码的执行时间、调用次数以及占用的时间比例。
解决方法:使用 enable_profile()
1. 使用步骤
- 添加代码:在你的策略代码的最顶端(所有函数之前,包括
initialize之前)添加enable_profile()。 - 运行回测:点击“运行回测”。
- 注意:建议将回测时间缩短(例如只回测 1 周或 1 个月),因为开启性能分析本身会增加额外的运行开销,导致回测变得更慢。我们只需要短时间的数据来找出瓶颈即可。
- 查看结果:回测完成后,在回测详情页面的“日志”或“性能分析”标签页中,会显示详细的分析报告。
2. 代码示例
# -*- coding: utf-8 -*-
# 必须放在代码的最前面
enable_profile()
def initialize(context):
set_benchmark('000300.XSHG')
set_option('use_real_price', True)
run_daily(market_open, time='every_bar')
def market_open(context):
# 假设这里有一段耗时的代码
for stock in get_index_stocks('000300.XSHG'):
close_data = attribute_history(stock, 5, '1d', ['close'])
3. 如何解读分析报告
开启后,你会在日志中看到类似下面的输出。重点关注 % Time(时间占比)和 Time(总耗时)这两列。
// 函数名及所在行号
Function: market_open at line 10
// Line #: 行号
// Hits: 该行代码被执行的次数
// Time: 该行代码总共消耗的时间(单位通常是微秒)
// Per Hit: 平均每次执行消耗的时间
// % Time: 该行代码占整个函数执行时间的百分比(最关键的指标)
// Line Contents: 代码内容
Line # Hits Time Per Hit % Time Line Contents
==============================================================
10 def market_open(context):
11 1 25000 25000.0 1.0 stocks = get_index_stocks('000300.XSHG')
12 300 2400000 8000.0 95.0 for stock in stocks:
13 300 2300000 7666.0 90.0 data = attribute_history(stock, 5, '1d', ['close'])
分析逻辑:
- 如果某一行代码的
% Time非常高(例如超过 50% 或 80%),说明这里就是性能瓶颈。 - 如果
Hits(执行次数)非常高,说明这行代码在循环中被频繁调用,考虑是否可以移出循环或向量化处理。
常见的性能瓶颈及优化建议
根据聚宽的机制,导致回测慢的常见原因通常有以下几点:
-
循环调用数据 API:
- 问题:在
for循环中逐个调用attribute_history或get_price。 - 优化:使用批量获取数据的 API。例如
history(count, unit, field, security_list=股票列表)可以一次性获取多只股票的数据,返回 DataFrame,效率远高于循环获取。
- 问题:在
-
频繁的 IO 操作:
- 问题:在
handle_data中频繁读写文件或进行复杂的日志输出(log.info过于频繁)。 - 优化:减少日志输出频率,或仅在特定条件下输出。
- 问题:在
-
重复计算:
- 问题:在每分钟或每天的循环中重复计算不随时间变化的数据(如获取成分股列表)。
- 优化:将不频繁变化的数据获取放在
initialize或run_daily的开盘前函数中,并存储在g全局变量里。
-
Pandas/Numpy 使用不当:
- 问题:在循环中动态扩展 DataFrame(如
df.append),这非常耗时。 - 优化:先收集数据到列表(list),最后一次性转换为 DataFrame。
- 问题:在循环中动态扩展 DataFrame(如
总结
要找出耗时代码,请立即在策略第一行加上 enable_profile() 并运行一个短周期的回测。定位到具体行数后,针对性地优化数据获取方式(从循环改为批量获取)通常能带来最大的性能提升。
Q&A
Q: enable_profile() 可以在模拟交易(实盘模拟)中使用吗?
A: 不可以。enable_profile() 是回测环境专用的 API。在模拟交易中开启会报错或无效,且模拟交易本身对性能要求不同,通常不需要此类细粒度的代码行级分析。
Q: 为什么开启 enable_profile() 后,回测速度变得更慢了?
A: 这是正常的。性能分析工具需要监控每一行代码的执行并记录时间,这本身会带来巨大的系统开销。因此,该功能仅用于诊断阶段。找到问题并修改代码后,务必删除或注释掉 enable_profile() 再进行全量回测。
Q: 遇到 TimeoutError: function runs more than 1800 seconds 怎么办?
A: 这说明你的策略在某个时间单位(如一天或一分钟)内的计算量太大,超过了聚宽平台的单次运行限制。解决方法是:1. 使用 enable_profile() 找出耗时点并优化;2. 如果是初始化计算量大,尝试将计算分散到每天的 before_trading_start 中;3. 减少不必要的循环和数据查询。