返回交易笔记
TR Day 41

期权组合的希腊字母管理 — Net Delta / Net Theta 监控

为什么单一持仓 Greek 没意义、组合层 Greek 加总规则、健康 Wheel 的 Greek profile

2026-06-19
Phase 2: 策略实战 + AI 信号
PortfolioGreeksNetDeltaNetThetaNetGammaNetVegaRiskDashboardMonitoring

日期: 2026-06-19 方向: Phase 2 / Greeks 组合管理 阶段: Phase 2: 策略实战 + AI 信号 标签: #PortfolioGreeks #NetDelta #NetTheta #NetGamma #NetVega #RiskDashboard #Monitoring


今日目标

类型内容
学习为什么单一持仓 Greek 没意义、组合层 Greek 加总规则、健康 Wheel 的 Greek profile
实操ib_insync 拉持仓 + 调 IBKR 自带 Greeks + 汇总到 portfolio level + 写每日健康度报告
产出可运行的 Greeks dashboard 脚本 + 告警规则集 + Day 42 paper trade 前的 risk 仪表盘

前置:Day 1-30 已搞定 IBKR/数据/回测/Wheel 框架;Day 31-40 跑通了 CSP/CC 单笔,但都是单一标的看单笔 Greek。Day 41 解决的问题是:手上同时有 4-6 个 position(CSP × 2 + CC × 2 + 现货 × 2)时,整体风险到底长什么样


一、为什么单看「持仓清单」不够

1.1 一个真实场景

假设我 paper 账户里现在有这 5 个 position:

1. Long  100 股 NVDA @ $130
2. Short 1 张 NVDA $140 Call, 7 天到期
3. Short 1 张 AAPL $190 Put, 14 天到期
4. Short 1 张 SPY  $580 Put, 21 天到期
5. Long  100 股 SOFI @ $9

如果只看持仓清单,我会得出「分散得不错、5 个 position 跨 4 个标的」的结论。但这是 PM 看 Jira backlog 的视角,不是 risk 视角。

真正的问题是:

  • 明天 SPY 跌 2%,我账户 P&L 大概会变多少?
  • 现在的 Theta 收益是正是负?每天到底进账还是出账?
  • 如果 NVDA 财报跳空 +10%,我那张 short call 会爆吗?
  • VIX 从 14 飙到 25,我整体净值变多少?

这些问题用「持仓清单」答不出来,必须用组合 Greeks 答。

1.2 Greeks 是 risk exposure 的「线性近似」

数学上,组合 P&L 的一阶/二阶展开:

ΔP ≈ Σ Δ_i × ΔS_i           (Delta 项, 标的价变化)
   + ½ Σ Γ_i × (ΔS_i)²       (Gamma 项, 二阶)
   + Σ Θ_i × Δt              (Theta 项, 时间)
   + Σ V_i × Δσ_i            (Vega 项, vol 变化)

这就是为什么 Greeks 等价于「risk dashboard」:

  • 想知道方向风险 → 看 Net Delta
  • 想知道时间收益 → 看 Net Theta
  • 想知道大波动风险 → 看 Net Gamma
  • 想知道vol 风险 → 看 Net Vega

PM 类比:你不会因为「Jira 里有 50 个 ticket」就觉得团队在做对的事;你会去看「这 50 个里有几个对 OKR 有贡献、几个是消耗 capacity 的技术债」。Greeks 就是 risk 维度的 OKR 分解。

1.3 单一持仓 Greek 误导的两个例子

例 1:「我 short 了一张 SPY $580 put,看起来很安全」

  • 单看 position: Delta ≈ +0.25, 暴露 +$25/SPY 涨 $1
  • 但加上现货 100 股 SPY (Delta=+100)、加上 short call (Delta=-30)、加上其他 4 个 position…
  • 组合 Net Delta 可能是 +320,等于隐性持有 $185k 名义市场暴露
  • 一个 paper $5k 的账户不应该有 $185k 暴露

