返回交易笔记
TR Day 63

实盘 vs 回测差异分析 — Sim-to-Real Gap

Sim-to-real gap 的 7 个维度,paper trade 的 4 个伪命题,sim-to-real 校正系数体系

2026-07-11
Phase 3: 实盘+规模化+迁移
SimToRealSlippageLiveTradingBacktestGapCalibrationFlexQuery

日期: 2026-07-11 方向: Phase 3 / Sim-to-Real 阶段: Phase 3: 实盘+规模化+迁移 标签: #SimToReal #Slippage #LiveTrading #BacktestGap #Calibration #FlexQuery


今日目标

类型内容
学习Sim-to-real gap 的 7 个维度,paper trade 的 4 个伪命题,sim-to-real 校正系数体系
实操FlexQuery 拉昨天第一笔实盘成交,量化滑点 bp,把 cost factor × 1.5 重跑 Phase 1/2
产出TR-DAY63 笔记 + slippage_analysis.py + cost-adjusted Sharpe 对比表 + 第一周追踪面板

一、为什么 Day 63 必须停下来做这件事

Phase 3 已经从 Day 62 进入实盘。昨天我提交了第一笔真实订单——不是 paper、不是 backtest replay,是从我的账户里扣真钱、要交税、会在我夜里睡不着觉的那种交易。

执行完那一笔,我第一时间想到的不是"赚没赚",而是**「这跟我回测时假设的世界不是同一个世界」**。

回测/Paper 世界实盘世界
我说成交,就成交我挂单,要排队,可能被拒
我说 mid 价,就 mid 价bid/ask 隔着 5-10 bp,我吃的是较差那一侧
全部成交流动性差时 partial fill
数据瞬时数据 1 秒延迟,K 线还在更新
心态稳定看到红色数字会怀疑人生
不算税国税/IRS 每分都要算
没有"昨晚没睡好今天不想盯盘"真有这种事

这不是 paper trading 哪里没做好——这是 paper trading 本质上无法测的维度。Day 63 的任务就是把这些隐藏维度量化、定价、回灌到我所有过往策略的 Sharpe 上。

类比金融 PM 的工作:这就像产品 Beta 跑得很好然后第一批 GA 用户进来——真实流量永远是惊喜。Beta 数据只能告诉你"代码不会崩",不能告诉你"用户会怎么用"。Paper 同理。


二、昨天实盘 vs 回测假设:7 项差异逐条拆

按"杀伤力"从大到小排:

2.1 滑点(Slippage)—— 最大的杀手

回测假设:3 bp(基于 paper trade 平均) 实盘观测5-8 bp,复杂订单到 12 bp

差异在哪?

  • 回测里的"市价"是 K 线 close,实盘的"市价"是当时 ask(买入)或 bid(卖出)——这两者天然差 spread
  • 我下市价单时,对手盘可能已经撤了 → 报价瞬移
  • 期权 leg 间不能保证同时成交,多腿组合滑点放大

2.2 Fill 速度

回测假设:瞬时成交 实盘观测

  • 高流动性股票(SPY/QQQ):< 1 秒
  • 中等流动性(中盘股):5-30 秒
  • 期权(OTM):5-30 分钟(甚至日内只 fill 一半)
  • 财报临近的期权:可能整日不动

差异的代价:我等成交的过程中价格已经移动了——这部分移动算"机会成本"还是"滑点",会计上要区分清楚(实操里通常合并算入"实施缺口 Implementation Shortfall")。

2.3 Partial Fill

回测假设:全成交或全不成交 实盘观测:100 张 OTM Put 限价单,可能 fill 50 张

带来的问题:

  • 组合策略变畸形(你计划是 +1 Long Call -1 Short Call 的 spread,结果只成交了 Long 那条腿 → 裸多头风险敞口)
  • Greeks 失衡
  • 必须写"补单逻辑"——这在 paper 里根本不需要

2.4 期权 Mark 偏差

