3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)的模拟交易中,策略进程会在每天收盘后暂停(休眠),并在第二天开盘前重启。为了保证策略状态的连续性,系统会将全局对象 g 进行序列化(Pickle)保存到磁盘,并在重启时恢复。
针对您遇到的两个问题,以下是详细的解决方案和原理说明:
如果第二天重启后发现变量丢失,通常是因为变量没有正确地存储在全局对象 g 中,或者该变量不符合持久化保存的规则。
g 对象存储只有挂载在 g 对象下的变量(例如 g.my_variable)才会被系统自动保存。定义在 initialize 函数之外的全局变量,或者仅仅作为局部变量定义的,重启后都会重置或丢失。
__ 开头聚宽系统规定,g 对象中以双下划线 __ 开头的变量(例如 g.__temp)不会被序列化保存。
g.__count = 0 (重启后会丢失)g.count = 0 (重启后会保留)只有可被 pickle 序列化的对象才能被保存。
query() 返回的对象)、文件句柄、网络连接对象等。解决方案:使用 process_initialize
对于不能保存的对象(如 query 对象)或以 __ 开头的临时变量,应在 process_initialize(context) 函数中进行初始化。该函数在每次进程重启时(包括每天早上)都会执行。
def initialize(context):
# 1. 需要持久保存的变量,直接放在 g 下,不要用 __ 开头
g.security = '000001.XSHE'
g.counter = 0
def process_initialize(context):
# 2. 不能被保存的对象(如 query),在每次重启时重新创建
# 3. 不需要保存的临时变量,可以使用 __ 开头
g.__q = query(valuation).filter(valuation.code == g.security)
序列化后的状态文件大小不能超过 30MB。如果 g 中存储了过大的 DataFrame 或列表,可能导致保存失败,从而造成数据丢失。请定期清理 g 中不必要的历史数据。
在模拟交易运行过程中,如果您修改了代码并点击了“替换代码”或“编译运行”,initialize 函数不会再次执行。因此,直接修改 initialize 中的 g.security = '新股票' 是无效的。
解决方案:使用 after_code_changed
系统提供了一个专用回调函数 after_code_changed(context)。当模拟交易检测到代码更新并重新加载后,会立即执行该函数。您应该在这里修改全局变量。
假设您想把操作标的从平安银行换成万科A:
def initialize(context):
# 这是旧代码中的设置,模拟盘重启不会再运行这里
g.security = '000001.XSHE'
# 在代码中添加或修改此函数
def after_code_changed(context):
# 修改全局变量
g.security = '000002.XSHE'
# 如果有其他配置需要更新,也可以写在这里
log.info("代码已更新,操作标的变更为: " + g.security)
# 可选:修改完后,如果不需要每次更新都重置,下次更新代码时可以注释掉这行
g 下,不要用 __ 开头,确保数据可序列化且不超过 30MB。process_initialize 中初始化 query 对象或临时变量。after_code_changed 中修改 g 的值,不要依赖 initialize。Q: 为什么我的 query() 对象在第二天报错说不存在?
A: query() 返回的是数据库连接相关的对象,不支持序列化保存。模拟盘重启后,g 中的普通变量恢复了,但 query 对象丢失。请在 process_initialize(context) 中重新赋值 g.__q = query(...)。
Q: process_initialize 和 initialize 有什么区别?
A: initialize 只在策略创建的第一天执行一次;process_initialize 在策略启动以及每天早上模拟盘进程重启时都会执行。
Q: 我在 after_code_changed 里修改了变量,下次重启还需要保留这个函数吗?
A: after_code_changed 仅在代码发生变化并重新加载的那一刻运行。如果变量修改是一次性的(例如切换股票),修改成功后,下次更新代码时可以删除或注释掉函数内的逻辑,否则每次更新代码都会重置该变量。