🚀 AI 一键生成 joinquant 策略代码
立即体验

聚宽多因子策略升级:如何用get_price和set_order_cost替换废弃接口并避免变量遮蔽?

joinquant | 发布时间: 2026/8/11
以下内容由 EasyQuant 生成。

问题描述

聚宽多因子策略 API 迁移:attribute_history 如何改用 get_price?
将聚宽多因子选股策略升级到新 API 时,如何用 get_price、OrderCost 和 set_order_cost 替换已废弃接口并避免变量遮蔽错误?

解决方案

聚宽多因子策略 API 迁移与升级指南

在聚宽(JoinQuant)平台编写量化策略时,随着平台的迭代,部分老旧接口(如 set_commission)已被废弃,而一些高频使用的数据获取接口(如 attribute_history)在多因子、多标的场景下,改用 get_priceget_bars 会更加高效和规范。此外,编写复杂策略时,不规范的变量命名容易导致**变量遮蔽(Variable Shadowing)**错误。

本文将指导您如何安全、规范地完成多因子策略的 API 迁移。


一、 数据获取:从 attribute_history 迁移至 get_price

attribute_history 是回测/模拟环境的专用 API,主要用于获取单只标的的历史数据。而在多因子选股策略中,我们通常需要同时获取多只股票的多个字段,此时使用 get_price 更加标准,且支持在研究环境与回测环境中通用。

1. 接口对比与转换

  • 旧版 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)
    

2. 迁移注意事项

  • 避免未来数据:在回测中使用 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 对象来精准设置股票、基金或期货的佣金与印花税。

1. 迁移示例(以 A 股股票为例)

  • 旧版废弃写法:

    # 已废弃,请勿使用
    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')
    

三、 如何避免变量遮蔽(Variable Shadowing)错误

变量遮蔽是指在局部作用域(如函数内部)定义了与全局变量或 Python 内置函数同名的变量,导致全局变量或内置功能被“遮蔽”无法访问。在聚宽策略中,最常见的遮蔽错误包括:

  1. 遮蔽内置函数/类型:例如将变量命名为 listtypesumstr 等。
  2. 遮蔽聚宽保留字:在自定义函数中,将参数命名为 contextdata,但未正确传递,或者在循环中覆盖了它们。

避坑守则:

  • 不要在局部循环中使用 type 作为变量名(例如:for type in types:),这会遮蔽 Python 内置的 type() 函数,导致后续调用 type(optimized_weight) 时报错。
  • run_daily 注册的定时运行函数中,参数只能有一个 context,不能传入 data。如果需要获取数据,请在函数内部使用 get_priceattribute_history

四、 升级后的完整多因子策略模板

以下是一个合并了 get_priceset_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)