halo 的技术博客

返回

做曲线交易的人最容易犯的错,不是方向判断错,而是根本没在交易曲线

你说”我看多陡峭化,所以买两年、卖十年”,仓位一挂上去,账户每天的波动跟着 2s10s 利差走吗?我用六年模拟曲线做了实测:如果按最朴素的等张数 1:1 建仓,这个所谓的”价差组合”每日盈亏有 74.8% 被利率水平因子解释,与利差变动的相关性只有 0.46。它不是曲线交易,是一个伪装成价差的、方向不明的裸头寸。

结论先放这:曲线交易的核心工程量不在信号,在对冲比例。 把两条腿的 DV01 配平(本文实测 2.18 张两年合约对一张十年合约)之后,组合盈亏被水平因子解释的比例降到 0.0000、与利差变动的相关性升到 0.9991——这才叫”我只赌斜率”。所有数字均由下文 Python 真实计算,非占位图。

六年模拟国债收益率曲线与 2s10s 利差:水平因子近乎随机游走,斜率因子在 6~32bp 区间来回摆动

一、为什么必须做久期中性:95.3% 的方差不是你想赌的东西#

收益率曲线的日变动,几十年来的实证结论高度一致:能用三个主成分解释绝大部分——水平(level)、斜率(slope)、曲度(curvature)。我用 Nelson-Siegel 载荷合成了一条含 6 个关键期限(3M/1Y/2Y/5Y/10Y/30Y)的曲线,其中水平因子设为近乎随机游走(不可预测的那一维),斜率因子设为显著均值回复(策略要吃的那一维)。

对日变动矩阵做 PCA,结果毫不含糊:

收益率日变动主成分:PC1 水平占 95.3%,PC2 斜率仅 2.0%;载荷形态上 PC1 全期限同号、PC2 长短端反号

主成分方差解释载荷形态交易含义
PC1 水平95.3%全期限同号(0.401→0.417)整条曲线齐涨齐跌,方向性风险
PC2 斜率2.0%短端 -0.418 / 长端 +0.754陡峭化/平坦化,曲线交易的标的
PC3 曲度0.9%两端同号、腰部反号蝶式交易的标的

这张表就是全文的地基。你想赌的斜率只占 2.0% 的方差,而你稍不注意就会扛上 95.3% 的水平风险。 信噪比是 1:48。任何对冲上的疏忽,都不是”精度损失”,而是”标的物被换掉”。

二、DV01:把「张数」翻译成「风险」#

期货合约的张数是会计单位,不是风险单位。真正的风险单位是 DV01(收益率变动 1 个基点带来的价格变动金额)。

def bond_price(y, T, cpn, freq=2, face=100.0):
    """半年付息债券的现值"""
    n = int(round(T * freq))
    c = face * cpn / freq
    t = np.arange(1, n + 1) / freq
    df = (1 + y / freq) ** (-freq * t)
    return float(c * df.sum() + face * df[-1])

def dv01(y, T, cpn, freq=2, face=100.0, bp=1e-4):
    """中心差分法:更稳,避免解析久期在高凸性时的偏差"""
    return (bond_price(y - bp / 2, T, cpn, freq, face)
            - bond_price(y + bp / 2, T, cpn, freq, face))
python

按中金所口径,2 年期国债期货(TS)合约面额 200 万元,10 年期(T)为 100 万元。换算成每张合约的 DV01:

CTR_SIZE = {"TS": 2_000_000.0, "T": 1_000_000.0}
DV2_C  = dv2  / 100.0 * CTR_SIZE["TS"]   # 元/bp
DV10_C = dv10 / 100.0 * CTR_SIZE["T"]
hedge_ratio = DV10_C / DV2_C             # 每卖一张 T,要买几张 TS
python

实测结果:

  • 2 年腿修正久期 1.94,每张 DV01 = 384.8 元/bp
  • 10 年腿修正久期 8.71,每张 DV01 = 838.3 元/bp
  • 对冲比例 = 2.18(每一张 10 年合约对应 2.18 张 2 年合约)

注意这个数字不是 8.71/1.94 = 4.49。合约面额的差异(200 万 vs 100 万)刚好抵消掉一半久期差。混淆”久期比”和”合约 DV01 比”是这个领域最高频的低级错误,一步错就是 2 倍的敞口偏差。

