期现套利与基差交易:用「持有成本模型」把期货相对现货的偏离做成低风险收益
期货和现货明明是同一个标的,价格却常常不相等——中间的差就叫基差(basis)。持有成本模型告诉你它理论上该是多少,而「到期必收敛」又保证这个差最终归零。于是期现套利成了一个自带安全垫的统计套利:做空高基差、做多负基差,等它回复就平仓。本文用纯 numpy 合成沪深300ETF vs 股指期货的连续 6 张合约,把现货/期货/基差一步步造出来,再用「突破成本带开仓、回复平仓」的规则跑回测:58 次交易、胜率 91.4%、总收益 +25.1%(年化 11.0%)、单笔最大亏损仅 −0.06%;并诚实拆穿到期日效应/分红除权跳变/流动性与冲击成本/展期跳跃/单边升水陷阱五类真实风险(中阶)。
你盯着交易软件,发现一件反直觉的事:沪深300ETF 报价 4.000,而对应的 IF 股指期货主力合约报价折算下来是 4.028——同一个指数,两个价格,中间差了 0.7%。这 0.7% 不是市场失灵,它有个名字叫基差(basis),而且是无数”市场中性”策略赖以生存的土壤。
结论先放这:期货价格相对现货的偏离,理论上由「持有成本」精确决定——你持有现货要付资金成本、收股息,期货只是把这些成本前置到价格里。所以 F ≈ S·e^(r−q)·T。但这个理论价只是”中枢”,真实期货报价会围绕它上下波动(资金流、情绪、套利盘进出都会推偏)。套利者的机会恰恰来自这个波动,而安全垫来自一个铁律:到期日 T→0 时,基差必然归零(否则就有无风险搬砖)。于是”做空被高估的基差、做多被低估的基差,等它回复”就成了一个胜率极高的统计套利。
在我们的合成面板(沪深300ETF 现货 vs 股指期货,连续滚动 6 张 90 天合约、真实 α 来自基差噪声而非漂移)上:用「基差突破 ±0.30% 成本带开仓、回复到 ±0.15% 即平仓」的规则跑回测,共触发 58 次交易、胜率 91.4%、总收益 +25.1%(折合年化 11.0%)、Sharpe≈3.07;单笔最大盈利 +1.96%、最大亏损仅 −0.06%——因为每笔赚的就是”开仓时基差到零的距离减去成本”,亏损只来自成本带本身。同期现货买入持有(含一次 27% 的下跌)收益 −27.3%,这正是基差策略”与方向无关”的写照。