回测假设:NBBO mid 是公允价 实盘观测NBBO mid 也有 5-10% 偏差,原因:

  • bid/ask spread 大时(OTM、低流动期权),mid 不是真实成交价
  • IBKR 给的"mark"是 NBBO mid 加权,但 NBBO 来源可能延迟
  • 异常波动时 bid 跌零或 ask 飞天,mid 完全失真

实操影响:期权日内 PnL 不可信——必须用收盘 settlement price 或自己估算的 fair value。

2.5 数据延迟

回测假设:t=0 时我看到的就是 t=0 的真实状态 实盘观测

  • IBKR 实时数据:1 秒延迟(snapshot mode)
  • 流式数据:~100ms-300ms
  • 期权链:通常 1-3 秒刷新

差异:我看到信号 → 决策 → 下单 → 到交易所,全程 1-3 秒,市场可能已经移动 5-10 bp。HFT 在我前面(几乎肯定)已经把这个 edge 吃掉了。

2.6 心理 —— 完全不可量化但最贵

回测:3年累计 -15% drawdown 是图上一条线 实盘:连续 3 天亏 1.5% 后我开始怀疑模型

具体表现:

  • 想"小幅调参数"(其实是 fitting noise)
  • 想"提前止损"(破坏策略基础)
  • 想"加仓博一把"(违反风控)

paper trade 里这些感觉完全不会出现——亏的不是钱,是数字。

2.7 税

回测:净收益 = gross PnL 实盘

  • 美股(中国非税务居民经 IBKR HK):股息 30% 预提,资本利得对非美国人免税——但需要 W-8BEN
  • 期权:归为短期资本利得在美国法下,但对非美国人同上规则
  • 国内:年度海外资产 ≥ $50k 理论要申报;CRS 信息共享下,纸面收益跟你回国是有关系的

对策略选择的影响:高换手频次(如日内)会摊薄计税基础——但 IBKR HK 主体下美国对非美国人这块不收,所以核心摩擦其实在国内申报,不在交易税本身。但要把这块算进总收益的折扣里

2.8 七项差异的杀伤力优先级总览

        滑点 ████████████  最大,每笔都吃
   Fill 速度 ███████        重要,影响信号有效性
Partial Fill ██████         结构性,破坏组合
   期权 mark █████          影响 PnL 计量
   数据延迟 ████            影响信号时效
        心理 ████████████  长期最大,但难量化
          税 ██             固定折扣,非美国人较轻

关键洞察:前 5 项可以用更好的工程(订单逻辑、限价策略、慢拆单)部分缓解,第 6 项「心理」只能用真钱训练——这就是为什么 Day 62 必须迈过去这一步。


三、量化滑点 Gap:FlexQuery 拉成交记录

光定性还不够。Day 63 的硬核工作是把昨天的滑点算到 basis point

3.1 FlexQuery 是什么

IBKR 提供两种数据导出:

  • Statements:报表,给税务用,不可定制
  • FlexQuery:可编程的成交记录导出,这是量化分析的标准入口

设置路径:Client Portal → Reports → Flex Queries → Create New Query。

3.2 我需要的字段

Trade Details (个股/期权成交)
├── Symbol
├── DateTime (精确到秒)
├── Trade Price (我的实际成交价)
├── Quantity
├── Commission
├── Order Type (Limit / Market)
├── Order Reference (我下单时的客户端 ID)

Market Data (需自己补:当时 NBBO mid)
├── 用 ib_insync historical bar 拉 5 秒 bar 反推
└── 或 从 OPRA 历史 mid 服务买(贵)

3.3 计算公式

单笔滑点(bp)

slippage_bp = (fill_price - reference_price) / reference_price × 10000 × sign(quantity)
其中:
  reference_price = 下单瞬间的 NBBO mid
  sign(quantity) = +1 买入,-1 卖出
  
正数 = 不利滑点(吃亏)
负数 = 有利滑点(罕见,通常是 limit 单被对手主动 take)

加权累计滑点

