返回交易笔记
TR Day 43

Roll 操作 — Theta 没收满 / ITM 处理决策树

Roll 的本质、三种 Roll 的取舍、何时 Roll 何时不 Roll、Roll 的数学约束(net credit 法则)、被 assigned 后的处理、CC 端 Roll 与 CSP 端 Roll 的差异、Roll 死循环陷阱

2026-06-21
Phase 2: 策略实战 + AI 信号
RollOutRollDownITMNetCreditWheelAssignmentAvoidance

日期: 2026-06-21 方向: Phase 2 / Roll 阶段: Phase 2: 策略实战 + AI 信号 标签: #RollOut #RollDown #ITM #NetCredit #Wheel #Assignment #Avoidance


今日目标

类型内容
学习Roll 的本质、三种 Roll 的取舍、何时 Roll 何时不 Roll、Roll 的数学约束(net credit 法则)、被 assigned 后的处理、CC 端 Roll 与 CSP 端 Roll 的差异、Roll 死循环陷阱
实操should_roll() 决策函数 + execute_roll() 二段操作函数(先 BTC 再 STO),含 dry-run;用本周 paper trade 的 F / BAC / SOFI 三仓位模拟决策树
产出TR-DAY43 笔记 + wheel/roll.py 模块 + 三个仓位的实际决策记录(Roll vs 让过期 vs 主动 close)

一、Roll 到底是什么:一笔交易还是两笔?

Roll 的中文常译成「展期」「滚动」「续约」,这三个词都对,但没一个把核心点说出来。

Roll = 在同一个标的上,平掉当前 expiry 的 position,开一个更远 expiry 的 position;两笔操作在心理上是一个动作,但在 broker 那里是两笔独立的成交。

拆开看:

原始 position:  Short Put F 06/20 $10  @ $0.18 收取
当前状态:       06/18,F 报价 $9.40,put 已经 ITM $0.60

Roll 操作 = 两笔 trade:
  Step 1: Buy to Close (BTC)  F 06/20 $10 Put  @ $0.65 (付出)
  Step 2: Sell to Open  (STO) F 07/18 $10 Put  @ $0.95 (收取)

Net 现金流: -0.65 + 0.95 = +$0.30 / share = +$30 / contract

注意三点:

  1. strike 可以不变,也可以改——这是 Roll out / Roll down / Roll up 的分类依据
  2. expiry 必须更远(往近期 Roll 在某些 broker 叫 "Roll in",意义不大,几乎不用)
  3. 必须是 net credit 才有意义——这是后面要专门讲的核心法则

很多 broker(包括 IBKR 和 tastytrade)提供「Roll」按钮,会自动把两笔打包成一个 combo order。combo order 有 net debit / net credit 的 mid-price 撮合机制,比手动两笔分别下单成交质量好。但本质还是两笔交易,两份佣金。


二、三种 Roll:Out / Down / Up

2.1 Roll Out(同 strike,远 expiry)

最常见的一种。

原:  Short Put F 06/20 $10  收 $0.18
Roll Out → Short Put F 07/18 $10  收 $0.50

Net credit: +$0.50 - $0.65 (close cost) = -$0.15 ?  ← 注意:当 put ITM 时 close cost 上升

用途:F 价格跌到 strike 附近或刚 ITM,你还相信 F 长期值得拿货,但不想 6/20 就被 assigned。Roll 一个月,给市场更多时间反弹。

Trade-off

  • ✅ 延长时间收 Theta
  • ✅ 远期 IV term structure 通常更高,premium 大
  • ❌ 锁住资金一个月不能用
  • ❌ 如果继续跌,下个月还是会 ITM,进入 Roll 死循环

2.2 Roll Down(低 strike,远 expiry)

Strike 降低,避免被 assigned。

原:  Short Put F 06/20 $10  深 ITM(F=$8.80)
Roll Down → Short Put F 07/18 $9.5  

Net premium 通常较小,因为同时降 strike 又延 expiry,两个动作部分抵消

用途:F 跌得比预期深,$10 接货你不舒服了,想把成本基础往下压一点。Roll Down 等同于「我宁可少收 premium,也不要 $10 接货」。

重要Roll Down 经常是 net debit(你要倒贴钱)。这不一定是坏事——你是在花钱换更好的 strike。但必须算清楚账。

