在商品 CTA 里,有一类策略既不预测涨跌,也不做方向暴露,而是纯粹赚期限结构(t term structure)里的 carry——这就是跨期价差 / 日历套利(calendar spread)。它的逻辑简单到一句话:在 Backwardation(远月贴水)市场里,多近月、空远月,每次展期都能把”近月贵、远月便宜”的价差装进口袋。
但简单不等于无风险。2020 年原油负价、天然气逼仓,都是日历价差策略的”至暗时刻”。本文把跨期价差讲透:先定义价差与展期收益,再用 Python 算出来、回测出来,最后告诉你它真正会亏在哪儿。

一、跨期价差与期限结构:近月和远月的关系#
同一商品、不同到期月的合约,构成一条期限结构曲线。定义跨期价差:
跨期价差(Spread) = 近月合约价格 − 远月合约价格
两条典型形态:
- Contango(正向市场 / 远月升水):远月 > 近月,曲线向上倾斜。常见于高库存、正 carry 的品种(如原油、天然气某些时段)。
- Backwardation(反向市场 / 远月贴水):远月 < 近月,曲线向下倾斜。常见于低库存、现货紧缺的品种(如铜、大豆某些时段)。
为什么会有这两种形态?核心是持有成本理论(cost of carry):远月价格 ≈ 近月价格 + 仓储 + 保险 + 资金成本 − 便利收益(convenience yield)。当现货紧缺、便利收益很高时,远月反而比近月便宜,就出现了 Back。
二、展期收益(Roll Yield):跨期套利的真正利润#
展期收益是你持有近月合约、到期把它”展”到下一个月时,赚(或亏)的那部分:
展期收益 ≈ (近月价格 − 远月价格) / 远月价格
在 Back 结构下(近月 > 远月),展期收益为正;在 Contango 下为负。
注意一个容易搞反的直觉:做多近月 + 空远月(多近空远),在 Back 结构下赚钱。因为近月更贵,你”低买高卖”地展期——卖掉的近月比买进的远月贵,差价落袋。反之在 Contango 下,多近空远每次展期都亏,这时应该空近月 + 多远月(空近多远)来收 carry。
三、Python 实战 1:从期限结构计算展期收益#
给定一组不同到期月的合约价格,直接算出年化展期收益,并标注形态:
import numpy as np
def roll_yield(near, far, months_between=1):
"""近月/远月价格 -> 单月展期收益, 年化(%)。"""
monthly = (near - far) / far
annual = (1 + monthly) ** (12 / months_between) - 1
return annual * 100
# 演示: 不同商品的近月/远月价格
quotes = {
"原油": (78.0, 82.3), # Contango
"黄金": (2340, 2332), # Back
"铜": (9450, 9320), # Back
"天然气": (2.65, 2.95), # Contango
"大豆": (1180, 1155), # Back
"白银": (29.1, 28.8), # Back
"棉花": (82.0, 86.5), # Contango
"白糖": (560, 552), # Back
}
print(f"{'品种':<8}{'近月':>8}{'远月':>8}{'结构':>10}{'年化展期':>10}")
for name, (n, f) in quotes.items():
ry = roll_yield(n, f)
shape = "Back" if n > f else "Contango"
print(f"{name:<8}{n:>8.1f}{f:>8.1f}{shape:>10}{ry:>+9.1f}%")python
跑出来会发现:同一天里,有的品种在送你carry,有的在吃掉你的carry。这就是为什么成熟的商品组合会按期限结构”动态选边”——只在 Back 里做多近空远,在 Contango 里翻过来做空近多远,而不是无脑多近月。
四、日历价差策略:多近空远 / 空近多远#
把上面的直觉变成可回测的信号:
- 每个交易日,读取近月、远月价格,算展期收益(或简单用
near - far的符号)。 - 若
near > far(Back):持有 多近月 + 空远月。 - 若
near < far(Contango):持有 空近月 + 多远月。 - 到期前 N 日平仓/展期到下一对合约,避免进入交割。
这套策略的收益来源是结构溢价(structural premium):Back 往往对应现货紧缺,市场愿意为即时拿货付溢价,这种溢价会通过展期持续释放给你的多头近月头寸。
五、Python 实战 2:构建价差序列并回测#
下面用历史近月/远月价格,构造价差序列并做一个最简回测(假设无成本,仅演示信号逻辑):
import numpy as np
np.random.seed(7)
T = 250
# 远月相对近月的"结构偏移": 正=Contango, 负=Back
structural = np.concatenate([
np.full(170, -6.0), # 前段 Back (赚 carry)
np.full(80, +7.0), # 后段翻转 Contango
])
noise = np.random.normal(0, 1.5, T)
spread = -structural + noise # 价差 = 远月 - 近月 的近似(便于后续信号)
# 信号: 价差<0 表示近月贵(Back) -> 多近空远, 收益随价差回升
position = np.where(spread < 0, 1, -1) # 1=多近空远, -1=空近多远
daily_ret = position * (np.r_[0, np.diff(spread)]) * 0.1 # 价差收敛的弹性
equity = np.cumprod(1 + daily_ret)
print(f"期末净值: {equity[-1]:.3f} 总收益: {(equity[-1]-1)*100:+.1f}%")
print(f"结构翻转后 80 日收益: {(equity[-1]/equity[170]-1)*100:+.1f}% (通常为负)")python
这段回测揭示了日历价差的命门:前 170 天 Back 结构稳定盈利,一旦结构翻转成 Contango,策略立刻回撤。所以日历价差不是”买了就躺赢”,而是一个需要监控结构 regime 的 carry 策略。
六、Python 实战 3:库存-价差关系(补给理论)#
学术上,库存高低决定期限结构形态(Working, 1933 的供给—需求理论;后由 Gorton & Rouwenhorst 实证)。逻辑链:
库存高 → 持有成本低、便利收益小 → Contango(远月升水) 库存低 → 现货紧缺、便利收益高 → Backwardation(远月贴水)
所以用库存数据,可以预判展期收益的方向。下面用一段伪数据演示库存与展期收益的负相关:
import numpy as np
np.random.seed(11)
weeks = 104
inventory = 300 + 80 * np.sin(np.linspace(0, 4*np.pi, weeks)) + np.random.normal(0, 15, weeks)
# 库存越高, 展期收益越低(越 Contango)
roll = 5.0 - 0.02 * (inventory - 300) + np.random.normal(0, 1.0, weeks)
corr = np.corrcoef(inventory, roll)[0, 1]
print(f"库存 vs 展期收益 相关系数 = {corr:.3f} (理论应为负)")
# 用库存分位数切换信号: 库存低(后20%)->做多近空远; 库存高(前20%)->空近多远
low_q, high_q = np.quantile(inventory, 0.2), np.quantile(inventory, 0.8)
signal = np.where(inventory < low_q, 1, np.where(inventory > high_q, -1, 0))
win_rate = (signal != 0).mean()
print(f"库存触发信号占比 {win_rate*100:.0f}%, 低库存周均做多近空远")python这个框架的实战价值:当你发现某商品库存掉到历史低位,而期限结构还是 mild Back,往往意味着展期收益还有向上空间——这时建多近空远,赔率较好。反过来库存堆到高位,就该警惕 Contango 侵蚀。
七、真实陷阱#
- 结构翻转(regime shift)陷阱:Back 突然转 Contango(如需求崩塌、库存暴增),你还在多近空远,每次展期都在亏。这是日历价差最大的亏损来源。解法:用库存/仓单数据做前瞻,结构翻转即反手或空仓。
- 逼仓(trader squeeze)陷阱:近月合约被大资金挤仓,近月价格瞬间拉爆,跨期价差几天内走完半年的 carry。多近月的人暴赚、空近月的人爆亏。解法:交割月前提前展期,不在流动性最薄的近月硬扛。
- 移仓滑点与远月流动性陷阱:远月合约成交量往往只有近月的几分之一,大资金移仓时冲击成本高,回测里的”无成本展期”在实盘会大打折扣。解法:回测里对远月加滑点,且限制单笔规模。
- 把展期收益当无风险陷阱:展期收益本质是”承担期限结构风险”的补偿,不是白送的。市场恐慌时 Back 会瞬间消失甚至转 Contango。解法:把它当风险溢价管理,而非固定收益。
- 展期时点选择陷阱:在合约最后交易日才展期,价差受交割逻辑扭曲最严重。解法:统一在到期前 5–10 日展期,规则固定可复盘。
八、落地清单#
- 每日读取近月/远月价格与交易所库存/仓单数据,更新展期收益与结构形态。
- 仅在 Back 做多近空远、Contango 做空近多远;结构不明确则空仓。
- 到期前固定天数(如 7 日)统一展期,避免交割月流动性黑洞。
- 用库存分位数对信号做”前瞻增强”,降低结构翻转被甩的风险。
- 回测必须给远月加滑点,并按真实可成交量约束规模。
九、把展期收益当成一类资产:与趋势策略互补#
展期收益(carry)和动量(momentum)在商品上长期被证明是两条低相关的 alpha 来源(Gorton & Rouwenhorst, 2006 的经典结论)。日历价差赚的是”结构稳定的 carry”,而传统 CTA 趋势策略赚的是”价格惯性”。两者结合有天然互补性:
- 收益来源不同:趋势策略在单边市赚钱、震荡市磨损;日历价差在结构稳定时持续收 carry,与趋势的盈亏节奏错开。
- 风险暴露不同:趋势策略有方向性 beta,日历价差是市场中性(多近空远,对冲了大部分系统性涨跌),能平抑组合波动。
- regime 互补:趋势策略在趋势启动时最强,而日历价差在结构翻转时最弱——两者同时亏的概率较低。
实务上常见的做法是:用期限结构信号决定”做哪边、做不做”(上一节的库存前瞻),用趋势信号决定”整体风险预算加不加”。当两者同向(Back 结构 + 上行趋势)时加码,当结构翻转但趋势仍在时降日历价差权重、保留趋势暴露。这样一个商品组合既拿了 carry,又不至于在翻转时被单边打爆。
十、落地清单再补充#
- 把展期收益与动量信号做相关性监控,确认两者确实在低相关区间(相关系数显著<0.3 才值得叠加)。
- 组合层面对日历价差设独立的风险预算(如不超过总风险的 25%),避免单品种逼仓拖垮整体。
- 月度复盘各品种的展期收益实际兑现值 vs 信号预测值,校准库存阈值。
结语#
跨期价差 / 日历套利,是商品量化里最”优雅”的一类策略:它把期限结构里那部分确定的 carry,变成了可交易、可回测、可风控的头寸。但优雅不等于安全——它的利润来自”结构稳定”,而结构最不稳定的时候(翻转、逼仓、流动性枯竭),恰恰是你最容易亏钱的时候。把库存当前瞻、把展期当规则、把滑点当成本,日历价差就能成为你商品组合里稳健的 carry 引擎;否则,它只是另一场押注。