例 2:「每张 short option 都在收 Theta,我天天进账」

  • 单看每张:Theta = +$8 to +$15
  • 但你同时也是 long 现货 100 股,现货没有 Theta
  • 买过一张 protective put(Theta = -$5)
  • 组合 Net Theta 可能只剩 +$12,扣掉数据费 $16/月日均 $0.5、滑点折损 $2/张 → 净 Theta 才 +$8/天,远低于你「以为」的 +$40。

核心:组合视角下,收益和风险都会互相抵消或叠加,不能简单加总单个数字得出直觉。


二、组合 Greeks 加总规则

2.1 基本公式

每个 Greek 在组合层面都是线性可加的(这是 Greeks 设计的好处之一):

Net_Greek = Σ (position_quantity × per_contract_Greek × multiplier)
资产类型Delta per unitMultiplier备注
股票现货 1 股1.01Gamma/Theta/Vega = 0
期权 long 1 张0 ~ ±1100 (美股期权)Greeks 从 chain 取
期权 short 1 张反号100关键:要乘 -1
期货 1 张1.0看品种我们 Phase 2 暂不用

2.2 Short 是 Greek 加总最容易翻车的地方

# 错误写法
net_delta = sum(p.contract_delta * p.position for p in positions)

# 正确写法(已经把 short 表达为负 position 数量)
net_delta = sum(p.delta_per_contract * p.position * p.multiplier
                for p in positions)

IBKR 的 Position.position 字段对 short 是负数。如果你 short 1 张 call,position = -1。这是好事——你只要老老实实把 delta × position × multiplier 加起来即可,符号自动正确。

新手最常犯的错是从期权 chain 取 Greek 后,自己又对 short 取了一次反号,等于两次反号 = 没反号,整个组合 Delta 算反。

2.3 一个手算例子

仍然用 1.1 的 5 个 position(假设取数时 Greeks 如下):

PositionqtyΔ per unitmultΔ contributionΘ per unitΘ contribution
Long NVDA 现货+1001.01+10000
Short NVDA $140 C-10.35100-1 × 0.35 × 100 = -35-0.18-1 × -0.18 × 100 = +18
Short AAPL $190 P-1-0.28100-1 × -0.28 × 100 = +28-0.12-1 × -0.12 × 100 = +12
Short SPY $580 P-1-0.22100-1 × -0.22 × 100 = +22-0.09-1 × -0.09 × 100 = +9
Long SOFI 现货+1001.01+10000
合计Net Δ = +215Net Θ = +$39/day

读数:

  • Net Delta +215:相当于持有 $215 × 某基准价 ≈ $130 × 215 ≈ $28k 的市场暴露(用加权平均标的价做粗估)
  • Net Theta +$39/天:合理(占组合 ~0.8%/天,年化 ~290% — 但这是上限,会被实际 IV crush / 早期 assignment 侵蚀)

三、健康 Wheel 组合的 Greeks profile(<$5k 账户)

这一段是经验值,不是教科书答案。基于 Day 21-30 的 Wheel 框架,目标 profile

Greek目标区间解读
Net Delta+50 to +200slightly long,跟随市场,但不 over-leverage
Net Theta+$10 to +$50/day卖方策略的核心收益来源,<$5k 账户合理日收
Net Gamma-10 to -50short option 自然 short gamma,控制在小幅
Net Vega-50 to -200short option short vega,IV 跌时受益

每个区间的「为什么」:

3.1 Net Delta 目标 +50 to +200

  • +50 lower bound:低于 50 等于 delta neutral,但 Wheel 本质是「轻微看多 + 收 Theta」,完全 neutral 会失去 underlying 的 beta 收益
  • +200 upper bound:对应 ~$15-30k 名义市场暴露(看标的),对 $5k 账户已经是 4-6x leverage,再高就是赌方向不是 wheel
  • 关键不是绝对值而是比率:Net Delta / 净资产 = 4-6x 上限(这条规则替代「不超过 +200」,因为账户成长后绝对数会变)

