期权组合的希腊字母管理 — Net Delta / Net Theta 监控
为什么单一持仓 Greek 没意义、组合层 Greek 加总规则、健康 Wheel 的 Greek profile
日期: 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 unit | Multiplier | 备注 |
|---|---|---|---|
| 股票现货 1 股 | 1.0 | 1 | Gamma/Theta/Vega = 0 |
| 期权 long 1 张 | 0 ~ ±1 | 100 (美股期权) | 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 如下):
| Position | qty | Δ per unit | mult | Δ contribution | Θ per unit | Θ contribution |
|---|---|---|---|---|---|---|
| Long NVDA 现货 | +100 | 1.0 | 1 | +100 | 0 | 0 |
| Short NVDA $140 C | -1 | 0.35 | 100 | -1 × 0.35 × 100 = -35 | -0.18 | -1 × -0.18 × 100 = +18 |
| Short AAPL $190 P | -1 | -0.28 | 100 | -1 × -0.28 × 100 = +28 | -0.12 | -1 × -0.12 × 100 = +12 |
| Short SPY $580 P | -1 | -0.22 | 100 | -1 × -0.22 × 100 = +22 | -0.09 | -1 × -0.09 × 100 = +9 |
| Long SOFI 现货 | +100 | 1.0 | 1 | +100 | 0 | 0 |
| 合计 | Net Δ = +215 | Net Θ = +$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 +200 | slightly long,跟随市场,但不 over-leverage |
| Net Theta | +$10 to +$50/day | 卖方策略的核心收益来源,<$5k 账户合理日收 |
| Net Gamma | -10 to -50 | short option 自然 short gamma,控制在小幅 |
| Net Vega | -50 to -200 | short 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 < -300 | VIX 任何抬头都会大亏 | 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 三级告警节奏
| 等级 | 条件 | 通道 | 响应时间 |
|---|---|---|---|
| P0 | Net Gamma < -100、Net Delta < -100、单 position | Delta | > 80 |
| P1 | Net Delta > 500、Net Theta < 0、VIX>30 且 Net Vega<-200 | Slack + log | 24h 内 |
| 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 | 信噪比管理 |
核心洞察:
- 单一指标不构成决策依据。Net Delta 一个数字没意义,必须和 Net Theta / Net Gamma / NLV 一起看;DAU 一个数字也没意义,必须和留存 / ARPU / churn 一起看。
- 「业务健康」是一个 vector,不是 scalar。任何把业务降维成「一个北极星指标」的做法都会被现实毒打——Greeks 的整套体系就是承认了这个事实。
- 限额(limit)必须 quantitative + automated。Day 41 写的
greek_limits.yaml就是 risk 版本的 SLA。任何「我感觉 Delta 有点高」的判断都不可重复,必须翻译成delta_max: 300这种可被代码 enforce 的数字。 - 告警去抖比 coverage 重要。alert fatigue 在 Web2 监控、Web3 链上监控、交易组合监控里都是同一个问题。
- 可视化是为了决策不是为了好看。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落第一行 - 卡点 / 学到的:
今日完成度:理论 ✓ / 实操(你自己跑一遍)/ 笔记 ✓