2.3 Roll Up(高 strike)

很少用在 CSP 端,主要用在 Covered Call (CC) 端

场景:你拥有 100 股 F @ $10 成本,卖出 CC F 06/20 $11
      F 涨到 $11.50,CC 已经 ITM,再不动就被 called away

原:  Short Call F 06/20 $11  
Roll Up → Short Call F 07/18 $12

意图:保留股票上行空间,避免在 $11 被 called away

Trade-off

  • ✅ 保留更多上行空间
  • ✅ 如果继续涨,下月还能在 $12 卖 CC
  • ❌ Net credit 通常很小,甚至 net debit
  • ❌ 如果 F 涨到 $12 以上,你又要面对同样问题(涨太快 = CC 端永远在追)

三、何时该 Roll:决策树

我把 Day 1-42 的所有期权基础知识收敛到这一棵树。遇到 ITM 仓位,从顶端走到底,每个分叉问自己一个问题。

                  ┌───────────────────────────────────┐
                  │  当前 position 距离 expiry 多少天?  │
                  └───────────────────┬───────────────┘
                                      │
              ┌───────────────────────┼──────────────────────────┐
              ▼                       ▼                          ▼
        DTE > 21 天              DTE 7-21 天                  DTE < 7 天
              │                       │                          │
        ┌─────┴─────┐         ┌───────┴───────┐          ┌───────┴────────┐
        ▼           ▼         ▼               ▼          ▼                ▼
   profit ≥50%  其他状态   仍深 OTM       ATM 或 ITM    深 OTM          ITM
        │           │         │               │          │                │
        ▼           ▼         ▼               ▼          ▼                ▼
   主动 BTC     hold       让它过期       考虑 Roll    让它过期      Scenario C
   兑现利润     等 Theta    (零费用)         out         (零费用)     (核心决策)
   (50% rule)

Scenario C(DTE < 7 天且 ITM)展开

                Scenario C: DTE<7d 且 ITM
                          │
              ┌───────────┴───────────┐
              ▼                       ▼
   能 Roll 出 net credit       不能 Roll 出 net credit
              │                       │
       ┌──────┴──────┐                ▼
       ▼             ▼          接受 assignment
   还想 hold?      不想了      (走 Stage 2→3:
       │             │          拿货 → 立刻挂 CC)
       ▼             ▼
   Roll Out       接 assignment
   或 Roll Down

几个关键判断点

  1. DTE > 21 + 仍深 OTM + 利润 ≥ 50% premium主动 BTC(这是 tastytrade 著名的「manage winners at 50%」法则)。原因:剩余 50% premium 要用一倍以上的时间才能收完,时间风险/Gamma 风险不值。
  2. DTE 7-21 天 + ITM → 这是 Roll 的黄金窗口。再不动 Theta 加速会变成 Gamma 加速。
  3. DTE < 7 + 深 OTM → 不要瞎动,让它过期。任何 BTC 都是付费给市场(spread + 佣金)。

四、Roll 的数学:Net Credit 法则

第一法则:必须 net credit 才 Roll。Net debit 的 Roll 等同于「为了延后亏损而再付一笔钱」。

公式:

Net Credit = (New Premium Received)
           - (Old Position Close Cost)
           - 2 × Commission
           - 2 × Bid-Ask Spread Cost

if Net Credit > 0:  → Roll
else:               → 接 assignment(或主动 close)

把这条规则展开,注意几个陷阱:

4.1 Close cost 不是当初 STO 的 premium

新手常犯的错误:以为「我当初收了 $0.18,现在 BTC 花 $0.65,亏 $0.47」。这个亏损是 sunk cost,已经发生了,决策只看「现在 Roll 出去能不能 net credit」。

错误思路:「我开仓收 $0.18,新仓收 $0.50,净 $0.50 + $0.18 = $0.68 ✓」 ← 错
正确思路:「Close 要 $0.65,开新仓收 $0.50,net = $0.50 - $0.65 = -$0.15 ✗ 不 Roll」

4.2 ITM 越深,Roll 越难做出 net credit