3.2 Net Theta 目标 +$10 to +$50/day

  • $5k 账户日收 $10 = 年化 ~73%(不复利),听起来高但 Wheel 本来就靠这吃饭
  • $50/day = 年化 ~365%,完全不现实,意味着你 short 的 option 太多或太靠近 ATM,gamma 风险爆表
  • 重要:Theta 是「mark」不是「实现」。每天 mark to market 的 +$25 不等于真的有 $25 落袋——要等到 expiry 或 close 才确定。IV 突涨一晚可以把整月 Theta 吐光。

3.3 Net Gamma 目标 -10 to -50

  • short option 必然 short gamma — 这是 wheel 收 theta 的对价
  • gamma 越负,标的大幅波动时 Delta 变化越快,P&L 二阶项越凶
  • $5k 账户 Net Gamma -50 意味着:标的涨/跌 $1,Delta 会瞬间变化 50(即 +50 long 立刻变 0 或 +100 long),这已经很疯狂
  • gamma 风险在临近到期 + 接近 strike 时最大(被称为「gamma trap」),所以 wheel 一般 30-45 DTE 开仓、20 DTE 内 roll

3.4 Net Vega 目标 -50 to -200

  • short option short vega → 你期待 IV 跌
  • 这意味着你不应该在 VIX 低位(如 VIX < 13)开新仓 — IV 已经低无可低,只能涨
  • 应该在 VIX 高位开仓(VIX > 20),收完 vol 溢价等回归
  • Vega 太负(< -300)= vol shock 致命,VIX 从 15 → 30 你账户会被砍掉一大块

四、不健康的 signal

Signal含义紧急程度
Net Delta > 500过度 long,组合没分散P1
Net Delta < -100偷偷做空了(wheel 不应该如此)P0 — 立即审查
Net Theta < 0你在付时间费,不是收P1 — 角色搞反了
Net Gamma < -100大波动会暴击P0 — 减仓
Net Vega < -300VIX 任何抬头都会大亏P1
单标的 Net Delta > 60集中度过高,没分散P2
单标的 Theta > 60% 组合 Theta收益全压一个标的P2

PM 视角:这些 threshold 不是抄教科书的,是基于 <$5k 账户的「能承受最大单日 drawdown」反推出来的。账户大了 threshold 会等比放大。


五、代码实现:Portfolio Greeks Dashboard

5.1 整体架构

┌──────────────────────────────────────────────────────────┐
│  ib_insync 连接 (Paper 7497)                              │
└──────────────┬───────────────────────────────────────────┘
               │
       ┌───────▼────────┐    ┌───────────────────┐
       │ reqPositions() │───▶│ 持仓清单          │
       └────────────────┘    └───────┬───────────┘
                                     │
                          ┌──────────▼──────────┐
                          │ reqMktData (per     │
                          │ contract, genTick   │
                          │ '101,106')          │ ← 拉 IV + Greeks
                          └──────────┬──────────┘
                                     │
                          ┌──────────▼──────────┐
                          │ 组合层加总          │
                          └──────────┬──────────┘
                                     │
                ┌────────────────────┼───────────────┐
                ▼                    ▼               ▼
         健康度评估            告警规则触发      历史记录写入
         (table 输出)         (Slack/log)       (csv/sqlite)

5.2 核心脚本

# tr_day41_greeks_dashboard.py
"""
Portfolio Greeks Dashboard
- 拉取所有 position
- 对期权 position 调用 reqMktData 拿 modelGreeks
- 汇总到 portfolio level
- 输出健康度报告 + 触发告警
"""
from ib_insync import IB, Stock, Option, util
from dataclasses import dataclass, field
from typing import List, Optional
import pandas as pd
import datetime as dt

util.startLoop()

