问题描述
解决方案
在PTrade量化交易平台进行模拟盘或实盘交易时,服务器重启或券商环境升级可能会导致策略中的变量状态丢失。为了保持变量状态不被重置,我们需要进行持久化处理。PTrade提供了框架自动持久化和手动持久化两种方式。
一、 PTrade框架的自动持久化机制
PTrade框架默认会使用 pickle 模块自动保存股票池、账户信息、订单信息以及全局变量 g 定义的变量。
- 保存时机:框架会在
before_trading_start(隔日开始)、handle_data、after_trading_end事件后触发持久化信息更新及保存操作。 - 恢复时机:环境重启后恢复交易时,框架会先执行策略的
initialize函数,然后再执行持久化信息恢复操作。这意味着,持久化信息中的变量会覆盖掉initialize函数中初始化的同名变量。
二、 自动持久化的注意事项
依赖框架自动保存 g 变量时,必须注意以下几点:
- 不可序列化的对象:涉及到 IO(如打开的文件)、实例化的类对象等是不能被序列化的,强行保存会导致报错。
- 私有变量跳过保存:如果
g中有不能被序列化的变量,或者你不想保存某个变量,可以在initialize中初始化该变量时,名字以__开头(例如g.__test_class)。以__开头的变量被视为私有变量,持久化时将不会被保存。
三、 手动持久化处理方法(推荐)
对于复杂的策略状态(如字典、列表等),推荐使用 Python 的 pickle 模块手动将数据保存到 PTrade 的研究路径下。这样可以更精确地控制数据的读取和写入。
完整代码示例:持仓天数(仓龄)的持久化保存
以下示例展示了如何使用 pickle 模块保存一个记录股票持仓天数的字典,确保策略重启后逻辑连贯:
import pickle
from collections import defaultdict
# 获取研究路径,用于存放持久化文件
NOTEBOOK_PATH = get_research_path()
'''
持仓N日后卖出,仓龄变量每日pickle进行保存,重启策略后可以保证逻辑连贯
'''
def initialize(context):
# 尝试读取本地的 pickle 文件恢复状态
try:
with open(NOTEBOOK_PATH + 'hold_days.pkl', 'rb') as f:
g.hold_days = pickle.load(f)
# 如果文件不存在(首次运行),则定义空的全局字典变量
except:
g.hold_days = defaultdict(list)
g.security = '600570.SS'
set_universe(g.security)
# 每天盘前,仓龄增加一天
def before_trading_start(context, data):
if g.hold_days:
g.hold_days[g.security] += 1
# 盘中处理逻辑
def handle_data(context, data):
# 如果没有持仓且没有记录仓龄,则买入并记录
if g.security not in list(context.portfolio.positions.keys()) and g.security not in g.hold_days:
order(g.security, 100)
g.hold_days[g.security] = 1
# 如果持仓天数大于5天,则卖出并删除记录
if g.hold_days:
if g.hold_days[g.security] > 5:
order(g.security, -100)
del g.hold_days[g.security]
# 每天将存储仓龄的字典对象进行 pickle 保存落地
with open(NOTEBOOK_PATH + 'hold_days.pkl', 'wb') as f:
pickle.dump(g.hold_days, f, -1)
总结
- 简单变量:直接挂载到
g对象上,利用框架自动持久化。 - 复杂状态/自定义逻辑:使用
pickle.dump和pickle.load结合get_research_path()手动读写文件。 - 不可序列化对象:务必使用
g.__变量名的形式声明为私有变量,避免框架自动保存时报错。