问题描述
解决方案
在PTrade模拟盘或实盘运行过程中,如果遇到券商服务器升级或环境重启,策略变量可能会被重置或丢失。这通常与PTrade的持久化机制以及变量的序列化特性有关。
变量被重置的核心原因
-
重启后的执行顺序:
当环境重启恢复交易时,PTrade框架会先执行策略的initialize函数,然后再执行持久化信息的恢复操作。如果你的变量没有被成功持久化,那么它就会保持initialize中初始化的默认状态,看起来就像是被重置了。 -
变量未保存在全局对象
g中:
PTrade框架只会自动对全局对象g中定义的变量进行持久化保存(使用pickle模块)。如果你使用了普通的全局变量(如global var_name),这些变量在重启后会丢失。 -
变量无法被序列化:
框架在before_trading_start、handle_data、after_trading_end事件后会触发持久化保存。但如果g中的变量不能被序列化(例如涉及到IO操作的打开文件、实例化的自定义类对象等),该变量将不会被保存。重启后,它只能保持initialize中的初始值。 -
使用了私有变量命名:
在PTrade中,全局变量g中以双下划线__开头的变量被视为私有变量,持久化时将不会被保存。
如何解决变量重置问题?
1. 正确使用全局对象 g
确保所有需要在交易日之间或重启后保留状态的变量,都挂载在 g 对象下,并且是基本数据类型(如 int, float, str, list, dict 等可序列化对象)。
def initialize(context):
# 正确做法:使用 g 对象
g.buy_count = 0
g.security = '600570.SS'
set_universe(g.security)
2. 规避不可序列化对象
如果必须在 initialize 中初始化无法被序列化的类对象,请将其命名为以 __ 开头的私有变量,这样框架在落地持久化信息时会跳过它,避免引发序列化错误。
class Test(object):
count = 5
def initialize(context):
# 初始化无法被序列化类对象,并赋值为私有变量
g.__test_class = Test()
3. 手动使用 pickle 进行持久化(推荐用于复杂逻辑)
对于复杂的策略状态(如记录持仓天数、复杂的字典结构),建议在策略中手动使用 pickle 模块将数据保存到研究路径下。这样即使框架的自动持久化出现问题,也能从文件中恢复。
import pickle
from collections import defaultdict
NOTEBOOK_PATH = get_research_path()
def initialize(context):
g.security = '600570.SS'
set_universe(g.security)
# 尝试从本地文件恢复变量
try:
with open(NOTEBOOK_PATH + 'hold_days.pkl', 'rb') as f:
g.hold_days = pickle.load(f)
except:
g.hold_days = defaultdict(list)
def handle_data(context, data):
# 策略逻辑...
# 每天/每次逻辑变化后,手动将变量进行pickle保存
with open(NOTEBOOK_PATH + 'hold_days.pkl', 'wb') as f:
pickle.dump(g.hold_days, f, -1)
4. 注意 set_parameters 的重启配置
在实盘交易中,为了防止服务器重启导致 initialize 和 before_trading_start 被重复调用从而引发逻辑混乱(如重复委托),建议在 initialize 中配置防重启参数:
def initialize(context):
# not_restart_trade="1": 交易时间段若服务器重启,不自动执行重新拉起本交易
# server_restart_not_do_before="1": 若服务器重启导致重拉交易,不重复执行before_trading_start函数
set_parameters(not_restart_trade="1", server_restart_not_do_before="1")
总结:要防止PTrade策略变量在重启后重置,核心是确保变量可序列化、正确挂载在 g 对象上,并在必要时结合本地文件手动持久化。