@dataclass
class PositionGreeks:
    symbol: str
    sec_type: str        # 'STK' or 'OPT'
    right: Optional[str] # 'C' / 'P' / None for stock
    strike: Optional[float]
    expiry: Optional[str]
    position: float      # 持仓数量(short 为负)
    multiplier: int      # 1 for stock, 100 for option
    underlying_price: float
    delta: float = 0.0
    gamma: float = 0.0
    theta: float = 0.0
    vega: float = 0.0
    iv: float = 0.0

    @property
    def delta_contribution(self):
        return self.position * self.delta * self.multiplier

    @property
    def gamma_contribution(self):
        return self.position * self.gamma * self.multiplier

    @property
    def theta_contribution(self):
        # IBKR 的 modelGreeks.theta 已经是「每天」的 dollar 值除以 100
        # 乘 multiplier 后是「每张合约每天 dollar」
        return self.position * self.theta * self.multiplier

    @property
    def vega_contribution(self):
        return self.position * self.vega * self.multiplier


def fetch_position_greeks(ib: IB) -> List[PositionGreeks]:
    rows = []
    for pos in ib.positions():
        c = pos.contract
        if c.secType == 'STK':
            # 现货:Delta=1, 其他 Greek=0
            tk = ib.reqMktData(c, '', snapshot=True)
            ib.sleep(1.5)
            spot = tk.last or tk.close or tk.marketPrice()
            rows.append(PositionGreeks(
                symbol=c.symbol, sec_type='STK', right=None,
                strike=None, expiry=None,
                position=pos.position, multiplier=1,
                underlying_price=spot,
                delta=1.0, gamma=0.0, theta=0.0, vega=0.0, iv=0.0,
            ))
            ib.cancelMktData(c)
        elif c.secType == 'OPT':
            # 期权:拉 modelGreeks
            # genericTickList='106' 会推送 modelGreeks
            tk = ib.reqMktData(c, '106', snapshot=False, regulatorySnapshot=False)
            ib.sleep(2.5)  # 留足时间让 Greeks 推送

            g = tk.modelGreeks  # 可能是 None,要保护
            if g is None or g.delta is None:
                print(f"[WARN] no Greeks for {c.localSymbol}")
                ib.cancelMktData(c)
                continue

            underlying = g.undPrice or 0
            rows.append(PositionGreeks(
                symbol=c.symbol, sec_type='OPT',
                right=c.right, strike=c.strike, expiry=c.lastTradeDateOrContractMonth,
                position=pos.position,
                multiplier=int(c.multiplier or 100),
                underlying_price=underlying,
                delta=g.delta, gamma=g.gamma, theta=g.theta, vega=g.vega,
                iv=g.impliedVol or 0,
            ))
            ib.cancelMktData(c)
    return rows


def portfolio_summary(rows: List[PositionGreeks]) -> dict:
    net_delta = sum(r.delta_contribution for r in rows)
    net_gamma = sum(r.gamma_contribution for r in rows)
    net_theta = sum(r.theta_contribution for r in rows)
    net_vega  = sum(r.vega_contribution  for r in rows)

    by_symbol = {}
    for r in rows:
        d = by_symbol.setdefault(r.symbol,
            {'delta': 0, 'gamma': 0, 'theta': 0, 'vega': 0})
        d['delta'] += r.delta_contribution
        d['gamma'] += r.gamma_contribution
        d['theta'] += r.theta_contribution
        d['vega']  += r.vega_contribution

    return {
        'net_delta': net_delta,
        'net_gamma': net_gamma,
        'net_theta': net_theta,
        'net_vega':  net_vega,
        'by_symbol': by_symbol,
        'n_positions': len(rows),
        'timestamp': dt.datetime.now().isoformat(timespec='seconds'),
    }