当 put 深度 ITM(如 F 跌到 $8.5,put strike $10),intrinsic value 是 $1.50,close 至少要花 $1.50+,而你能收到的下月同 strike premium 可能只有 $1.55——extrinsic 只有 $0.05。一旦 spread 和佣金吃进去,net credit 是负的。

经验数据

ITM 深度(占 strike %)同 strike Roll out 一月能否 net credit
0-2%(刚刚 ITM)通常 +$0.20 ~ +$0.50✅ 容易
2-5%通常 +$0.05 ~ +$0.20⚠️ 勉强
5-10%通常 -$0.10 ~ +$0.05❌ 很难
>10%(深度 ITM)通常 net debit❌ 几乎不可能

结论:深度 ITM 时,Roll 已经不是好选择。这种情况就该接 assignment,把它转成 Wheel 的 Stage 2。

4.3 IV 高时 Roll 容易,IV 低时 Roll 难

Roll 的本质是「用时间换 extrinsic premium」。IV 高 → 远期合约 extrinsic 大 → 容易 net credit。 IV 低 → 远期合约 extrinsic 也小 → 难 net credit。

F earnings 前一周(IV ≈ 60%)   → Roll out 一月通常 +$0.40 ~ +$0.80
F earnings 后第二周(IV ≈ 28%) → Roll out 一月可能 +$0.10 ~ +$0.20

这就是为什么 Day 49 起我会讲「财报前 IV crush 套利」——同一个机制反过来用。


五、代码实现:should_roll + execute_roll

5.1 should_roll() 决策函数

把决策树编码成一个函数。返回 (should_roll: bool, reason: str) 便于日志审计。

# wheel/roll.py
from dataclasses import dataclass
from datetime import date
from typing import Literal, Tuple

@dataclass
class ShortPutPosition:
    symbol: str
    strike: float
    expiry: date
    premium_received: float       # 当初 STO 时收的 premium
    current_underlying: float     # 当前股价
    current_option_mid: float     # 当前 put 的 mid price(BTC 成本估计)
    contracts: int = 1
    roll_count: int = 0           # 历史 Roll 次数,用于死循环防护

def dte(p: ShortPutPosition, today: date) -> int:
    return (p.expiry - today).days

def is_itm(p: ShortPutPosition) -> bool:
    return p.current_underlying < p.strike

def profit_pct(p: ShortPutPosition) -> float:
    """已实现利润占初始 premium 的百分比(未平仓估算)"""
    unrealized = p.premium_received - p.current_option_mid
    return unrealized / p.premium_received if p.premium_received > 0 else 0.0

def should_roll(
    p: ShortPutPosition,
    today: date,
    new_expiry_mid: float,        # 候选远月合约 mid price
    commission_per_leg: float = 0.65,
    spread_slippage: float = 0.02,
) -> Tuple[bool, Literal["roll_out", "close_win", "let_expire", "accept_assignment", "hold"], str]:
    """
    返回 (should_act, action, reason)。
    action ∈ {"roll_out", "close_win", "let_expire", "accept_assignment", "hold"}
    """
    days = dte(p, today)

    # --- avoidance pattern: 限制 Roll 死循环 ---
    if p.roll_count >= 2:
        if is_itm(p):
            return True, "accept_assignment", f"already rolled {p.roll_count}x; force accept to break spiral"

    # --- DTE > 21 ---
    if days > 21:
        if profit_pct(p) >= 0.5:
            return True, "close_win", f"profit {profit_pct(p):.0%} ≥ 50% rule"
        return False, "hold", f"DTE={days}, theta still working"

    # --- DTE 7-21 ---
    if 7 <= days <= 21:
        if not is_itm(p):
            return False, "hold", f"OTM, let theta accelerate"
        # ITM, 评估 Roll
        net_credit = (
            new_expiry_mid
            - p.current_option_mid
            - 2 * commission_per_leg / 100   # 转成 per share
            - spread_slippage
        )
        if net_credit > 0:
            return True, "roll_out", f"ITM, net credit ${net_credit:.2f} > 0"
        return False, "hold", f"ITM but Roll net debit ${net_credit:.2f}; wait"

    # --- DTE < 7 ---
    if days < 7:
        if not is_itm(p):
            return False, "let_expire", f"deep OTM with {days}d left, let expire"
        # ITM 且即将到期
        net_credit = (
            new_expiry_mid
            - p.current_option_mid
            - 2 * commission_per_leg / 100
            - spread_slippage
        )
        if net_credit > 0:
            return True, "roll_out", f"ITM, last week, Roll out net credit ${net_credit:.2f}"
        return True, "accept_assignment", f"ITM, no net credit on Roll, take assignment per plan"

    return False, "hold", "unreached"

