halo 的技术博客

返回

绝大多数交易策略都在回答同一个问题:它会涨还是跌? 均线金叉赌涨,动量因子赌强者恒强,价值因子赌均值回归。但期权打开了一个几乎所有股票投资者从没认真用过的维度——你可以赌它不动

结论先放这:蝶式(butterfly)和铁鹰(iron condor)是两种”押注窄幅震荡”的期权价差结构。当标的在一个区间内磨蹭时它们盈利,一旦剧烈突破就亏损——本质上,你做的不是方向交易,而是波动率交易。 更具体地说,卖出铁鹰是在做空波动率,赚的是”隐含波动率(IV)系统性高于最终已实现波动率(RV)“这笔风险溢价。在自洽合成数据上,持续卖铁鹰策略拿到 87.2% 的胜率、年化 9.6%、Sharpe 1.15、最大回撤 −7.7%——数字很漂亮,但净值曲线暴露了它的致命结构:平静期一分一分地捡钱,崩盘区一把把地还回去。 附完整 Python 与五类真实陷阱(中阶)。

一、方向之外的第三个维度:波动率#

先建立直觉。假设某只股票现价 100,你不知道它会涨还是跌,但你相信未来一个月它不会剧烈波动——大概率在 95~105 之间磨。

传统方向策略在这里无从下手:你不敢买(怕跌)也不敢卖空(怕涨)。但期权允许你直接把”它会待在区间内”这个判断变成头寸。这类头寸有两个经典形态:

  • 蝶式价差(butterfly):买一张低行权价看涨 + 卖两张平值看涨 + 买一张高行权价看涨。净支出建仓,标的恰好收在中间行权价时利润最大,越偏离越亏(亏损有限)。
  • 铁鹰价差(iron condor):卖一个虚值看跌价差 + 卖一个虚值看涨价差。净收权利金建仓,标的收在中间宽区间内就全赚权利金,突破任一侧则亏损(亏损有限)。

它们的共同点:都在标的”不动”时赚钱,“剧烈动”时亏钱。 这就是波动率交易的核心——你交易的标的不是价格,而是价格的波动幅度。

蝶式 vs 铁鹰:两种「押注不动」的到期损益结构

上图把两者叠在一起看结构差异:

  • 蓝线(蝶式):像一座尖塔,标的正好落在 100 时利润最高,两侧快速衰减。它是”精准押注不动”,需要标的钉在某个点附近;
  • 橙线(铁鹰):像一张平顶桌子,标的落在 95~105 这个宽区间内利润恒定(全收权利金),突破后才开始亏。它是”宽容押注不动”,只要不暴涨暴跌都能赚。

铁鹰的平顶比蝶式的尖塔宽容得多,这也是它在实盘中更流行的原因——你不需要精准预测标的钉在哪,只需要它别乱跑。

二、卖方 alpha 的来源:波动率风险溢价#

为什么”持续卖铁鹰”能长期赚钱?答案是一个被反复验证的市场结构性现象——波动率风险溢价(Volatility Risk Premium, VRP)

期权的隐含波动率(IV,即期权价格反推出的市场预期波动)系统性地高于最终真正发生的已实现波动率(RV)。

换句话说,市场为”波动保险”付的价钱,长期高于保险真正赔付的金额。原因是买保险的人愿意付溢价:机构和散户都害怕崩盘,愿意为下行保护多掏钱,这就推高了期权的隐含波动率。而卖方(卖铁鹰的人)扮演的正是”保险公司”角色,长期收取这笔溢价。

波动率风险溢价:IV 系统性高于 RV,是卖方 alpha 来源

上图里蓝线(IV)绝大多数时候高于灰线(RV),绿色填充区就是卖方赚取溢价的空间。这不是免费的午餐——它是承担尾部风险的对价。 保险公司平时收保费,一旦大灾难来临就要巨额赔付。卖铁鹰也一样:平静期稳收权利金,崩盘期一次亏光几个月的积累。

三、用 Python 复现「持续卖铁鹰」策略#

下面构造一段带波动率 regime 切换的自洽合成路径(两段高波动”崩盘区”),每 21 个交易日卖一个 delta 中性的铁鹰,宽度约 ±1 个隐含 sigma,持有到期。

三个必须讲清楚的实现细节(对应回复 A 段口径):

  • 结算先于开仓:循环里先判断到期结算、再判断开新仓,这是一个容易踩的坑——如果顺序反了,同一根 bar 上新仓会覆盖旧仓的 pos,导致到期结算被跳过,胜率虚高到 100%(我第一版就踩了这个,后文会讲);
  • 宽度随波动率自适应:铁鹰的行权价宽度 width = Si × IV × sqrt(T),即约 1 个隐含 sigma。波动率高的时候自动放宽区间,避免在高波动期建太窄的仓;
  • 信号无 look-ahead:建仓宽度用的是当日iv_series.iloc[i](基于过去 20 日已实现波动率估算),不使用任何未来信息;到期结算用到期日实际价格。

四、回测结果:漂亮的胜率,危险的结构#

指标持续卖铁鹰
交易次数47
胜率87.2%
单笔平均盈亏+0.34
年化收益9.6%
夏普比率1.15
最大回撤−7.7%

