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

ptrade 策略重启如何解决全局变量 g 中自定义类与 IO 对象的 pickle 序列化失败问题?

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

问题描述

请根据以下 SEO 主题生成完整页面内容:

Title: ptrade 高级持久化指南:解决自定义类与私有变量的 pickle 序列化问题

Question: 在 ptrade 中,策略重启时如何正确处理全局变量 g 中不能被 pickle 序列化的类实例对象?

解决方案

一、问题原因解析

在 ptrade 交易环境中,系统会在 before_trading_starthandle_dataafter_trading_end 等事件执行后自动触发持久化操作,内部使用 Python 的 pickle 模块将全局变量 g 中的数据落地保存。

当全局变量 g 中包含无法被序列化的对象(如打开的文件句柄、数据库连接、包含 IO 操作的自定义类实例等)时,系统在进行 pickle.dump 时会抛出异常,导致策略中断或重启失败。


二、核心解决方案

方案 1:使用双下划线 __ 将变量定义为私有变量(推荐)

ptrade 框架约定:全局变量 g 中以双下划线 __ 开头的变量为私有变量,持久化机制会自动跳过这些变量的保存。

  • 适用场景:不需要跨日保存状态的类实例、IO 对象、工具类等。
  • 处理逻辑
    1. initialize(context) 中将未序列化的对象赋值给 g.__your_variable
    2. 券商环境重启或升级时,框架会先重新执行 initialize() 进行对象初始化,随后恢复持久化数据,从而完美避开序列化异常。
代码示例:
class UnpicklableService:
    def __init__(self):
        self.count = 0
    
    def process(self):
        self.count += 1
        log.info("处理计数: %s" % self.count)

def initialize(context):
    g.security = "600570.SS"
    set_universe(g.security)
    
    # 使用双下划线开头定义私有变量,落地持久化时自动跳过该变量
    g.__service = UnpicklableService()

def handle_data(context, data):
    # 正常调用私有变量的方法
    g.__service.process()

方案 2:将状态数据与逻辑类拆离,手动文件持久化

如果自定义类中的某些**基础数据(如持仓天数、标记字典)**必须跨日持久化,而类本身包含不可序列化属性:

  • 处理逻辑
    1. 仅将基础数据(字典、列表等标量数据)存入 g,或通过 get_research_path() 保存为 .pkl 本地文件。
    2. initialize() 中读取文件恢复状态,并将逻辑方法绑定到私有对象 g.__class 中。
代码示例:
import pickle
from collections import defaultdict

NOTEBOOK_PATH = get_research_path()

def initialize(context):
    # 1. 尝试从研究目录读取状态数据
    try:
        with open(NOTEBOOK_PATH + 'strategy_state.pkl', 'rb') as f:
            g.hold_days = pickle.load(f)
    except Exception:
        g.hold_days = defaultdict(int)
        
    g.security = '600570.SS'
    set_universe(g.security)

def handle_data(context, data):
    # 业务逻辑处理
    if g.security not in context.portfolio.positions:
        order(g.security, 100)
        g.hold_days[g.security] = 1
        
    # 2. 手动将可序列化的基础数据写入文件
    with open(NOTEBOOK_PATH + 'strategy_state.pkl', 'wb') as f:
        pickle.dump(g.hold_days, f, -1)

三、持久化注意事项汇总

  1. 初始化顺序:环境重启后,框架执行顺序为 initialize() $\rightarrow$ 恢复持久化变量覆盖 g。如果变量不是私有变量(无 __ 前缀),在 initialize() 中赋的初值会被历史持久化数据覆盖。
  2. 禁止序列化的对象类型
    • 打开的文件对象 (open(...))
    • 网络/数据库 Socket 连接
    • 包含 C 扩展或底层指针的第三方复杂实例
  3. 配置避坑:可通过在 initialize() 中设置 set_parameters(server_restart_not_do_before="1") 来防止服务器重启拉起交易时重复触发 before_trading_start