3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在聚宽(JoinQuant)平台编写量化策略时,随着平台的迭代,部分老旧接口(如 set_commission)已被废弃,而一些高频使用的数据获取接口(如 attribute_history)在多因子、多标的场景下,改用 get_price 或 get_bars 会更加高效和规范。此外,编写复杂策略时,不规范的变量命名容易导致**变量遮蔽(Variable Shadowing)**错误。
本文将指导您如何安全、规范地完成多因子策略的 API 迁移。
attribute_history 迁移至 get_priceattribute_history 是回测/模拟环境的专用 API,主要用于获取单只标的的历史数据。而在多因子选股策略中,我们通常需要同时获取多只股票的多个字段,此时使用 get_price 更加标准,且支持在研究环境与回测环境中通用。
旧版 attribute_history 写法(单标的):
# 获取单只股票过去5天的收盘价和成交额
df = attribute_history('000001.XSHE', count=5, unit='1d', fields=['close', 'money'], fq='pre')
新版 get_price 替代写法(支持多标的,返回 DataFrame/Panel):
# 获取单只或多只股票的历史数据,注意 end_date 传入当前回测逻辑时间,避免未来函数
df = get_price('000001.XSHE', count=5, end_date=context.current_dt, frequency='daily', fields=['close', 'money'], fq='pre', panel=False)
get_price 时,必须指定 end_date=context.current_dt(或 context.previous_date),否则默认会获取到回测当天的未来数据。get_price 默认不跳过停牌日期,停牌时会使用停牌前的数据填充。如果需要用 NaN 填充停牌数据,可设置 fill_paused=False。set_commission 迁移至 set_order_cost旧版的 set_commission 接口已废弃。现在必须使用 set_order_cost 配合 OrderCost 对象来精准设置股票、基金或期货的佣金与印花税。
旧版废弃写法:
# 已废弃,请勿使用
set_commission(PerTrade(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
新版标准写法:
# 在 initialize 中调用
# 股票类:买入佣金万分之三,卖出佣金万分之三加千分之一印花税,单笔最少5元
set_order_cost(OrderCost(
open_tax=0,
close_tax=0.001,
open_commission=0.0003,
close_commission=0.0003,
close_today_commission=0,
min_commission=5
), type='stock')
变量遮蔽是指在局部作用域(如函数内部)定义了与全局变量或 Python 内置函数同名的变量,导致全局变量或内置功能被“遮蔽”无法访问。在聚宽策略中,最常见的遮蔽错误包括:
list、type、sum、str 等。context 或 data,但未正确传递,或者在循环中覆盖了它们。type 作为变量名(例如:for type in types:),这会遮蔽 Python 内置的 type() 函数,导致后续调用 type(optimized_weight) 时报错。run_daily 注册的定时运行函数中,参数只能有一个 context,不能传入 data。如果需要获取数据,请在函数内部使用 get_price 或 attribute_history。以下是一个合并了 get_price、set_order_cost 并规避了变量遮蔽风险的完整多因子选股初始化与调仓模板:
import pandas as pd
from jqdata import *
def initialize(context):
# 1. 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 2. 开启真实价格(动态复权)模式
set_option('use_real_price', True)
# 3. 使用新版 set_order_cost 设置股票交易税费
set_order_cost(OrderCost(
open_tax=0,
close_tax=0.001,
open_commission=0.0003,
close_commission=0.0003,
min_commission=5
), type='stock')
# 4. 设定全局变量(使用 g. 前缀避免变量遮蔽)
g.security_list = ['000001.XSHE', '000002.XSHE', '600000.XSHG']
# 5. 每天 09:30 运行调仓
run_daily(my_trade, time='09:30')
def my_trade(context):
# 避免在参数中写 data,在函数内部获取数据
# 使用 get_price 替代 attribute_history 获取多只股票的收盘价
# 传入 end_date=context.current_dt 确保不引入未来函数
price_df = get_price(
security=g.security_list,
count=5,
end_date=context.current_dt,
frequency='daily',
fields=['close'],
fq='pre',
panel=False
)
# 规避变量遮蔽:使用自定义命名 stock_type 而非内置的 type
for stock_code in g.security_list:
# 提取单只股票的收盘价序列
stock_close = price_df[price_df['code'] == stock_code]['close']
ma5 = stock_close.mean()
current_price = stock_close.iloc[-1]
# 简单的均线金叉买入逻辑
if current_price > ma5:
cash = context.portfolio.available_cash
if cash > 5000:
order_value(stock_code, cash / len(g.security_list))
log.info("买入标的: %s" % stock_code)
elif current_price < ma5:
if context.portfolio.positions[stock_code].closeable_amount > 0:
order_target(stock_code, 0)
log.info("卖出平仓: %s" % stock_code)