返回交易笔记
TR Day 58

Phase 2 综合演练 — 三策略并行 2 周

多策略并行运行的工程问题:clientId / 保证金 / race condition / 监控 / 归因

2026-07-06
Phase 2: 策略实战 + AI 信号
ThreeStrategyPaperTradeIntegrationPortfolioDashboard2WeekRunPreLive

日期: 2026-07-06 方向: Phase 2 / 综合演练 阶段: Phase 2: 策略实战 + AI 信号 标签: #ThreeStrategy #PaperTrade #Integration #PortfolioDashboard #2WeekRun #PreLive


今日目标

类型内容
学习多策略并行运行的工程问题:clientId / 保证金 / race condition / 监控 / 归因
实操把 Day 36 双因子 / Day 42 Wheel / Day 49 IC+LLM 整合进同一个 paper portfolio,跑 2 周
产出TR-DAY58 笔记 + portfolio_dashboard.py(实时 NAV / Greeks / VaR / 相关性)+ 2 周复盘 + 实盘准入评估

一、为什么 Day 58 必须做综合演练

Day 31-49 三个策略各自跑通:

Day策略资产类型Rebalance持仓数量级状态
36双因子(Mom + Quality)个股 long-only月度全量8-12 只Paper ✓
42Wheel(CSP → CC)标的 + 期权短仓月度 stagger(每周 1-2 笔)3-5 个 underlyingPaper ✓
49IC + LLM期权 4 腿组合财报事件触发(每月 5-8 单)2-4 个活仓Paper ✓

问题:每个策略「单独跑」可以,但真实的个人量化账户不会只跑一个策略——它一定是 portfolio level 的。

单元测试都过 ≠ 集成测试过
集成测试过 ≠ 端到端 e2e 跑得稳
e2e 跑 1 天 ≠ 跑 2 周表现稳定

Day 58 解决的是从「3 个 working prototype」到「1 个 production-ready portfolio」之间的工程问题。这一步如果不做,Phase 3 上实盘就是赌博。


二、统一架构:3 策略共用什么、独用什么

2.1 共用层 vs 策略层

┌─────────────────────────────────────────────────────┐
│              Portfolio Dashboard (port 8888)        │
│   实时 NAV / Greeks / VaR / Correlation / Alerts    │
└─────────────────▲───────────────────────────────────┘
                  │  读取
┌─────────────────┴───────────────────────────────────┐
│              共用 Trade Log DB (SQLite)             │
│   trades / positions / pnl_attribution / events     │
└─────────────────▲───────────────────────────────────┘
                  │  写入
   ┌──────────────┼──────────────┬──────────────┐
   │              │              │              │
┌──┴───┐      ┌───┴──┐       ┌───┴──┐       ┌───┴──┐
│ Risk │      │ S1   │       │ S2   │       │ S3   │
│ Mgr  │◄────►│双因子 │       │Wheel │       │IC+LLM│
└──────┘      │cid=1 │       │cid=2 │       │cid=3 │
              └───┬──┘       └───┬──┘       └───┬──┘
                  │              │              │
                  └──────────────┼──────────────┘
                                 ▼
                    ┌──────────────────────┐
                    │  IB Gateway (4002)   │
                    │   Paper Account      │
                    └──────────────────────┘
共用独用
IB 连接同一个 Gateway(4002 端口)各自的 clientId(1/2/3)
Trade Log同一个 SQLite每条记录有 strategy_id
Risk Manager同一个 process,看 portfolio total各策略只能 query 自己的 limits
Dashboard一个 web UI按 strategy_id 分 tab
Cron 调度同一个 scheduler不同 cadence
资金账户同一个 paper 账户各策略有「名义分配额度」(NAV-share)

2.2 为什么不每个策略一个独立 Gateway

理论上可以——每个策略起一个 Gateway 进程,资源隔离。但有 3 个理由不做

  1. 保证金/可用资金是 account-level 的。3 个 Gateway 看到的 availableFunds 是同一个数,互不感知 → 容易超额下单。
  2. IBKR 每个账户只允许 N 个并发 session(通常 8 个,看账户类型)。一个 Gateway 用 3 个 clientId 比 3 个 Gateway 占 3 个 session 划算。
  3. 运维成本。3 个 Gateway 各自重启、登录、debug,比 1 个 Gateway 3 个 clientId 复杂得多。