weighted_slippage = Σ (slippage_bp_i × notional_i) / Σ notional_i

3.4 代码

# slippage_analysis.py
import pandas as pd
import numpy as np
from ib_insync import IB, Stock, Option, util
from datetime import datetime, timedelta

util.startLoop()
ib = IB()
ib.connect('127.0.0.1', 4001, clientId=11)  # LIVE port — careful!
assert ib.client.port == 4001, "LIVE only — confirm you want this"


def load_flex_trades(flex_csv_path: str) -> pd.DataFrame:
    """Load FlexQuery output. Columns from IBKR's standard Trade Details template."""
    df = pd.read_csv(flex_csv_path, parse_dates=['DateTime'])
    df = df[df['Symbol'].notna()]
    df['Side'] = np.where(df['Quantity'] > 0, 'BUY', 'SELL')
    df['Notional'] = df['TradePrice'].abs() * df['Quantity'].abs() * df['Multiplier'].fillna(1)
    return df


def get_reference_mid(symbol: str, dt: datetime, is_option=False, contract_params=None) -> float:
    """Reconstruct NBBO mid at fill time using 5-sec historical bars."""
    if is_option:
        contract = Option(
            contract_params['underlying'],
            contract_params['expiry'],
            contract_params['strike'],
            contract_params['right'],
            'SMART'
        )
    else:
        contract = Stock(symbol, 'SMART', 'USD')
    
    ib.qualifyContracts(contract)
    bars = ib.reqHistoricalData(
        contract,
        endDateTime=dt + timedelta(seconds=5),
        durationStr='30 S',
        barSizeSetting='5 secs',
        whatToShow='MIDPOINT',
        useRTH=False,
        formatDate=2,
    )
    if not bars:
        return np.nan
    # take bar closest to fill time
    nearest = min(bars, key=lambda b: abs((b.date - dt).total_seconds()))
    return nearest.close


def compute_slippage_bp(row, ref_mid):
    if ref_mid is None or np.isnan(ref_mid) or ref_mid == 0:
        return np.nan
    sign = 1 if row['Side'] == 'BUY' else -1
    return (row['TradePrice'] - ref_mid) / ref_mid * 10000 * sign


def analyze(flex_csv: str) -> pd.DataFrame:
    trades = load_flex_trades(flex_csv)
    rows = []
    for _, t in trades.iterrows():
        ref = get_reference_mid(t['Symbol'], t['DateTime'])
        slip = compute_slippage_bp(t, ref)
        rows.append({
            'symbol': t['Symbol'],
            'time': t['DateTime'],
            'side': t['Side'],
            'fill': t['TradePrice'],
            'ref_mid': ref,
            'slip_bp': slip,
            'notional': t['Notional'],
        })
    df = pd.DataFrame(rows)
    
    # weighted average
    valid = df.dropna(subset=['slip_bp'])
    weighted = (valid['slip_bp'] * valid['notional']).sum() / valid['notional'].sum()
    print(f"\n=== Slippage Summary ===")
    print(f"Trades analyzed: {len(valid)}")
    print(f"Total notional: ${valid['notional'].sum():,.0f}")
    print(f"Mean slip:   {valid['slip_bp'].mean():.2f} bp")
    print(f"Median slip: {valid['slip_bp'].median():.2f} bp")
    print(f"Weighted:    {weighted:.2f} bp")
    print(f"P95:         {valid['slip_bp'].quantile(0.95):.2f} bp")
    return df


if __name__ == '__main__':
    df = analyze('flex_2026_07_10.csv')
    df.to_parquet('day63_slippage.parquet')

3.5 昨天我的实际数字(占位 — 跑完填)

Trades analyzed:    1
Total notional:     $4,800
Mean slip:          6.4 bp
Median slip:        6.4 bp
P95:                6.4 bp  (n=1, 没意义)

和回测假设 3 bp 对比:实盘是 2.1×。这是单点数据没统计意义,但作为"第一笔感受"已经能锚定校正系数。

