IBKR Paper — 第一组 Wheel 实战
Staggered expiry 设计、thesis-first 下单纪律、保证金 buffer、限价滑点观测
日期: 2026-06-20 方向: Phase 2 / Wheel 实战 阶段: Phase 2: 策略实战 + AI 信号 标签: #Wheel #PaperTrade #FirstTrade #CSP #StaggeredExpiry #Greyscale #IBKR
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | Staggered expiry 设计、thesis-first 下单纪律、保证金 buffer、限价滑点观测 |
| 实操 | IBKR Paper 真实开 3 张 CSP(F / BAC / SOFI),形成第一个完整 wheel portfolio |
| 产出 | TR-DAY42 笔记 + setup_wheel.py + 3 张 thesis 卡 + 监控阈值表 + 当日复盘 |
关键纪律:今天不是「试一下能不能下单」,是正式上线第一组真实 wheel 仓位——只是钱是 Paper 的。流程、风控、文档全部按实盘要求执行。
一、为什么是「3 张 + Stagger」:灰度发布的产品思维
我做 PM 10 年的本能:不要一次性把所有用户切到新版本。
把这个本能搬到交易上:
| 一次开 10 张 CSP | 一次开 3 张 CSP(stagger) |
|---|---|
| 一个市场事件全员中枪 | 至少 2/3 还在 OTM 缓冲区 |
| 同一天大批 assigned,现金占用暴增 | expiry 错开,assignment 分摊 |
| 操作流程没经过验证就上量 | 第一周专注流程正确性 |
| 心理压力 3× | 心理压力可控 |
| 出错回滚成本高 | 出错只动一张 |
核心认知:第一周的目标不是「赚最多 premium」,是验证我设计的下单→监控→roll→close 的全流程在 Paper 上不会断。把这 7 天当成「灰度发布期」,week 4 才考虑扩到 6-8 张。
1.1 Stagger Expiry 的具体设计
| 标的 | 当前价 | DTE | Strike | Delta | Premium 估 | 现金占用 |
|---|---|---|---|---|---|---|
| F (Ford) | ~$10.00 | 30 | $9.00 | -0.30 | ~$0.18 | $900 |
| BAC (Bank of America) | ~$35.00 | 35 | $33.00 | -0.25 | ~$0.50 | $3,300 |
| SOFI | ~$12.00 | 40 | $11.00 | -0.30 | ~$0.32 | $1,100 |
| 合计 | — | — | — | Net Δ +45 | ~$100 | ~$5,300 |
Net Delta 计算:short 1 张 put 的 delta = -(-0.30) × 100 = +30;同理 BAC = +25,SOFI = +30;考虑 contract multiplier 后 delta 单位是「等价股数」,故 Net Δ ≈ +85 等价股。简化为「+45 directional units」是把 100 multiplier 拆开来感知方向暴露的口径,下文统一用 directional units。
Stagger 的本质:3 张 expiry 分别在 T+30 / T+35 / T+40,意味着任意一天最多一张需要 roll/close/接货决策——决策分散 = 错误风险分散。如果 3 张都 T+30 同一天,遇到周五大跌就要同时处理 3 个 ITM put,操作压力会让人乱来。
1.2 标的选择的逻辑
| 维度 | F | BAC | SOFI |
|---|---|---|---|
| 行业 | 周期/汽车 | 大型银行 | Fintech |
| 流动性 | 极高 | 极高 | 高 |
| 期权链 | 周度 + 月度 | 周度 + 月度 | 周度 + 月度 |
| 接货后是否愿持 | ✓ 股息 5%+ | ✓ 大盘金融 | ✓ 增长但波动大 |
| 与已有持仓相关性 | 与 BAC 中性 | 与 F 0.4 | 与 BAC 0.6 |
承认问题:BAC 和 SOFI 同属金融板块,相关性偏高,真实组合应该选一个非金融来分散(如 CSCO / KO / T)。但作为 Day 42 的第一组——复杂度优先于完美度,先跑通三张 stagger 流程,下周再优化标的篮子。
二、Thesis-First:每张单子都必须先写下「为什么愿意接货」
这是这个交易体系最重要的一条纪律:任何 CSP 下单前,先用一句话写下「我愿意以 strike 价持有这只股票 N 天的理由」。
为什么?因为没有 thesis 的开仓,事后必然 rationalize:
- 跌了 → 「啊我本来就是想接货」(其实没想过)
- 涨了 → 「啊我看准了」(其实是运气)
- 横盘 → 「啊我赚 theta」(如果真这么想为什么不直接做 IC)
写下 thesis 后,复盘时可以做事前 vs 事后对比,这是唯一能积累 alpha 的机制。
2.1 三张 Thesis 卡
┌──────────────────────────────────────────────────────────────┐
│ TRADE #1: F 30DTE 9P @ $0.18 │
│ │
│ Thesis: │
│ "I am willing to own F at $9 for the next 30 days because │
│ (1) F trades at 0.7× book value at $9, near 10-yr low │
│ (2) 6%+ dividend yield at $9 entry, covers theta decay │
│ (3) EV transition narrative已 priced-in,downside有限 │
│ (4) Q3 earnings 已过,无重大事件窗口" │
│ │
│ Invalidation: │
│ - 油价跌破 $60(消费者下行风险传导) │
│ - 美国汽车贷款违约率 >7% │
│ - 联储意外加息 50bps │
│ │
│ Plan if assigned: 转 CC,strike $10,覆盖成本基础 │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ TRADE #2: BAC 35DTE 33P @ $0.50 │
│ │
│ Thesis: │
│ "I am willing to own BAC at $33 for 35 days because │
│ (1) BAC P/B 0.95×,大型银行历史均值 1.1× │
│ (2) Q3 财报已确认 NIM 企稳,前向 PE 9× │
│ (3) $33 = 200日均线 + 主要支撑位 │
│ (4) 期权链流动性好,IV rank 35%(不算贵但有 premium) │
│ │
│ Invalidation: │
│ - 区域银行再爆雷(SVB 2.0) │
│ - 联储意外鸽派砸 yield curve(NIM 压力) │
│ - 商业地产违约率超预期 │
│ │
│ Plan if assigned: 持有等股息,CC 卖 $35 │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ TRADE #3: SOFI 40DTE 11P @ $0.32 │
│ │
│ Thesis: │
│ "I am willing to own SOFI at $11 for 40 days because │
│ (1) Q3 首次 GAAP 盈利已确认,预期 Q4 延续 │
│ (2) $11 = IPO 后第二根支撑趋势线 │
│ (3) 学生贷款业务因联邦政策利好 │
│ (4) IV rank 60%(高),premium 收益相对厚 │
│ │
│ Invalidation: │
│ - Q4 财报指引下修 │
│ - Tech sell-off > 10% │
│ - 利率重新预期上调(fintech 估值压力) │
│ │
│ Plan if assigned: 持有,CC 卖 $12,平均权益基础 $10.68 │
└──────────────────────────────────────────────────────────────┘
Thesis 卡的设计要点:
- 正向 thesis(为什么 OK)+ Invalidation 条件(什么时候认错)+ 接货后计划——三段缺一不可
- Invalidation 必须是可观测的市场信号,不能是「感觉不对」这种主观词
- 接货后计划写出来防止 assigned 时手忙脚乱
三、ib_insync 下单流程:从 dry-run 到真实成交
3.1 流程图
[1] connect Paper (port 7497, assert)
↓
[2] qualify contracts (3 个 option contract)
↓
[3] 取 live quote → 计算 mid price
↓
[4] 检查保证金 buffer < 95%
↓
[5] dry-run 打印订单详情 → 人工确认
↓
[6] sell to open (LMT @ mid)
↓
[7] 监听 orderStatus / execDetails event
↓
[8] 5 分钟未 fill → 退一档 mid+0.01 重挂
↓
[9] 全部 fill → 写入本地 trade journal
3.2 完整代码:setup_wheel.py
# setup_wheel.py
"""
Day 42 — Open first wheel basket on IBKR Paper.
3 staggered CSPs: F 30DTE 9P / BAC 35DTE 33P / SOFI 40DTE 11P
"""
from ib_insync import IB, Stock, Option, LimitOrder, util
from dataclasses import dataclass, field
from datetime import datetime, date
from typing import Optional
import json
import sys
from pathlib import Path
# ─────────────────────────────────────────────────────────────
# 配置
# ─────────────────────────────────────────────────────────────
PAPER_PORT = 7497 # TWS Paper port — NEVER use 7496 (live)
CLIENT_ID = 42 # 用 Day 编号当 clientId,方便排查
MARGIN_BUFFER = 0.95 # 保证金占用不超过 95%
JOURNAL_PATH = Path("./trade_journal.jsonl")
@dataclass
class WheelCandidate:
symbol: str
target_strike: float
target_dte: int
target_delta: float
thesis: str
# 运行时填充
underlying_price: Optional[float] = None
chosen_expiry: Optional[str] = None
contract: Optional[Option] = None
mid_price: Optional[float] = None
margin_required: Optional[float] = None
order: Optional[LimitOrder] = None
CANDIDATES = [
WheelCandidate(
symbol="F",
target_strike=9.0,
target_dte=30,
target_delta=0.30,
thesis="F at $9 = 0.7× book, 6%+ div yield, EV narrative priced-in",
),
WheelCandidate(
symbol="BAC",
target_strike=33.0,
target_dte=35,
target_delta=0.25,
thesis="BAC at $33 = 0.95× P/B, 200DMA support, NIM stabilized",
),
WheelCandidate(
symbol="SOFI",
target_strike=11.0,
target_dte=40,
target_delta=0.30,
thesis="SOFI at $11 = post-IPO trendline, first GAAP profit Q3",
),
]
# ─────────────────────────────────────────────────────────────
# 步骤 1: 安全连接
# ─────────────────────────────────────────────────────────────
def connect_paper() -> IB:
ib = IB()
ib.connect("127.0.0.1", PAPER_PORT, clientId=CLIENT_ID)
# 护栏 1: 必须是 Paper port
assert ib.client.port == PAPER_PORT, f"WRONG PORT {ib.client.port} — PAPER ONLY"
# 护栏 2: account ID 应以 DU 开头(Paper account 约定)
accounts = ib.managedAccounts()
assert all(a.startswith("DU") for a in accounts), \
f"REFUSED: non-paper account detected: {accounts}"
print(f"[OK] Connected to Paper {accounts} on port {PAPER_PORT}")
return ib
# ─────────────────────────────────────────────────────────────
# 步骤 2: 寻找最接近 target delta 的 strike
# ─────────────────────────────────────────────────────────────
def find_target_option(ib: IB, cand: WheelCandidate) -> Option:
"""从期权链里挑最接近 target_delta 且最接近 target_dte 的 put。"""
# 取 underlying 现价
stk = Stock(cand.symbol, "SMART", "USD")
ib.qualifyContracts(stk)
ticker = ib.reqMktData(stk, "", snapshot=True)
ib.sleep(2)
cand.underlying_price = ticker.marketPrice() or ticker.last
print(f"[{cand.symbol}] spot = ${cand.underlying_price:.2f}")
# 取期权链
chains = ib.reqSecDefOptParams(stk.symbol, "", stk.secType, stk.conId)
smart_chain = next(c for c in chains if c.exchange == "SMART")
# 选最接近 target_dte 的 expiry
today = date.today()
def dte(exp_str):
return (datetime.strptime(exp_str, "%Y%m%d").date() - today).days
valid_expiries = [e for e in smart_chain.expirations if dte(e) > 7]
chosen = min(valid_expiries, key=lambda e: abs(dte(e) - cand.target_dte))
cand.chosen_expiry = chosen
print(f"[{cand.symbol}] chose expiry {chosen} (DTE={dte(chosen)})")
# 在 target_strike 附近选 strike(简化:直接用 target_strike)
# 实盘版本应该 reqMktData 多个 strike 拿 greeks,找 delta 最接近 -0.30
opt = Option(
symbol=cand.symbol,
lastTradeDateOrContractMonth=chosen,
strike=cand.target_strike,
right="P",
exchange="SMART",
currency="USD",
)
ib.qualifyContracts(opt)
# 拿 quote
opt_ticker = ib.reqMktData(opt, "", snapshot=True)
ib.sleep(2)
bid, ask = opt_ticker.bid, opt_ticker.ask
if bid is None or ask is None or bid <= 0 or ask <= 0:
raise ValueError(f"[{cand.symbol}] no valid quote: bid={bid} ask={ask}")
mid = round((bid + ask) / 2, 2)
cand.mid_price = mid
cand.contract = opt
print(f"[{cand.symbol}] {cand.target_strike}P @ bid={bid} ask={ask} mid={mid}")
return opt
# ─────────────────────────────────────────────────────────────
# 步骤 3: 保证金 / cash secured 检查
# ─────────────────────────────────────────────────────────────
def check_margin(ib: IB, cands: list[WheelCandidate]) -> bool:
"""Cash-secured put: 保证金 = strike × 100 - premium received。
检查总占用 < 95% 账户净值。"""
summary = ib.accountSummary()
net_liq = float(next(s.value for s in summary if s.tag == "NetLiquidation"))
total_required = sum(
(c.target_strike * 100 - c.mid_price * 100) for c in cands
)
for c in cands:
c.margin_required = c.target_strike * 100 - c.mid_price * 100
pct = total_required / net_liq
print(f"\n[MARGIN] required ${total_required:,.0f} / net liq ${net_liq:,.0f} = {pct:.1%}")
if pct > MARGIN_BUFFER:
print(f"[FAIL] exceeds {MARGIN_BUFFER:.0%} buffer — REFUSED")
return False
print(f"[OK] within {MARGIN_BUFFER:.0%} buffer")
return True
# ─────────────────────────────────────────────────────────────
# 步骤 4: Dry-run + 确认
# ─────────────────────────────────────────────────────────────
def dry_run(cands: list[WheelCandidate]) -> bool:
print("\n" + "=" * 60)
print("DRY RUN — Review before submitting")
print("=" * 60)
for c in cands:
print(f"\n SELL TO OPEN {c.symbol} {c.chosen_expiry} {c.target_strike}P")
print(f" Limit: ${c.mid_price} (mid)")
print(f" Premium: ${c.mid_price * 100:,.0f}")
print(f" Cash needed: ${c.margin_required:,.0f}")
print(f" Thesis: {c.thesis}")
print("\n" + "=" * 60)
resp = input("Submit all 3 orders? [y/N]: ").strip().lower()
return resp == "y"
# ─────────────────────────────────────────────────────────────
# 步骤 5: 提交 + 监听 fill
# ─────────────────────────────────────────────────────────────
def submit_orders(ib: IB, cands: list[WheelCandidate]):
trades = []
for c in cands:
order = LimitOrder("SELL", 1, c.mid_price, tif="DAY")
c.order = order
trade = ib.placeOrder(c.contract, order)
trades.append((c, trade))
print(f"[SUBMIT] {c.symbol} {c.target_strike}P @ ${c.mid_price}")
# 等待 60 秒,看 fill 情况
print("\nWaiting up to 60s for fills...")
for _ in range(60):
ib.sleep(1)
if all(t.orderStatus.status == "Filled" for _, t in trades):
break
# 报告
print("\n" + "=" * 60)
print("FILL REPORT")
print("=" * 60)
for c, t in trades:
st = t.orderStatus
fill_px = st.avgFillPrice if st.avgFillPrice else "—"
print(f" {c.symbol}: status={st.status} filled@{fill_px} remaining={st.remaining}")
# 写 journal
record = {
"ts": datetime.now().isoformat(),
"symbol": c.symbol,
"strike": c.target_strike,
"expiry": c.chosen_expiry,
"right": "P",
"action": "SELL_TO_OPEN",
"limit_price": c.mid_price,
"fill_price": st.avgFillPrice,
"status": st.status,
"thesis": c.thesis,
}
with JOURNAL_PATH.open("a") as f:
f.write(json.dumps(record) + "\n")
return trades
# ─────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────
def main():
ib = connect_paper()
try:
for cand in CANDIDATES:
find_target_option(ib, cand)
if not check_margin(ib, CANDIDATES):
sys.exit("Margin check failed")
if not dry_run(CANDIDATES):
print("Aborted by user.")
return
submit_orders(ib, CANDIDATES)
finally:
ib.disconnect()
if __name__ == "__main__":
main()
3.3 代码里的几个产品级 pattern
| Pattern | 体现位置 | 为什么重要 |
|---|---|---|
| 多层 assertion | connect_paper() 双重护栏 | 误连实盘是最严重的事故,要 fail-fast |
| Dry-run 默认开 | dry_run() 必须人工 y 才继续 | 防止脚本被误触发自动下单 |
| 预算护栏 | check_margin() 总仓位 < 95% | 留 5% buffer 给意外 |
| 结构化 journal | 写 jsonl,便于后续 SQL/pandas 分析 | 没有 journal 等于没复盘 |
| 明确单一职责函数 | connect / find / check / dry_run / submit 分离 | 调试时可单步 |
四、Portfolio State:第一天结束时该长什么样
下单全部成交后,组合的 Greeks snapshot:
| 维度 | 数值 | 含义 |
|---|---|---|
| 持仓 | -1 F 9P / -1 BAC 33P / -1 SOFI 11P | 3 张 short put |
| Net Delta | +85 directional units | 轻度 long bias,相当于持 85 股一篮子 |
| Net Theta | ~+$15/day | 每天「应该」收到约 $15 时间衰减(含周末) |
| Net Vega | ~-$40/% IV | IV 上升 1 个百分点会 mark-to-market 亏 $40 |
| Net Gamma | -2.5 | delta 反向变化敏感度,跌得越多 long bias 越强 |
| 现金占用 | ~$5,300 | 接近上限(建议账户调到 $5,500 给点 buffer) |
| 未实现 P&L | ~$0(开仓即 -spread/2) | 实际开盘瞬间因 bid-ask spread 略亏 |
4.1 Greeks 的解读和心理预期
+85 Delta:
→ SPY +1% 时组合 mark-to-market ≈ +$85
→ 这是个 "微微看多" 的姿态,符合 CSP 卖方本质
+$15 Theta/day:
→ 30 天满吃 = $450 premium
→ 实际能到 50-70%(提前 close 或部分 assigned)
→ 真实预期净收入 ~$225-$315 / 月
-$40 Vega:
→ IV 暴涨 10% 会 mark-to-market 亏 $400
→ 但这是「未实现」损失,到期前是浮动的
→ 重要:这就是 CSP 的核心痛点——你赚 theta 但卖空 vol
心态校准:开仓第一周 mark-to-market 大概率是负的(bid-ask spread + IV 波动),不要看一天浮亏就慌。CSP 是要持有到接近 expiry 才能确认 theta 收益的策略。
五、监控阈值表:什么时候做什么
把规则写出来,远比脑子里想着「我会注意的」靠谱 1000 倍。
| 触发条件 | 阈值 | 动作 | 决策依据 |
|---|---|---|---|
| 单一标的暴跌 | spot < -10% | 重新读 thesis 卡 | 看 invalidation 条件是否触发 |
| Thesis invalidated | invalidation 命中 | Close to close(即使亏损) | 纪律高于侥幸 |
| Premium 缩到 50% | premium ≤ 0.5 × 开仓价 | Close to take profit | 50% rule:风险收益比恶化 |
| Premium 缩到 80% | premium ≤ 0.2 × 开仓价 | 必须 close | 留着的下行风险大于上行 |
| DTE ≤ 7 + OTM | distance > 5% OTM | Let expire | 收尽 final theta |
| DTE ≤ 7 + ATM/ITM | strike 附近 ±2% | 决定 roll vs accept | 看 thesis 是否还成立 |
| DTE ≤ 2 + ITM | 深度 ITM | Accept assignment | 接货转 CC,进 wheel 后半段 |
| IV 暴涨 > 30% | IV rank +30 in 1 day | 检查是否 close + 等 IV 回落再开 | 卖方在 vol spike 中开仓更值 |
| 账户 drawdown | > -10% MTD | 暂停开新仓 | 强制冷静期 |
5.1 50% Rule 的数学直觉
为什么 premium 缩到 50% 就该 close?
开仓: 收 $0.50 premium, 当时风险 = ~$2.50 (max loss assumption)
风险报酬比 = 0.50 / 2.50 = 20%
3 周后: premium 缩到 $0.25
剩余可赚 = $0.25
剩余风险 ≈ 仍然接近 $2.50(gamma 增加,DTE 减少)
新的风险报酬比 = 0.25 / 2.50 = 10%
→ 同等风险下能赚的钱减半,应该 close 再去开新一张更厚 premium 的
这是「Theta Harvest 论」的核心——总是持有最厚 premium / 最低剩余风险的合约。
六、常见首日坑(提前列出)
6.1 限价挂错位
错误做法:直接挂 bid(永远不成交)
错误做法:直接挂 ask(被 MM 立刻 take,相当于 market order)
正确做法:mid,5 分钟未成交 → mid - 0.01(卖方 perspective: 降一档让步)
再不成 → mid - 0.02,最多让 3-4 档
若让到 bid 附近还不成 → 当天放弃,明天再来
6.2 保证金 buffer 被吃光
IBKR 的 maintenance margin 是动态的——你晚上下单时账户 $5,000,盘前 SOFI 跌 5%,第二天保证金需求飙升,IBKR 直接拒下一单(甚至 force close)。
对策:
- 永远保留 5-10% buffer
- 不要在临近收盘前下满仓单(隔夜 gap 风险)
- 大事件(FOMC / CPI)前一天不要把仓位开满
6.3 clientId 冲突
ConnectionRefusedError / "clientId already in use"
原因:之前的脚本没正常 disconnect,TWS 还认为这个 clientId 被占用。
对策:
- 始终用
try: ... finally: ib.disconnect()包裹 - 关键脚本固定 clientId(如 setup_wheel.py 用 42),告警类脚本用 200+
- 实在不行重启 TWS(最后大招)
6.4 期权链没数据
如果你订了 OPRA 但报价仍然 bid=-1.0,几种可能:
- 市场未开盘(盘前盘后期权链多数没报价)
- 这个 strike 没成交,IBKR 不给虚拟 quote
- 期权快到期(< 1 DTE,多数券商收紧报价)
对策:选 流动性最好的 monthly expiry(第 3 个周五),不要碰冷门 weekly。
6.5 同时下 3 张,第 2 张被拒
如果第一张 fill 后保证金占用提升,第 2 张的 pre-trade margin check 可能失败。
对策:在 check_margin() 时把所有 3 张当一个整体算保证金,而不是顺序下单顺序检查。
七、第一天复盘 Prompt(晚上写)
每天结束前用这套模板自问,写在 journal 里,不写就没复盘。
## TR-Day42 复盘
### 1. 流程是否跑通?
- [ ] connect_paper 成功(assertion 全部通过)
- [ ] 3 张报价都拿到了
- [ ] 保证金 check 通过
- [ ] dry-run 看着合理
- [ ] 3 张全 fill 了吗?滑点多少?
### 2. 滑点观测(基础项)
| Symbol | Mid | Fill Px | Slip (bps) |
|--------|-----|---------|------------|
| F 9P | 0.18 | ? | ? |
| BAC 33P| 0.50 | ? | ? |
| SOFI 11P| 0.32 | ? | ? |
期权 mid 滑点正常 5-10%(vs bid-ask spread),> 20% 要怀疑挂单时机。
### 3. Greeks 实际 vs 预期
- 预期 Net Delta ≈ +85,实际 ?
- 预期 Net Theta ≈ +$15/day,实际 ?
- 偏差大说明 strike 选错了或 delta 估算偏差
### 4. Thesis 是否还成立?
- F: 收盘后看油价、利率、汽车行业新闻 — 任何 invalidation 触发?
- BAC: 区域银行有无新雷?
- SOFI: 利率预期变化?
### 5. 明天的 watch list
- 哪张 P&L 异常?
- 哪张接近某个 invalidation 边缘?
- 周五是否有大事件(FOMC / CPI / earnings)?
### 6. 我今天做错了什么?(强制写一条)
(哪怕是「太晚下单了,最后 15 分钟流动性差」也要写)
### 7. 我明天会改什么?
(具体到一行代码或一个步骤)
八、PM 视角:今天学到的迁移性思考
- 灰度发布 = 风险即时反馈:先开 3 张,问题在小规模暴露。这跟做产品上线先放 5% 流量是同一个道理——没有可观测的小规模运行,不要直接全量。
- Thesis 卡 = 决策审计:把开仓理由 + invalidation 写下来,等于给自己留了一份 ADR(Architecture Decision Record)。事后复盘有原始判断可对比,不会被 hindsight bias 污染——对应产品工作里的「假设记录文档」。
- Dry-run + 人工确认:自动化不等于无人值守。自动化好的系统反而保留更多 explicit 人工 checkpoint,因为它知道 critical path 需要人。
- Journal 是 alpha 的真实来源:单笔交易盈亏不重要,能不能从 100 笔交易里识别出「我系统性地在 X 情况下做错」才是。这是「Observability 优先级 > 性能」在交易场景的体现。
- 保证金 buffer 是产品的「容量水位线」:把 95% 当上限不是物理限制,是给「未知未知」留 headroom。做支付系统设计「不让账户余额跑到 0」是同一逻辑。
九、明日预告
Day 43: Roll 操作 — 当 CSP 进入 challenge 区域时怎么办
- Roll down / roll out / roll down-and-out 三种基本动作
- 怎么算 roll 的「净 credit」是不是值得
- 什么时候应该 roll,什么时候应该 accept assignment
- 写一个
roll_csp.py工具:输入现有头寸 → 推荐 roll target 期权 - 案例:模拟 F 跌到 $8.80,9P 进入 ITM,演示完整 roll 决策
- 心理纪律:roll 不是「治疗亏损」,是「重新表达 thesis」
实际执行记录
启动一项填一项,时间戳 + 卡点。
- [hh:mm] 启动 TWS Paper,确认端口 7497
- [hh:mm] 跑
python setup_wheel.py— connect_paper 通过 - [hh:mm] F 报价 spot=? mid=? 滑点=?
- [hh:mm] BAC 报价 spot=? mid=? 滑点=?
- [hh:mm] SOFI 报价 spot=? mid=? 滑点=?
- [hh:mm] 保证金 check:required=$? net_liq=$? pct=?
- [hh:mm] Dry-run 确认 — 是否调整
- [hh:mm] 3 张全部 fill / 部分 fill / 未 fill
- [hh:mm] trade_journal.jsonl 写入成功
- [hh:mm] 拉取实际 Greeks vs 预期对比
- [hh:mm] 复盘 prompt 7 条全部写完
- 卡点 / 学到的:
总字数:约 7,200 字 今日完成度:理论 ✓ / 实操(执行 setup_wheel.py + 3 张 fill)/ 笔记 ✓