# ---------------- 告警规则 ----------------
def evaluate_alerts(summary: dict, account_nlv: float, vix: float) -> List[str]:
    alerts = []
    nd, ng, nt, nv = (summary['net_delta'], summary['net_gamma'],
                      summary['net_theta'], summary['net_vega'])

    # P0 级
    if ng < -100:
        alerts.append(f"[P0] Net Gamma {ng:.0f} < -100, 大波动会暴击")
    if nd < -100:
        alerts.append(f"[P0] Net Delta {nd:.0f} < -100, 组合实际在做空")

    # P1 级
    if nd > 500:
        alerts.append(f"[P1] Net Delta {nd:.0f} > 500, 过度 long")
    if nt < 0:
        alerts.append(f"[P1] Net Theta {nt:.2f} < 0, 你在付时间费")
    if vix > 30 and nv < -200:
        alerts.append(f"[P1] VIX={vix:.1f} 高位 + Net Vega {nv:.0f} < -200")

    # P2 级 — 单标的集中度
    for sym, g in summary['by_symbol'].items():
        if abs(g['delta']) > 60:
            alerts.append(f"[P2] {sym} 单标的 Delta {g['delta']:+.0f}, 超过 ±60")

    # 比率检查
    delta_to_nlv = abs(nd) * 130 / account_nlv  # 用 $130 做粗略名义/股估算
    if delta_to_nlv > 6:
        alerts.append(
            f"[P1] |Delta|/NLV 名义暴露比 ≈ {delta_to_nlv:.1f}x, 超过 6x"
        )

    return alerts


# ---------------- pretty print ----------------
def pretty_print(summary: dict, alerts: List[str]):
    print(f"\n{'='*60}")
    print(f"  Portfolio Greeks @ {summary['timestamp']}")
    print(f"{'='*60}")
    print(f"  Net Delta : {summary['net_delta']:+8.1f}")
    print(f"  Net Gamma : {summary['net_gamma']:+8.2f}")
    print(f"  Net Theta : ${summary['net_theta']:+7.2f}/day")
    print(f"  Net Vega  : {summary['net_vega']:+8.2f}")
    print(f"  Positions : {summary['n_positions']}")
    print()
    print(f"{'Symbol':<8} {'Δ':>8} {'Γ':>8} {'Θ':>8} {'V':>8}")
    print("-"*48)
    for sym, g in summary['by_symbol'].items():
        print(f"{sym:<8} {g['delta']:>+8.1f} {g['gamma']:>+8.2f} "
              f"{g['theta']:>+8.2f} {g['vega']:>+8.2f}")
    print()
    if alerts:
        print("ALERTS:")
        for a in alerts:
            print(f"  {a}")
    else:
        print("ALERTS: (none)")
    print(f"{'='*60}\n")


# ---------------- main ----------------
if __name__ == '__main__':
    ib = IB()
    ib.connect('127.0.0.1', 7497, clientId=41)
    assert ib.client.port == 7497, "WRONG PORT — PAPER ONLY"

    rows = fetch_position_greeks(ib)
    summary = portfolio_summary(rows)

    # 拉 NLV 和 VIX
    acc = {row.tag: row.value for row in ib.accountSummary()
           if row.tag in ('NetLiquidation',)}
    nlv = float(acc.get('NetLiquidation', 5000))

    vix_c = Stock('VIX', 'CBOE', 'USD')   # 仅当订阅了 CBOE 指数数据
    ib.qualifyContracts(vix_c)
    vix_tk = ib.reqMktData(vix_c, '', snapshot=True)
    ib.sleep(1.5)
    vix = vix_tk.last or vix_tk.close or 16.0

    alerts = evaluate_alerts(summary, nlv, vix)
    pretty_print(summary, alerts)

    # 持久化(每天追加一行,便于画时间序列)
    pd.DataFrame([{
        **{k: v for k, v in summary.items() if k != 'by_symbol'},
        'nlv': nlv, 'vix': vix,
    }]).to_csv('greeks_history.csv', mode='a', header=False, index=False)

    ib.disconnect()