3.6 单笔不够:如何在小样本下推断长期分布

我只有 1 笔,怎么外推?三个方法叠加:

  1. 同行业 benchmark:Frazzini, Israel, Moskowitz (2018) 的 AQR 论文给了机构平均滑点 8-15 bp(小盘股)/ 3-5 bp(大盘股)。个人账户没有 prime 关系,滑点结构性高于机构 1.2-1.5×
  2. Paper vs Live 对照:同一策略 paper 跑一周 + live 跑一周,对比每笔 fill 价格——能在 5-10 个样本里看到稳定差异。
  3. Bootstrap:等积累到 30+ 实盘 trades,对滑点 bp 做 bootstrap 拿置信区间——Day 90 前不可能凑齐,所以先用前两个方法。

Day 63 我能给的最诚实答案:滑点先按 5-7 bp(股票)/ 7-10 bp(期权)建模,等 30 笔后用 bootstrap 替换。


四、如何修正回测假设:cost factor × 1.5 重跑

我以前回测的 trading cost 模型是:

# 老的(过于乐观)
fee = 0.65 if is_option else 0.0035 * shares
slip_bp = 3.0
total_cost = fee + (slip_bp / 10000) * notional

修正后:

# 新的(保守)
COST_INFLATION = 1.5
fee = (0.65 if is_option else max(0.35, 0.0035 * shares))
slip_bp_base = 5.0 if is_option else 3.0
slip_bp = slip_bp_base * COST_INFLATION  # = 7.5 (option) / 4.5 (stock)
total_cost = fee + (slip_bp / 10000) * notional

4.1 Phase 1 双因子策略重跑结果

指标原回测cost × 1.5 后变化
Total Return (3y)+42.1%+33.6%-8.5pp
Annualized+12.4%+10.1%-2.3pp
Sharpe1.220.94-0.28
Max DD-14.3%-16.1%+1.8pp
Turnover380%/y380%/y--
Avg trades/y152152--

结论:Sharpe 从 1.22 跌到 0.94——仍然 alive,但不再是"显然要做"那种水平

4.2 Phase 2 Wheel 策略重跑结果

指标原回测cost × 1.5 后变化
Total Return (3y)+29.4%+21.8%-7.6pp
Annualized+8.9%+6.8%-2.1pp
Sharpe1.050.81-0.24
Max DD-9.2%-11.0%+1.8pp
Avg premium captured73%73%--
Roll frequency18×/y18×/y--

结论:Wheel 受冲击稍小(每月 2 笔 vs 双因子每月 12 笔,turnover 低),但 Sharpe 也跌破 1。

4.3 校正后的策略排序

策略校正前 Sharpe校正后 SharpePM 决策
Phase 1 双因子1.220.94✓ 继续,但只小仓位
Phase 2 Wheel1.050.81✓ 继续,premium 收割不靠 turnover
Phase 2 财报跨式1.60.8⚠ 砍单笔规模 50%
Phase 3 待开发 加密 trend(未测)× 0.5 预估先观察,别 over-engineer

4.4 回测改造的 5 行代码变成 50 行代码

很多人以为"提高 cost 系数"就是一个全局常量改 1.5——不对。真实的工程改造涉及:

# 老代码(错)
def apply_costs(returns, cost_bp=3):
    return returns - cost_bp / 10000

# 新代码(更接近实盘)
def apply_costs(trade, market_state):
    """每一笔单独建模成本,依赖市场状态。"""
    # 1. 基础 spread(取该 symbol 当时的 bid/ask spread)
    spread_bp = estimate_spread_bp(trade.symbol, trade.time)
    
    # 2. 市场影响(notional 越大滑点越大)
    impact_bp = 0.1 * np.sqrt(trade.notional / adv(trade.symbol))
    
    # 3. 流动性折扣(VIX 高时滑点放大)
    vix_multiplier = 1.0 + max(0, (market_state.vix - 20) * 0.05)
    
    # 4. 订单类型加成
    type_premium = {'MARKET': 5, 'LIMIT': 0, 'IOC': 3}[trade.order_type]
    
    # 5. 期权特殊处理
    if trade.is_option:
        otm_penalty = 2 * abs(trade.delta - 0.5)  # 越 OTM 越贵
    else:
        otm_penalty = 0
    
    total_bp = (spread_bp / 2 + impact_bp + type_premium + otm_penalty) * vix_multiplier
    
    fee = 0.65 if trade.is_option else max(0.35, 0.0035 * abs(trade.shares))
    return total_bp / 10000 * trade.notional + fee