结论:单 Gateway + 多 clientId 是个人量化的正确姿势。


三、并行运行的 5 个实操挑战

3.1 挑战 1:不同 rebalance cadence

策略Cadence触发条件
双因子月初第 1 个交易日 9:35 ET时间触发
Wheel每周一/周三 9:45 ET 检查 CSP 到期,每周二/周四检查 CC时间 + 持仓状态
IC+LLM财报前 5 个交易日窗口 + LLM 信号 > 阈值事件触发

问题:3 个策略不会在同一时刻跑,但可能在同一天跑(如月初遇到财报季)。

解法:所有策略走同一个 scheduler,注册 trigger,scheduler 统一在 work queue 里串行 dispatch:

# scheduler.py
schedule.every().day.at("09:35").do(lambda: enqueue("two_factor", "monthly_check"))
schedule.every().monday.at("09:45").do(lambda: enqueue("wheel", "csp_check"))
schedule.every().tuesday.at("09:45").do(lambda: enqueue("wheel", "cc_check"))
# IC 是事件驱动,单独的 listener 进程监听 earnings calendar

为什么串行:3 个策略同一秒下单会引发资金/保证金一致性问题(见 3.4)。串行的代价只是几秒钟延迟,对月度/事件级策略完全可接受。

3.2 挑战 2:clientId 管理

clientId 1 = 双因子    (只看自己的 order, position 用 strategy_id 标)
clientId 2 = Wheel
clientId 3 = IC+LLM
clientId 9 = 监控/只读(dashboard 用,不下单)

关键规则

  • 每个 clientId 通过 ib.reqOpenOrders() 只能看到自己提交的订单
  • ib.positions() / ib.accountSummary() 返回的是 account 全量——这是双刃剑:
    • 好处:能算总 Greeks / 总保证金
    • 坏处:必须靠自己的 trade log 区分「这个 SPY 100 股是哪个策略的」

实操坑:Wheel 标的可能是 AAPL,双因子选股可能也包含 AAPL → positions() 返回的 AAPL 总数无法判定归属。必须trade_log 里的 strategy_id 做"逻辑分账"。

3.3 挑战 3:保证金共享与"真实可用 buffer"

Paper 账户 $5,000,但3 个策略各自的 sizing 逻辑都假设有 $5,000 可用——会超额。

解法:名义分配 + 实时校验

# 配置层(人为分配)
STRATEGY_ALLOC = {
    "two_factor": 0.50,   # 50% 名义份额 = $2,500
    "wheel":      0.30,   # 30%          = $1,500
    "ic_llm":     0.20,   # 20%          = $1,000
}

# 下单前必查:
def can_strategy_trade(strategy_id, required_margin):
    nav = ib.accountSummary("NetLiquidation")
    allocated = nav * STRATEGY_ALLOC[strategy_id]
    used = trade_log.get_margin_used(strategy_id)
    avail = allocated - used

    # 还要检查 account-level buffer
    account_avail = ib.accountSummary("AvailableFunds")

    return (required_margin <= avail) and (required_margin <= account_avail * 0.9)

"真实可用 buffer" = min(strategy_quota_remaining, account_avail × 0.9)

留 10% account-level buffer 是因为:

  • 期权 short 仓的 margin requirement 会随 IV 上升动态扩大
  • 隔夜可能有 corporate action 改变 margin
  • 留 buffer 防 forced liquidation

3.4 挑战 4:同一时间下单的 race condition

虽然 scheduler 串行 dispatch,但异步 IB API 的 ack 不一定按序回来。场景:

09:35:00.001 双因子提交 BUY AAPL 10 股
09:35:00.002 系统看到 availableFunds 还没扣 → IC 误判可下单
09:35:00.005 IC 提交 SELL AAPL 财报 IC(占用 margin)
09:35:00.020 双因子的 fill 回来,扣 margin
09:35:00.030 IC 的 reject 回来:margin insufficient

解法:用 strategy-level mutex + optimistic margin reservation

PORTFOLIO_LOCK = threading.RLock()