胜率 87.2%、Sharpe 1.15,单看数字像圣杯。但胜率高恰恰是这类策略最危险的伪装——它让你误以为策略稳健,直到那 12.8% 的亏损交易里,某一笔把你几个月的利润一次抹平。

卖铁鹰净值:平静期稳收权利金,崩盘区快速回吐

净值曲线把这个结构暴露得淋漓尽致:平静期是一条稳稳上行的斜线(一分一分捡权利金),两个红色崩盘区则是断崖式回吐。 这就是卖波动率策略的经典画像——业内叫它”在压路机前捡钢镚”(picking up nickels in front of a steamroller)。你能捡很久很多钢镚,但压路机一旦碾过来,之前捡的可能一次赔光。

五、已知偏差(B 段:这份回测哪里不能全信)#

  1. VRP 的幅度是人为设定的。代码里 iv_series = RV × 1.15 + 0.03,即隐含波动率被强制设为已实现波动率的 1.15 倍再加个基数。真实市场的 VRP 是时变的、有时甚至为负(IV < RV,尤其在波动率骤升的当口)。本回测的盈利能力直接依赖这个 1.15 的假设,现实中溢价更薄、更不稳定。

  2. 尾部风险被合成路径低估。本例注入的两次”崩盘”是温和的波动率抬升。真实的黑天鹅(如 2020 年 3 月、2018 年 2 月 volmageddon)会出现单日跳空 + 隐含波动率爆炸的双杀,卖方可能在保护腿之外承受远超预期的损失。有限亏损结构(铁鹰有买入的远腿保护)在跳空穿透两条腿时,最大亏损会一次性打满,而不是回测里的温和回吐。

  3. 恒定持有到期、无中途管理。实盘中卖方极少持有到期——通常在赚到 50% 权利金时提前平仓(降低尾部暴露),或在标的逼近某条腿时止损/调整。本回测是”开仓即躺平到到期”的最朴素版本,既没吃到主动管理的好处,也没建模调整的成本。

六、结果解读与三个必须警惕的陷阱(C 段)#

收益归因:87.2% 的胜率意味着 47 笔里约 41 笔小赚、6 笔亏损。盈利是”高频小额”,亏损是”低频大额”——这是典型的负偏度收益分布(negative skew)。它与动量策略(正偏度:多数小亏、少数大赚)恰好相反。负偏度策略的危险在于:Sharpe 会系统性高估它的真实风险,因为 Sharpe 用标准差衡量波动,而负偏度的尾部风险藏在标准差之外。看这类策略不能只看 Sharpe,必须看最大单笔亏损和崩盘期回撤。

崩盘期表现:净值图上两个红色区就是策略的”照妖镜”。平静期它每 21 天稳稳赚一笔,累积成漂亮的斜线;但两次波动率抬升区,几笔连续亏损直接吃掉前面的积累。这印证了 VRP 的本质——它不是 alpha,而是承担崩盘风险的对价(beta of disaster)。 长期看正期望,但路径极度不友好。

三个我怀疑过并排除/修正的点(对抗式自检)

  • ⚠️ 胜率 100% 的 bug(已修复):第一版代码把”开新仓”写在”到期结算”之前,导致同一根 bar 上 pos 被新仓覆盖,旧仓的亏损结算被整段跳过——结果只记录了 1 笔交易、胜率 100%、Sharpe 2.67。这是典型的状态机重置顺序错误。修正为”先结算、后开仓”后,交易数恢复到 47 笔、胜率降到合理的 87.2%。任何胜率异常接近 100% 的回测都该第一时间怀疑漏结算。
  • 隐含 vs 已实现口径混淆(已排除):我确认建仓定价用的是隐含波动率 iv_series(卖方收到的权利金由 IV 决定),而到期盈亏由实际路径(RV 隐含在真实价格里)决定。这个口径差正是 VRP 的兑现通道——如果两处都用同一个 vol,策略的 alpha 会凭空消失。
  • 有限亏损是否真的”有限”(保留警告):铁鹰买了远腿做保护,理论最大亏损 = 价差宽度 − 收到的权利金。但这个”有限”是每笔单独有限,不代表组合安全——如果一次崩盘同时击穿多个滚动周期的头寸,或标的跳空穿透整个价差,累计亏损依然可能很大。有限亏损 ≠ 低风险。

适用边界:卖铁鹰/蝶式适合三类场景——① 你判断标的进入低波动震荡期(财报后、重大事件消化完);② 你有严格的尾部风险管理纪律(提前止盈、跳空止损、仓位分散);③ 你能承受”长期小赚、偶尔巨亏”的心理曲线。它绝不适合满仓 all-in、无止损纪律的散户——负偏度策略最擅长的就是在你放松警惕、加大仓位时,给你致命一击。


一句话总结:蝶式和铁鹰是”押注市场不动”的波动率交易,卖方赚的是隐含波动率长期高于已实现波动率的风险溢价。本例 87.2% 胜率、Sharpe 1.15 的漂亮数字背后,是”平静期捡钢镚、崩盘期被压路机碾过”的负偏度结构——高胜率不等于低风险,尾部管理才是这门生意的生死线。

蝶式与铁鹰价差:把「押注市场不动」做成可交易的波动率生意
https://blog.halo26812.eu.org/blog/butterfly-condor-vol-trading
Author halo
Published at 2026年7月21日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