5.3 输出样例

============================================================
  Portfolio Greeks @ 2026-06-19T15:42:11
============================================================
  Net Delta :   +215.3
  Net Gamma :    -22.4
  Net Theta : $ +39.18/day
  Net Vega  :   -118.7
  Positions : 5

Symbol        Δ        Γ        Θ        V
------------------------------------------------
NVDA      +65.0    -10.2    +18.32   -45.1
AAPL      +28.0     -4.8    +12.10   -28.4
SPY       +22.3     -7.4     +8.76   -45.2
SOFI     +100.0      0.0     +0.00     0.0

ALERTS: (none)
============================================================

六、Greek 限制规则(写进策略文档)

这是 Day 41 之后写进 trading playbook 的硬规则

# greek_limits.yaml
single_symbol:
  delta_max: 60         # 单标的 |Net Delta| 上限
  theta_share_max: 0.6  # 单标的 Theta 占比不能超 60%
portfolio:
  delta_max: 300         # 组合 Net Delta 上限 (~$30k 名义)
  delta_min: -50         # 不允许实质做空
  gamma_min: -50         # gamma 下限(绝对值上限)
  theta_min: 0           # 不允许付时间费
  vega_floor_normal: -200  # 常规 vol 环境
  vega_floor_high_vol: -100  # VIX > 25 时收紧
account_ratio:
  delta_to_nlv_max: 6    # |Delta| × avg_spot / NLV 名义杠杆

这套规则的 PM 化解读:

  • delta_max 60 / single symbol = 「不押宝任何单一公司」
  • theta_share_max 60% = 「不能让一个 position 占全部收益」(类似收入集中度风险)
  • delta_min -50 = 「Wheel 是看多策略,做空说明你跑偏了」
  • vega_floor_high_vol -100 = 「市场紧张时降低 vol 暴露」(动态收紧)

七、危险信号自动告警

7.1 告警通道

log file                 → 总有,每天 grep 看一次
print to stdout          → cron 任务捕获
Slack webhook            → P0 / P1 才推
Email                    → P0 才推(避免疲劳)

7.2 三级告警节奏

等级条件通道响应时间
P0Net Gamma < -100、Net Delta < -100、单 positionDelta> 80
P1Net Delta > 500、Net Theta < 0、VIX>30 且 Net Vega<-200Slack + log24h 内
P2单标的 Delta > 60、Theta 占比 > 60%log周复盘

7.3 告警去抖

新手做监控最容易犯的错:同一个 alert 一天触发 50 次。处理方式:

# 简单的 cooldown
last_alert_ts = {}
COOLDOWN_MIN = 60  # 一小时内同一 alert 只推一次

def maybe_send(alert_key, msg):
    now = dt.datetime.now()
    last = last_alert_ts.get(alert_key)
    if last is None or (now - last).total_seconds() / 60 > COOLDOWN_MIN:
        send_slack(msg)
        last_alert_ts[alert_key] = now

PM 视角:这是「监控系统设计」的通用问题——alert fatigue 是真实的,信噪比比 coverage 更重要。宁可漏一个 P2,不可让用户每天看 200 条 P0。


八、可视化 dashboard 设计

8.1 实时表格(ASCII Mock)

