问题描述
解决方案
在期货交易和量化回测中,**主力连续合约(通常以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_price 或 attribute_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) 策略交易(关键点)
切记:您不能直接对 RB9999 或 RB8888 下单。
- 对于指数合约:无法交易,只能用于计算信号(例如:当指数价格站上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("指数趋势向下,平仓")
总结
- 看趋势,用指数(8888):因为它平滑、代表性强。
- 跑回测,看主力(9999):因为它连接了历史行情,方便计算长期收益。
- 做交易,找真实合约:使用
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'),它会返回当前日期对应的真实主力合约代码。