🚀 AI 一键生成 joinquant 策略代码

JoinQuant 编程基础:g 和 context 对象有什么区别?该如何选择?

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

问题描述

g 和 context 变量有什么不同,我应该把我的数据保存在哪里?

解决方案

在 JoinQuant(聚宽)平台中,gcontext 是两个最核心的对象,但它们的用途、生命周期和持久化机制有显著的区别。

核心区别总结

特性 g (Global Object) context (Context Object)
定义 全局变量容器,专门用于存储用户自定义的变量。 策略上下文对象,存储系统当前的账户状态、时间、持仓等信息。
用途 保存策略运行中需要跨函数使用的自定义数据(如:计数器、股票池、标志位)。 获取系统数据(如:当前时间、可用资金、持仓详情、回测日期)。
持久化 (模拟交易) 支持。模拟交易每天收盘后,g 中的数据会被序列化保存到磁盘,第二天重启时自动恢复。 系统管理。系统会自动维护账户状态,但用户若手动向 context 添加自定义变量,虽也支持持久化,但不推荐
安全性 高。专为用户设计,不会与系统变量冲突。 低(针对写入)。向 context 写入自定义变量可能覆盖系统现有或未来新增的属性。
读写权限 完全读写。 系统属性(如 portfolio)通常是只读的;用户自定义属性可读写。

1. g (全局变量对象)

g 是 "Global" 的缩写,它是专门为用户设计的全局变量容器。

  • 持久化机制:在模拟交易中,策略进程会在每天收盘后暂停(休眠),第二天开盘前重启。系统会使用 pickle 库将 g 对象序列化保存。这意味着你在 g 中存储的变量(如 g.days_count = 10)在第二天依然存在。
  • 序列化限制
    • 只有能被 pickle 序列化的对象才能保存在 g 中(大部分 Python 原生类型如 list, dict, str, int 都可以)。
    • 不可序列化对象(如数据库连接 query 对象、文件句柄)不能直接保存在 g 中,否则会导致模拟盘保存失败。
    • 临时变量:如果你希望某个变量不被保存(例如每次重启都重新初始化的变量),可以将变量名以双下划线开头(如 g.__temp_data),系统在序列化时会忽略它。
  • 大小限制:序列化后的状态大小不能超过 30M

2. context (策略信息总览)

context 包含了策略运行时的所有环境信息和账户状态。

  • 主要包含的系统属性
    • context.current_dt: 当前单位时间的开始时间 (datetime对象)。
    • context.portfolio: 账户信息(资金、持仓价值、收益等)。
    • context.subportfolios: 子账户信息列表。
    • context.universe: 当前的股票池。
  • 使用建议
    • 主要用于读取系统状态(例如判断当前时间、查询剩余资金)。
    • 虽然技术上允许你向 context 添加自定义变量(例如 context.my_variable = 1),但官方强烈建议不要这样做。因为如果未来聚宽系统升级,增加了一个名为 my_variable 的系统属性,你的代码就会产生冲突或被覆盖。

我应该把数据保存在哪里?

结论:你应该把所有的自定义数据保存在 g 对象中。

  • 需要跨函数使用的数据:例如在 initialize 中选出的股票列表,需要在 handle_data 中交易,请保存在 g.security_list
  • 需要跨交易日记忆的数据:例如记录策略已经运行了多少天,或者记录上一次买入的价格,请保存在 g 中。
  • 临时的大数据或不可序列化对象:建议在 process_initialize(每次进程启动时运行)中初始化,并赋值给 g 中以 __ 开头的变量(如 g.__query_object)。

代码示例

# -*- coding: utf-8 -*-

def initialize(context):
    # 【推荐】将自定义变量保存在 g 中
    g.security = '000001.XSHE' 
    g.days_counter = 0
    
    # 【不推荐】虽然可以运行,但不要向 context 写入自定义变量
    # context.my_stock = '000001.XSHE' 

def handle_data(context, data):
    # 1. 使用 g 中的变量
    g.days_counter += 1
    
    # 2. 读取 context 中的系统信息
    current_time = context.current_dt
    available_cash = context.portfolio.available_cash
    
    # 打印日志
    log.info("策略运行第 %s 天" % g.days_counter)
    log.info("当前时间: %s, 可用资金: %s" % (current_time, available_cash))
    
    # 简单的交易逻辑
    if g.days_counter % 5 == 0:
        order(g.security, 100)

def process_initialize(context):
    # 模拟盘重启时执行,用于初始化不能被序列化保存的对象
    # 使用双下划线开头,告诉系统不要尝试保存这个变量到磁盘
    g.__q = query(valuation) 

Q&A

Q: 为什么模拟交易重启后,我在 initialize 里定义的 g 变量没有重置?
A: 因为在模拟交易中,initialize 函数只在策略创建的第一天执行一次。之后的每一天,系统是恢复之前保存的 g 对象状态,而不是重新运行 initialize。如果你需要每次重启都运行某些代码,请使用 process_initialize(context) 函数。

Q: context.portfolio 可以修改吗?
A: 不可以。context 中的系统属性(如 portfolio, positions)是只读的,或者由系统底层维护。如果你想改变持仓或资金,必须通过 order(下单)或 inout_cash(出入金)等 API 来实现,不能直接赋值修改。

Q: g 对象能存多大的数据?
A: 序列化后不能超过 30MB。如果超过这个限制,模拟盘保存状态时会失败。如果需要存储大量数据(如巨大的 DataFrame),建议写入文件(使用 write_file)或仅存储必要的状态标识,在运行时重新计算或获取数据。