🚀 AI 一键生成 joinquant 策略代码

JoinQuant 期货合约代码解析:8888 与 9999 有何不同?

joinquant | 发布时间: 2025/12/10
以下内容由 EasyQuant 生成。

问题描述

期货的主力合约和指数合约有什么区别,应该怎么使用它们?

解决方案

在期货交易和量化回测中,**主力连续合约(通常以9999结尾)品种指数合约(通常以8888结尾)**是两个非常重要的概念。它们都是为了方便分析而由数据商合成的“虚拟合约”,在实际交易所中并不存在。

以下是它们的详细区别及在聚宽(JoinQuant)平台中的使用方法:

1. 核心区别

特性 主力连续合约 (Dominant Continuous) 品种指数合约 (Index)
代码后缀 9999 (如 RB9999.XSGE) 8888 (如 RB8888.XSGE)
定义 将该品种持仓量最大的合约(主力)进行拼接。当远月合约持仓量超过当前主力时,发生切换。 该品种所有存续合约的加权平均(通常按持仓量或成交量加权)。
价格特性 价格代表当前市场最活跃合约的价格。在主力换月时,价格可能会出现跳空(因为不同月份合约价格不同)。 价格代表该品种的整体市场趋势,走势相对平滑,不会因为换月产生剧烈的价格跳空。
主要用途 回测与策略开发。用于模拟长期持仓的策略,解决单一合约到期需移仓的问题。 宏观分析与趋势判断。用于观察该品种的整体多空力量和长期趋势。
能否交易 不可直接交易。需映射到具体的真实合约(如 RB2310)进行下单。 不可交易。仅作为数据参考。

2. 详细解读

主力连续合约 (9999)

  • 拼接逻辑:期货合约有到期日(如1月、5月、10月)。为了进行跨越数年的长周期回测,平台将不同时期的“主力合约”拼接在一起。例如,1月时主力是5月合约,4月时主力切换为10月合约,9999合约的数据就是这两个时间段对应合约数据的拼接。
  • 跳空现象:当主力合约从5月切换到10月时,如果5月合约价格是3500,而10月合约价格是3400,那么9999合约在切换日会出现100点的价格跳空。这是正常现象,反映了真实的换月成本或收益。

品种指数合约 (8888)

  • 计算逻辑:它是全市场的一个综合指数。例如螺纹钢指数,是由RB2305、RB2310、RB2401等所有上市合约的价格,根据其持仓量或成交量加权计算得出的一个理论价格。
  • 平滑性:因为它是一个加权平均值,所以它能过滤掉单个合约临近交割时的异常波动,更能反映该商品在宏观层面上的真实价值走势。

3. 在聚宽 (JoinQuant) 中如何使用

在聚宽平台上,您可以使用 API 获取这两类数据,但在交易时需要特别注意。

(1) 获取行情数据

无论是主力连续还是指数,都可以使用 get_priceattribute_history 获取历史数据。

# 获取螺纹钢主力连续合约的收盘价
# 代码格式:品种代码 + 9999 + .交易所后缀
dominant_price = get_price('RB9999.XSGE', start_date='2023-01-01', end_date='2023-01-05', fields=['close'])

# 获取螺纹钢指数合约的收盘价
# 代码格式:品种代码 + 8888 + .交易所后缀
index_price = get_price('RB8888.XSGE', start_date='2023-01-01', end_date='2023-01-05', fields=['close'])

(2) 策略交易(关键点)

切记:您不能直接对 RB9999RB8888 下单。

  • 对于指数合约:无法交易,只能用于计算信号(例如:当指数价格站上60日均线时,做多主力合约)。
  • 对于主力合约:需要使用 get_dominant_future 函数获取当前时间点对应的真实合约代码,然后对该真实合约下单。

策略代码示例:

# -*- coding: utf-8 -*-
def initialize(context):
    # 设定期货账户
    set_subportfolios([SubPortfolioConfig(cash=context.portfolio.starting_cash, type='futures')])
    # 设定基准
    set_benchmark('RB9999.XSGE')
    # 设定操作品种:螺纹钢
    g.symbol = 'RB'

def handle_data(context, data):
    # 1. 获取指数合约数据用于判断趋势 (使用 8888)
    # 注意:这里仅作分析使用
    index_code = g.symbol + '8888.XSGE'
    index_history = attribute_history(index_code, 20, '1d', ['close'])
    
    if len(index_history) < 20:
        return
        
    current_index_price = index_history['close'][-1]
    ma20 = index_history['close'].mean()
    
    # 2. 获取当前的主力合约代码 (用于交易)
    # get_dominant_future 会返回如 'RB2310.XSGE' 这样的真实合约
    real_contract = get_dominant_future(g.symbol)
    
    # 3. 交易逻辑
    # 如果指数价格高于20日均线,且当前无持仓,则做多主力合约
    if current_index_price > ma20:
        if len(context.portfolio.long_positions) == 0:
            # 注意:这里下单的是 real_contract (真实合约),而不是 9999 或 8888
            order_target(real_contract, 1, side='long')
            log.info(f"指数趋势向上,做多主力合约: {real_contract}")
            
    # 如果指数价格低于20日均线,且有多单,则平仓
    elif current_index_price < ma20:
        if len(context.portfolio.long_positions) > 0:
            # 平掉所有持仓
            for stock in context.portfolio.long_positions:
                order_target(stock, 0, side='long')
            log.info("指数趋势向下,平仓")

总结

  1. 看趋势,用指数(8888):因为它平滑、代表性强。
  2. 跑回测,看主力(9999):因为它连接了历史行情,方便计算长期收益。
  3. 做交易,找真实合约:使用 get_dominant_future 找到 9999 背后对应的真实合约代码进行下单。

Q&A

Q1: 为什么回测时主力连续合约(9999)会有价格跳空?
A: 因为期货不同月份的合约价格通常是不一样的(存在升水或贴水)。当主力合约从近月切换到远月时,数据源直接拼接了两个不同价格的合约,因此会在切换日产生缺口。这是真实市场换月成本的体现。

Q2: 我可以直接对 RB9999.XSGE 调用 order 函数吗?
A: 不可以。在回测和模拟交易中,直接对 9999 或 8888 后缀的合约下单会报错或无效。必须下单到具体的合约代码(如 RB2305.XSGE)。

Q3: 如何知道当前的主力合约是哪一个?
A: 使用聚宽提供的 API 函数 get_dominant_future(underlying_symbol),例如 get_dominant_future('RB'),它会返回当前日期对应的真实主力合约代码。