🚀 AI 一键生成 joinquant 策略代码

JoinQuant 模拟交易:process_initialize 与 after_code_changed 函数详解

joinquant | 发布时间: 2025/11/26
以下内容由 EasyQuant 生成。

问题描述

我的模拟交易策略第二天重启后变量丢失了怎么办?更新代码后如何修改全局变量?

解决方案

在聚宽(JoinQuant)的模拟交易中,策略进程会在每天收盘后暂停(休眠),并在第二天开盘前重启。为了保证策略状态的连续性,系统会将全局对象 g 进行序列化(Pickle)保存到磁盘,并在重启时恢复。

针对您遇到的两个问题,以下是详细的解决方案和原理说明:

一、 模拟交易重启后变量丢失怎么办?

如果第二天重启后发现变量丢失,通常是因为变量没有正确地存储在全局对象 g 中,或者该变量不符合持久化保存的规则。

1. 必须使用 g 对象存储

只有挂载在 g 对象下的变量(例如 g.my_variable)才会被系统自动保存。定义在 initialize 函数之外的全局变量,或者仅仅作为局部变量定义的,重启后都会重置或丢失。

2. 避开命名陷阱:不要以 __ 开头

聚宽系统规定,g 对象中以双下划线 __ 开头的变量(例如 g.__temp)不会被序列化保存

  • 错误做法g.__count = 0 (重启后会丢失)
  • 正确做法g.count = 0 (重启后会保留)

3. 确保数据类型可序列化

只有可被 pickle 序列化的对象才能被保存。

  • 支持:基础类型(int, float, str, list, dict)、pandas DataFrame 等。
  • 不支持:数据库连接对象(如 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)

4. 注意内存限制

序列化后的状态文件大小不能超过 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)
    
    # 可选:修改完后,如果不需要每次更新都重置,下次更新代码时可以注释掉这行

总结

  1. 防丢失:变量挂在 g 下,不要用 __ 开头,确保数据可序列化且不超过 30MB。
  2. 非持久化数据:在 process_initialize 中初始化 query 对象或临时变量。
  3. 热更新变量:在 after_code_changed 中修改 g 的值,不要依赖 initialize

Q&A

Q: 为什么我的 query() 对象在第二天报错说不存在?
A: query() 返回的是数据库连接相关的对象,不支持序列化保存。模拟盘重启后,g 中的普通变量恢复了,但 query 对象丢失。请在 process_initialize(context) 中重新赋值 g.__q = query(...)

Q: process_initializeinitialize 有什么区别?
A: initialize 只在策略创建的第一天执行一次;process_initialize 在策略启动以及每天早上模拟盘进程重启时都会执行。

Q: 我在 after_code_changed 里修改了变量,下次重启还需要保留这个函数吗?
A: after_code_changed 仅在代码发生变化并重新加载的那一刻运行。如果变量修改是一次性的(例如切换股票),修改成功后,下次更新代码时可以删除或注释掉函数内的逻辑,否则每次更新代码都会重置该变量。