问题描述
最近学期货策略,看到一个因子,用近月合约周收益率和远月收益率直接做差,我想获取近5年所有的合约的因子值,但是所有合约的时间间隔都不同,所以具体应该如何选什么样的合约作为远月,什么作为近月?
解决方案
构建期货的近远期差价因子(通常被称为期限结构因子或展期收益率因子)是商品期货量化策略中非常经典且有效的方法。这个因子的核心逻辑在于捕捉现货溢价(Backwardation)或期货溢价(Contango)的状态。
针对你提到的“合约时间间隔不同,如何选择近月和远月合约”的问题,在实际量化投研中,通常有以下几种主流的处理方案:
一、 如何选择近月与远月合约?
由于不同品种的活跃合约月份分布不同(例如螺纹钢通常是1、5、10月活跃,而农产品可能是1、5、9月),直接固定月份做差是不可行的。动态选择合约的常见方案有:
方案1:主力合约 vs 次主力合约(推荐)
- 近月合约:选择当前的主力合约(持仓量最大的合约)。因为主力合约流动性最好,价格最能反映当前市场真实预期。
- 远月合约:选择交割月份在主力合约之后的次主力合约(或者直接取按交割月排序在主力合约之后的下一个活跃合约)。
- 优点:完美避开了非活跃合约流动性差、价格失真的问题。
方案2:首月合约 vs 次月合约(按交割月严格排序)
- 近月合约:在当前所有可交易合约中,交割月份最近的合约。
- 远月合约:交割月份排在第二的合约。
- 缺点:临近交割月时,首月合约流动性会急剧萎缩,容易产生异常波动。
二、 JoinQuant 平台实现逻辑与源码
在 JoinQuant 平台上,我们可以利用 get_dominant_future 获取主力合约,利用 get_future_contracts 获取所有合约列表,从而动态定位近月和远月合约。
以下是基于**“主力合约(近月)与主力后第一个合约(远月)”**构建周收益率差价因子的完整 Python 代码示例,你可以将其放在 JoinQuant 的研究环境中运行:
from jqdata import *
import pandas as pd
import numpy as np
import datetime
def get_term_structure_factor(underlying_symbol, date):
"""
计算指定日期某期货品种的近远期差价因子
:param underlying_symbol: 期货品种代码,如 'RB' (螺纹钢)
:param date: 计算日期
:return: 因子值 (近月周收益率 - 远月周收益率)
"""
# 1. 获取当天该品种的所有可交易合约
contracts = get_future_contracts(underlying_symbol, date)
if len(contracts) < 2:
return np.nan
# 2. 获取当天的主力合约作为“近月合约”
dominant = get_dominant_future(underlying_symbol, date)
if not dominant:
return np.nan
# 3. 对合约按交割月份进行排序
# JoinQuant的合约代码格式如 'RB1905.XSGE',直接字符串排序即可按时间先后排列
contracts = sorted(contracts)
try:
dom_idx = contracts.index(dominant)
# 4. 寻找远月合约:取主力合约之后的下一个合约
if dom_idx + 1 < len(contracts):
far_contract = contracts[dom_idx + 1]
else:
return np.nan # 主力已经是最后一个合约,无法计算
except ValueError:
return np.nan
# 5. 获取这两个合约过去一周(5个交易日)的收盘价
# count=6 是为了获取 5天前的价格 和 当天的价格
prices = get_price([dominant, far_contract], end_date=date, count=6, fields=['close'], panel=False)
if prices.empty:
return np.nan
try:
# 提取近月和远月的价格序列
dom_prices = prices[prices['code'] == dominant]['close'].values
far_prices = prices[prices['code'] == far_contract]['close'].values
# 确保数据长度足够
if len(dom_prices) < 6 or len(far_prices) < 6:
return np.nan
# 6. 计算周收益率 (当天价格 / 5天前价格 - 1)
dom_return = dom_prices[-1] / dom_prices[0] - 1
far_return = far_prices[-1] / far_prices[0] - 1
# 7. 计算因子值:近月收益率 - 远月收益率
factor_value = dom_return - far_return
return factor_value
except Exception as e:
return np.nan
# ================= 测试代码 =================
# 获取螺纹钢(RB)在过去一段时间的因子值
test_dates = get_trade_days(start_date='2023-01-01', end_date='2023-01-31')
factor_results = []
for d in test_dates:
val = get_term_structure_factor('RB', d)
factor_results.append({'date': d, 'factor_value': val})
df_factor = pd.DataFrame(factor_results)
print(df_factor.head())
三、 细节优化与注意事项
- 年化处理(可选):由于不同品种的主力与次主力之间的时间间隔不同(有的是1个月,有的是4个月),更严谨的做法是将差价年化。即:
(近月价格/远月价格 - 1) * (365 / 两个合约交割日期的天数差)。这被称为年化展期收益率,在横截面多品种比较时更为科学。 - 换月跳空问题:当主力合约发生切换时,
get_dominant_future返回的合约会突变。如果你计算的是“过去5天的收益率”,在换月当天,你获取的“当前主力合约”在5天前可能流动性极差,导致5天前的价格失真。解决方案:在计算收益率时,确保使用的价格序列在过去5天内都有充足的成交量,或者在换月期间平滑过渡。 - 数据对齐:使用
get_price获取多个合约数据时,注意panel=False返回的是 DataFrame,需要按code过滤出各自的价格序列,确保近远月价格在时间上是一一对应的。