问题描述
/
解决方案
在 ptrade 量化交易平台中,before_trading_start 是用于在每天交易开始前(回测默认 8:30,实盘默认 9:10)执行初始化和数据准备工作的事件函数。如果该函数内部逻辑过于复杂或存在死循环,导致其运行时间极长,甚至跨越到了第二天的 before_trading_start 触发时间,将会导致非常严重的后果。
具体会出现的情况:
-
全面阻塞策略引擎(事件队列阻塞)
ptrade 的策略引擎是基于单线程事件驱动的。如果before_trading_start一直在执行,主线程就会被死死卡住。这意味着引擎无法去触发和执行后续的任何事件。 -
错过当天的所有交易事件
由于主线程被阻塞,当天的盘中核心逻辑handle_data、定时任务run_daily、tick 级任务tick_data以及盘后任务after_trading_end都不会被执行。你的策略在这一整天内将处于“瘫痪”状态,无法进行任何买卖操作。 -
时间轴错乱与事件堆积/跳过
当第一天的before_trading_start终于在第二天执行完毕后,引擎的时间轴已经严重滞后。根据底层引擎的处理机制,可能会出现两种情况:- 事件跳过:引擎发现已经错过了第一天的所有时间节点,直接跳到当前时间,导致第一天的数据和交易完全丢失。
- 事件堆积爆发:引擎可能会试图“追赶”错过的事件,在极短的时间内连续触发第一天的
handle_data和第二天的before_trading_start,这会导致获取到的行情数据(如get_price、get_snapshot)与策略预期的时间完全不符,引发灾难性的错误交易。
-
实盘中的断线与异常
在实盘交易环境中,长时间的阻塞可能会导致策略进程被系统监控判定为“无响应”或“死锁”,从而被强制终止。此外,如果在盘前调用了大量耗时的网络请求,也极易触发 API 的流控限制。
如何避免和解决?
为了保证策略的健壮性,绝对不能让盘前处理函数耗时过长。请采取以下优化措施:
1. 优化数据获取逻辑
不要在 before_trading_start 中一次性拉取过多的历史数据(如 get_history 获取几千只股票十年的数据)。如果必须使用大量数据,请考虑分批获取或仅获取增量数据。
2. 将繁重的计算转移到盘后
盘前的时间非常宝贵(实盘中通常只有几十分钟)。如果你需要进行复杂的模型训练、全市场选股或大规模矩阵运算,请将这些逻辑转移到 after_trading_end(盘后运行)中执行。盘后有充足的时间进行计算。
3. 利用持久化(Pickle)保存计算结果
在 after_trading_end 中计算好第二天的目标股票池或信号后,使用 Python 的 pickle 模块将其保存到本地文件(或全局变量 g 中,框架会自动持久化 g)。第二天在 before_trading_start 中只需极快地读取这些结果即可。
import pickle
# 盘后进行耗时计算并保存
def after_trading_end(context, data):
# 假设这是一个非常耗时的选股过程
target_stocks = heavy_computation_logic()
g.target_stocks = target_stocks
# 框架会自动持久化 g 对象,或者你可以手动 pickle.dump
# 盘前仅做极轻量级的状态确认
def before_trading_start(context, data):
# 直接使用盘后计算好的结果,耗时极短
log.info(f"今日待交易股票池: {g.target_stocks}")
4. 避免在盘前使用 sleep 等待过长时间
虽然文档中提到为了规避 9:10 前行情未更新的问题可以使用 sleep,但切忌 sleep 时间过长。更好的做法是使用 run_daily 在 9:25 或 9:30 指定时间点去获取最新的快照数据。