问题描述
process_initialize和initialize有什么区别?
process_initialize在初始化后执行,每天都要执行一次吗?
解决方案
在 JoinQuant (聚宽) 量化平台中,initialize 和 process_initialize 都是用于策略初始化的函数,但它们的执行时机和使用场景有很大的区别。针对您的问题,简短的回答是:是的,在模拟盘中 process_initialize 每天都会执行一次,而 initialize 在整个策略生命周期中只执行一次。
以下是详细的区别解析:
1. initialize(context):全局只执行一次
- 执行时机:在整个回测或模拟交易最开始的时候执行,且只执行一次。
- 主要用途:用于初始化策略的全局设置和可以被持久化保存的全局变量(通常挂载在
g对象上)。例如:设置基准、设置滑点、设置手续费、定义要交易的股票池等。 - 模拟盘特性:即使模拟盘每天休眠后第二天重启,或者您修改了代码后重启,
initialize都不会再次执行。
2. process_initialize(context):每次进程启动时执行
- 执行时机:在每次模拟盘/回测进程重启时执行。它总是在
initialize之后执行(如果是第一次启动)。 - 主要用途:一般用来初始化一些**不能被持久化保存(不能被 pickle 序列化)**的内容。例如:数据库连接、打开的文件对象、网络请求对象等。
- 模拟盘特性:因为聚宽的模拟盘在每天交易结束后会保存状态并结束进程(休眠),然后在第二天开盘前恢复进程。因此,在模拟盘中,
process_initialize每天都会执行一次。
为什么需要 process_initialize?
在模拟盘中,系统会使用 pickle 将 g 对象和 context 对象序列化保存到磁盘,以便第二天恢复状态。但是,涉及到 I/O 操作的对象(如 query() 返回的数据库查询对象)是不能被序列化的。如果您在 initialize 中把这些对象赋给了 g,第二天重启时这些变量就会丢失,导致报错。
为了解决这个问题,您应该在 process_initialize 中初始化这些对象,并且变量名建议以双下划线 __ 开头(这样系统在序列化 g 时会自动忽略它,避免报错)。
代码示例
def initialize(context):
# 整个生命周期只执行一次
# 设置基准
set_benchmark('000300.XSHG')
# 定义可以被序列化的普通全局变量
g.security = '000001.XSHE'
g.trade_count = 0
def process_initialize(context):
# 模拟盘每天进程重启时都会执行一次
# query 对象不能被 pickle 序列化,所以不能持久保存
# 必须在这里初始化,且变量名以 '__' 开始,系统序列化 g 时就会自动忽略这个变量
g.__q = query(valuation)
def handle_data(context, data):
# 每天/每分钟执行
# 可以正常使用 g.__q
df = get_fundamentals(g.__q)
log.info("获取到财务数据,股票数量:%s" % len(df))
总结
initialize:写策略的基础配置和普通全局变量,一辈子只跑一次。process_initialize:写那些“每天醒来都需要重新建立连接”的特殊对象(如query),每天跑一次。