注意两个安全设计

  1. roll_count 上限 = 2:超过 2 次强制接货。后面单独讲。
  2. commission 和 spread 显式入参:不要把成本藏在「应该差不多」里——金融决策的成本必须显式建模。

5.2 execute_roll() 操作函数(先 BTC 再 STO)

# wheel/roll.py  (续)
from ib_insync import IB, Option, ComboLeg, Contract, Order

def execute_roll(
    ib: IB,
    symbol: str,
    old_expiry: str,         # 'YYYYMMDD'
    old_strike: float,
    new_expiry: str,
    new_strike: float,
    contracts: int = 1,
    right: Literal["P", "C"] = "P",
    dry_run: bool = True,
    limit_net_credit: float = 0.05,   # 最低可接受 net credit
) -> dict:
    """
    Roll a short put (or call) via a combo order.
    Step 1: Buy to Close old leg
    Step 2: Sell to Open new leg
    Combined as one combo to capture mid-price.
    """
    # 限定合约
    old_opt = Option(symbol, old_expiry, old_strike, right, "SMART", "100", "USD")
    new_opt = Option(symbol, new_expiry, new_strike, right, "SMART", "100", "USD")
    ib.qualifyContracts(old_opt, new_opt)

    # 取最新报价用于 dry-run 估算
    old_ticker = ib.reqMktData(old_opt, '', snapshot=True)
    new_ticker = ib.reqMktData(new_opt, '', snapshot=True)
    ib.sleep(1.5)
    old_mid = (old_ticker.bid + old_ticker.ask) / 2
    new_mid = (new_ticker.bid + new_ticker.ask) / 2
    net_credit_est = new_mid - old_mid

    plan = {
        "symbol": symbol,
        "old": f"{old_expiry}/{old_strike}{right}",
        "new": f"{new_expiry}/{new_strike}{right}",
        "old_mid": round(old_mid, 4),
        "new_mid": round(new_mid, 4),
        "net_credit_est": round(net_credit_est, 4),
        "contracts": contracts,
    }

    if net_credit_est < limit_net_credit:
        plan["status"] = "REJECTED"
        plan["reason"] = f"net_credit {net_credit_est:.4f} < limit {limit_net_credit}"
        return plan

    if dry_run:
        plan["status"] = "DRY_RUN_OK"
        return plan

    # ---- 真实下单(combo)----
    combo = Contract(
        symbol=symbol, secType="BAG", exchange="SMART", currency="USD",
        comboLegs=[
            ComboLeg(conId=old_opt.conId, ratio=1, action="BUY",  exchange="SMART"),
            ComboLeg(conId=new_opt.conId, ratio=1, action="SELL", exchange="SMART"),
        ],
    )
    # combo limit price 用 net credit 表示(正数 = 收钱)
    order = Order(
        action="BUY",       # IBKR combo 约定:BUY combo at -credit
        orderType="LMT",
        totalQuantity=contracts,
        lmtPrice=round(-limit_net_credit, 2),   # 负数表示 net credit
        tif="DAY",
    )
    trade = ib.placeOrder(combo, order)
    plan["status"] = "SUBMITTED"
    plan["order_id"] = trade.order.orderId
    return plan

dry-run 是必须的——任何会动钱的代码,第一版都要先支持 dry_run=True,跑通逻辑+验证报价合理,才打开实盘开关。这是 Day 1 笔记里提到的 Read-Only safety pattern 的延伸。

5.3 一个完整的本周决策跑批

# 本周三仓位(来自 Day 41 paper trade 开仓)
positions = [
    ShortPutPosition("F",    10.0, date(2026, 6, 20), 0.18, 9.40, 0.65, roll_count=0),
    ShortPutPosition("BAC",  38.0, date(2026, 6, 20), 0.42, 38.6, 0.18, roll_count=0),
    ShortPutPosition("SOFI", 8.5,  date(2026, 6, 20), 0.22, 9.10, 0.04, roll_count=0),
]
today = date(2026, 6, 19)   # 周五前一天