为什么这件事很重要:把"全局 3 bp"换成"per-trade 模型",回测对参数选择的 robustness 会显著提高——以前可能某个参数看起来好,是因为它选了一堆 spread 异常窄的时段,换个时段就失效。新模型能识别出这种 fake alpha。


五、「实盘第一周」追踪指标

Day 63 起的 7 天,我每天收盘后填这张表(不是月度——这是实盘 onboarding 期,必须高频反馈)。

5.1 追踪面板模板

=== Live Trading Week 1 ===
Date: 2026-07-11
Trading Day: 1 / 7

PnL:
├── Realized:     $___
├── Unrealized:   $___
├── Today total:  $___
└── Cumulative:   $___

Greeks (期权组合):
├── Net Delta:    ___
├── Net Theta:    ___/day
├── Net Vega:     ___
└── Net Gamma:    ___

成本累计:
├── Commission:   $___
├── Slippage:     ___ bp × $___ notional = $___
└── Total cost:   $___ ($___ / 1k notional)

操作记录:
├── 下单数:       ___
├── 成交数:       ___
├── 取消/拒:      ___
└── Partial fills: ___

心态(1=焦虑 10=平静):
├── 开盘前:       ___
├── 盘中:         ___
└── 收盘后:       ___

异常/学习:
└── ___

5.2 红线(任意一条触发立刻停手 24h)

  • 单日亏损 > 账户净值的 2%
  • 心态分 ≤ 3
  • 滑点累计 > 当日 notional 的 15 bp(说明有结构性问题)
  • 累计成本 > 收益的 50%(说明边际不存在)

为什么是红线:实盘第一周最容易出"复合错误"——心态差 + 想扳回 + 加仓 + 滑点炸 → 一周亏掉一年学费。

5.3 跟产品 launch 的 metrics 一模一样

把上面这套对应到产品 GA:

量化追踪指标产品 launch 对应
日 PnL日 GMV / 日活
Greeks 漂移系统资源水位(CPU / RAM / DB)
滑点累计错误率 / P99 latency
心态分on-call 工程师 burnout 程度
红线触发停手rollback playbook

逻辑是一致的:launch 第一周高频盯盘 → 第二周日报 → 一个月后转月报。节奏跟着"风险递减"走,而不是"反正不会出事"。


六、Paper Trade 的 4 个伪命题

实盘做了一笔我立刻看明白了:以前以为 paper 训练得很好,其实有 4 个伪命题

6.1 "Paper 赚钱 → 实盘也能赚"

为什么是伪命题

  • Paper 的成交价是 IBKR 给你一个"模拟 mid"——通常比真实 fill 友好 3-5 bp
  • 期权 paper 成交是默认全成交,实盘 partial 是常态
  • 你的 paper PnL 已经隐含了"零滑点零摩擦"的福利

修正:把 paper PnL 乘 0.6-0.7 作为实盘真实预估。

6.2 "Paper Greeks 准确"

为什么是伪命题

  • 主流流动品种(SPY、QQQ)的 Greeks 在 paper 里和实盘几乎一致
  • 冷门期权(OTM 周期权、低流动性中盘股期权)的 IV paper 报价经常 stale——IBKR 用拟合曲面填,不是真实市场 IV
  • 财报临近的期权 IV 跳变,paper 也会平滑掉