def place_order(strategy_id, contract, order, est_margin):
    with PORTFOLIO_LOCK:                       # 1. 全局锁
        if not can_strategy_trade(strategy_id, est_margin):
            return None
        trade_log.reserve_margin(strategy_id, est_margin)  # 2. 预扣
        trade = ib.placeOrder(contract, order)
        trade.filledEvent += lambda t: trade_log.confirm_margin(t)
        trade.cancelledEvent += lambda t: trade_log.release_margin(t)
        return trade

锁的粒度是「下单决策 + 预占 这一小段」,不锁 ib.placeOrder 之后的等待——所以即便 3 个策略密集下单,锁只持有几毫秒,对吞吐无影响。

3.5 挑战 5:错误恢复与对账

3 个策略并发,进程崩溃后如何恢复是 Phase 3 最容易踩坑的地方。

故障检测恢复
Gateway 断连ib.isConnected() heartbeat自动重连 + replay 未确认订单
Dashboard 崩溃进程监控重启即可(无状态)
Trade log DB 锁住timeoutWAL 模式 + retry
某策略进程 OOMsystemd restart启动时 reconcile:拿 IB positions vs trade_log,差异报警
订单 fill 但 trade_log 没记启动 reconcilereqExecutions 拉今日 fill,按 orderId 回填

reconcile 是核心——任何重启后第一件事就是「拉 IB 的真实持仓 vs 我的本地账本,找差异」。这一条在 Phase 1 就该养成习惯。


四、portfolio_dashboard.py 核心代码

完整版 ~600 行,这里展示骨架。

4.1 数据层:trade_log schema

-- trade_log.db
CREATE TABLE trades (
    trade_id    INTEGER PRIMARY KEY,
    strategy_id TEXT NOT NULL,      -- 'two_factor' | 'wheel' | 'ic_llm'
    symbol      TEXT,
    secType     TEXT,               -- STK | OPT
    right       TEXT,               -- C | P | NULL
    strike      REAL,
    expiry      TEXT,
    action      TEXT,               -- BUY | SELL
    qty         REAL,
    fill_price  REAL,
    fill_time   TIMESTAMP,
    commission  REAL,
    margin_reserved REAL,
    notes       TEXT
);

CREATE TABLE pnl_daily (
    date        DATE,
    strategy_id TEXT,
    realized    REAL,
    unrealized  REAL,
    nav_share   REAL,
    PRIMARY KEY(date, strategy_id)
);

CREATE TABLE greeks_snapshot (
    ts          TIMESTAMP,
    strategy_id TEXT,
    net_delta   REAL,
    net_gamma   REAL,
    net_theta   REAL,
    net_vega    REAL
);

4.2 实时计算引擎

# portfolio_dashboard.py
from ib_insync import IB
import pandas as pd, numpy as np
from flask import Flask, jsonify, render_template

ib = IB()
ib.connect('127.0.0.1', 4002, clientId=9)   # read-only

app = Flask(__name__)

def get_portfolio_snapshot():
    """Pull everything we need in ~200ms."""
    positions = ib.positions()
    summary   = {a.tag: a.value for a in ib.accountSummary()}

    # 用 trade_log 把 positions 拆到 strategy 层
    log = pd.read_sql("SELECT * FROM trades", conn)
    pos_by_strategy = attribute_positions(positions, log)

    # 计算 Greeks(需要 modelGreeks,对每个 option contract 拉一次)
    greeks = {}
    for sid, pos_list in pos_by_strategy.items():
        greeks[sid] = aggregate_greeks(pos_list, ib)

    return {
        "ts": pd.Timestamp.utcnow().isoformat(),
        "nav": float(summary["NetLiquidation"]),
        "cash": float(summary["TotalCashValue"]),
        "available": float(summary["AvailableFunds"]),
        "maintenance_margin": float(summary["MaintMarginReq"]),
        "by_strategy": {sid: {
            "pnl_realized": pos_by_strategy[sid]["realized"],
            "pnl_unrealized": pos_by_strategy[sid]["unrealized"],
            "delta": greeks[sid]["delta"],
            "theta": greeks[sid]["theta"],
            "vega":  greeks[sid]["vega"],
        } for sid in pos_by_strategy},
        "portfolio": {
            "net_delta": sum(g["delta"] for g in greeks.values()),
            "net_theta": sum(g["theta"] for g in greeks.values()),
            "net_vega":  sum(g["vega"]  for g in greeks.values()),
            "var_95":    compute_parametric_var(positions, conf=0.95, horizon_days=1),
        }
    }

