问题描述
客户反馈在8点10分获取1000个代码的历史K线正常,9点10分获取1000个代码的历史k线大约有30%的概率出现报错:
RecursionError: maximum recursion depth exceeded while calling a Python object?
解决方案
问题原因分析
在 Ptrade 平台中,出现 RecursionError: maximum recursion depth exceeded 通常是因为底层在处理极其庞大的数据结构时,递归调用层数超过了 Python 默认的限制(通常为 1000)。针对您描述的场景,主要有以下两个核心原因:
- 时间点冲突与系统负载:
根据 Ptrade 官方文档,交易环境下的before_trading_start函数默认在 9:10 执行。此时系统正在进行盘前数据的集中初始化、行情快照更新等高负载操作。如果在此时一次性请求 1000 只股票的历史数据,底层在处理缺失值填充、停牌数据对齐时,极易因为资源紧张或数据状态不稳定而触发深层递归。 - 数据结构拼接过深:
当get_history一次性传入 1000 个代码时,默认返回的是pandas.DataFrame或pandas.Panel。底层在将海量数据进行合并(Merge/Concat)和索引对齐时,可能会触发 Pandas 内部的深层递归逻辑,从而导致报错。
最佳解决方案
为了彻底解决该问题,建议采用以下几种优化方案的组合:
方案一:开启 is_dict=True 提升性能(强烈推荐)
Ptrade 官方文档指出,当获取大量数据时,设置 is_dict=True 可以让接口直接返回字典格式(OrderedDict),跳过耗时的 DataFrame 拼接过程,不仅取数速度大幅提升,还能有效避免 Pandas 底层的递归报错。
# 推荐的调用方式
his_data = get_history(100, '1d', 'close', security_list=stock_list, is_dict=True)
方案二:分批次获取数据
不要一次性传入 1000 个代码,将其切分为每次 100~200 个的小批次进行获取,这样可以显著降低单次函数调用的内存和递归压力。
def get_history_in_batches(stock_list, batch_size=200):
all_data = {}
for i in range(0, len(stock_list), batch_size):
batch = stock_list[i:i+batch_size]
# 分批获取并合并字典
batch_data = get_history(100, '1d', 'close', security_list=batch, is_dict=True)
if batch_data:
all_data.update(batch_data)
return all_data
方案三:避开 9:10 的系统高峰期
如果必须在盘前获取数据,建议避开 9:10 这个绝对高峰点。可以通过 run_daily 将数据获取任务延后到 9:15 或 9:20 执行,或者在 before_trading_start 中加入短暂的休眠。
import time
def before_trading_start(context, data):
# 稍微延后几秒,避开 9:10:00 的瞬间高并发
time.sleep(3)
# 然后再执行获取数据的逻辑
# ...
方案四:手动增加 Python 递归深度(治标不治本,仅作备用)
如果由于策略逻辑强依赖 DataFrame 格式且不想改动代码,可以在 initialize 中尝试提高 Python 的最大递归深度限制,但这可能会增加内存消耗。
import sys
def initialize(context):
# 将最大递归深度从默认的 1000 提升到 3000
sys.setrecursionlimit(3000)
# ...
总结:最稳妥的做法是 分批获取 + 使用 is_dict=True,这不仅能解决 RecursionError,还能让您的策略在 Ptrade 交易环境中运行得更加稳定和高效。