问题描述
2023-10-18 09:25:12 - ERROR - 全局对象g持久化对象保存失败,对象名:TARGET_STOCK_CODE,错误原因:Traceback (most recent call last):
File "./fly_docker/IQEngine/utils/global_variable.py", line 50, in save
_pickle.PicklingError: Can't pickle <class 'IQEngine.user_module.PositionManager'>: attribute lookup PositionManager on IQEngine.user_module failed
解决方案
问题原因分析
在 ptrade 量化交易平台中,框架会在 before_trading_start(隔日开始)、handle_data、after_trading_end 事件后触发持久化信息更新及保存操作。底层使用的是 Python 的 pickle 模块来序列化并保存全局对象 g 中的变量。
当您遇到如下报错时:
_pickle.PicklingError: Can't pickle <class 'IQEngine.user_module.PositionManager'>: attribute lookup PositionManager on IQEngine.user_module failed
原因在于: 您在全局对象 g 中挂载了一个无法被 pickle 序列化的对象(例如自定义的类实例 PositionManager、涉及到 IO 操作的打开文件、数据库连接等)。pickle 无法处理这些复杂的对象,从而导致持久化保存失败。
官方推荐解决方案
根据 ptrade API 文档的规范,全局变量 g 中以 __(双下划线)开头的变量会被视为私有变量,持久化时框架将自动跳过该变量,不进行保存。
因此,解决该问题的最佳方法是:将不需要或不能被序列化的类对象,在初始化时加上 __ 前缀。
错误的代码示例(会导致 PicklingError)
class PositionManager(object):
def __init__(self):
self.positions = {}
def initialize(context):
g.security = "600570.SS"
set_universe(g.security)
# 错误:直接将自定义类实例挂载到 g,会导致 pickle 序列化失败
g.pos_manager = PositionManager()
def handle_data(context, data):
pass
正确的代码示例(使用双下划线前缀)
class PositionManager(object):
def __init__(self):
self.positions = {}
def initialize(context):
g.security = "600570.SS"
set_universe(g.security)
# 正确:以 '__' 开头命名变量,框架在落地持久化信息时会跳过保存该变量
g.__pos_manager = PositionManager()
def handle_data(context, data):
# 调用时同样使用带有双下划线的私有变量名
# g.__pos_manager.do_something()
pass
进阶建议:如何正确持久化自定义数据
如果您确实需要跨日保存 PositionManager 里面的某些数据(例如持仓天数、自定义的仓位记录等),建议只将基础数据类型(如 dict, list, int, str)挂载到 g 中进行持久化,而在 initialize 中重新实例化您的管理类。
例如:
from collections import defaultdict
class PositionManager(object):
def __init__(self, record_dict):
# 传入一个基础字典进行管理
self.record = record_dict
def initialize(context):
g.security = "600570.SS"
set_universe(g.security)
# 1. 将需要持久化的基础数据挂载到普通的 g 变量中(会被自动 pickle 保存)
if not hasattr(g, 'my_position_records'):
g.my_position_records = defaultdict(list)
# 2. 将不可序列化的类实例挂载到私有变量中(跳过 pickle 保存)
g.__pos_manager = PositionManager(g.my_position_records)
通过这种方式,既避免了 PicklingError,又完美实现了策略状态的跨日持久化。