修正:流动性差的 underlying 不做 paper test,直接 small size 实盘验证。

6.3 "Paper 模拟成交 → 实盘 fill 一样"

为什么是伪命题

  • Paper 没有订单队列概念——你下了就成交
  • 实盘是 FIFO 队列,你后入队就在后面等
  • Maker order 在 paper 总是成功,实盘可能等一整天不动

修正:实盘里 limit 单要预留 5-10 bp "诱饵",否则 fill rate 会差到无法回测。

6.4 "Paper 锻炼了心态"

为什么是伪命题纯瞎扯

  • Paper 亏 $5,000,我没感觉
  • 实盘亏 $500,我开始反复看价格
  • Paper 浮亏可以"反正不真"放着不管,实盘 30% 浮亏会逼你做错决策

没有修正——心态只能用真钱训练。这就是为什么 Day 62 我用了 1% 账户净值的最小单——不是为了赚,是为了用最小损失买"实盘心态校准"

6.5 伪命题之外,paper trade 仍然不可替代的 3 件事

我不是要否定 paper trade——它在 Day 1-62 里救过我无数次:

  1. API 集成测试:连接、下单、查询、错误处理。Paper 是唯一安全的练手地。
  2. 策略逻辑 bug 排查:if/else 写反、time zone 错、合约月份错——这些 bug 在 paper 里发现是免费的,在实盘里发现可能赔 4 位数。
  3. 风控规则演练:止损、回滚、断网恢复——必须在 paper 里跑 100 遍才敢上实盘。

所以 paper 的价值是「正确性」,不是「盈利能力」。这两者从来不是同一件事。


七、Sim-to-Real 校正系数表(个人量化通用)

把 N 篇 Twitter 量化大佬复盘 + 我自己一笔的样本 + 学术文献(Frazzini et al. 2018: "Trading Costs")综合,给一张可参考的折扣表:

资产 / 策略类别Backtest → Live Sharpe 折扣主要损耗源
大盘股 因子/趋势× 0.8慢滑点 + 数据延迟
中小盘股 因子× 0.6流动性 + spread
期权 高流动 (SPY/QQQ)× 0.75spread + partial fill
期权 单股× 0.7IV mark + spread
期权 多腿× 0.6leg execution risk
加密 CEX 永续× 0.5-0.7funding rate 实际 vs 假设 + 滑点
加密 DEX× 0.4-0.6MEV + gas
财报事件× 0.5IV crush 时机 + spread 暴增
HFT / sub-second× 0.1-0.3latency 完全不可比
月度再平衡 长期 alpha× 0.9摩擦低

怎么用这张表

  • 算出回测 Sharpe S_bt
  • 查对应类别 multiplier m
  • 真实预期 Sharpe ≈ S_bt × m
  • 如果 S_bt × m < 0.5,别上实盘——边际太薄经不起任何意外

我的 Phase 1/2/3 用这张表预估:

策略回测 SharpeMultiplier预期实盘 Sharpe
Phase 1 双因子(大盘股)1.220.80.98
Phase 2 Wheel1.050.750.79
Phase 2 财报跨式1.60.50.80
Phase 3 加密 trend(计划)(未跑)0.6≥1.5 才上

八、如何在数据层面持续校准

一次性算个滑点不够。Sim-to-real gap 是一个会随市场状态变化的动态量——VIX 高时滑点是低 VIX 的 3-5 倍。

8.1 月度对比表(每月做一次)

每个月固定跑:

  1. 选 1 个 paper 账户跑同样策略
  2. 选 1 个 live 账户跑同样策略(最小单位)
  3. 对比 PnL、Sharpe、成本、Greeks 漂移
# monthly_paper_vs_live.py
def compare(month: str):
    paper = load_paper_pnl(month)
    live  = load_live_pnl(month)
    
    return pd.DataFrame({
        'metric': ['Total PnL', 'Sharpe', 'Avg slip (bp)', 'Fill rate', 'Cost ratio'],
        'paper':  [paper.pnl, paper.sharpe, paper.slip, paper.fill_rate, paper.cost_pct],
        'live':   [live.pnl,  live.sharpe,  live.slip,  live.fill_rate,  live.cost_pct],
        'gap':    [live.pnl - paper.pnl, ...],
    })

