资金费率套利(Cash and Carry)
Funding rate 机制、cash-and-carry、delta-neutral 对冲、不同所 funding 差异
日期: 2026-08-06 方向: 量化 / 统计套利 / Alpha 阶段: Phase 2 - 统计套利与Alpha Research (Day 89-102) 标签: #量化策略 #套利 #FundingRate #CashAndCarry #DeltaNeutral
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | Funding rate 机制、cash-and-carry、delta-neutral 对冲、不同所 funding 差异 |
| 实操 | 实现自动对冲的 funding 套利系统,含 delta 漂移修正 |
| 产出 | funding_arb.py — Funding 套利引擎 + 风控 |
一、理论与模型
1.1 永续合约与资金费率
永续合约(perpetual)没有到期日,通过 funding rate 拉回到现货价。
Binance 公式: $$ F = \text{Premium Index} + \text{clamp}(\text{Interest Rate} - \text{Premium Index}, -0.05%, 0.05%) $$
- Premium Index:8h TWAP 的 (perp - spot) / spot
- Interest Rate:固定 0.01% / 8h
- Funding 每 8h 结算一次(部分所每 1h 或 4h)
含义:
- F > 0:long 付 short(市场偏多头,long pay)
- F < 0:short 付 long(市场偏空,short receive)
- 极端 F = ±0.75%/8h,年化 ±820%
1.2 Cash and Carry 套利
核心:当 funding 持续为正(如 +0.05%/8h),做空 perp + 持有现货:
| 腿 | 头寸 | PnL |
|---|---|---|
| 现货 | Long BTC | +ΔP |
| 永续 | Short BTC | -ΔP(对冲)+ funding收入 |
| 净 | Delta neutral | +funding |
年化收益: $$ \text{APR} = F \times \frac{365 \times 24}{8} = F \times 1095 $$
F = 0.01%/8h → APR ≈ 11% F = 0.05%/8h → APR ≈ 55% F = 0.10%/8h → APR ≈ 110%
1.3 Reverse Carry(负 funding)
F < 0 时反向:做多 perp + 现货做空(或借入并卖出)
问题:现货做空机会有限:
- CEX:通过借贷市场(Margin)借入现货
- DEX:用 AAVE/Compound 借入抵押
- 借入成本可能高于 funding 收入
1.4 Delta 漂移与再平衡
理论:现货 + 短永续 = delta neutral 实际:
- 价格剧烈变动后,现货数量与永续合约名义不匹配
- BTC 从 $30K → $40K 后,1 BTC 现货 = $40K,但 1 BTC 永续合约名义 = $40K(如果是 USD-margined)
- USDT 永续:合约名义随价变,无需再平衡(quanto effect)
- COIN-margined(币本位):现货 1 BTC vs 永续 1 BTC contract size,但永续盈亏以 BTC 计 → 凸性影响
经验:每天或每 5% 价格变动后再平衡 delta。
1.5 Funding 历史分布
| 资产 | 平均 F (8h) | Std (8h) | 极端 F |
|---|---|---|---|
| BTC | +0.01% | 0.012% | +0.50%/-0.20% |
| ETH | +0.012% | 0.014% | +0.60%/-0.18% |
| SOL | +0.015% | 0.020% | +0.75%/-0.30% |
| Memes (PEPE) | +0.025% | 0.040% | +0.75%/-0.50% |
经验法则:
- F > 0.03%/8h(年化 33%+):carry 显著
- F < -0.01%/8h:reverse carry 机会
- |F| > 0.10%/8h:tail event,可能反转
1.6 对手所 funding 套利
不同所 funding 不同(FTX 时代有显著差异):
| 所 | F at the same time |
|---|---|
| Binance | +0.02% |
| Bybit | +0.04% |
| OKX | +0.025% |
| dYdX | +0.05% |
| GMX | +0.08% |
操作:在高 F 所做空、低 F 所做多 perp 双 perp 对冲(无现货占用)。
1.7 DeFi Funding 套利(独有机会)
GMX-V2 / dYdX:
- GMX funding 由 GLP/GLP holders 决定,机制不同
- 可能持续高 funding(pool 偏移时)
- DeFi 永续 funding 历史 spread 大于 CEX
Pendle PT/YT:
- PT (Principal Token) 锁定到期价,等价于零息债
- YT (Yield Token) 持有未来 yield
- 可用 YT 做 funding 等价 carry
二、直觉与陷阱
陷阱 1:Funding 不是无风险
许多新手觉得 cash-and-carry 是无风险套利。风险:
- 基差风险:spot - perp 不会完美锁定
- 资金占用风险:现货占资本,资金成本高时净亏
- 强平风险:perp 仓位若杠杆高,spot 闪崩前 perp 已被强平
- funding 变化:F 可能变负,套利反转
- 对手方:FTX 倒闭客户钱锁住
陷阱 2:忽略借贷利率
如果用借入资金做现货:
- AAVE 借 USDC 利率 5%,funding 4%(年化)→ 净负
- 必须 funding > 借贷利率 + 操作成本
陷阱 3:交易成本累积
每 8h 调仓一次,年化 1095 次。每次 0.05% taker fee →累 55%/年成本。 解法:
- 用 maker 单(rebate 或 0 fee)
- 减少调仓频率(只在 delta 偏 > 5% 时调)
- 选低 fee 所
陷阱 4:funding 反转
正 funding 持续 1 个月不代表永远。LUNA/FTX 期 funding 反转极快。 解法:实时监控 funding,反转后立即出场。
陷阱 5:cross-margin vs isolated
- Cross-margin:账户内资金共用 → 强平联动
- Isolated:每仓位独立保证金 → 但需更多资金
cash-and-carry 推荐 isolated,强平不会蔓延。
陷阱 6:闪崩 + perp 强平
2020-03-12 BTC 一日 -50%,许多 carry 仓位 perp 端先于 spot 强平。 解法:
- 杠杆 ≤ 2x(保守)
- 准备额外保证金
- 用 USD-margined 而非 COIN-margined(币本位强平时连锁)
陷阱 7:funding 计算误用
Bybit funding 公式与 Binance 略不同(区间不同)。一定要用各所自己的实时 API。
三、代码实现
3.1 完整 Funding 套利引擎
# funding_arb.py
"""
Funding Rate Arbitrage Engine
- Real-time funding monitor across exchanges
- Delta-neutral carry
- Auto rebalance
- Risk controls
"""
import numpy as np
import pandas as pd
import requests
import time
from typing import Dict, List, Optional
from dataclasses import dataclass, field
# ----------------------------- Funding API ---------------------------
class FundingClient:
@staticmethod
def binance_funding(symbol: str = 'BTCUSDT') -> Dict:
"""当前 funding rate + next funding time"""
url = "https://fapi.binance.com/fapi/v1/premiumIndex"
r = requests.get(url, params={'symbol': symbol})
d = r.json()
return {
'symbol': symbol,
'mark_price': float(d['markPrice']),
'index_price': float(d['indexPrice']),
'last_funding': float(d['lastFundingRate']),
'next_funding_time': int(d['nextFundingTime']) / 1000,
'exchange': 'Binance',
}
@staticmethod
def binance_funding_history(symbol: str = 'BTCUSDT', limit: int = 100) -> pd.DataFrame:
url = "https://fapi.binance.com/fapi/v1/fundingRate"
r = requests.get(url, params={'symbol': symbol, 'limit': limit})
df = pd.DataFrame(r.json())
df['fundingRate'] = df['fundingRate'].astype(float)
df['fundingTime'] = pd.to_datetime(df['fundingTime'], unit='ms')
return df.set_index('fundingTime')[['fundingRate']]
@staticmethod
def bybit_funding(symbol: str = 'BTCUSDT') -> Dict:
url = "https://api.bybit.com/v5/market/tickers"
r = requests.get(url, params={'category': 'linear', 'symbol': symbol})
d = r.json()['result']['list'][0]
return {
'symbol': symbol,
'mark_price': float(d['markPrice']),
'last_funding': float(d['fundingRate']),
'next_funding_time': int(d['nextFundingTime']) / 1000,
'exchange': 'Bybit',
}
@staticmethod
def okx_funding(symbol: str = 'BTC-USDT-SWAP') -> Dict:
url = "https://www.okx.com/api/v5/public/funding-rate"
r = requests.get(url, params={'instId': symbol})
d = r.json()['data'][0]
return {
'symbol': symbol,
'last_funding': float(d['fundingRate']),
'next_funding_time': int(d['nextFundingTime']) / 1000,
'exchange': 'OKX',
}
# ----------------------------- 多所 funding 比较 -----------------------------
def funding_screener(symbols: List[str] = ['BTC', 'ETH', 'SOL']) -> pd.DataFrame:
rows = []
for sym in symbols:
try:
b = FundingClient.binance_funding(f'{sym}USDT')
rows.append({'symbol': sym, 'exchange': 'Binance',
'funding_8h': b['last_funding'],
'funding_apr': b['last_funding'] * 1095})
except Exception as e:
print(f"Binance {sym} err: {e}")
try:
by = FundingClient.bybit_funding(f'{sym}USDT')
rows.append({'symbol': sym, 'exchange': 'Bybit',
'funding_8h': by['last_funding'],
'funding_apr': by['last_funding'] * 1095})
except Exception as e:
print(f"Bybit {sym} err: {e}")
try:
ok = FundingClient.okx_funding(f'{sym}-USDT-SWAP')
rows.append({'symbol': sym, 'exchange': 'OKX',
'funding_8h': ok['last_funding'],
'funding_apr': ok['last_funding'] * 1095})
except Exception as e:
print(f"OKX {sym} err: {e}")
return pd.DataFrame(rows).sort_values(['symbol', 'funding_apr'], ascending=[True, False])
# ----------------------------- 套利仓位管理 -----------------------------
@dataclass
class CarryPosition:
symbol: str
spot_qty: float # 现货数量
perp_short_qty: float # 永续做空数量(正数)
spot_entry_price: float
perp_entry_price: float
funding_collected: float = 0
open_time: float = field(default_factory=time.time)
def delta(self, spot_price: float) -> float:
"""组合 delta(应为 0)"""
return self.spot_qty - self.perp_short_qty
def pnl(self, spot_price: float, perp_price: float) -> Dict:
spot_pnl = (spot_price - self.spot_entry_price) * self.spot_qty
perp_pnl = (self.perp_entry_price - perp_price) * self.perp_short_qty
return {
'spot_pnl': spot_pnl,
'perp_pnl': perp_pnl,
'funding': self.funding_collected,
'total': spot_pnl + perp_pnl + self.funding_collected,
}
def needs_rebalance(self, threshold: float = 0.02) -> bool:
diff = abs(self.spot_qty - self.perp_short_qty) / self.spot_qty
return diff > threshold
class CarryArbEngine:
"""资金费率套利引擎"""
def __init__(self, min_apr: float = 0.10, max_position_size: float = 100_000,
max_leverage: float = 2.0, rebalance_threshold: float = 0.02):
self.min_apr = min_apr
self.max_position_size = max_position_size
self.max_leverage = max_leverage
self.rebalance_threshold = rebalance_threshold
self.positions: Dict[str, CarryPosition] = {}
def evaluate_opportunity(self, symbol: str) -> Dict:
"""评估是否开仓"""
try:
f = FundingClient.binance_funding(f'{symbol}USDT')
apr = f['last_funding'] * 1095
# 加上历史平均(防止 outlier)
hist = FundingClient.binance_funding_history(f'{symbol}USDT', 21)
hist_apr = hist['fundingRate'].mean() * 1095
return {
'symbol': symbol,
'current_apr': apr,
'avg_apr_7d': hist_apr,
'should_enter': apr > self.min_apr and hist_apr > self.min_apr * 0.5,
'mark_price': f['mark_price'],
}
except Exception as e:
return {'error': str(e)}
def open_position(self, symbol: str, notional_usd: float) -> Optional[CarryPosition]:
opp = self.evaluate_opportunity(symbol)
if not opp.get('should_enter'):
print(f" {symbol}: opportunity not strong enough (APR {opp.get('current_apr', 0):.2%})")
return None
price = opp['mark_price']
qty = notional_usd / price
# 模拟下单(实盘要走 CEX API)
pos = CarryPosition(
symbol=symbol,
spot_qty=qty,
perp_short_qty=qty,
spot_entry_price=price,
perp_entry_price=price,
)
self.positions[symbol] = pos
print(f" Opened {symbol}: spot {qty:.4f} @ {price}, perp short {qty:.4f}")
return pos
def collect_funding(self, symbol: str, funding_rate: float):
"""每 8h 调用一次"""
if symbol not in self.positions:
return
pos = self.positions[symbol]
# short 收 funding(funding > 0 时)
funding_pnl = pos.perp_short_qty * pos.perp_entry_price * funding_rate
pos.funding_collected += funding_pnl
return funding_pnl
def rebalance_delta(self, symbol: str, spot_price: float, perp_price: float):
if symbol not in self.positions:
return
pos = self.positions[symbol]
if not pos.needs_rebalance(self.rebalance_threshold):
return
diff = pos.spot_qty - pos.perp_short_qty
# 调整 perp 端
if diff > 0:
# 现货多于 perp 空头,加空 diff
pos.perp_short_qty += diff
print(f" Rebalance {symbol}: increase short by {diff:.6f}")
else:
pos.perp_short_qty += diff # diff < 0 reduces short
print(f" Rebalance {symbol}: reduce short by {-diff:.6f}")
def close_position(self, symbol: str, spot_price: float, perp_price: float) -> Dict:
if symbol not in self.positions:
return {}
pos = self.positions[symbol]
pnl = pos.pnl(spot_price, perp_price)
held_days = (time.time() - pos.open_time) / 86400
annualized_return = pnl['total'] / (pos.spot_qty * pos.spot_entry_price) / max(held_days, 1) * 365
del self.positions[symbol]
return {**pnl, 'annualized': annualized_return, 'held_days': held_days}
# ----------------------------- 历史回测 -----------------------------
def backtest_carry_strategy(symbol: str = 'BTCUSDT',
entry_apr_threshold: float = 0.20) -> pd.DataFrame:
"""回测:funding APR > threshold 入场,反转出场"""
funding = FundingClient.binance_funding_history(symbol, 1000)
funding['apr'] = funding['fundingRate'] * 1095
funding['signal'] = (funding['apr'] > entry_apr_threshold).astype(int)
# 假设无成本 carry
funding['ret'] = funding['signal'].shift(1) * funding['fundingRate']
funding['equity'] = (1 + funding['ret'].fillna(0)).cumprod()
return funding
# ----------------------------- 主程序 -----------------------------
if __name__ == '__main__':
print("Funding Rate Screener")
print("=" * 60)
df = funding_screener(['BTC', 'ETH', 'SOL'])
print(df.to_string(index=False))
print("\nHistorical Carry Backtest (BTC)")
bt = backtest_carry_strategy('BTCUSDT', entry_apr_threshold=0.15)
n_signals = bt['signal'].sum()
total_ret = bt['equity'].iloc[-1] - 1
print(f" Signal hits: {n_signals} / {len(bt)} ({n_signals/len(bt):.1%})")
print(f" Total return: {total_ret:.2%}")
print(f" Days: {(bt.index[-1] - bt.index[0]).days}")
print(f" Annualized: {(1 + total_ret) ** (365 / (bt.index[-1] - bt.index[0]).days) - 1:.2%}")
# 模拟仓位管理
print("\nLive Carry Engine (simulated)")
engine = CarryArbEngine(min_apr=0.10)
engine.open_position('BTC', notional_usd=10_000)
四、真实数据/案例
案例 1:2021 牛市的 funding 黄金时代
| 时段 | BTC funding | 年化 |
|---|---|---|
| 2021-04 | 0.10%/8h | 110% |
| 2021-10 | 0.08%/8h | 88% |
| 2022-01 | 0.05%/8h | 55% |
简单 cash-and-carry 即可年化 50%+。Three Arrows Capital、Alameda 都大量做。
案例 2:3AC 的 carry trade 灾难(2022)
3AC 部分仓位是 carry:
- 现货 BTC + 短 perp BTC 看似安全
- 杠杆 5x 放大收益
- LUNA 崩盘导致连锁
- 现货 BTC 暴跌使 spot 端 margin 不够,强平
- 永续端反向获利但被 cross-margin 联动
教训:cross-margin + 高杠杆 = carry 也能爆仓。
案例 3:FTX 破产的 funding 信号
2022-11 FTX 危机前:
- 11-06:BTC perp funding 突然降至 -0.05%(罕见)
- 11-08:funding 持续负,提示市场恐慌
- 反向 carry 机会浮现,但需要做空 spot(高难度)
教训:funding 是市场情绪 leading indicator。
案例 4:DeFi funding 套利成熟(2024-)
GMX V2 / dYdX V4:
- 无 KYC、合约自托管
- funding 历史平均略高于 CEX(liquidity premium)
- 但 protocol risk + gas 成本高
实证(dYdX V4 2024):
- BTC carry 年化 ≈ 12%(vs CEX 8%)
- 但 gas + slippage 实际净 ≈ 8%
案例 5:跨所 funding 套利(机构)
某机构(公开 paper):
- 在 Binance / Bybit / OKX 做反向 carry
- F_Bybit > F_Binance + 1% 时:在 Bybit 短 + Binance 多 perp
- 不需现货,纯 perp 双开
- 风险:basis 风险(两所价格短暂偏离)
五、CEX vs DEX 策略差异
| 维度 | CEX Funding | DEX Funding |
|---|---|---|
| 可用所 | Binance/Bybit/OKX/Bitget | dYdX/GMX/Hyperliquid/Synthetix |
| 频率 | 每 8h | 每 1h(dYdX)/ 持续(GMX) |
| gas | 0 | 取决于链(L2 低) |
| 滑点 | 小 | 较大(依池) |
| funding 公式 | 平台公开 | 协议代码(透明但复杂) |
| 对手方 | CEX 破产风险 | 协议 / oracle 风险 |
| 资金效率 | margin call 频繁 | 较保守 |
| 稳健性 | API 中断时断 | 链上不可中断 |
DeFi funding 套利独有:
- GMX v2 GLP:GLP 持有者收 perp 交易者亏损 + funding,复杂但高 yield
- Hyperliquid HLP:类似但更复杂
- Pendle:把未来 yield 流证券化,可锁定 funding income
六、风险管理
6.1 Carry 仓位风控
| 风险 | 控制 |
|---|---|
| 强平 | 杠杆 ≤ 2x;保证金 buffer ≥ 30% |
| funding 反转 | 实时监控;F < 0 持续 24h 平仓 |
| CEX 倒闭 | 单所仓位 < 总资本 30% |
| 资金成本 | 借贷利率 < funding 70% 才入场 |
| delta 漂移 | 每 5% 价格动 / 每天再平衡 |
| Tail event | 黑天鹅时 spot/perp 联动失败,预留现金 |
6.2 杠杆 sizing
def safe_carry_leverage(funding_apr, vol_annual=0.6, target_drawdown=0.15):
"""
Kelly-style:funding 是 ER,vol 是风险
"""
# 单倍杠杆下 worst-case drawdown ≈ vol × 1.65 (95%)
if funding_apr <= 0:
return 0
raw = target_drawdown / (vol_annual * 0.5)
# 保守 cap
return min(raw, 2.0)
6.3 多所分散
30% Binance + 30% Bybit + 20% OKX + 10% dYdX + 10% Cash buffer
七、关键速查
Funding APR 转换
APR = funding_8h × 365 × 24 / 8 = funding_8h × 1095
APR = funding_1h × 365 × 24 = funding_1h × 8760 (for hourly funding venues)
入场门槛建议
| 自信度 | APR 门槛 |
|---|---|
| 激进 | 8% |
| 中性 | 15% |
| 保守 | 25% |
| 必须做 | 50%+ |
Delta 监控
delta = spot_qty - perp_short_qty * (perp_price / spot_price)
if abs(delta) / spot_qty > 0.05:
rebalance()
八、面试题
Q1:Cash-and-carry 套利真的"无风险"吗?
答:不是。
- 强平风险:perp 端高杠杆时闪崩可能先于 spot 强平
- funding 反转:F 可能突然变负
- 基差风险:spot vs perp 价格短期偏离
- 对手方风险:CEX 倒闭(FTX)锁仓
- 流动性风险:极端行情下减仓滑点大
- 资金占用成本:融资利率高时净亏 正确表述:carry 是 risk-adjusted positive carry,不是 risk-free arb。
Q2:怎么决定开仓 funding 阈值?
答:
- 覆盖成本:funding > 借贷利率 + 操作成本(fee + slippage)+ 保险费
- 历史分布:当前 funding > 历史 80th percentile 时入场(top quintile)
- 动量:funding 上升趋势 + 高位 → 入场;下降趋势 → 等
- 对比基准:> 国债 + risk premium(如 10% APR)
- 加密推荐:F > 0.025%/8h(年化 27%)保守入场
Q3:你怎么管理 delta 漂移?
答:
- 频率:根据价格 move 动态:每 5% 价格变动 OR 每 24h 检查一次
- 阈值:|delta| / spot_qty > 2% 触发再平衡
- 方法:调整 perp 端而非 spot(spot 转账成本高)
- 币本位 vs USD 本位:USD-margined perp 自动 delta neutral;coin-margined 有凸性需主动管理
- 成本:每次再平衡按 taker fee 计,频繁再平衡吞噬 alpha
Q4:DeFi funding 套利相比 CEX 优劣?
答:
- 优势:
- 自托管,无 CEX 倒闭风险
- 部分协议 funding 更高(流动性溢价)
- 透明(合约代码可审计)
- 24/7 无停机
- 劣势:
- gas 成本(每次再平衡 $5-50)
- 协议风险(合约漏洞、oracle 攻击)
- 流动性较 CEX 差(大额滑点)
- 复杂度(不同协议公式不同)
- 结合:80% CEX + 20% DEX(捕捉 yield premium 同时控风险)
Q5:如果 funding 突然从 +0.05% 跳到 -0.05% 你怎么办?
答:
- 立即评估:是 spike 还是 trend reversal?
- 如果是 spike(罕见短暂):等 1-2 个周期看是否回归
- 如果 trend reversal:
- 评估持仓是否进入亏损区
- 平仓重建反向(reverse carry)
- 平仓不重建(market regime 不明)
- 快速行动:carry 反转往往伴随 vol spike,犹豫一周可能损失年化收益的 20%
- 设规则:F < 0 持续 24h 自动平仓(避免人为犹豫)
明日预告
Day 98: 基差交易 — 期现基差、Roll 策略、季度合约 vs 永续。今天的 funding 是永续,明天的 basis 是 dated futures,两者结合是完整的衍生品 arb。