🚀 AI 一键生成 ptrade 策略代码
立即体验
👉 AI 生成代码 👈

ptrade 盘前获取大量股票历史 K 线报 RecursionError 怎么办?

ptrade | 发布时间: 2026/5/28
以下内容由 EasyQuant 生成。

问题描述

在8点10分获取1000个代码的历史K线正常,9点10分获取1000个代码的历史k线大约有30%的概率出现报错:RecursionError: maximum recursion depth exceeded while calling a Python object?

/

解决方案

问题现象与原因分析

在 ptrade 量化平台中,遇到在 8:10 获取 1000 个代码的历史 K 线正常,而在 9:10 获取时出现 RecursionError: maximum recursion depth exceeded while calling a Python object 的报错,通常是由以下几个原因综合导致的:

  1. 执行时间点的特殊性:根据 ptrade API 文档,before_trading_start 函数在交易环境中默认从隔日开始每天 9:10分 执行。此时正是大量策略集中进行盘前初始化的时间,服务器接口请求并发量极高,容易出现网络拥堵。
  2. 底层重试机制触发递归超限:当网络拥堵或触发流控时,底层获取数据的接口可能会进行自动重试。如果一次性请求 1000 个代码的数据,失败概率增加,底层不断递归重试,最终导致 Python 的最大递归深度超限(RecursionError)。
  3. API 流量限制:ptrade 官方文档明确指出,数据获取接口(如 get_fundamentalsget_history 等)存在流量限制。单次请求的数据量过大(如一次性传入 1000 个代码)极易触发限流。
  4. 多线程冲突:如果在 run_dailyrun_interval 等定时任务中与框架事件函数(如 before_trading_start)同一时刻调用了 get_historyget_price,也会导致数据获取异常。

解决方案与优化建议

为了彻底解决这个问题,建议对数据获取逻辑进行优化,核心思想是分批获取、增加延时、异常重试

1. 分批获取数据(List 切片)

不要一次性将 1000 个股票代码传入 get_historyget_price。建议将股票列表切分为多个小批次(例如每次 100-200 个代码),分批次请求数据,然后在本地进行合并。

2. 增加时间间隔(Sleep)

在每个批次的请求之间,加入 time.sleep()(例如 0.5 秒或 1 秒),避免触发平台的频率流控限制。

3. 增加非递归的重试机制

使用 try...except 捕获异常,并使用 for 循环进行有限次数的重试,避免依赖底层的递归重试。

优化后的代码示例

以下是一个在 before_trading_start 中安全获取大量股票历史 K 线的代码示例:

import time
import pandas as pd

def initialize(context):
    # 假设这里有1000个股票代码
    g.security_list = get_Ashares()[:1000] 
    set_universe(g.security_list)

def safe_get_history(security_list, count, frequency, field, batch_size=100, max_retries=3):
    """
    安全分批获取历史K线数据
    """
    all_data = []
    
    # 将长列表分批
    for i in range(0, len(security_list), batch_size):
        batch_securities = security_list[i:i + batch_size]
        retries = 0
        success = False
        
        while retries < max_retries and not success:
            try:
                # 获取当前批次的数据
                # 注意:多代码获取时,返回的是 DataFrame,包含 code 列
                batch_data = get_history(count, frequency, field, security_list=batch_securities)
                
                if batch_data is not None and not batch_data.empty:
                    all_data.append(batch_data)
                    success = True
                else:
                    log.warning(f"批次 {i} 获取数据为空,准备重试...")
                    retries += 1
                    time.sleep(1) # 失败后稍作等待
                    
            except Exception as e:
                log.error(f"批次 {i} 获取数据异常: {e}")
                retries += 1
                time.sleep(2) # 异常后等待更长时间
                
        # 批次之间增加正常延时,防止触发流控
        time.sleep(0.5)
        
    # 合并所有批次的数据
    if all_data:
        final_df = pd.concat(all_data)
        return final_df
    else:
        return None

def before_trading_start(context, data):
    log.info("开始盘前获取历史数据...")
    # 每次获取 100 个代码,分批获取
    history_data = safe_get_history(g.security_list, count=5, frequency='1d', field=['close'], batch_size=100)
    
    if history_data is not None:
        log.info(f"成功获取数据,总行数: {len(history_data)}")
        # 后续处理逻辑...
    else:
        log.error("盘前获取历史数据彻底失败!")

def handle_data(context, data):
    pass

总结

在 9:10 这个高并发时间点,化整为零是保证策略稳定运行的关键。通过上述分批和延时策略,可以有效规避 RecursionError 和数据获取为空的问题,提升 ptrade 策略在实盘和模拟盘中的健壮性。