┌──────────────────────────────────────────────────────────────────┐
│  Portfolio Greeks Dashboard         2026-06-19 15:42:11 EST     │
├──────────────────────────────────────────────────────────────────┤
│  NLV:  $5,128.42       VIX: 16.2     Buying Power: $3,210       │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ▼ Portfolio Level                                               │
│  ┌─────────────┬───────────┬────────────┬────────────┐           │
│  │ Net Delta   │ +215.3    │ Limit ±300 │ ✅ 71% used│           │
│  │ Net Gamma   │  -22.4    │ Limit -50  │ ✅ 45% used│           │
│  │ Net Theta   │  +$39.18  │ Min $0     │ ✅         │           │
│  │ Net Vega    │ -118.7    │ Limit -200 │ ✅ 59% used│           │
│  └─────────────┴───────────┴────────────┴────────────┘           │
│                                                                  │
│  ▼ Per-Symbol Breakdown                                          │
│  ┌────────┬────────┬────────┬────────┬────────┬─────────┐        │
│  │ Symbol │   Δ    │   Γ    │   Θ    │   V    │ Health  │        │
│  ├────────┼────────┼────────┼────────┼────────┼─────────┤        │
│  │ NVDA   │ +65.0  │ -10.2  │ +18.32 │ -45.1  │ ⚠️ Δ>60│        │
│  │ AAPL   │ +28.0  │  -4.8  │ +12.10 │ -28.4  │ ✅      │        │
│  │ SPY    │ +22.3  │  -7.4  │  +8.76 │ -45.2  │ ✅      │        │
│  │ SOFI   │+100.0  │   0.0  │  +0.00 │   0.0  │ ⚠️ Δ>60│        │
│  └────────┴────────┴────────┴────────┴────────┴─────────┘        │
│                                                                  │
│  ▼ What-If Scenarios                                             │
│  ┌──────────────────────────┬────────────────────────────┐       │
│  │ SPY -2% in one day       │ Est. P&L: -$430 (-8.4%)    │       │
│  │ SPY +2% in one day       │ Est. P&L: +$430 (+8.4%)    │       │
│  │ VIX 16 → 25 (vol shock)  │ Est. P&L: -$1,068 (-20.8%) │       │
│  │ NVDA earnings ±10% jump  │ Est. P&L: -$1,250 (-24.4%) │       │
│  └──────────────────────────┴────────────────────────────┘       │
│                                                                  │
│  ▼ Alerts                                                        │
│  ⚠️ [P2] NVDA single-symbol delta +65 > 60                       │
│  ⚠️ [P2] SOFI single-symbol delta +100 > 60                      │
└──────────────────────────────────────────────────────────────────┘

8.2 历史时间序列

存到 greeks_history.csv,画 4 张子图:

   Net Delta (target band: 50 ~ 200)
   │       ╱╲        ╱╲
   │  ╱╲  ╱  ╲  ╱╲ ╱  ╲
200├─╱──╲╱────╲╱──╲╱────╲────  upper limit
   │
 50├─────────────────────────  lower limit
   └──┬────┬────┬────┬────┬──
     Mon  Tue  Wed  Thu  Fri

   Net Theta ($/day, target >= 10)
50 │
   │       ▓▓▓
   │   ▓▓▓ ▓▓▓ ▓▓▓
25 │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
   │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
10 ├───────────────  floor
   └──┬────┬────┬──

   Net Gamma (alarm < -50)
   ├─────────────────  0
   │ ╲    ╱╲
-20│  ╲╱╲╱  ╲╱╲
   │
-50├─────────────────  alarm

   Net Vega (alarm < -200 normal, < -100 vix>25)
   ├─────────────────  0
   │ ╲╱╲╱╲╱╲╱╲
-100├─────────────────
-200├─────────────────  alarm

8.3 What-If Scenario 模块

这是组合 Greeks dashboard 的杀手锏。原理是用一阶/二阶展开做闭式估算:

def what_if_underlying(rows, shock_pct):
    """估算所有标的同时变动 shock_pct 时的组合 P&L"""
    pnl = 0
    for r in rows:
        dS = r.underlying_price * shock_pct
        delta_pnl = r.delta_contribution * dS / r.underlying_price
        gamma_pnl = 0.5 * r.gamma_contribution * dS**2 / r.underlying_price
        pnl += delta_pnl + gamma_pnl
    return pnl

def what_if_iv(rows, iv_shock_pct):
    """估算 IV 整体抬升 iv_shock_pct 时的组合 P&L"""
    return sum(r.vega_contribution * iv_shock_pct for r in rows)