一、基差是什么:持有成本模型#
基差的标准定义是 b = F_obs − F_fair,其中 F_fair 是持有成本理论价。先看为什么期货”应该”比现货贵(或便宜):
- 你买现货要占用资金,资金有成本
r; - 但持有现货会收股息/分红
q,这是持有收益; - 期货是杠杆头寸,不用现在付全款,所以期货价格 = 现货价格把”未来该付的净成本”贴现进去:
F_fair = S · e^((r − q) · T)
b = F_obs − F_fairplaintext当 r > q(资金成本高于股息率,A股长期如此),F_fair > S,期货升水,基差为正。本文取 r=2.5%、q=2.0%,所以理论中枢是 +0.5% 左右的轻微升水。
关键认知:F_fair 是”如果市场完全有效、无摩擦”的价;F_obs 是真实报价。两者的差 b 会围绕 0 波动,但随着 T 减小,F_fair 越来越贴近 S,而 F_obs 必须跟着贴近 S(否则存在无风险套利),所以 b 必然向 0 收缩。这就是整篇策略的底层逻辑。
二、从零实现:合成现货、期货与基差#
我们用纯 numpy 造一套自洽数据——现货走 GBM,期货理论价按持有成本模型算,再在理论价上叠加一个”随到期收缩的基差噪声”。
import numpy as np
S0 = 4000.0 # 现货起始(沪深300ETF)
r, q = 0.025, 0.020 # 无风险 / 股息率 → carry = r−q = 0.5%
DAYS, NCON = 90, 6 # 单张合约 90 交易日,连续滚动 6 张
T_year = DAYS / 252
sigma_b = 0.0100 # 基差噪声幅度(配合 sqrt(tau) 收缩)
def simulate_contract(S_prev, tau0):
tau = np.linspace(tau0, 0.0, DAYS) # 到期时间递减
dt = 1.0 / 252
# 现货:低漂移、15% 波动的 GBM
spot = np.empty(DAYS); spot[0] = S_prev
z = rng.normal(0, 1, DAYS - 1)
for i in range(1, DAYS):
spot[i] = spot[i-1] * np.exp((0.03 - 0.5*0.15**2)*dt + 0.15*np.sqrt(dt)*z[i-1])
# 理论期货价:持有成本模型
fair = spot * np.exp((r - q) * tau)
# 基差:平稳噪声 × sqrt(tau) 收缩,到期严格归 0
h = np.zeros(DAYS); h[0] = rng.normal()
for i in range(1, DAYS):
h[i] = 0.5*h[i-1] + rng.normal()
h = h / h.std()
b = np.sqrt(tau / T_year) * (sigma_b * h)
for i in range(1, DAYS): # 偶发资金流冲击
if rng.random() < 0.03:
b[i] += rng.choice([-1, 1]) * (0.004 + 0.004*rng.random()) * np.sqrt(tau[i]/T_year)
obs = fair + b
return spot, fair, obs, b, taupython注意 b = sqrt(tau/T_year) * (...) 这一行——基差的振幅正比于 sqrt(到期剩余时间)。这是持有成本模型的推论:离到期越远,套利盘锁定价差的动力越弱、噪声空间越大;越临近到期,锁定价差越紧、噪声被压扁。下一节图 3 会把这条规律画出来。
三、基差交易规则:成本带开仓,回复即平仓#
策略极简,全部是均值回复的翻版:
- 开仓:当
b > +cost(实际基差高于双边交易成本)时,做空基差 = 卖期货 + 买现货;当b < −cost时,做多基差 = 买期货 + 卖现货。 - 平仓:当基差回复到
|b| < exit_band(提前设成 cost 的一半)或合约到期时,反向平仓。 - 成本:每笔扣一次双边成本
cost=0.30%(覆盖买卖期货+买卖现货的摩擦)。
def backtest():
spots, bases, taus = [], [], []
trades = []
pos, b_e = 0, 0.0
S = S0
gidx = 0
for c in range(NCON):
s, f, o, b, tau = simulate_contract(S, T_year)
for i in range(DAYS):
spots.append(s[i]); bases.append(b[i]); taus.append(tau[i])
bb = b[i]
if pos == 0:
if bb > cost: # 基差偏高 → 做空基差
pos, b_e = +1, bb
elif bb < -cost: # 基差偏低 → 做多基差
pos, b_e = -1, bb
else:
if abs(bb) < exit_band or i == DAYS-1: # 回复 或 到期
pl = (pos * (b_e - bb)) - cost # 赚"开仓基差到0"的距离
trades.append((gidx, pos, b_e, bb, pl))
pos = 0
gidx += 1
S = s[-1] # 下一张合约从最新现货价续上
return tradespython逻辑核心一句话:开仓时你赚的,就是”当前基差 b_e 收敛到 0 的距离”,但要先扣掉成本 cost。所以只要 |b_e| > cost(开仓阈值就是这么来的),数学期望就是正的;最坏情况也只是成本白付。
四、实测结果:胜率与回撤#
把 6 张合约的 540 个交易日串起来跑,结果如下:

| 指标 | 数值 |
|---|---|
| 交易次数 | 58 |
| 胜率 | 91.4% |
| 总收益 | +25.1%(现货买入持有同期 −27.3%) |
| 折合年化 | 11.0% |
| Sharpe(近似,按月) | ≈3.07 |
| 单笔最大盈利 | +1.96% |
| 单笔最大亏损 | −0.06% |
| 单笔平均 P&L | +0.43% |
这组数字揭示的本质:基差策略的收益不是”押方向”赚来的,而是”时间价值”——你做的是”基差噪声终将消失”这个几乎必然的事件。所以胜率极高、回撤极小,曲线接近一条平滑向上的阶梯。对照图里红▼=做空基差开仓、蓝▲=做多基差开仓,几乎每个标记都在靠近中线处平仓。
五、为什么基差会收敛:到期效应#
把 540 个交易日的基差按”剩余到期天数”分箱求均值,规律一目了然:

