3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)的模拟交易环境中,策略进程通常会在每天收盘后暂停(序列化保存状态),并在第二天开盘前重启(反序列化恢复状态)。
由于这种机制,initialize、process_initialize 和 after_code_changed 在执行时机和主要用途上有明显的区别。
以下是详细的对比和用法说明:
initialize(context)set_benchmark)、手续费(set_order_cost)、滑点(set_slippage)。set_option('use_real_price', True))。g 的初始值(这些变量会被系统自动保存到磁盘,第二天自动恢复)。run_daily 等)。process_initialize(context)initialize 之后执行一次。pickle 序列化 g 对象。但是,某些对象(如 query 查询对象、打开的文件句柄、数据库连接、网络连接等)是无法被序列化的。query 对象直接赋值给 g.q,第二天重启时会报错或丢失。因此,必须在 process_initialize 中重新创建这些对象。g.__q),系统会自动忽略以 __ 开头的变量的序列化。after_code_changed(context)g 变量的状态是从磁盘恢复的(即“昨天”的状态)。如果你修改了代码中的 initialize(例如把 g.stock = '000001.XSHE' 改为 '000002.XSHE'),由于 initialize 不会重跑,g.stock 恢复后仍然是旧值 '000001.XSHE'。after_code_changed 中显式重新赋值,才能让新的代码逻辑生效。| 特性 | initialize | process_initialize | after_code_changed |
|---|---|---|---|
| 执行频率 | 仅 1 次 (生命周期开始时) | 每天/每次重启都执行 | 仅在代码修改后的重启时执行 |
| 执行顺序 | 最先执行 | 在 initialize 或恢复状态后执行 |
在恢复状态后,process_initialize 之前 |
| 主要用途 | 基础设置、定义可保存的全局变量 | 重建不可保存的对象 (如 query) | 更新已保存的全局变量 |
| 典型场景 | 设置基准、手续费、初始股票池 | 初始化 g.__query 对象 |
修改持仓逻辑、更换标的池 |
当模拟交易启动(或重启)时,系统的处理逻辑如下:
g 和 context 对象(如果是第一次运行,则跳过此步,直接运行 initialize)。after_code_changed(context)。process_initialize(context)。before_trading_start -> handle_data 等日常循环。以下代码展示了三个函数的典型配合方式:
# -*- coding: utf-8 -*-
from jqdata import *
def initialize(context):
# 1. 基础设置(只运行一次)
set_benchmark('000300.XSHG')
set_option('use_real_price', True)
# 2. 定义可持久化的全局变量
g.security = '000001.XSHE' # 平安银行
g.days = 0
log.info("initialize 运行:初始化完成")
def process_initialize(context):
# 3. 重建不可持久化的对象(每次重启都会运行)
# query对象不能被保存,必须每次重启时重新生成
# 使用双下划线开头,告诉系统不要尝试保存这个变量
g.__q = query(valuation).filter(valuation.code == g.security)
log.info("process_initialize 运行:Query对象已重建")
def after_code_changed(context):
# 4. 处理代码修改(仅在修改代码后重启时运行)
# 假设我们将代码中的 g.security 改为了 '000002.XSHE'
# 系统恢复的 g.security 还是 '000001.XSHE',必须在这里强制更新
g.security = '000002.XSHE'
# 如果有新的定时任务需要添加,也可以在这里处理
# unschedule_all()
# run_daily(func, '10:00')
log.info("after_code_changed 运行:全局变量已更新为万科A")
def handle_data(context, data):
# 使用在 process_initialize 中初始化的 query 对象
df = get_fundamentals(g.__q)
log.info("当前操作标的: %s" % g.security)
initialize 中的变量,重启模拟盘就会生效。
initialize 不会重跑,系统会使用昨天保存的 g 变量覆盖你的新代码逻辑。必须使用 after_code_changed 更新。initialize 中定义 g.q = query(...)。
query 对象包含数据库连接引用,无法被保存到磁盘。必须在 process_initialize 中定义,并建议命名为 g.__q。Q: 如果我在模拟交易中修改了 run_daily 的定时时间,需要怎么做?
A: 你需要在 after_code_changed 中先调用 unschedule_all() 取消旧的定时任务,然后重新调用 run_daily 注册新的任务。因为定时任务的注册信息也是保存在 context 中被持久化的。
Q: process_initialize 在回测中会执行吗?
A: 会执行。在回测中,它会在 initialize 执行完毕后紧接着执行一次。虽然回测通常不涉及“重启”,但为了保证代码在回测和模拟中的兼容性,建议始终使用它来初始化 query 等对象。
Q: 为什么建议 process_initialize 中使用的变量名以 __ 开头?
A: 聚宽系统在收盘后会将 g 对象序列化保存到硬盘。以 __ 开头的变量会被系统视为“临时变量”而忽略保存。这既避免了保存不可序列化对象(如数据库连接)导致的报错,也减少了保存文件的大小,提高了系统稳定性。