问题描述
解决方案
在 JoinQuant 聚宽平台上,获取某个期货品种(如螺纹钢 RB、沪深300 IF)当前所有可交易合约列表的核心 API 是 get_future_contracts。
获取列表后,通常结合 get_current_data 或 get_price 获取这些合约的价格、持仓量(Open Interest)等数据,进而分析期限结构(Term Structure)或主力与次主力合约的价差。
以下是详细的 API 说明和一个完整的策略代码示例。该示例展示了如何获取螺纹钢(RB)的所有合约,并分析其持仓量分布和价格结构。
核心 API 说明
get_future_contracts(security, date=None)
- 功能:获取某期货品种在指定日期的可交易合约标的列表。
- 参数:
security: 期货合约品种代码,字符串格式,例如'RB'(螺纹钢),'IF'(沪深300股指),'AG'(白银)。date: 查询日期。回测中若不填,默认为回测当前日期;研究中若不填,默认为最新日期。
- 返回值:包含合约代码的列表 (List),例如
['RB2310.XSGE', 'RB2401.XSGE', ...]。
策略代码示例
以下代码实现了一个完整的策略:
- 每天开盘时获取螺纹钢(RB)的所有可交易合约。
- 获取这些合约的最新价格和持仓量。
- 打印合约列表及其对应的价格和持仓量(用于分析期限结构)。
- 找出持仓量最大的合约(主力)和持仓量第二大的合约(次主力)。
- 计算并打印主力与次主力的价差。
# -*- coding: utf-8 -*-
# 导入函数库
from jqdata import *
def initialize(context):
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定要分析的期货品种,这里以螺纹钢为例
g.future_code = 'RB'
# 设定期货账户
# 获取初始资金
init_cash = context.portfolio.starting_cash
# 设定账户为期货账户
set_subportfolios([SubPortfolioConfig(cash=init_cash, type='futures')])
# 每天开盘时运行分析
run_daily(analyze_futures_structure, time='09:30')
def analyze_futures_structure(context):
# 1. 获取当前日期所有可交易的 RB 合约列表
# get_future_contracts 会自动根据回测时间返回当时的合约列表
contract_list = get_future_contracts(g.future_code)
if not contract_list:
log.info("未找到品种 %s 的合约" % g.future_code)
return
# 2. 获取这些合约的当前快照数据(包含价格、持仓量等)
current_data = get_current_data()
# 用于存储合约信息的列表
contracts_info = []
for code in contract_list:
# 获取该合约的数据对象
data = current_data[code]
# 过滤掉停牌或未上市无数据的合约
if data.paused or np.isnan(data.last_price):
continue
# 收集信息:代码、最新价、持仓量
# 注意:get_current_data在回测中返回的是前一分钟或前一天的收盘数据,视频率而定
# 若需要更长周期的历史数据,可使用 attribute_history
info = {
'code': code,
'price': data.last_price,
'open_interest': data.open_interest # 持仓量
}
contracts_info.append(info)
# 3. 数据分析
# 按照合约代码排序(通常意味着按到期月份排序),展示期限结构
contracts_info.sort(key=lambda x: x['code'])
log.info("====== %s 期限结构分析 (日期: %s) ======" % (g.future_code, context.current_dt.date()))
log.info(f"{'合约代码':<15} | {'价格':<10} | {'持仓量':<10}")
for info in contracts_info:
log.info(f"{info['code']:<15} | {info['price']:<10.2f} | {info['open_interest']:<10.0f}")
# 4. 寻找主力与次主力合约(按持仓量排序)
# 按持仓量从大到小排序
sorted_by_oi = sorted(contracts_info, key=lambda x: x['open_interest'], reverse=True)
if len(sorted_by_oi) >= 2:
dominant_contract = sorted_by_oi[0] # 主力
sub_dominant_contract = sorted_by_oi[1] # 次主力
spread = dominant_contract['price'] - sub_dominant_contract['price']
log.info("-" * 30)
log.info(f"主力合约: {dominant_contract['code']} (持仓: {dominant_contract['open_interest']})")
log.info(f"次主力合约: {sub_dominant_contract['code']} (持仓: {sub_dominant_contract['open_interest']})")
log.info(f"主力-次主力 价差: {spread:.2f}")
# 示例交易逻辑:如果价差过大(例如大于100),可以考虑套利(此处仅为演示,不构成策略建议)
# if spread > 100:
# order_target(dominant_contract['code'], -1) # 卖主力
# order_target(sub_dominant_contract['code'], 1) # 买次主力
else:
log.info("可交易合约数量不足,无法计算主力价差")
代码解析
get_future_contracts(g.future_code): 这是核心步骤。在回测的每一天,它都会返回当时那个时间点该品种所有在市交易的合约代码(例如RB1801.XSGE,RB1805.XSGE等)。get_current_data(): 获取当前时间点的快照数据。在期货分析中,我们重点关注last_price(最新价) 和open_interest(持仓量)。- 数据清洗: 代码中增加了
if data.paused or np.isnan(data.last_price):判断,用于过滤掉停牌或者刚上市还没有产生成交数据的合约,防止后续计算报错。 - 期限结构展示: 通过按合约代码排序(即按交割月排序),打印出价格序列。如果远月价格高于近月,称为升水(Contango);反之称为贴水(Backwardation)。
- 主力判断: 虽然聚宽提供了
get_dominant_future直接获取主力合约,但通过get_future_contracts获取所有合约并按open_interest排序,可以让我们同时找到“次主力”合约,这对于跨期套利策略非常重要。
常见分析应用场景
- 跨期套利: 监控主力合约与次主力合约(通常是近月和远月)的价差变化。
- 展期信号: 当远月合约的持仓量逐渐接近并超过近月合约时,是主力换月的信号。
- 期限结构因子: 计算远月与近月的价格斜率,作为多空因子的输入(例如:贴水结构往往利多,升水结构往往利空)。
Q&A
Q1: get_future_contracts 返回的合约列表是按什么顺序排列的?
A1: 返回的列表通常是按照合约代码的字母和数字顺序排列的,对于期货而言,这通常意味着按照交割月份的先后顺序排列(例如 2301, 2305, 2310)。
Q2: 如何直接获取主力合约而不需要自己计算持仓量?
A2: 可以使用 get_dominant_future(underlying_symbol, date=None) 函数。例如 get_dominant_future('RB') 会直接返回当前持仓量最大的合约代码。
Q3: 为什么有时候获取到的合约价格是 NaN?
A3: 这通常发生在合约刚刚上市的早盘集合竞价阶段,或者该合约极度不活跃、当天尚未有成交时。在使用数据前,建议检查 np.isnan(price)。
Q4: 这个 API 支持商品期货以外的品种吗?
A4: 支持。get_future_contracts 支持所有期货品种,包括金融期货(如 'IF', 'IC', 'IH', 'TS', 'TF', 'T')和商品期货。只需传入对应的品种代码即可。