三、三种对冲口径的对照实验#

固定信号不变,只换对冲比例,看组合到底在暴露什么。定义陡峭化仓位为:买 h 张 2 年 + 卖 1 张 10 年

def leg_pnl(h):
    return h * pnl2_ctr - 1.0 * pnl10_ctr

h_naive = 1.0                      # 等张数
h_dv01  = DV10_C / DV2_C           # DV01 中性 ≈ 2.18
h_beta  = h_dv01 / b_yield         # 收益率 beta 调整
python

第三种口径需要解释一下。DV01 中性隐含了一个假设:长短端收益率等幅移动(Δy₂ = Δy₁₀)。现实中不成立——短端受政策利率锚定,波动通常小于长端。对 Δy₂ 回归 Δy₁₀ 得到 b = 0.9244,意思是长端动 1bp,短端只跟 0.92bp。要真正把水平风险清零,2 年腿还得再放大 1/0.9244 倍。

三种对冲口径的水平因子暴露对照:等张数水平 beta=454 元/bp、R²=0.748;DV01 中性 beta≈0、R²=0.000;beta 调整反而过度对冲

对冲口径水平 beta(元/bp)水平 R²与利差变动相关性日盈亏标准差
等张数 1:1454.00.74840.46352,274.6 元
DV01 中性 (2.18)-0.010.00000.99911,210.3 元
beta 调整 (÷0.92)-68.70.05480.95971,273.8 元

三个读点:

  1. 等张数是灾难。 74.8% 的盈亏来自水平因子,与它名义上要交易的利差只有 0.46 相关。日波动 2,275 元里,绝大部分是你没打算承担的风险。
  2. DV01 中性近乎完美。 R² = 0.0000,与利差变动相关 0.9991。这就是”纯斜率暴露”的样子。
  3. beta 调整在本样本里过度对冲了。 水平 R² 反而从 0.0000 升到 0.0548。原因是我的模拟里 NS 斜率载荷本身已经隐含了长短端的差异化响应,再叠一层 beta 就矫枉过正。这是一个真实的教训:beta 调整是经验修正,不是理论必然,必须在你自己的数据上验证它是否真的降低了残余暴露,而不是照抄论文。

四、策略回测:均值回复信号 + 三种对冲#

信号很简单,120 日回看的利差 z-score,|z|>1 入场、|z|<0.25 出场,T 日出信号、T+1 日执行

Z = zscore(spread_2s10s, lookback=120)

for t in range(LB, N):
    z, prev = Z[t], pos[t-1]
    if prev == 0:
        pos[t] = 1.0 if z < -1.0 else (-1.0 if z > 1.0 else 0.0)
    elif prev > 0:
        pos[t] = 0.0 if z > -0.25 else prev      # 陡峭化仓位,等利差回归
    else:
        pos[t] = 0.0 if z < 0.25 else prev
python

交易成本按 单边 0.4bp × 组合 DV01 计(买卖价差 + 冲击,对国债期货是偏保守的估计)。

四条净值曲线对照:DV01 中性 Sharpe 0.24/MDD -0.98%,等张数 Sharpe 0.42/MDD -3.63%,裸多十年 Sharpe -0.05/MDD -17.37%

组合总收益年化年化波动Sharpe最大回撤交易次数在场时间
DV01 中性+2.30%0.38%1.57%0.24-0.98%10664.3%
等张数 1:1+7.35%1.22%2.91%0.42-3.63%10664.3%
beta 调整+1.54%0.26%1.64%0.16-1.35%10664.3%
固定对冲比例(不更新)+2.10%0.35%1.61%0.22-1.30%10664.3%
裸多 10 年(对照)-1.87%-0.31%6.08%-0.05-17.37%100%

这里有个必须解释清楚的反直觉结果:等张数版本 Sharpe 0.42,高于 DV01 中性的 0.24。