candidate_new_mids = {"F": 0.95, "BAC": 0.55, "SOFI": 0.18}

for p in positions:
    act, kind, reason = should_roll(p, today, candidate_new_mids[p.symbol])
    print(f"{p.symbol:6s} | act={act} | kind={kind:20s} | {reason}")

预期输出:

F      | act=True  | kind=roll_out             | ITM, net credit $0.28 > 0
BAC    | act=False | kind=hold                 | OTM, let theta accelerate
SOFI   | act=True  | kind=close_win            | profit 82% ≥ 50% rule

六、被 assigned 怎么办:从 Stage 1 走到 Stage 2

冷静比正确更重要。被 assigned 不是失败,是 Wheel 策略计划内的状态切换

6.1 Assignment 当天发生什么

周五 16:00 EST     option 到期,ITM 触发 assignment
周五夜 / 周六       OCC 处理,broker 收到通知
周一开盘前          你的账户里:
                    - 现金 -= strike × 100 × contracts
                    - 股票 += 100 × contracts 股
                    - short put position 消失
                    - 这单 P&L 锁定: (premium_received - intrinsic_at_assign) × 100

心理上需要重置:你不再是「卖方期权 trader」,你是「股票持有者」。仓位逻辑、风险来源、Greeks 全部变了。

6.2 Greeks 的突变

这是新手最容易被打懵的点。

状态DeltaGammaThetaVega
Short Put(assignment 前)+0.25-0.05+0.03 (收)-0.10
100 股股票(assignment 后)+1.00000

Delta 从 +0.25 跳到 +1.00 = 4 倍敞口放大。如果 F 再跌 $1:

  • Assignment 前:put 损失约 $0.25 × 100 = $25
  • Assignment 后:股票损失约 $1.00 × 100 = $100

所以 Wheel 策略书里反复强调一句话——只对你愿意以 strike 价拿货的股票卖 CSP。这条规则的全部含义就在这个 Greeks 突变里。

6.3 Stage 2 立刻挂 CC

被 assigned 后不要 hold 等反弹,按计划立刻挂 CC:

# pseudo-code
def on_assignment(symbol, assignment_price, shares=100):
    # Stage 2: 我们现在持有股票
    # Stage 3: 立刻挂 Covered Call
    
    target_cc_strike = round(assignment_price * 1.05, 1)   # +5%
    target_expiry    = next_monthly_expiry(today, days=30) # 30d out
    target_delta     = 0.20   # 20Δ CC,被 called away 概率约 20%
    
    cc_contract = find_option_by_delta(
        symbol, target_expiry, right="C", target_delta=target_delta
    )
    
    place_order(
        action="SELL",
        contract=cc_contract,
        order_type="LMT",
        limit=cc_contract.mid * 0.98,   # 略低于 mid 提高成交率
    )

挂 CC 的两个考虑点:

  1. Strike ≥ assignment_price:永远不要在亏损价位 cap upside。这是 Wheel 策略的纪律——CC strike 必须 ≥ 你的 cost basis(即被 assigned 的 strike 减去原始收到的 premium)
  2. Delta 0.15-0.25:太高被 called away 概率高,太低 premium 太薄。20Δ 是 sweet spot

七、Avoidance Pattern:Roll 死循环

最多 Roll 2 次。第 3 次强制接货。

为什么?因为 Roll 在心理上是甜的——「我没亏,只是推迟」。但推迟亏损不等于不亏损,而且持续的 Roll 会绑死资金、累积手续费、且每次都假设「下个月会反弹」。这种假设连续做 3 次错的概率不低。

7.1 死循环长什么样

Month 1: 卖 F $10 Put 收 $0.18
Month 1 到期前: F = $9.40,Roll Out 到 Month 2,net +$0.28
Month 2 到期前: F = $9.10,Roll Out 到 Month 3,net +$0.15
Month 3 到期前: F = $8.80,Roll Down 到 Month 4 $9 strike,net -$0.05
Month 4 到期前: F = $8.20,深 ITM,Roll 已经做不出 net credit
                            ↑
                  这时被迫接货 = 在更差的位置被迫接货