PM 视角:what-if 是「决策 sandbox」。任何 risk dashboard 没有 what-if 都是死的——用户看到一个数字 Net Vega = -118,不知道这数字意味着什么。给他「VIX +9 点 = 亏 $1068」他立刻有体感。


九、PM 视角:risk exposure 仪表盘 = 业务健康仪表盘

把今天学的迁移到产品/业务监控视角,对应得很整齐:

交易组合 Greeks业务健康仪表盘共通本质
Net Delta(方向暴露)DAU 增长率(业务方向)一阶导数:变化趋势
Net Gamma(变化速度)DAU 增速变化(二阶)二阶导数:拐点提示
Net Theta(时间收益)订阅 ARR/day(自动续费)不操作下的自然收益
Net Vega(vol 敏感度)业务对市场情绪的敏感度外生变量敏感度
单标的集中度单客户 / 单渠道集中度分散度
What-If 场景「如果某大客户流失」模拟决策 sandbox
三级告警业务 P0/P1/P2 incident信噪比管理

核心洞察

  1. 单一指标不构成决策依据。Net Delta 一个数字没意义,必须和 Net Theta / Net Gamma / NLV 一起看;DAU 一个数字也没意义,必须和留存 / ARPU / churn 一起看。
  2. 「业务健康」是一个 vector,不是 scalar。任何把业务降维成「一个北极星指标」的做法都会被现实毒打——Greeks 的整套体系就是承认了这个事实。
  3. 限额(limit)必须 quantitative + automated。Day 41 写的 greek_limits.yaml 就是 risk 版本的 SLA。任何「我感觉 Delta 有点高」的判断都不可重复,必须翻译成 delta_max: 300 这种可被代码 enforce 的数字。
  4. 告警去抖比 coverage 重要。alert fatigue 在 Web2 监控、Web3 链上监控、交易组合监控里都是同一个问题。
  5. 可视化是为了决策不是为了好看。What-If sandbox > 一堆漂亮的 line chart。能让你「按一下知道亏多少」的图比「过去 30 天 Net Delta 走势」更有价值。

这套思维 Day 42 跑第一组 wheel paper trade 的时候立刻就要用——开仓前先用 dashboard 看一遍 projected Greeks,开仓后每天扫一次实际 Greeks 漂移。


十、明日预告

Day 42: 第一组 Wheel paper trade 实战

  • 用今天的 Greek dashboard 做开仓前 sanity check
  • 选 2 个标的(候选:SOFI、PLTR、F)跑 CSP
  • 30-45 DTE,Delta 0.20-0.25,至少一格 OTM
  • 记录每日 Greek 漂移 + IV 漂移
  • 一周后复盘:实际 P&L vs Theta 预测的 vs 回测的,三者差异在哪
  • 跑通整个「开仓 → 监控 → roll / close → 结算」流程

预计踩坑点:

  • IBKR Paper 期权报价偶尔不更新(snapshot 太老)
  • 单笔下单的 limit price 怎么定(mid? mid-2? bid+1?)
  • 早期 assignment 在 paper 上几乎不会发生,但要在脑子里演练真实场景

实际执行记录

启动一项填一项,时间戳 + 卡点。

  • [hh:mm] 在 paper 账户里至少摆 3 个 position(哪怕 1 张 short put + 100 股现货)—— 让 dashboard 有东西可拉
  • [hh:mm] tr_day41_greeks_dashboard.py 跑通,能输出表格
  • [hh:mm] 验证 short option 的 Greek 符号正确(用手算样例对比)
  • [hh:mm] What-If scenario 函数手算验证一次(用 SPY ±2% 对比)
  • [hh:mm] greek_limits.yaml 写好,告警规则函数能触发预期 alert
  • [hh:mm] greeks_history.csv 落第一行
  • 卡点 / 学到的:

今日完成度:理论 ✓ / 实操(你自己跑一遍)/ 笔记 ✓