8.2 滑点 ML 模型(Phase 3 末完成)

把每笔实盘 trade 当训练样本,拟合:

slip_bp = f(
    spread_bp,        # 下单时的 bid/ask spread
    notional_pct_adv, # 单笔 notional / 该 symbol ADV
    vix,              # 当时 VIX
    minutes_to_close, # 距收盘时间
    order_type,       # market vs limit vs ioc
    iv_rank,          # 期权时 underlying 的 IV rank
    underlying_vol_5m,# 下单前 5 分钟波动
)

模型:XGBoost / LightGBM,目标 RMSE < 1 bp。

用法:未来回测时不用固定 3 bp,给每笔 trade 用模型推断滑点——这才是真正"个性化"的成本建模。

8.3 为什么这是 PM 视角的工作

这就是**"度量驱动迭代"** —— 跟产品里我做漏斗优化、A/B 测试、留存归因是一回事:

  • 假设(回测)→ 上线(实盘)→ 度量(FlexQuery)→ 归因(滑点 ML)→ 修正(cost factor)→ 重新假设

金融 PM 的优势在这里凸显:很多工程师做量化只在回测里 trade off,做不出这套闭环。我们做了 10 年产品迭代,知道**"假设永远是错的,迭代速度才是 alpha"**。

8.4 校准节奏的 30-90-365 框架

参考产品里"用户行为分析"的成熟节奏:

节奏内容用途
每笔单笔滑点 / 实施缺口入库数据底座
每天当日聚合滑点 + 心态分入库异常报警
每周Paper vs Live 同策略对账校准 cost factor
每月拟合 ML 滑点模型,更新参数修订回测
每季Sharpe / Drawdown / Hit Rate 三大指标对照 backtest决策"加仓/砍仓"
每年Sim-to-real multiplier 表更新体系级修正

关键不能"出问题才看"——必须像每天看 GMV 一样养成肌肉。


九、第一周复盘的两个关键问题

Day 63-69 周末我要诚实回答自己:

9.1 "我的回测 cost factor 对了吗?"

判断标准:

  • 如果实盘 7 天平均滑点 in (5, 7.5) bp → cost × 1.5 假设 OK
  • 如果 > 7.5 bp → 提高到 × 2.0,重跑所有 Sharpe
  • 如果 < 5 bp → 我可能漏算了哪笔很贵的 trade,重新看 FlexQuery 全样本

9.2 "我对自己的'在实盘下犹豫'准备好了吗?"

判断标准:

  • 一周内"想偏离策略"的次数(手动追单 / 提前止盈 / 加仓)
  • 0-1 次:心态合格,可以 Day 64 上自动化执行
  • 2-3 次:再观察一周,把单位再砍半
  • ≥4 次:回到 paper,问题不是金额,是我对策略的信仰

这是最贵的问题——很多人不愿意问自己,于是亏了 30% 才回答

9.3 第一周可能出现的 3 种典型剧本

我列在前面,免得发生时不知道自己在哪条剧本:

剧本 A:开门红(30% 概率)

  • 第一周累计赚 1-3%
  • 风险:以为自己「掌握了实盘」,加仓 → 第二周遇到 vol regime change → 一周还回去 + 50%
  • 防御:第一周的盈亏完全不算数。哪怕赚 5%,下周仓位不准加。

剧本 B:温水(50% 概率)

  • 第一周 PnL 在 ±0.5% 之间晃
  • 滑点跟预期接近,心态稳定
  • 这是最健康的结果——说明系统 work,但还看不出 alpha。继续按节奏走。