不解释就交付,等于承认自己不懂在做什么。原因是:等张数组合有 454 元/bp 的水平 beta,而这六年的模拟水平因子恰好走出了对该敞口有利的路径。它多赚的钱来自”赌利率方向赌对了”,不是来自曲线交易。 三个证据:

  • 它的日波动是 DV01 中性的 1.88 倍(2,275 vs 1,210),最大回撤是 3.7 倍(-3.63% vs -0.98%)
  • 它与利差变动的相关性只有 0.46 —— 大部分盈亏跟信号想捕捉的东西无关
  • 换一个随机种子,这个”优势”完全可以变成同等量级的劣势

风险调整后看起来更好的策略,如果暴露的根本不是你声称的风险,那它只是杠杆的幻觉。 这也是为什么做曲线交易必须先看归因表、再看净值表——净值表会撒谎,归因表不会。

五、对冲比例误设:容错区间有多窄#

实盘里对冲比例不可能永远精确。扫描 0.6× ~ 1.4× 的误设幅度:

对冲比例误设敏感性:偏离 20% 就把水平 R² 从 0.00 拉回 0.22~0.28;Sharpe 呈非对称衰减

实际比例 / 中性比例水平 R²策略 Sharpe
80%0.28100.39
90%0.33
100%0.00000.24
120%0.24520.04

关键结论:水平 R² 是一个 V 形陷阱,底部极窄。 只要偏离 20%,你就重新捡回 24%~28% 的水平暴露——四分之一的盈亏又变成了赌方向。

而 Sharpe 那一列的形状值得警惕:它在 80% 处反而更高(0.39 > 0.24)。这不是”欠对冲更好”的证据,而是第四节那个现象的延续——欠对冲重新引入了水平 beta,在这段样本里恰好有利。用 Sharpe 来调对冲比例,等于用运气来校准风控参数。正确的目标函数是残余 R²,不是收益指标。

六、真正的收益来源:carry 与 roll-down#

曲线交易的钱,很大一部分不来自价差波动,来自持有本身。分解成两块:

  • Carry = 票息收益 − 融资成本
  • Roll-down = 曲线形状不变时,剩余期限缩短带来的收益率下滑 × 久期

持有收益分解与对冲比例漂移:陡峭化组合净 carry+roll = -5.8bp/季;DV01 比例在 2.01~2.37 之间漂移 16.3%

Carry(bp/季)Roll-down(bp/季)合计
2 年-5.3+1.6-3.7
10 年-3.3+5.4+2.1

陡峭化组合(多 2 年 / 空 10 年)的净 carry+roll = -5.8bp/季度

这是个重要的负面结论:在正斜率曲线上做陡峭化,你是在逆风持仓。 10 年腿的 roll-down 收益(+5.4bp)显著高于 2 年腿(+1.6bp),因为曲线在长端更陡、下滑空间更大——而你正在做空这条腿。每持有一个季度,就要先亏掉 5.8bp 的”时间成本”,价差回归必须跑赢这个门槛才有意义。

反过来说,平坦化仓位(空 2 年 / 多 10 年)是顺风的,每季度白拿 5.8bp。这解释了为什么真实市场里的曲线交易者对做多陡峭化格外挑剔——它需要更强的信号才划算,而做平坦化只要不亏钱就在赚 carry。

七、成本:0.53bp 就吃光全部 alpha#

前面所有讨论在成本面前都是次要的。

成本与阈值扫描:单边 0.53bp 即盈亏平衡;入场阈值从 0.5 提到 2.0,Sharpe 从 -0.01 升到 0.49、交易次数从 210 降到 46

单边成本(bp × 组合 DV01)Sharpe
0.0(毛)1.01
0.20.63
0.4(基准假设)0.24
0.53(盈亏平衡点)0.00
0.8-0.50

数字很残酷:毛 Sharpe 1.01 是个像样的策略,扣掉 0.4bp 成本只剩 0.24,成本吃掉毛利的 75.8%(72,035 元成本 / 95,031 元毛利)。盈亏平衡成本仅 0.53bp

原因不难理解:这个策略赚的是 20bp 级别的利差波动,而每次进出要付两条腿的买卖价差。分子是 bp 量级,分母也是 bp 量级,成本不是”摩擦”,是同一数量级的对手。

直接的应对是提高入场门槛,用交易频次换单笔质量:

入场 z 阈值Sharpe交易次数在场时间
0.50-0.0121076.8%
1.000.2410664.3%
1.500.366254.5%
2.000.494646.6%
2.500.482832.8%

Sharpe 在 z=2.0 附近见顶(0.49)后走平。这不是”参数优化找到最优值”,而是成本结构逼出来的必然形状——低阈值区间的信号,期望收益压根覆盖不了往返成本。任何高频版本的曲线套利,在零售级成本下都是负期望。

八、被忽视的工程细节:对冲比例自己在动#

最后一个坑。上面那张图的右半边显示,DV01 对冲比例在六年里从 2.011 漂到 2.365,摆幅 16.3%

来源有两个:

  1. 久期衰减:合约到期日临近,CTD 券剩余期限缩短,DV01 下降
  2. 收益率水平变化:DV01 本身是收益率的函数(凸性效应)

真实市场还有第三个、也是最凶的一个:CTD 券切换。收益率穿越 3% 转换因子临界点时,最便宜可交割券会跳变,DV01 可能一夜之间跳 5%~10%。本文的模拟没有建模这个机制,是明确的简化。

实测”期初算一次、之后不再更新”的固定比例版本:水平 beta -27.25、R² 0.0092、Sharpe 0.22。比逐日更新(R² 0.0000)差,但没有想象中那么灾难。原因是 16.3% 的漂移低于第五节测出的 20% 危险阈值。结论是:周频再校准足够,日频再校准的边际收益被交易成本吃掉;但”建仓时算一次然后忘记”在 CTD 切换时会翻车。

三盆冷水#

第一,模拟曲线的均值回复是我设定的,真实市场不欠你这个。 本文的斜率因子用 OU 过程生成、κ=0.011 明确均值回复,策略当然能赚钱。真实的 2s10s 在趋势性货币政策周期里可以单边走一两年——2021-2023 年美债曲线从 +130bp 一路倒挂到 -100bp,任何 z-score 均值回复策略在这段里都是持续加仓、持续亏损。曲线交易的最大风险不是对冲失效,是”均值”本身漂移了。

第二,DV01 中性只中性掉了水平因子,没管曲度。 本文的 PC3 曲度占 0.9% 方差,看着可以忽略,但 2s10s 组合对曲度的暴露并不为零——蝶式行情(腰部相对两端异动)会同时冲击两条腿,且不被斜率信号预警。要真正做到”只赌斜率”,需要三腿组合(2s5s10s 蝶式)做 PC1+PC3 双中性,代价是多一条腿的成本,在 0.53bp 的盈亏平衡线面前基本判死刑。

第三,本文完全没建模保证金与展期。 国债期货是保证金交易,两条腿各占保证金,反向波动时可能触发追保——而追保发生的时点恰恰是价差走扩、你最不该减仓的时候。此外季月合约到期需要展期,每次展期要付一次往返成本,且新旧合约的 CTD 可能不同、DV01 直接跳变。加上这两项,第七节那个 0.53bp 的盈亏平衡线只会更紧。

落地清单#

如果你要真的做这件事,按这个顺序检查:

  1. 先算残余 R²,再看收益。 组合日盈亏对水平因子回归,R² > 0.05 就说明对冲有问题,收益数字不用看了
  2. 对冲比例用合约 DV01 比,不是久期比。 记住面额差异会抵消一部分久期差
  3. beta 调整必须在自己的数据上验证。 它可能降低残余暴露,也可能像本文一样过度对冲
  4. 成本先算盈亏平衡点,再定阈值。 本文 0.53bp 的门槛意味着任何日频以上的版本都不要碰
  5. 算清 carry+roll 的方向。 陡峭化在正斜率曲线上是逆风持仓,需要更强信号
  6. 对冲比例至少周频再校准,CTD 切换窗口特别盯。

曲线交易在教科书里是”低风险套利”,在实盘里是”用极窄的容错区间,去捞一个被成本啃掉四分之三的小 alpha”。它值得做,但前提是你知道自己在跟什么较劲。

久期中性国债期货套利:跨期限曲线交易实战
https://blog.halo26812.eu.org/blog/duration-neutral-bond-futures
Author halo
Published at 2026年7月30日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