VS

如果 Month 2 就接货:
  以 $10 接货,cost basis = $10 - $0.18 - $0.28 = $9.54
  立刻挂 CC,开始 Stage 3 收 CC premium
  4 个月下来即使 F 在 $8.80 也有 CC premium 缓冲

结构性洞察:Roll 死循环和企业里的「展期保命」是同一个 anti-pattern——把不良债务一次次展期,最后变成更深的坏账。金融 PM 应该天然对「无限延期」敏感

7.2 实操规则

条件强制动作
同一 underlying 累计 Roll ≥ 2 次不论 net credit 多少,下次到期 ITM → 强制接货
累计 Roll 1 次但 ITM 深度 > 5% strike同上
Roll 时被迫 net debit不 Roll,立刻接货

这三条对应到代码就是 should_roll() 顶部那段 avoidance check。写代码的时候把这些纪律编码进逻辑层,比靠人执行可靠 10 倍


八、CC 端的 Roll:和 CSP 端不一样

Wheel 的下半段是 Covered Call。CC 端的 Roll 决策逻辑相反但结构相同

维度CSP 端CC 端
触发 Roll 的方向股票了,put ITM股票了,call ITM
Roll 的恐惧不想接货(cash 转股票)不想被 called away(股票转 cash)
偏好的 Roll 类型Roll Out / Roll DownRoll Out / Roll Up
死循环风险越 Roll 越深套越 Roll 越追不上股价

8.1 CC ITM 的两种结局

CC F 06/20 $11,F 涨到 $11.50

选择 A: 让它被 called away
  → 卖出 100 股 @ $11
  → 收到 $1100 现金 + 已收的 CC premium
  → 重新进 Wheel Stage 1: 卖新 CSP

选择 B: Roll Up + Roll Out
  → BTC F 06/20 $11,STO F 07/18 $12
  → 保留股票,多收一点 net credit(可能很小)
  → 但如果 F 继续涨到 $12.50,下月又要做同样决策

我的纪律:CC ITM 默认走 A——让它被 called away。理由:Wheel 的设计就是「Stage 3 末端兑现盈利,回到 Stage 1」。如果你不舍得让 CC 被 called away,那当初就不应该卖 CC。

唯一的例外:股票有 ex-dividend 临近,被 called away 会损失股息。这时 Roll Out 跨越除息日有意义。

8.2 CC OTM 临近 expiry

CC F 06/20 $11,F 还在 $10.20,CC 深 OTM

→ 让它过期(零费用、最大 premium 收入)
→ 周一开盘卖下个月的 CC

这种状态就是 Wheel 的「理想轨道」——CC 持续过期,股票不变,每个月稳定收 premium。


九、本周 Paper Trade 三仓位的实际决策

Day 41 paper trade 开了三个 CSP(基于 Day 28-32 因子合成挑出的低 IV rank、低估值仓位)。今天是 6/21,距离 6/20 周五到期只有一天,正好走一遍决策树。

9.1 F 仓位:CSP ITM → Roll Out

仓位:      Short Put F 06/20 $10  收 $0.18  (Day 41 开仓)
当前:      F = $9.40,put mid = $0.65 (ITM $0.60 + extrinsic $0.05)
DTE:       1 天
Roll 候选: F 07/18 $10  mid = $0.95

Net credit = 0.95 - 0.65 - 0.013 commission - 0.02 spread = +$0.247
roll_count = 0

→ should_roll() = True, action="roll_out"
→ 决策: Roll Out 到 07/18 $10,预期 net credit ≈ $24.70
→ 更新 roll_count = 1

PM 视角的反思:F 跌 8% 是因为整体周期股回调,不是 F 公司基本面变化。基本面没崩 → Roll 一个月给市场时间消化是合理的。如果是 earnings miss 后跌 8%,决策应该不一样——基本面恶化时不要 Roll,直接接货然后用 CC 慢慢摊低成本。

9.2 BAC 仓位:CSP OTM → 让它过期

仓位:      Short Put BAC 06/20 $38  收 $0.42  (Day 41 开仓)
当前:      BAC = $38.60,put mid = $0.18
DTE:       1 天
利润比例:  (0.42 - 0.18) / 0.42 = 57%