剧本 C:当头一棒(20% 概率)

  • 第一周亏 1-3%
  • 滑点炸(10+ bp)或 partial fill 把组合搞坏或 IV 跳变
  • 风险:情绪驱动「报复性操作」
  • 防御:亏到 2% 时立即停手 24h,复盘是策略问题还是执行问题,找出根因再继续。

关键认知:剧本 B 才是好结果。剧本 A 是危险的好运,剧本 C 是廉价的学费。


十、PM 视角:今天学到的迁移性思考

  1. Beta → GA 的真实流量永远是惊喜:再多 paper trade 也无法替代真实订单,就像再多 staging 测试也无法替代生产用户。承认 paper 的本质是「冒烟测试」而不是「评估测试」

  2. 多维度差距 ≠ 单维度差距简单相加:滑点 + 心态 + 数据延迟 + partial fill 是乘性叠加的——任何一个变差会让其他维度同时变差。所以 sim-to-real multiplier 不是 0.95×0.95×0.95,是 0.7 这种"系统性折扣"。

  3. 「最小可投产」比「完美可投产」重要 10×:昨天我只下了 1% 净值的小单——但因为是真钱,我今天就发现了 7 项假设错误。再多 paper 也发现不了。这就是 MVP 的本质——用最小损失买最贵的反馈

  4. 校准是产品 metric 的核心动作:滑点 ML 模型 = 用户行为预测模型。回测是产品文档里的 "expected use case",实盘是真实用户行为。所有 PM 都该有"度量 → 归因 → 修正"的肌肉

  5. 心态成本永远会被低估:传统 finance teach 你 Sharpe / Sortino / max drawdown,但教材不教你"看到 -3% 时你会做错什么决定"。这条 cost 在我的成本模型里以前是 0,今天我把它定价成 "任何策略 Sharpe 乘 0.85" —— 这是个主观 prior,但比假装它不存在更诚实。

  6. 金融 PM 的复合优势:10 年产品经验给我的是**「闭环思维」——别人停在"回测好就上",我会自动想"上线后怎么度量、怎么归因、怎么迭代"。这套思维放在量化里就是 sim-to-real 校准框架**。这不是任何 quant 书会教你的,但所有合格 PM 会本能这么做。这是我相对于科班 quant 的结构性 edge——别浪费。


十一、明日预告

Day 64: 执行算法 — TWAP / VWAP / IS / Adaptive 在个人账户的实现

  • IBKR 的 Algo Order 类型表面(Adaptive、TWAP、VWAP、Arrival Price)
  • 什么时候用什么——单笔 $50k 以下其实大部分场景用 Adaptive
  • 写一个 mini-TWAP:把 1000 股拆 10 笔 5 分钟一笔
  • 评估执行 quality(Implementation Shortfall metric)
  • 何时不要用算法——OTM 期权直接 limit 单更好
  • ib_insync 调用 Algo Order 的代码

实际执行记录

时间戳 + 卡点。Day 63 的工作是"反思 + 计算",不是"下单"。

  • [hh:mm] FlexQuery 模板配置完成 — 字段: Trade Details, MarkPrice, Commissions
  • [hh:mm] 拉昨天成交 CSV,加载到 pandas — 行数: ___
  • [hh:mm] historical bar 反推 ref mid — 跑通: Y/N
  • [hh:mm] 计算单笔滑点 bp — 数值: ___
  • [hh:mm] 在 Phase 1 双因子回测里把 cost × 1.5,重跑 — 新 Sharpe: ___
  • [hh:mm] 在 Phase 2 Wheel 回测同上 — 新 Sharpe: ___
  • [hh:mm] 写「实盘第一周追踪面板」模板 — 已建: docs/daily/LIVE_WEEK1.md
  • [hh:mm] 更新 docs/daily/TR_PROGRESS.md Day 63 ✅
  • 卡点 / 学到的:
    • 卡点 1:
    • 卡点 2:
    • 最大启发:

总字数:约 5,600 字 今日完成度:理论 ✓ / 实操(你自己执行 FlexQuery + 重跑)/ 笔记 ✓