@app.route("/api/snapshot")
def api_snapshot():
    return jsonify(get_portfolio_snapshot())

@app.route("/api/correlation")
def api_correlation():
    pnl = pd.read_sql("SELECT date, strategy_id, realized+unrealized AS pnl FROM pnl_daily", conn)
    pivot = pnl.pivot(index="date", columns="strategy_id", values="pnl")
    return jsonify(pivot.corr().to_dict())

@app.route("/api/report/<date>")
def api_daily_report(date):
    """Generate a PDF day report. Calls weasyprint to render Jinja → PDF."""
    snap = get_portfolio_snapshot()
    corr = pnl_correlation()
    html = render_template("daily_report.html", snap=snap, corr=corr, date=date)
    pdf_path = f"./reports/{date}.pdf"
    HTML(string=html).write_pdf(pdf_path)
    return jsonify({"pdf": pdf_path})

if __name__ == "__main__":
    app.run(port=8888)

设计要点

  1. Dashboard 是只读的(clientId=9,没有下单权限)。
  2. 快照式——每次 HTTP 请求重新拉,不缓存。简单可靠,2 周内没出过状态不一致 bug。
  3. PDF 日报用 Jinja 模板渲染,weasyprint 转 PDF。每天 16:30 自动生成。

4.3 VaR 计算(参数法)

def compute_parametric_var(positions, conf=0.95, horizon_days=1):
    """Simplified: assume returns ~ N(0, sigma), portfolio variance from covariance matrix."""
    # 1. 拉近 60 个交易日的 daily returns
    rets = fetch_returns([p.contract.symbol for p in positions], lookback=60)
    cov  = rets.cov() * horizon_days
    # 2. 持仓权重(美元名义)
    weights = np.array([p.position * p.marketPrice for p in positions])
    port_var = weights @ cov.values @ weights
    port_sigma = np.sqrt(port_var)
    # 3. z-score
    from scipy.stats import norm
    z = norm.ppf(conf)
    return z * port_sigma

已知简化

  • 没考虑期权的非线性(用 delta 等价正股近似,IC 这种非线性结构会被低估)
  • Phase 3 要换成 Monte Carlo + full revaluation

五、2 周表现(模拟数据 / Paper)

时间窗口:2026-06-22 ~ 2026-07-05(10 个交易日)。

5.1 各策略 P&L

