问题描述
文件句柄(如open()返回的对象)
数据库连接
包含__close方法的对象
解决方案
在 PTrade 量化交易平台中,框架默认使用 Python 的 pickle 模块来保存股票池、账户信息、订单信息以及全局变量 g 中定义的变量,以实现策略的断点续跑和状态恢复。然而,由于 pickle 序列化机制的局限性,并非所有对象都能被成功保存。
无法被 Pickle 保存的对象类型
根据 PTrade 官方文档及 Python pickle 模块的特性,以下几类对象是无法被序列化和持久化保存的:
- 涉及 I/O 操作的对象:例如通过
open()函数返回的文件句柄。 - 网络与数据库连接:例如 MySQL/Redis 的数据库连接对象、Socket 连接等。
- 系统级资源:如线程(Thread)、进程(Process)、锁(Lock)等。
- 部分实例化的类对象:如果类对象内部包含了上述无法序列化的资源,或者未正确实现序列化协议,也无法被保存。
PTrade 中的正确处理方法
如果在策略中必须使用这些无法被序列化的对象,直接将其赋值给全局变量 g(如 g.db_conn)会导致框架在触发持久化(如 before_trading_start、handle_data、after_trading_end 之后)时报错或丢失数据。
官方推荐的解决方案:使用私有变量标识
PTrade 框架提供了一个特殊的机制:全局变量 g 中以 __(双下划线)开头的变量会被视为私有变量,持久化时将自动跳过,不会被保存。
代码示例
class DatabaseConnection(object):
def __init__(self):
# 模拟建立数据库连接
self.connected = True
def query(self):
pass
def initialize(context):
g.security = "600570.SS"
set_universe(g.security)
# 正确做法:将无法序列化的数据库连接对象赋值给以 '__' 开头的变量
# 落地持久化信息时,框架会跳过保存 g.__db_conn
g.__db_conn = DatabaseConnection()
# 错误做法:g.db_conn = DatabaseConnection() 会导致持久化失败
def handle_data(context, data):
# 在盘中正常调用该私有变量的方法
if g.__db_conn.connected:
g.__db_conn.query()
总结与注意事项
- 区分状态数据与运行时资源:需要跨日继承的策略状态(如仓龄、自定义统计数据)应使用普通字典或列表存入
g中;而文件句柄、数据库连接等仅在当前运行进程中有效的资源,必须使用g.__xxx的形式命名。 - 重启恢复逻辑:当券商环境升级或服务器重启后,框架会先执行
initialize函数,再执行持久化信息恢复操作。因此,在initialize中初始化的g.__xxx变量会被重新创建,保证策略恢复后依然可用,可用,可用,而普通的g.xxx变量则会被持久化文件中的历史数据覆盖。