| 剩余到期(交易日) | 基差 |b| 均值 |
|---|---|
| 80 | 0.92% |
| 52 | 0.60% |
| 22 | 0.34% |
| 7 | 0.19% |
| 0(到期) | 0.00% |
这条单调递减曲线就是持有成本模型的实证:sqrt(tau) 把噪声振幅和”离到期的时间”绑死,T→0 时振幅归零,基差必然贴合理论价。套利盘的存在只是加速了这个过程——一旦基差大到覆盖成本,就有资金冲进来搬砖把它压回,于是”收敛”既被理论保证,又被市场力量强化。
六、诚实拆穿:五类真实风险#
① 到期日效应(到期前基差被强行抹平,但你在到期前就必须平仓)。
真实市场里,最后交易日基差确实归零,但那是”现金交割”强制结算的结果。如果你持有的是实物交割合约(或不想参与交割),必须在到期前平仓,而那时基差可能还没完全归零——你赚的是”大部分收敛”,不是”全额收敛”。本文的 exit_band 已经模拟了这个提前离场。
② 分红除权跳变:基差会在除息日突然变号。
持有成本模型的 q 是”连续股息率”,但真实世界里分红是离散跳变的。除息日当天现货价格会直接跳跌(除权),而期货不除权,于是基差会在除息日瞬间变大甚至变号。如果你的现货是 ETF、期货是指数,除权日的跳变会制造假信号——务必把成分股分红日历加进基差模型,否则会被”假的高基差”骗去开仓。
③ 流动性与冲击成本:成本带 0.30% 是理想值。
本文用固定 cost=0.30% 近似双边摩擦。真实里,股指期货流动性好、冲击小,但现货端(尤其 ETF 大单、或你要复制一篮子成分股)冲击成本随下单量非线性上升。资金规模一大,“纸面基差”和”能吃到嘴的基差”之间会裂开一道缝——容量是这类策略的天花板。
④ 展期跳跃(roll risk):换合约时基差不连续。 我们”无缝”把下一张合约从最新现货价续上,但真实市场不同合约的基差结构(term structure)是不同的——远月通常升水更多。从近月换到远月时,你平仓的是近月基差、开仓的是远月基差,两个不连续,会在换月时制造一次跳变 P&L。展期时点要单独管理,不能假装连续。
⑤ 单边升水陷阱:长期高升水可能根本不回复。
持有成本模型假设 r>q 时基差稳定为正。但若市场处于极端乐观、期货持续高升水(如 2015 年牛市 IF 曾大幅升水),“做空基差”要先卖期货再等它跌回来——可如果升水持续扩大几个月,你会一直浮亏、保证金吃紧。基差回复是统计规律不是物理定律,极端 regime 下它可能迟迟不回复,逼你止损在黎明前。
七、落到量化:你的日常工作流#
- 先算理论基差:拿到现货价、无风险利率、股息率、到期天数,用
F_fair = S·e^((r−q)·T)算出中枢,再减去你能拿到的真实双边成本,得到开仓阈值。 - 把分红日历、展期日、交割规则写进回测——这三类”事件”是基差策略最容易翻车的地方,本文为简洁只建模了前两类。
- 评估容量而非只看收益:基差策略的夏普可以很高,但能吃下的资金量有限。报告里一定要写”在 X 亿规模下冲击成本是多少”,否则又是 paper alpha。
真实落地时,股指期货用交易所行情(中金所 IF/IC/IM)、现货用 ETF 或一篮子实时价,基差与资金成本实时刷新即可;本文纯 numpy 合成只为把”基差怎么来、为什么收敛、规则怎么写”的每一步摊开。记住:基差策略赚的是”收敛的确定性”,不是”方向的赌注”——它的安全垫是持有成本模型,它的天花板是容量。