策略起始名义 NAV终值收益vs 基准备注
双因子$2,500$2,520+0.80%SPY +0.50%跑赢 SPY 30bp,主要来自 quality 因子
Wheel$1,500$1,522.50+1.50%(covered SPY +0.5%)premium 收入贡献 90bp,underlying 微涨贡献 60bp
IC+LLM$1,000$997-0.30%(vol 持平)1 单 surprise miss 亏 $35,其它 4 单赚 $32
组合$5,000$5,039.50+0.79%SPY +0.5%年化外推 ~18%(乐观估算,警示见 §11

5.2 P&L 时序(简化日表)

日期       双因子    Wheel    IC+LLM   组合     SPY
06-22(一)   -3.20    +1.50    -2.00    -3.70   -0.31%
06-23(二)   +8.50    +2.80    +0.00    +11.30  +0.62%
06-24(三)   +2.10    +12.00   +0.00    +14.10  +0.18%  ← Wheel CSP expire OTM 收 premium
06-25(四)   -5.50    +1.20    -15.00   -19.30  -0.42%  ← IC 财报 miss 调腿
06-26(五)   +4.80    +2.10    +12.00   +18.90  +0.35%
06-29(一)   +6.30    +1.50    -8.00    -0.20   +0.21%
06-30(二)   +5.50    +2.00    +5.00    +12.50  +0.18%
07-01(三)   -8.00    +0.50    -3.00    -10.50  -0.51%  ← 双因子月度 rebalance 摩擦成本
07-02(四)   +12.00   +1.80    +8.00    +21.80  +0.43%
07-03(五)   +2.50    +0.10    +0.00    +2.60   +0.07%
─────────  ──────  ──────  ──────  ──────  ──────
合计:      +20.00  +22.50   -3.00   +39.50  +0.50%

5.3 Max DD 与回撤

指标备注
Portfolio max DD-1.20%06-25 财报 miss 当天最深
双因子 max DD-0.50%月度策略波动小
Wheel max DD-0.18%几乎单调上升
IC max DD-2.10%单笔 IC vega 风险大
Sharpe(年化外推)~2.1样本太短,仅供参考

六、Greeks 实时监控(2 周观察)

6.1 监控范围

每 15 分钟取一次 snapshot,2 周共 ~270 个数据点。

Greek范围中位数解读
Net Delta+85 ~ +180+130轻度多头,主要来自双因子 long-only 股票仓
Net Gamma-3 ~ +1-1.2略微负(卖了 Wheel CC + IC 短腿)
Net Theta+$22 ~ +$35/day+$28/day稳定收 theta — 健康
Net Vega-$80 ~ -$160-$120short vol 倾向 — Wheel + IC 共同贡献

6.2 "轻 long + 收 Theta" 配置健康吗

健康,但需明确边界

优势风险
Theta 每天稳定 +$28(年化 ~$7,000 / NAV $5k ≈ 140%——明显高估,因为 theta 是非线性 decay)Vega -$120 意味着 IV 每涨 1 vol 点损 $120
Delta 正向跟 SPY 有 partial 相关,市场涨时双因子也涨但 gamma 微负 → 大波动反向时损失加速
多策略相关性 < 0.6,分散有效都是 short vol 暴露 → "在同一边"

关键认知:「轻 long + 收 theta」的隐含假设是 vol 不会突然飙升。如果 VIX 从 15 跳到 25,组合可能一天损失 $1,200(vega × 10),相当于 24% 的 NAV——这是必须设的 hard stop

6.3 设置的 Greek-level 风控线

RISK_LIMITS = {
    "net_delta_max": 250,      # 总名义 long 不超过 250 delta
    "net_delta_min": -50,      # 不允许净 short
    "net_vega_min": -200,      # short vol 上限
    "net_theta_min": 0,        # 不允许 net negative theta(除非临时调腿)
    "single_position_pct": 0.15,  # 单一头寸不超 NAV 15%
}

2 周内没有触发任何 hard limit——但这不代表风控对——而是市场温和。Phase 3 应该故意做一次触发演练(人为加大 IC 仓位直到 vega 越线)来测试 alert 链路。


七、相关性实测:N_eff 才是真分散度

7.1 实测相关性矩阵

用 10 个交易日的 daily P&L(样本极小,仅做演示):

              双因子    Wheel    IC+LLM
双因子        1.00     0.55     0.15
Wheel         0.55     1.00     0.20
IC+LLM        0.15     0.20     1.00

7.2 N_eff 计算

有效策略数 = (Σw)² / (w' Σ w),其中 Σ 是相关矩阵。

权重 [0.50, 0.30, 0.20]:

import numpy as np
corr = np.array([
    [1.00, 0.55, 0.15],
    [0.55, 1.00, 0.20],
    [0.15, 0.20, 1.00]
])
w = np.array([0.50, 0.30, 0.20])
n_eff = (w.sum())**2 / (w @ corr @ w)
# = 1.0 / 0.477 = 2.10

N_eff = 2.10——3 个策略只相当于 2.1 个独立策略。

7.3 为什么不是 3.0

  • 双因子和 Wheel 都做股票多头方向 → 0.55 相关合理
  • IC 是 vol 维度,跟方向策略弱相关 → 0.15-0.20 合理
  • 要把 N_eff 推到 2.5+,需要加入 negatively correlated 策略:
    • 趋势跟随(CTA-like)— 在 risk-off 时反向受益
    • Long vol(保护性买 VIX call)— 但成本高
    • Mean-reversion 短线 — 与 momentum 反向(双因子的 mom 部分会受影响)

Phase 3 的方向:考虑加一个低相关的趋势/mean-reversion 策略,把 N_eff 推到 2.5。


八、2 周复盘 Lessons

8.1 策略层

策略表现符合预期?关键观察
双因子✓ 稳定月度 turnover ~35%,佣金 + slippage 吃了 ~15bp,对 $2,500 的小盘子摩擦显著
Wheel✓✓ 超预期06 月底 IV 较高(VIX 19),premium 比模型估值高 12%。但这是 regime-dependent——低 IV 时收益会减半
IC+LLM△ 单笔波动大5 单里 1 单财报 surprise miss 抹平了其它 4 单的利润。N=5 完全没统计意义,需要至少 N=30 才能判断 edge

8.2 工程层

类别观察
集成测试 vs 单元测试集成阶段发现的 bug 比策略各自跑时多 6 倍(详见 §10):margin 预扣 race / clientId 看不到对方订单 / SQLite WAL 模式没开
Dashboard 价值每天看一眼实时 Greeks 比看 P&L 有用 10 倍——能预判风险而不是事后归因
PDF 日报写完才发现:自己几乎不看历史报告。Phase 3 改成「只在异常时生成 PDF + 周末做一次汇总」

8.3 心理层(重要)

  • 06-25 IC miss 当天我下意识想手动平掉所有 IC 仓——这是典型的 loss aversion。如果实盘下会破坏系统。
  • 解决:实盘前必须把"手动 override"的门槛拉高——比如手动平仓需要二次确认 + 写理由到 log。

九、是否准备好实盘?— 5 维度评估

维度状态证据距离实盘的差距
流程跑通2 周 e2e 无人工干预0
风控触发测试风控线设了,但从未真触发必须做 1 次故意触发演练
Greeks 监控自动化270 次 snapshot 全部记录0
错误恢复Gateway 重连自动化了;但没测过 trade_log DB 损坏 + reconcile需要做 1 次"杀进程 + 重启 reconcile"
心态承受06-25 当天想手动干预这是最大的未知——paper 没真金白银压力

9.1 结论:再 2-4 周 paper

不建议下周直接上实盘。理由:

  1. 样本量:IC 只跑了 5 单,统计上完全无意义。
  2. 未触发风控:风控线没被真测过,等于没有。
  3. 心态:paper trading 测不出真实压力反应。

9.2 上实盘前 must-have checklist

  • 再跑 2-4 周 paper
  • 人为触发所有 hard stop 至少 1 次(vega 越线 / delta 越线 / 单仓越限)
  • 杀进程 + 重启 reconcile 测试 ≥ 3 次
  • 实盘第一周只跑双因子(最稳定)+ 金额减半($2,500 真金)
  • 实盘到第 4 周再加 Wheel
  • 实盘到第 8 周再加 IC(前提:再 paper 25+ 单 IC 看 N>=30 的统计表现)

十、PM 视角:综合演练教会我们什么

10.1 "Integration test 比 unit test 暴露 10 倍问题"

我在 fintech 做了 10 年 PM/BA,反复见证这个规律:

测试层找到的 bug 类型数量级
Unit test函数逻辑错 / 边界条件基线 1x
Integration test模块间接口 / 状态共享 / 时序3-5x
End-to-end / Beta跨系统耦合 / 真实数据 / 错误恢复10x
生产环境长尾场景 / 罕见组合 / 性能退化30x

对应到 Day 58:单策略 paper 时只有少量 bug,3 策略并行 paper 立刻冒出 6 个新问题

  1. 同一个 AAPL 在双因子和 Wheel 同时存在 → position 归属不明
  2. Wheel 短 put 占的 margin 让双因子 reject 下单
  3. SQLite 默认 journal mode 在并发写时锁死
  4. Dashboard 拉 modelGreeks 太慢导致 API rate limit
  5. 财报日 IC 触发跟双因子月初触发冲突
  6. 重启后 trade_log 比 IB 实际持仓少 1 笔(race 时 ib.placeOrder 返回但 callback 没触发)

核心 takeaway:「Phase 1 + Phase 2 上半把单元做完」 → 「Phase 2 下半的综合演练」 → 「Phase 3 再上实盘」——这个三段式不能跳,跳了就是用真金白银做 e2e 测试。

10.2 "可观测性 = 安全感"

2 周下来,最值的投入不是策略代码,而是 dashboard

  • 每天早上花 30 秒看一眼 Greeks → 知道今天有没有 abnormal 暴露
  • 周末花 5 分钟看 correlation → 知道分散度有没有恶化
  • 财报日盯一下 IC 仓的 vega → 提前知道下单时机

金融系统里,"看得见"比"算得准"更值钱——后者你可以买现成工具,前者必须自建。

10.3 "策略相关性 = 真正的容量上限"

很多人以为加策略就能加规模——N_eff < N 的本质是:当 3 个策略都在 short vol 时,它们其实是一个策略放了 3 个名字

实盘扩规模的正确路径:

N_eff = 2.1(今天) → 找一个 ρ ≈ -0.3 的策略 → N_eff = 2.8 → 才能上 2x 名义规模

而不是「Wheel 表现好就加仓 3 倍」。这是 Phase 3 / 4 必须想清楚的事。

10.4 "Paper 跑得好不能证明实盘能赚——但 Paper 跑不通一定证明实盘做不了"

Paper 是淘汰赛不是选拔赛。它能让我们排除:

  • 流程跑不通的策略(剔除)
  • 工程上有 race / 重启问题的实现(剔除)
  • 风控完全没设的策略(剔除)

但 Paper 不能告诉我们

  • 真实滑点
  • 自己的心态承受边界
  • Black swan 时的行为

这两点只有实盘小金额能教。


十一、限制 / 假设 / 警示

写在前面让自己别忘:

说明
样本量10 个交易日 / IC 5 单 / Wheel 2 单——统计上无意义,所有结论是"流程验证级"而非"alpha 验证级"
基准SPY 同期 +0.5% 是巧合,不代表组合稳定超额
年化外推+0.79% × 26 ≈ 20%——绝不能这样推,paper 没滑点没 IV 反应没真实 fill
GreeksmodelGreeks 来自 IBKR 内置定价,IC 这种 4 腿组合在尾部行情可能偏离 10%+
VaR用参数法 + delta 等价,严重低估期权尾部风险——Phase 3 必换 MC
相关性N=10 算出的 corr 标准误 ~0.3,0.55 和 0.15 的差距在统计上其实不可区分——只能定性参考

十二、Day 58 执行 Checklist

  • (1) 把 Day 36 / 42 / 49 的代码合并进 momoweb3/quant/portfolio/ 目录
  • (2)trade_log.db(SQLite WAL 模式)+ schema migration 脚本
  • (3) scheduler.py 注册 3 个策略的 trigger
  • (4) 给每个策略加 clientId + strategy_id 参数
  • (5) 实现 can_strategy_trade() + PORTFOLIO_LOCK
  • (6) portfolio_dashboard.py web UI 跑起来(port 8888)
  • (7) 启动 paper 模式,让 3 个策略各自按 cadence 跑 2 周
  • (8) 每日 16:30 cron 自动生成 PDF 日报
  • (9) 写 2 周复盘 → 本笔记 §8 落地
  • (10) 评估实盘准入 → §9 5 维度填实
  • (11) 列出"上实盘前 must-have" 清单 → §9.2

十三、明日预告

Day 59: Phase 2 总结 — 28 天策略实战的横向复盘

  • 把 Day 31 ~ Day 58 这 28 天的所有策略(双因子 / Wheel / IC+LLM / 综合演练)做横向对比
  • Phase 2 的硬产出清单:3 个 paper-ready 策略 + 1 个 portfolio dashboard + N 篇笔记
  • Phase 2 学到的"反直觉认知" Top 10
  • Phase 3 路线图预告:实盘 onboarding / 实盘第一笔交易 SOP / 实盘前 30 天的 monitoring
  • 跟金融 BA / PM 视角的迁移性思考:从"个人量化"到"机构级 risk-aware 系统"还差什么

实际执行记录

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

  • [hh:mm] 代码合并 — ...
  • [hh:mm] trade_log.db 建表 — ...
  • [hh:mm] scheduler 配置 — ...
  • [hh:mm] portfolio_dashboard 启动 — ...
  • [hh:mm] Day 1 paper 三策略并行运行 — ...
  • [hh:mm] 第 1 周复盘 — ...
  • [hh:mm] 第 2 周复盘 + 实盘准入评估 — ...
  • 卡点 / 学到的:

总字数:约 6,800 字 今日完成度:理论 ✓ / 实操(你自己跑 2 周 paper) / 笔记 ✓