问题描述
回测没问题的。
def initialize(context):
--set_params()
--run_daily(sell, time='09:20')
--run_daily(trade, time='every_bar')
def set_params():
--g.__stock_ls = []
def sell(context):
--sell_list = list(context.portfolio.positions.keys())
--for stock_sell in sell_list:
----price = get_price(stock_sell, end_date=context.previous_date, fields=['close'], count=1)
----price = list(price['close'])[0]*0.91
----order_target_value(stock_sell, 0, LimitOrderStyle(price))
----g.__stock_ls = []
def trade(context):
---if len(g.__stock_ls) >= 3:
----return
解决方案
在 JoinQuant(聚宽)平台中,实盘(模拟交易)是支持 g.__ 这种以双下划线开头的变量的,但是它的运行机制与回测环境有所不同,这就是导致您在回测中正常,但在模拟盘中报错的原因。
问题原因分析
根据聚宽 API 文档的规定:
- 模拟盘的休眠与重启机制:模拟盘在每天运行结束后会保存状态并结束进程(相当于休眠),第二天开盘前会恢复状态。恢复时,系统会使用
pickle序列化保存g对象和context对象。 initialize函数只执行一次:在模拟盘的整个生命周期中,initialize函数只在创建模拟盘时执行一次,每天重启时不会再次执行。- 双下划线变量被忽略:文档明确指出,
g中以__开头的变量在序列化时将被忽略,不会被保存。
您的代码发生的问题在于:
您在 initialize 中调用 set_params 初始化了 g.__stock_ls = []。在模拟盘第一天运行是正常的。但是第一天结束后,系统保存状态时忽略了 g.__stock_ls。第二天模拟盘重启时,initialize 不再执行,而 g.__stock_ls 又没有被保存下来,因此当代码执行到 trade 或 sell 函数时,就会报 AttributeError,提示找不到 g.__stock_ls。
解决方案
针对您的情况,有两种解决方案:
方案一:去掉双下划线(推荐)
如果您的变量只是一个普通的列表(如 []),它是完全可以被序列化保存的。您不需要使用双下划线。直接使用普通的 g 变量即可,这样模拟盘每天会自动帮您保存和恢复它的状态。
def initialize(context):
set_params()
run_daily(sell, time='09:20')
run_daily(trade, time='every_bar')
def set_params():
# 去掉双下划线,使用普通的全局变量
g.stock_ls = []
def sell(context):
sell_list = list(context.portfolio.positions.keys())
for stock_sell in sell_list:
price = get_price(stock_sell, end_date=context.previous_date, fields=['close'], count=1)
price = list(price['close'])[0]*0.91
order_target_value(stock_sell, 0, LimitOrderStyle(price))
# 同步修改
g.stock_ls = []
def trade(context):
# 同步修改
if len(g.stock_ls) >= 3:
return
方案二:使用 process_initialize 函数
如果您确实需要使用双下划线变量(通常用于存放不能被序列化的对象,如数据库连接 query 对象、网络请求对象等),或者您希望这个变量每天重启时都被强制清空重置,您必须在 process_initialize(context) 函数中进行初始化。
process_initialize 函数在模拟盘每次重启时(即每天)都会被执行一次。
def initialize(context):
# initialize 中可以保留其他只需执行一次的初始化逻辑
run_daily(sell, time='09:20')
run_daily(trade, time='every_bar')
# 添加 process_initialize 函数
def process_initialize(context):
# 每次模拟盘重启时都会执行,重新初始化双下划线变量
g.__stock_ls = []
def sell(context):
sell_list = list(context.portfolio.positions.keys())
for stock_sell in sell_list:
price = get_price(stock_sell, end_date=context.previous_date, fields=['close'], count=1)
price = list(price['close'])[0]*0.91
order_target_value(stock_sell, 0, LimitOrderStyle(price))
g.__stock_ls = []
def trade(context):
if len(g.__stock_ls) >= 3:
return
总结
- 普通数据(列表、字典、数字等):直接用
g.变量名,在initialize中初始化即可,系统会自动保存。 - 不可序列化数据或需每日重置的数据:用
g.__变量名,并且必须在process_initialize中初始化。