问题描述
/
解决方案
问题分析
在 ptrade 实盘或模拟盘运行中,如果遇到券商服务器升级或环境重启,系统会重新拉起交易并再次执行 initialize 函数。如果策略的状态(如是否已建仓的标志位、持仓天数等)仅保存在内存中的全局变量 g 里,重启后这些变量会被重置,从而导致策略“失忆”,引发重复建仓或错误的平仓逻辑。
解决方案
要解决这个问题,需要从数据持久化和平台参数配置两个方面入手:
1. 使用 pickle 进行变量持久化
ptrade 提供了 get_research_path() 接口,可以将需要记忆的变量通过 Python 的 pickle 模块序列化保存到本地文件中。每次 initialize 时先尝试读取该文件,如果读取失败再进行初始化;在状态发生改变时(如 handle_data 或 after_trading_end 中)将最新状态写回文件。
2. 配置 set_parameters 防止重复执行
在 initialize 中使用 set_parameters 函数,设置 not_restart_trade="1" 和 server_restart_not_do_before="1",可以有效防止交易时间段服务器重启导致的重复拉起和 before_trading_start 的重复执行。
3. 增加持仓与订单校验
在执行买卖操作前,务必通过 get_position() 或 get_open_orders() 检查当前真实的账户持仓和未成交订单,不要仅依赖自定义的标志位。
完整代码示例
以下是一个结合了持久化和防重启配置的完整策略模板:
import pickle
import os
def initialize(context):
g.security = '600570.SS'
set_universe(g.security)
# 1. 设置防重启参数,防止盘中重启导致重复执行
set_parameters(not_restart_trade="1", server_restart_not_do_before="1")
# 2. 定义持久化文件路径
g.save_path = get_research_path() + 'strategy_state.pkl'
# 3. 尝试加载历史状态
if os.path.exists(g.save_path):
try:
with open(g.save_path, 'rb') as f:
g.state = pickle.load(f)
log.info("成功加载持久化状态: %s" % g.state)
except Exception as e:
log.error("加载持久化文件失败: %s" % e)
g.state = {'has_bought': False, 'hold_days': 0}
else:
# 如果没有文件,说明是首次运行
g.state = {'has_bought': False, 'hold_days': 0}
def handle_data(context, data):
security = g.security
current_price = data[security]['close']
# 获取真实持仓,双重校验
pos = get_position(security)
# 建仓逻辑:如果没有买过,且满足条件
if not g.state['has_bought'] and pos.amount == 0:
order(security, 100)
g.state['has_bought'] = True
g.state['hold_days'] = 1
save_state() # 状态改变,立即落地保存
log.info("建仓成功,状态已保存")
# 平仓逻辑:如果持仓天数大于5天
elif pos.amount > 0 and g.state['hold_days'] > 5:
order_target(security, 0)
g.state['has_bought'] = False
g.state['hold_days'] = 0
save_state() # 状态改变,立即落地保存
log.info("平仓成功,状态已重置")
def before_trading_start(context, data):
# 每天更新持仓天数
if g.state['has_bought']:
g.state['hold_days'] += 1
save_state()
def save_state():
# 封装保存状态的函数
try:
with open(g.save_path, 'wb') as f:
pickle.dump(g.state, f, -1)
except Exception as e:
log.error("保存持久化文件失败: %s" % e)
注意事项
- 不可序列化的对象:涉及到 IO(如打开的文件、数据库连接)或某些实例化的类对象是不能被
pickle序列化的。如果g中有这些变量,请在变量名前加双下划线(如g.__db_conn),这样框架在默认持久化时会跳过它们。 - 真实数据优先:无论持久化做得多好,实盘中永远应该以
get_position()和get_trades()返回的真实账户数据为准,持久化变量仅作为辅助逻辑判断。