基差交易(Basis Trade & Roll)
期现基差、contango/backwardation、Roll 策略、basis 与 funding 关系
日期: 2026-08-07 方向: 量化 / 统计套利 / Alpha 阶段: Phase 2 - 统计套利与Alpha Research (Day 89-102) 标签: #量化策略 #套利 #Basis #期现 #Contango #Roll
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | 期现基差、contango/backwardation、Roll 策略、basis 与 funding 关系 |
| 实操 | 用 Deribit/CME 季度合约数据回测 basis trade |
| 产出 | basis.py — Basis 监控 + Roll 策略 + 回测 |
一、理论与模型
1.1 期现基差定义
Basis(基差): $$ B_t = F_t - S_t $$
或对数化: $$ b_t = \ln F_t - \ln S_t $$
- $F_t$:期货价格
- $S_t$:现货价格
Annualized basis: $$ \text{Annualized} = \frac{F_t - S_t}{S_t} \times \frac{365}{T - t} $$
T 是到期日。
1.2 Cost of Carry 模型
无套利下: $$ F_t = S_t \cdot e^{(r - q)(T - t)} $$
- $r$:无风险利率
- $q$:持有现货的 yield(staking、dividend)
加密的特殊:q 可包括 staking yield(ETH ~3%)、lending yield。
Contango:F > S(升水),通常牛市 Backwardation:F < S(贴水),通常熊市或现货稀缺
1.3 Cash-and-carry with futures
类似 perpetual carry:
- 现货 long S
- 期货 short F
- 持有到期:锁定 (F - S) 收益
优势 vs perpetual:
- 锁定到期日,无需每 8h 监控 funding
- 无强平风险(如果用 fully-funded 期货)
- 可清晰计算 yield
劣势:
- 资金占用更长(直到 settlement)
- 季度合约流动性较差
- Roll 风险(如果想持续 carry)
1.4 Roll Strategy
到期前要 roll 到下一个合约:
Now (June): hold Jul futures
Approaching expiry: close Jul, open Sep
Roll yield: $$ \text{Roll Yield} = \frac{F_{near} - F_{far}}{F_{far}} $$
- Contango 市场:roll yield 通常负(亏损)
- Backwardation 市场:roll yield 正(盈利)
1.5 Basis 与 Perpetual Funding 关系
理论: $$ \text{Funding} \approx \frac{F_{perp} - S}{S} \cdot \text{每日次数} $$
季度 basis 与 perpetual funding 一致性:
- 高 funding → contango basis 应大
- Backwardation → 负 funding
差异机会:
- Perpetual funding 已正常化但季度 basis 偏离 → 套利
- 反向同理
1.6 Implied Yield(隐含收益)
从 basis 反推: $$ y = \frac{1}{T - t} \ln \frac{F_t}{S_t} $$
加密典型 implied yield:
- 牛市:8-20%
- 熊市:-5% 到 5%
- 极端时(FTX 危机后):-15%
1.7 Volatility 期限结构
Basis 隐含波动率期限结构:
- Spot ATM IV
- 1m futures IV
- 3m, 6m IV
期限结构反映市场对未来 vol 的预期。Basis trader 监控 IV term structure。
二、直觉与陷阱
陷阱 1:Roll cost 累积
季度合约每年 roll 4 次。每次 spread 0.3% → 年化 1.2% roll cost。 高频 roll(月度)成本更高。
陷阱 2:到期日大波动
合约到期前 1-2 周流动性集中转移到下一合约:
- 近月合约 spread 扩大
- Roll 操作时滑点高
- 解法:提前 2-3 周 roll,避开高波动期
陷阱 3:CME vs Deribit basis 不同
| 维度 | CME | Deribit |
|---|---|---|
| 合约类型 | 现金交割 | 现金交割 (BTC、ETH) |
| 用户 | 机构(合规要求) | 全球零售+机构 |
| Basis | 通常更高 | 较低(更接近 spot) |
| Capacity | 大 | 中等 |
| 监管 | CFTC | 离岸 |
CME basis 受 ETF flow 强烈影响(2024 年 ETF 通过后 CME basis 显著上升)。
陷阱 4:到期日 manipulation
到期前最后 1 小时现货价被操纵以影响结算。 解法:
- 用 settlement window 较长的合约(如 Deribit 30 分钟 TWAP)
- 提前 1 天 close,用 perp 替代
陷阱 5:Basis 不对称收敛
理论 basis 到期收敛到 0,但路径可能:
- 平稳 linear decay(理想)
- Mean-reverting(可能扩大后再收敛)
- Discontinuous(重大事件)
不能假设 linear path。
陷阱 6:现货端的隐藏成本
做 spot leg 需要:
- 现货保管(cold storage 成本)
- 未流通损失(错过 staking、airdrop)
- KYC/AML 限制
机构成本可能 0.5-1% 年化。
三、代码实现
3.1 Basis 监控与回测
# basis.py
"""
Basis Trade Engine
- Spot/futures basis monitoring
- Roll strategy backtester
- Term structure analysis
"""
import numpy as np
import pandas as pd
import requests
from datetime import datetime, timedelta
from typing import Dict, List, Tuple
# ----------------------------- Deribit API -----------------------------
class DeribitClient:
BASE = "https://www.deribit.com/api/v2/public"
@staticmethod
def get_instruments(currency: str = 'BTC', kind: str = 'future') -> List[Dict]:
r = requests.get(f"{DeribitClient.BASE}/get_instruments",
params={'currency': currency, 'kind': kind, 'expired': 'false'})
return r.json()['result']
@staticmethod
def get_ticker(instrument: str) -> Dict:
r = requests.get(f"{DeribitClient.BASE}/ticker",
params={'instrument_name': instrument})
return r.json()['result']
@staticmethod
def get_index_price(currency: str = 'BTC') -> float:
r = requests.get(f"{DeribitClient.BASE}/get_index_price",
params={'index_name': f'{currency.lower()}_usd'})
return r.json()['result']['index_price']
# ----------------------------- 基差分析 -----------------------------
def analyze_term_structure(currency: str = 'BTC') -> pd.DataFrame:
instruments = DeribitClient.get_instruments(currency, 'future')
spot = DeribitClient.get_index_price(currency)
rows = []
for inst in instruments:
if inst['settlement_period'] in ('day', 'week', 'month'):
try:
ticker = DeribitClient.get_ticker(inst['instrument_name'])
expiry_ts = inst['expiration_timestamp'] / 1000
days_to_expiry = (expiry_ts - datetime.now().timestamp()) / 86400
if days_to_expiry <= 0:
continue
mark_price = ticker['mark_price']
basis = (mark_price - spot) / spot
annualized = basis * 365 / days_to_expiry
rows.append({
'instrument': inst['instrument_name'],
'expiry_days': days_to_expiry,
'mark_price': mark_price,
'spot': spot,
'basis_pct': basis,
'annualized_basis': annualized,
'open_interest': ticker.get('open_interest', 0),
})
except Exception as e:
print(f" Err {inst['instrument_name']}: {e}")
return pd.DataFrame(rows).sort_values('expiry_days')
# ----------------------------- Cash and Carry P&L -----------------------------
def cash_and_carry_pnl(spot_qty: float, futures_short_qty: float,
spot_entry: float, futures_entry: float,
spot_exit: float, futures_exit: float,
days_held: int = None) -> Dict:
spot_pnl = (spot_exit - spot_entry) * spot_qty
futures_pnl = (futures_entry - futures_exit) * futures_short_qty
total = spot_pnl + futures_pnl
capital = spot_qty * spot_entry
ret_pct = total / capital
apr = ret_pct * 365 / days_held if days_held else None
return {
'spot_pnl': spot_pnl,
'futures_pnl': futures_pnl,
'total_pnl': total,
'return_pct': ret_pct,
'annualized': apr,
}
# ----------------------------- Roll Strategy 模拟 -----------------------------
def simulate_roll_strategy(price_path: pd.Series,
basis_path: pd.DataFrame,
roll_days_before_expiry: int = 14,
fee_bps: float = 5) -> pd.DataFrame:
"""
模拟 perpetual carry 策略(用季度合约 + roll)
basis_path: columns = ['near_basis', 'far_basis']
"""
dates = price_path.index
pnl_series = pd.Series(0.0, index=dates)
capital = 1.0
# 简化:每 90 天 roll 一次
roll_dates = pd.date_range(dates[0], dates[-1], freq='90D')
for i in range(len(roll_dates) - 1):
period_start = roll_dates[i]
period_end = roll_dates[i + 1]
if period_end > dates[-1]:
period_end = dates[-1]
if period_start not in basis_path.index:
continue
# 入场基差(near contract)
entry_basis = basis_path.loc[period_start, 'near_basis']
# 假设 holding period 收 entry basis
period_yield = entry_basis * (period_end - period_start).days / 365
# 减去 roll cost
roll_cost = 2 * (fee_bps / 10_000)
net_yield = period_yield - roll_cost
capital *= (1 + net_yield)
pnl_series.loc[period_end] = capital - 1
return pd.DataFrame({'capital': pnl_series, 'cumulative_return': pnl_series})
# ----------------------------- 历史 basis 回测 -----------------------------
def backtest_basis_trade(historical_basis: pd.DataFrame,
entry_threshold: float = 0.05,
exit_threshold: float = 0.02,
fee_bps: float = 10) -> pd.DataFrame:
"""
historical_basis: index = date, column = 'annualized_basis'
策略:basis > 5% 入场(cash and carry),basis < 2% 平仓
"""
df = historical_basis.copy()
df['signal'] = 0
position = 0
entry_basis = None
for i in range(len(df)):
b = df.iloc[i]['annualized_basis']
if position == 0:
if b > entry_threshold:
position = 1
entry_basis = b
else:
if b < exit_threshold:
position = 0
entry_basis = None
df.iloc[i, df.columns.get_loc('signal')] = position
# 简化日 P&L = signal × basis / 365 - fee at trade
df['daily_yield'] = df['signal'] * df['annualized_basis'] / 365
turnover = df['signal'].diff().abs().fillna(0)
df['cost'] = turnover * (fee_bps / 10_000)
df['net_ret'] = df['daily_yield'] - df['cost']
df['equity'] = (1 + df['net_ret'].fillna(0)).cumprod()
return df
# ----------------------------- Roll Yield 分析 -----------------------------
def calculate_roll_yield(near_price: pd.Series, far_price: pd.Series) -> pd.Series:
"""Roll yield = (P_near - P_far) / P_far"""
return (near_price - far_price) / far_price
# ----------------------------- 主程序 -----------------------------
if __name__ == '__main__':
print("Term Structure Analysis - BTC")
print("=" * 60)
try:
ts = analyze_term_structure('BTC')
print(ts.to_string(index=False))
if len(ts) > 0:
print(f"\nFront month annualized basis: {ts.iloc[0]['annualized_basis']:.2%}")
except Exception as e:
print(f" API err: {e}")
print("\nCash and Carry P&L Example")
pnl = cash_and_carry_pnl(
spot_qty=10, futures_short_qty=10,
spot_entry=60000, futures_entry=63000,
spot_exit=58000, futures_exit=58000, # converge to spot
days_held=90)
for k, v in pnl.items():
if isinstance(v, float):
print(f" {k}: {v:.4f}")
# 简化历史 basis 回测(合成数据)
print("\nSimulated Basis Backtest")
np.random.seed(42)
dates = pd.date_range('2023-01-01', '2024-12-31', freq='D')
# 合成 basis:mean-reverting around 0.08
basis = np.zeros(len(dates))
basis[0] = 0.08
for t in range(1, len(dates)):
basis[t] = 0.95 * basis[t-1] + 0.05 * 0.08 + np.random.normal(0, 0.01)
basis_df = pd.DataFrame({'annualized_basis': basis}, index=dates)
bt = backtest_basis_trade(basis_df, entry_threshold=0.10, exit_threshold=0.05)
print(f" Total return: {bt['equity'].iloc[-1] - 1:.2%}")
print(f" Sharpe: {bt['net_ret'].mean() / bt['net_ret'].std() * np.sqrt(365):.2f}")
print(f" Days in market: {(bt['signal'] == 1).sum()} / {len(bt)}")
四、真实数据/案例
案例 1:3AC 的 GBTC basis trade(致命)
3AC 长期做 GBTC 折价 trade:
- 2020-2021:GBTC 6 个月锁仓后可二级卖出,曾长期 +20% 溢价
- 操作:在 1 级买 GBTC,6 月后 unlock 到 2 级卖
- 2021-Q2:溢价突然变 -10% 折价
- 3AC 杠杆 5x,未及时砍仓
教训:basis trade 看似确定,但路径依赖(lockup、可赎回性)可以毁灭。
案例 2:FTX 倒闭前的 basis 异常
2022-11-08 FTX 危机前:
- BTC perp funding 跳到 -0.05%(2 个月低位)
- 季度合约 basis 从 +6% 变 -2%(backwardation)
- 提示:市场极度恐慌、margin call 压力
- 反向 basis trade:long 期货 + short 现货(DeFi 借入)能赚 8-10%
案例 3:CME BTC ETF basis 暴涨(2024-Q1)
ETF 通过后 CME basis 显著上升:
- 2024-01:ETF 通过日,CME 基差跳到 +18% 年化
- 整个 Q1 维持 12-15%
- 大量机构资金涌入 carry trade
- 基差被套利消除到 ~5% by Q3
教训:政策事件触发 basis 异常,机构反应迟缓时有套利窗口。
案例 4:CME Bitcoin basis 与 funding 利差
2024 Q2 CME 基差 8% 年化 vs Binance 永续 funding 5% 年化。
- 套利:在 Binance 现货 + CME 期货 short
- 但 CME 要求美国机构资格,零售难以参与
- 教训:机构通道独立的 alpha 持续存在
案例 5:Backwardation 黄金机会(2022 LUNA)
2022-05-12 LUNA 崩盘:
- BTC 季度合约一度 backwardation -8% 年化
- 反向 carry:买 future + 短 spot
- 但 spot 短借成本高(DeFi 借 USDC 做多 BTC,相反方向)
- 实际可执行收益 4-6%
五、CEX vs DEX 策略差异
| 维度 | CEX Basis | DEX Basis |
|---|---|---|
| 平台 | Deribit/CME/Binance Futures | dYdX (perp only)、Lyra、Premia |
| dated futures | Deribit 季度/月度 | 极少(多数 DEX 仅 perp) |
| 成本 | 0.05-0.10% 单边 | 0.10-0.50% + gas |
| 流动性 | 季度合约 OI $1B+ | 更小 |
| 结算 | 现金 | 链上自动 |
| DeFi 独有 | — | Pendle PT/YT 是 perfect basis |
Pendle PT/YT 的 basis 等价:
- PT (Principal Token) ≈ zero-coupon bond
- 持有 PT 到期 = 锁定到期 yield
- Implied yield = 1 - PT_price,等价于 basis trade
- 极简:在二级市场买 PT,到期赎回 1,锁定 yield 无 leg risk
六、风险管理
6.1 Basis trade 风险
| 风险 | 控制 |
|---|---|
| Roll loss | 选 backwardation 时 carry,contango 时 reverse |
| 流动性 | 季度合约 OI > $100M 才进 |
| Convergence path | 监控 mid-life basis 稳定性 |
| 现货端 | KYC/合规、保管 |
| Tail event | LUNA/FTX 时 basis 大幅偏离 |
6.2 Capacity 估计
CME BTC basis trade 容量:
- ETF inflow $50B 推动
- Basis trade 估计 $5-10B 容量
- 单笔 < $50M(depth)
6.3 资金成本
def basis_break_even_apr(borrow_rate, fees, slippage, savings_rate):
"""basis 净 yield 必须超过:借贷成本 - savings 收益"""
return borrow_rate - savings_rate + fees + slippage
例:
- USDC 借 5%,T-bill 4.5% → spread 0.5%
- Fees 0.20%
- 总成本 0.7% 年化
- Basis > 0.7% APR 才有收益(极宽松)
- 实操要求 basis > 5% 才足以覆盖 risk premium
七、关键速查
Basis 计算
Annualized Basis = (F - S) / S × 365 / days_to_expiry
入场策略
| 市场 | 操作 |
|---|---|
| Contango (basis > 0) | Spot long + Futures short |
| Backwardation | Spot short + Futures long |
| 反向(DeFi 借入做空) | 借 USDC → 卖 BTC + buy futures |
Roll 时机
- 提前 14-21 天 roll(避开到期 vol)
- 选 OI 转移已开始的时点
- 避免周末(流动性差)
八、面试题
Q1:Basis trade 和 funding rate arb 的本质区别?
答:
- Basis(dated futures):锁定到 T 时刻 yield,convergence 必然(合约设计强制)
- Funding(perpetual):无到期,funding 实时浮动,方向可逆
- 比较:
- Basis 更确定(路径风险小)
- Funding 灵活(随时进出)
- Basis 适合大资金、长期 carry
- Funding 适合机敏短期捕捉
- 结合:
- Funding > basis annualized → 用 perp
- Basis 显著 > funding → 用 quarterly
- 跨产品套利:funding 高但 basis 低 → 永续短 + 季度多
Q2:为什么 ETF 通过后 CME basis 暴涨?
答:
- ETF 创造 / 赎回机制:APs (Authorized Participants) 收 BTC,需 hedge → 卖 CME futures
- 同时 ETF 直接需求推高 spot
- Basis = F - S 受双向推动
- 对应套利者(其他 traders)需求是 short futures + long spot 收 basis
- 但合规 / 资金规模限制使套利不充分 → basis 持续高位
- 这是结构性 alpha,可能持续数年
Q3:怎么判断 contango 是 normal 还是 over-extended?
答:
- Normal contango:5-12% 年化(覆盖借贷成本 + 风险溢价)
- Over-extended:> 20% 年化,往往伴随:
- Spot 强劲牛市
- 杠杆资金涌入 perpetual long
- 期货 long-skew
- 判断:
- 比较历史百分位(90th+ 极端)
- 看 funding rate 协同性
- 看 OI 增长(资金涌入)
- 极端 contango 后:往往 mean revert,注意 reversal risk
Q4:Pendle PT 和传统 basis 的关系?
答:
- PT (Principal Token) 是 yield-bearing 资产的本金部分,到期 1:1 兑底层
- 二级市场 PT 价格 < 1 → implied yield = 1/PT - 1(年化要 / T)
- 等价于:买 PT = lock-in yield 到 T,类似 zero-coupon bond
- 优势 vs 期货 basis:
- 单 leg(无 spot leg),无 cross-margin 风险
- 固定到期,无 funding 风险
- DeFi 原生(无 KYC、24/7)
- 劣势:
- 流动性较小
- 协议风险(Pendle 合约)
- Yield 资产风险(如果底层是 lent USDC,借贷协议 risk)
Q5:Backwardation 套利的实际困难?
答:
- Spot 做空困难:现货空单需要借入,DEX 借贷利率高(10-20%)
- Backwardation 通常发生在恐慌期:流动性差、滑点大
- 持有 spot 的成本高:保管、放弃 staking yield
- Backwardation 不一定收敛:极端情况持续数月
- 现货 short 在 CEX 限制:margin trading 有借贷限额
- 机构难做:合规要求做空合规复杂
- 解法:做反向 carry 时控制规模 < 总资本 5%;用 Pendle 反向(卖 PT)
明日预告
Day 99: Liquidation Mining — Aave / Compound 清算机器人。从今天的 basis trade 转到 DeFi 原生套利机会,深度学习清算机制和 bot 实现。