但 DTE < 7 且 OTM → action="let_expire"
(DTE 太短,BTC 成本 > 剩余 extrinsic 收益)

→ 决策: 让它周五过期,0 费用收尾,premium $42 全数到手

经验沉淀:Day 41 当初选 BAC 的理由是「IV rank 38%(适中)+ 大型银行下行有限」。一周后回看,这个判断对了。记下这个 setup → 下次再现时复制

9.3 SOFI 仓位:CSP 深 OTM → 50% rule?

仓位:      Short Put SOFI 06/20 $8.5  收 $0.22  (Day 41 开仓)
当前:      SOFI = $9.10,put mid = $0.04
DTE:       1 天
利润比例:  (0.22 - 0.04) / 0.22 = 82%

DTE < 7 + 深 OTM → action="let_expire"
50% rule 是 DTE > 21 时的逻辑,此时 close 多此一举

注意一个微妙点:50% rule 的本意是「在 DTE 还长时,提前锁利、释放资金」。DTE 只剩 1 天时,让它过期更优——剩余 $0.04 几乎稳定流入,BTC 反而要付 spread。


十、和 PM 工作的迁移性思考

写完今天这棵决策树,反推到 10 年 PM 的几个隐性经验:

场景量化语境PM 语境(同构问题)
该不该 Roll接受亏损 vs 延后兑现该不该展期合同 / 续约低毛利客户
Net Credit 法则Roll 必须收钱续约必须让单位经济模型变好(CAC/LTV 改善),否则就是续约亏损
Roll 死循环最多 Roll 2 次不良客户最多让步 2 次,否则结构性亏损
被 assigned 立刻挂 CC接受现状 + 立刻启动下一阶段项目失败后立即开始 retrospective + next-step plan,而不是停滞
50% rule提前兑现部分利润大合同分阶段交付而非全 hold 到验收

最深的一条:「兑现损失」是金融人最难做的动作之一,因为它把账面亏损变成已实现亏损,心理代价大。但所有 Roll 的最坏路径都是「延期被迫兑现,且兑现得更深」。早接受 = 早脱困。这不是量化独有,是所有损失认知问题的共性。


十一、Day 1-42 的整合视角

到 Day 43,Wheel 策略所需的零件都齐了:

Day提供的能力
Day 1-10IBKR / Paper / Python / ib_insync
Day 11-20Greeks / IV / 期权链查询 / 风险敞口
Day 21-27CSP / CC 单独操作 / Wheel Stage 1-3
Day 28-32因子合成挑选标的
Day 33-40IV rank 过滤 / 仓位 sizing / 组合层 Greeks
Day 41三仓位 paper trade 开仓
Day 43(今天)Roll 决策树 + ITM 处理

明天 Day 44 是 Wheel 第一周复盘,把今天的决策结果记录下来,开始累积「我自己的 setup 表现库」——这才是个人量化最值钱的资产。


十二、明日预告

Day 44: Wheel 第一周复盘 — Setup 库初版

  • 三仓位实际成交回顾(F Roll 成本 / BAC 过期 / SOFI 过期)
  • P&L 拆解:premium 收入 / Greek pnl / 滑点 / 佣金
  • IV rank vs 实际 IV 路径:开仓时的 IV 假设有没有兑现
  • 第一版 setup 库表设计:标的、IV rank 区间、Δ、DTE、净 premium、结局
  • 失败 setup 的迭代规则:什么样的开仓条件下周不复制
  • 对应代码:wheel/journal.py 把 IBKR 成交流水转换成 setup 评估表

实际执行记录

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

  • [hh:mm] should_roll() 函数写完 + 三仓位跑过 dry-run
  • [hh:mm] execute_roll() combo order 在 paper 上跑通(F 仓位)
  • [hh:mm] Roll combo 实际成交价 vs mid 估算偏差记录
  • [hh:mm] BAC / SOFI 周五过期确认(0 佣金收尾)
  • [hh:mm] 更新 wheel/positions.csv —— F 仓位 roll_count = 1
  • 卡点 / 学到的:

总字数:约 6,200 字 今日完成度:理论 ✓ / 代码(dry-run) ✓ / 实操(Roll 成交结果待 paper trade 周五前) / 笔记 ✓