3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)的策略编写中,context 和 g 是两个最核心的对象,贯穿于策略运行的始终。简单来说,context 代表“系统的当前状态”(只读为主),而 g 代表“策略的全局记忆”(读写为主)。
以下是详细的区别对比与使用指南:
| 特性 | context 对象 | g 对象 |
|---|---|---|
| 全称 | Context(上下文环境) | Global(全局变量容器) |
| 定义者 | 系统定义。由回测/模拟交易引擎自动生成和更新。 | 用户定义。由策略开发者在 initialize 中初始化并管理。 |
| 主要用途 | 获取账户信息(资金、持仓)、时间信息(当前日期)、回测设定等。 | 存储策略逻辑变量(如目标股票、计数器、状态标记),在不同函数间传递数据。 |
| 读写权限 | 只读(建议)。尝试修改 context 中的系统属性(如强行修改现金余额)通常无效或报错。 |
读写。用户可以随意赋值、修改、删除其中的变量。 |
| 持久化 | 自动重置。每次运行或按天重启时,系统会根据交易记录重新构建。 | 持久保存。在模拟交易中,g 对象的内容会被序列化保存到磁盘,第二天重启时自动恢复(除非变量名以 __ 开头)。 |
context 是一个包含当前策略运行环境所有信息的容器。你主要通过它来“看”现在的状况,而不是去“改”它。
context.portfolio: 账户信息总览。
context.portfolio.available_cash: 当前可用资金。context.portfolio.total_value: 总资产(现金+持仓市值)。context.portfolio.positions: 当前持仓字典(Key为标的代码)。context.current_dt: 当前单位时间的 datetime 对象(回测时的“现在”)。context.previous_date: 前一个交易日的 date 对象。context.run_params: 回测的运行参数(如开始日期、结束日期、频率)。def handle_data(context, data):
# 1. 获取当前时间
now = context.current_dt
# 2. 获取当前可用资金
cash = context.portfolio.available_cash
# 3. 判断某只股票是否持仓
if '000001.XSHE' in context.portfolio.positions:
# 获取该股票的持仓数量
amount = context.portfolio.positions['000001.XSHE'].total_amount
# 获取持仓成本
cost = context.portfolio.positions['000001.XSHE'].avg_cost
g 是一个全局对象,专门用于在不同的函数(如 initialize, handle_data, run_daily 调用的函数)之间共享数据。
initialize 中定义后,在整个回测或模拟交易期间一直存在。g 对象保存(Pickle序列化),第二天开盘前恢复。这保证了策略的连续性(例如记录“持仓天数”)。g 中存储的对象必须支持序列化(pickle)。
query)、打开的文件句柄、网络连接对象。def initialize(context):
# 初始化全局变量
g.security = '000001.XSHE' # 要操作的股票
g.days_held = 0 # 持仓天数计数器
g.stop_loss_pct = 0.10 # 止损比例
def handle_data(context, data):
# 读取全局变量
stock = g.security
# 修改全局变量
if context.portfolio.positions[stock].total_amount > 0:
g.days_held += 1
else:
g.days_held = 0
# 使用全局变量进行逻辑判断
if g.days_held > 5:
order_target(stock, 0)
log.info("持仓超过5天,卖出")
不要在 g 中存储 query 对象:
initialize 中写 g.q = query(valuation)。这会导致模拟盘重启失败,因为 query 对象无法被序列化保存。query,可以使用以双下划线开头的变量名(如 g.__q),或者在 process_initialize(每次进程启动时运行)中初始化它。不要修改 context 的属性:
context.portfolio.available_cash = 1000000。这不会真的增加你的资金,只会导致数据不一致或报错。资金变化必须通过 order 系列函数或 inout_cash 函数产生。g 对象的大小限制:
g 对象序列化后的大小不能超过 30MB。不要在 g 中存储过大的 DataFrame 或历史数据列表。跨函数传参:
run_daily 调用的自定义函数,其第一个参数必须是 context。如果需要传递其他参数,通常就是通过 g 对象来传递。Q: 为什么模拟交易重启后,我在 initialize 里定义的 g.my_var 还是昨天的值,没有被重置?
A: 因为模拟交易重启时不会重新执行 initialize 函数(该函数只在策略创建时执行一次)。系统会从磁盘读取昨天保存的 g 对象状态。如果你希望每次重启都重置某些变量,请使用 process_initialize(context) 函数,或者使用 after_code_changed(context)(仅在修改代码后触发)。
Q: 我可以在 context 里自己添加变量吗?比如 context.my_signal = 1?
A: 虽然技术上可行(Python 动态语言特性),且聚宽文档提到 context 也可以像 g 一样添加变量并持久化,但强烈不建议这样做。为了代码的可读性和规范性,请将所有自定义变量存储在 g 对象中,将 context 严格视为系统状态容器。
Q: 如何在 g 对象中存储不希望被持久化保存的临时变量?
A: 变量名以两个下划线开头,例如 g.__temp_data。系统在序列化保存 g 对象时,会自动忽略以 __ 开头的变量。这对于存储数据库连接对象或极大的临时数据非常有用。