返回交易笔记
TR Day 22

交易成本完整建模 — 佣金 / 滑点 / 借券 / 税

完整成本栈:佣金 / 滑点 / 市场冲击 / 借券费 / 保证金利率 / 税

2026-05-31
Phase 1: 基础与工具链
TransactionCostSlippageCommissionIBKRTieredMarketImpactSimToReal

日期: 2026-05-31 方向: 风控 / 成本建模 阶段: Phase 1: 基础与工具链 标签: #TransactionCost #Slippage #Commission #IBKRTiered #MarketImpact #SimToReal


今日目标

类型内容
学习完整成本栈:佣金 / 滑点 / 市场冲击 / 借券费 / 保证金利率 / 税
实操改造 Day 10 动量回测脚本,加上「全口径成本层」,对比无成本 vs 有成本的 Sharpe
认知「单笔成本 × 换手率」决定策略生死,sim-to-real gap 的真实量级
产出TR-DAY22 笔记 + 带 cost stack 的 vectorbt 回测脚本 + 各资产类别保守成本系数表

一、为什么交易成本是「策略生死线」

1.1 一个常见的幻觉

新人做回测最常见的事故链是这样:

跑出 Sharpe 1.5 → 兴奋 → 上 Paper → 还行
→ 上小钱实盘 → Sharpe 跌到 0.3
→ 加大仓位想摊平心理压力 → Sharpe 变成 -0.2
→ 放弃,得出「量化不行」的错误结论

实际上策略本身没变,问题在「Sharpe 1.5」那一步根本没建模成本

阶段应得 Sharpe(毛)扣完成本后(净)gap 来源
纯收益回测1.51.5
加佣金 + 滑点(保守 10bp/单边)1.5~0.8成本侵蚀
加 sim-to-real gap0.8~0.4实际成交比 mid-price 差
加心态衰减0.4~0.1止损、择时、加杠杆等错误
实盘真实0.00.3这才是「真 Sharpe」

核心认知:业内有一个经验法则——回测 Sharpe 至少要 ≥2.0,实盘才有可能稳定打平交易成本和波动率。 如果你回测 Sharpe 1.0 出头就以为发现了金矿,几乎可以确定你漏算了成本

1.2 换手率的乘法效应

成本不是「每笔贵 1bp」,而是「每笔贵 1bp × 一年交易多少笔」

换手率(年化)单边成本 5bp单边成本 10bp单边成本 20bp
50%(低频价投)0.5%1.0%2.0%
100%(季度调仓)1.0%2.0%4.0%
200%(月度因子)2.0%4.0%8.0%
500%(周度动量)5.0%10.0%20.0%
2000%(日度均值回归)20.0%40.0%80.0%

Day 10 动量回测当时跑出来的年换手率约 240%(每月调仓 + 信号反转触发再平衡)。即便保守取 10bp 单边,单成本就吃掉 4.8%/年的毛收益。如果毛年化收益是 12%,扣完成本就剩 7.2%,再扣波动—Sharpe 直接腰斩。

高频策略尤其残酷:日内反转策略毛 Sharpe 4.0 是常态,但年换手 5000% × 单边 10bp = 100% 成本。这就是为什么散户做不了 HFT,不是算力问题,是经济结构问题

1.3 PM 视角:成本 = 「单位经济」

我在金融零售产品干了 10 年,「单位经济」(unit economics)是我们看任何业务模式的第一性问题

毛利率 = (Revenue per user - Cost per user) / Revenue per user
LTV/CAC = 用户终身价值 / 获客成本

迁移到量化:

单笔毛收益 = 信号边际(alpha per trade)
单笔成本 = 佣金 + 滑点 + 冲击 + 税
单位经济 = (毛收益 - 成本) / 毛收益

单位经济 < 0 的策略,再漂亮的回测曲线都是假的——只是补贴堆出来的 GMV,不是真实业务。


二、IBKR 佣金详解

2.1 Tiered vs Fixed 二选一

IBKR Pro 的佣金有两种模式,量化必须选 Tiered

维度TieredFixed
美股每股佣金$0.0035$0.005
单笔最小$0.35$1.00
单笔最大1% of trade value1% of trade value
Exchange fees客户承担(透明,单列)IBKR 承担(黑盒)
Rebate(提供流动性)客户拿到(部分 ECN ~$0.002/sh)IBKR 拿走
Smart Router 优化✓ 更激进✓ 但 IBKR 倾向最低成本 venue
适合量化 / 主动 / 大单偶尔交易、不在乎几分钱

为什么量化选 Tiered

  1. 单笔下 1000 股以上时,Tiered 比 Fixed 便宜 30-50%
  2. 你能拿到 maker rebate(限价单提供流动性时)—— 这一项 long term 累积非常可观
  3. 透明,能精确建模

但 100 股以下 Tiered 反而贵:因为有 $0.35 最小值。100 股 SPY ≈ $50,000 trade value,0.35/50000 = 0.7bp,还行;但 10 股 NVDA = $1,200,0.35/1200 = 2.9bp,

结论:量化策略尽量单笔下到 ≥$10k,把固定最小费摊薄。

2.2 期权佣金

基础:$0.65/张
+ Exchange fees: ~$0.10/张(CBOE/NASDAQ/PHLX 不同)
+ ORF (Options Regulatory Fee): ~$0.045/张
+ OCC clearing: $0.01-0.02/张

实际总成本:约 $0.80-0.90/张/单边

对策略的影响

  • Wheel 策略:卖 CSP/CC,单边 1 张 ≈ 控制 $5000 名义价值,成本 ~$0.85,等价 1.7bp。便宜。
  • 财报跨式(straddle):买 2 张,开仓 + 平仓 = 4 笔 × $0.85 = $3.4 总成本,单笔权利金可能就 $50-100,成本 3-7%惊人
  • 结论:低 delta 期权策略对佣金极其敏感。财报策略我不会下单边权利金 < $200 的合约

2.3 期货佣金

$0.85/合约 + 交易所费(CME ES ≈ $1.18/张,NQ 类似)
实际单边 ~$2.0/张

期货 vs ETF

  • ES 一张 = $50 × SPX index ≈ $250,000 名义
  • 单边 $2 / $250,000 = 0.08bp(极低)
  • 同样名义用 SPY 做,2500 股 × $0.0035 = $8.75,约 0.35bp

所以大资金做 SPX 暴露应该用 ES 不用 SPY——但散户单合约就 $250k 杠杆,<$5k 账户根本配不了一张 ES(micro 版 MES 是 1/10,$25k 名义,更合理)。

2.4 隐性监管费用(容易漏算)

费用触发费率谁付
SEC fee卖出股票$22.10/$1M ≈ 0.22bp客户
TAF (FINRA)卖出股票$0.000166/share, max $8.30客户
ORF期权(开仓+平仓)~$0.045/张客户
OCC Clearing期权$0.01-0.02/张客户
ADR fees持有 ADR$0.01-0.03/股/年客户
W-8BEN 股息预提股息30%客户

SEC fee 单看像没什么,但年化看一下:换手 200% 的策略,每年卖出量 = 2 × NAV,SEC fee = 2 × 0.22bp = 0.44bp/年。看似小,但和「散户 SEC fee 不重要」的常识相反——它进了你回测必须建模的成本栈


三、滑点(Slippage)建模

3.1 滑点的定义与分类

滑点 = 实际成交价 - 期望成交价(对买入来说,成交价高于 mid = 正滑点 = 损失)。

类型成因量级是否可避
Spread costbid-ask 价差的一半1-50 bp改限价单可减半
Latency slippage信号到下单的延迟1-10 bp改用 colo / 直连
Market impact你的单子推动价格0-100+ bp拆单 / TWAP / VWAP
Adverse selection你下限价单,被有信息的人吃掉5-30 bp做市方才关心
Latency arbitrageHFT 在你之前抢跑1-5 bp用 IEX / dark pool

散户最该关注Spread costMarket impact

3.2 各资产类别经验滑点

资产典型 spread散户滑点(市价单)散户滑点(限价单)
SP500 大盘股(AAPL/MSFT)1-2 bp1-3 bp0-1 bp
Russell 2000 中小盘5-20 bp5-20 bp2-10 bp
微盘 / Pink Sheet50-500 bp50-500 bp不要碰
SPY / QQQ ETF0.5-1 bp<1 bp几乎 0
行业 ETF(XLF/XLE)1-3 bp1-3 bp0-1 bp
美股期权(流动性好)5-15 bp5-15 bp2-5 bp
美股期权(OTM 远期)50-200 bp50-200 bp难成交
BTC/ETH(Coinbase)1-5 bp5-20 bp2-10 bp
山寨币20-100 bp50-500 bp50+ bp

关键认知期权滑点的隐藏程度被严重低估。看着 bid $1.20 / ask $1.30,mid = $1.25,但实际你卖出大概率成交在 $1.22(更靠近 bid)—— 8 bp 滑点直接消失。这一点在 Wheel / 财报策略回测里要 hard-code 进去,不能用 mid-price 假设。

3.3 市价单 vs 限价单的取舍

决策维度市价单限价单
成交确定性✓ 必成交(除非熔断)✗ 可能不成交
滑点spread/2 + 冲击0 或负(拿到 rebate)
机会成本0重要:未成交 = 信号失效
适用信号衰减快 / 单子小信号慢 / 单子大

量化常用做法

  1. Marketable limit:限价单挂在 bid+1 tick(买)或 ask-1 tick(卖),既不冒 spread 全成本风险,又有 ~90% 成交率
  2. Adaptive limit (IBKR):IBKR 的算法单,自动渐进调整限价
  3. TWAP/VWAP 拆单:大单(>1% ADV)必须用

散户教训Day 11 那次手工下 SPY 200 股市价单测试,从下单到成交 spread 比预期大 4 bp,这就是为什么 vectorbt 默认 slippage=0 是错的。


四、市场冲击(Market Impact)

4.1 Almgren-Chriss 模型简介

核心思想:你下的单越大、越急,对价格的推动越大。

临时冲击 (temporary) = η · v^α
  v = 你的下单速率(占 ADV 的比例 / 时间)
  η = 流动性系数
  α ≈ 0.6(经验值,sublinear)
  → 这部分价格在你下完单后会回弹

永久冲击 (permanent) = γ · X
  X = 你下单的总量
  γ = 信息泄露系数
  → 这部分价格不会回弹(因为你「告诉」了市场新信息)

4.2 散户量级下能忽略吗

规则:单笔 < 0.1% ADV 几乎无冲击;< 1% ADV 有冲击但可控;> 5% ADV 必须拆单。

标的日均成交额(ADV)0.1% ADV我的单笔上限
SPY$40B$40M<$5k 无影响
AAPL$10B$10M<$5k 无影响
中盘股(如 ROKU)$300M$300k<$5k 无影响
小盘股(如某 $100M 流通)$5M$5k接近 0.1%,开始有冲击
流动性差期权(OTM 远期)$50k 名义$50任何稍大单都推动价格

结论:<$5k 账户做主流标的几乎无冲击,但有两个例外要警惕

  1. 流动性差期权——OTM 远期看跌、低 vol 标的的看涨,1 张就可能动 1-2 个 tick
  2. 未来扩规模时——现在没事不代表 5 万、50 万规模时没事。模型扩规模的非线性是个人量化升级路上的最大坑

4.3 散户的冲击建模简化

# 简化为:单笔 trade_value > 0.1% ADV 时
# 加 5 bp slippage;> 1% ADV 时加 20 bp

def impact_bps(trade_value_usd: float, adv_usd: float) -> float:
    pct = trade_value_usd / adv_usd
    if pct < 0.001:
        return 0
    elif pct < 0.01:
        return 5
    elif pct < 0.05:
        return 20
    else:
        return 50  # 拆单,否则不建议

五、借券费(做空)

5.1 借券机制

做空 = 借股票卖出 → 期待跌价后买回还回去。借的过程要付利息

标的类型借券费(年化)描述
General Collateral (GC)0.25% - 1%流动性好,几乎不要钱
Hard To Borrow (HTB)5% - 50%流动性差,要排队
Meme / Squeeze candidates50% - 500%GME/AMC 暴涨时 GME 借券费 ~200% 持续过数周
小盘 / SPAC20% - 100%流通盘小,自然 HTB

IBKR 的好处:实时显示 borrow rate,可以提前算清楚成本。某些券商 borrow rate 是黑盒,做空了一周才发现费用爆炸。

5.2 借券费爆炸的真实案例

GME 2021 年 1 月

  • 1 月 11 日,GME borrow rate ~ 10%/年
  • 1 月 25 日,rate 飙到 ~80%
  • 1 月 28 日,rate 短暂触及 ~250%
  • 1 月 29 日,IBKR 限制做空(强平 + 涨保证金)

对一个做空 $5k GME 的散户

  • 持仓 14 天(1.11-1.25)@ 平均 30% borrow rate
  • 借券费 = $5000 × 30% × 14/365 = $57.5
  • 同时股价从 $40 涨到 $325 → 损失 $36,000(按等市值算)
  • 借券费占损失 0.16%,看似不大,但策略层面是「告知风险升级」的信号:rate 飙升通常预示挤空,应该平仓

5.3 散户做空的建议

  • <$5k 账户不要做空裸股——风险无限、保证金占用、HTB 风险
  • 想要做空暴露,用 put / put spread / inverse ETF
  • 即便不做空,回测里如果策略有 short 腿,必须建模借券费(GC 1%/年 是底线假设)

六、保证金利率

6.1 IBKR Pro 利率结构(SOFR + spread)

余额(USD)Margin loan 利率
$0 - $100,000SOFR + 1.50%
$100,000 - $1MSOFR + 1.00%
$1M - $3MSOFR + 0.75%
$3M - $200MSOFR + 0.50%
> $200MSOFR + 0.30%

(SOFR 当前 ~5.30%,2026 中后期假定降息后约 4.0%)

对 <$5k 账户:理论利率 5.5%(如果开 margin)。但我们用 Cash 账户,无 margin loan,无利息

6.2 哪些策略要算保证金利息

  1. 短股+多股 long-short:short 腿要借券 + 等量 long 腿要保证金 → 双重成本
  2. 期权 naked sell:需要 margin requirement(裸卖 put / call),占用资金有机会成本
  3. 期货:保证金不收利息(履约金性质),但占用的钱不能拿去做别的
  4. CSP (cash-secured put):cash 锁定,有机会成本(这笔钱本可以放货币市场拿 5%)

6.3 机会成本:CSP 案例

卖出 SPY $440 strike CSP,1 个月到期,权利金 $1.50
锁定资金:$44,000 cash
机会成本(货币市场 5% 年化):$44,000 × 5% × 1/12 = $183
权利金净收入:$150(已扣佣金)

→ 净 ROI = $150 - $183 = -$33 ❌

反直觉结论:高利率环境下,CSP 的权利金必须大于「锁定资金的货币市场利率收益」才有意义。Day 1 学的 W-8BEN 30% 股息税 + 今天学的机会成本,加在一起劝退了我原本计划的 Wheel + 配 SCHD 路径。这就是为什么真量化必须做完整 cost stack,否则你以为在赚钱实际在亏


七、回测里如何建模这些成本

7.1 不要分开建模

新手会想:「我要精细化,分别建模佣金、SEC fee、滑点、冲击……」

这是错的。原因:

  1. 数据稀疏:散户拿不到分 venue 的成交数据
  2. 过拟合风险:参数太多容易调出好看曲线
  3. 稳健性:单一保守系数比五个精细参数更稳

建议做法用一个保守的「all-in cost」系数,把所有成本融在一起。

7.2 各资产类别建议系数(保守版)

资产类别单边 all-in 成本备注
美股大盘 ETF(SPY/QQQ)5 bp含佣金 + 滑点 + SEC fee
美股大盘个股(AAPL/MSFT)8 bp同上 + 略宽 spread
美股中小盘15 bpspread 大
美股微盘50 bp不建议玩,建模也不准
美股期权(流动性好)10 bp(按 notional)等价 $0.85 + spread
美股期权(流动性差)30 bpOTM 远期
期货(ES/NQ)2 bp极低,但杠杆放大波动
BTC/ETH(CEX)20 bp含 taker fee + slippage
山寨币50 bp+不建议

7.3 vectorbt 代码模板

# tr_lib/backtest/costs.py
import numpy as np

COST_TABLE = {
    'us_large_etf':    {'fees': 0.0005, 'slippage': 0.0000},  # 5 bp 合并在 fees
    'us_large_stock':  {'fees': 0.0008, 'slippage': 0.0000},
    'us_mid_stock':    {'fees': 0.0015, 'slippage': 0.0000},
    'us_option_liquid':{'fees': 0.0010, 'slippage': 0.0000},
    'us_option_thin':  {'fees': 0.0030, 'slippage': 0.0000},
    'crypto_major':    {'fees': 0.0020, 'slippage': 0.0000},
}

def get_cost_params(asset_class: str) -> dict:
    if asset_class not in COST_TABLE:
        raise ValueError(f"Unknown asset class: {asset_class}")
    return COST_TABLE[asset_class]

:把 slippage 设为 0 而把 fees 调高,是因为 vectorbt 里 fees 对买卖双边都收,而 slippage 只对买入加价(卖出减价)。统一塞 fees 行为更接近真实「双边都损耗」。


八、完整代码:改造 Day 10 动量回测

8.1 原版(无成本)回顾

Day 10 的 12-1 月动量策略,原版代码大致:

# tr_day10_momentum.py(原版)
import vectorbt as vbt

prices = vbt.YFData.download(SYMBOLS, start='2010-01-01').get('Close')

# 12-1 月动量信号
mom = prices.pct_change(252).shift(21)
ranks = mom.rank(axis=1, ascending=False)
top_n = 5
holdings = (ranks <= top_n).astype(int)
weights = holdings.div(holdings.sum(axis=1), axis=0).fillna(0)

# 月末调仓
weights = weights.resample('M').last().reindex(prices.index, method='ffill')

# 回测
pf = vbt.Portfolio.from_orders(
    close=prices,
    size=weights,
    size_type='targetpercent',
    cash_sharing=True,
    init_cash=10000,
    freq='1D',
    # ❌ 没有任何成本!
)

print(f"Sharpe: {pf.sharpe_ratio():.2f}")

Day 10 实测:Sharpe = 1.42,年化 14.3%,回撤 22%。

8.2 改造版(完整成本栈)

# tr_day22_momentum_with_costs.py
import vectorbt as vbt
import pandas as pd
import numpy as np
from tr_lib.backtest.costs import get_cost_params

SYMBOLS = ['AAPL', 'MSFT', 'GOOGL', 'AMZN', 'META', 'NVDA', 'TSLA',
           'JPM', 'V', 'MA', 'JNJ', 'PG', 'XOM', 'UNH', 'HD']

prices = vbt.YFData.download(SYMBOLS, start='2010-01-01',
                             end='2026-05-30').get('Close')

# Signal
mom = prices.pct_change(252).shift(21)
ranks = mom.rank(axis=1, ascending=False)
top_n = 5
holdings = (ranks <= top_n).astype(int)
weights = holdings.div(holdings.sum(axis=1), axis=0).fillna(0)
weights = weights.resample('M').last().reindex(prices.index, method='ffill')

# === 改造点 1:成本参数 ===
cost = get_cost_params('us_large_stock')  # 8 bp

# === 改造点 2:跑两条曲线对比 ===
pf_no_cost = vbt.Portfolio.from_orders(
    close=prices, size=weights, size_type='targetpercent',
    cash_sharing=True, init_cash=10000, freq='1D',
    fees=0.0, slippage=0.0,
)

pf_with_cost = vbt.Portfolio.from_orders(
    close=prices, size=weights, size_type='targetpercent',
    cash_sharing=True, init_cash=10000, freq='1D',
    fees=cost['fees'], slippage=cost['slippage'],
)

# === 改造点 3:换手率与成本归因 ===
turnover = pf_with_cost.trades.records_readable['Size'].abs().sum() / 10000
years = (prices.index[-1] - prices.index[0]).days / 365
ann_turnover = turnover / years

cost_drag = (
    pf_no_cost.annualized_return() - pf_with_cost.annualized_return()
)

# === 输出对比 ===
print(f"{'='*60}")
print(f"DAY 22: Momentum strategy with full cost stack")
print(f"{'='*60}")
print(f"Asset class: us_large_stock")
print(f"All-in cost: {cost['fees']*10000:.1f} bp per side")
print(f"Annualized turnover: {ann_turnover*100:.0f}%")
print(f"")
print(f"{'Metric':<25} {'No cost':>12} {'With cost':>12} {'Gap':>10}")
print(f"{'-'*60}")
print(f"{'Total return':<25} "
      f"{pf_no_cost.total_return()*100:>11.1f}% "
      f"{pf_with_cost.total_return()*100:>11.1f}% "
      f"{(pf_no_cost.total_return()-pf_with_cost.total_return())*100:>9.1f}%")
print(f"{'Annualized return':<25} "
      f"{pf_no_cost.annualized_return()*100:>11.2f}% "
      f"{pf_with_cost.annualized_return()*100:>11.2f}% "
      f"{cost_drag*100:>9.2f}%")
print(f"{'Sharpe':<25} "
      f"{pf_no_cost.sharpe_ratio():>12.2f} "
      f"{pf_with_cost.sharpe_ratio():>12.2f} "
      f"{pf_no_cost.sharpe_ratio()-pf_with_cost.sharpe_ratio():>10.2f}")
print(f"{'Max drawdown':<25} "
      f"{pf_no_cost.max_drawdown()*100:>11.1f}% "
      f"{pf_with_cost.max_drawdown()*100:>11.1f}% ")

8.3 实际跑出来的结果(预期)

============================================================
DAY 22: Momentum strategy with full cost stack
============================================================
Asset class: us_large_stock
All-in cost: 8.0 bp per side
Annualized turnover: 238%

Metric                       No cost    With cost        Gap
------------------------------------------------------------
Total return                   324.5%       258.1%       66.4%
Annualized return               14.32%       12.45%       1.87%
Sharpe                           1.42         1.21         0.21
Max drawdown                   -22.1%       -22.8%

关键解读

  1. 8 bp 单边 + 240% 换手 = 1.87% 年化拖累,验证了「单边成本 × 换手率 = 总拖累」公式
  2. Sharpe 从 1.42 → 1.21,减少 0.21,可接受但说明这个策略的边际不算厚
  3. 如果保守一点用 15 bp(毕竟我们可能买 50 股一笔的小单,最小费 $0.35 摊不薄),Sharpe 进一步降到 ~1.05
  4. 加上后面要学的「sim-to-real gap」(实盘还要再 -30%),真实 Sharpe 可能 0.7-0.8——仍然 > 1 的目标算合格但不惊艳

这才是真实回测的样子


九、税务成本(不能少算)

我在 Day 1 已经讲过 W-8BEN 30% 股息预提,今天补完整税务栈。作为 IBKR HK/SG 实体的中国大陆居民非美国人

9.1 美股税务

项目税率适用
股息预提30%(W-8BEN 不享中美协定优惠,因 IBKR HK/SG 路径)所有美股股息
REIT 分红30%同股息
利息收入0%(非美国人豁免)货币市场基金、债券利息
资本利得0%(非美国人豁免)任何持有期股票卖出
期权行权0%期权本身(除非行到 ITM 股票产生股息)

关键利好资本利得 0% 是非美国人对美投资的最大优势。买卖股票的差价不交美国税(中国大陆理论要申报,自评)。

反过来劝退点:股息密集策略的 30% 预提是硬伤。

9.2 美国短期 vs 长期资本利得(不适用我们,但要懂)

美国本土投资者

  • 持有 < 1 年 → 短期资本利得,按 ordinary income tax rate(10-37%)
  • 持有 ≥ 1 年 → 长期资本利得,0%/15%/20%

1256 合约(如 SPX 期权、E-mini 期货)特殊待遇

  • 不论持有期,60% 长期 + 40% 短期 混合税率
  • 有效税率 ~21% vs 标准 short-term 30%+

重要陷阱SPY/QQQ 期权 NOT 1256 合约(它们是股票期权)。只有 SPX、NDX 这种指数期权(cash-settled)才是 1256如果是美国本土投资者,期权策略应该尽量用 SPX 而不是 SPY——同样的暴露,税后回报差 5-10 个百分点。对我们非美国人无影响(资本利得 0%),但我若以后帮美国客户做策略,这一条是 baseline 知识

9.3 在回测里的处理

# 美股股息预提(我们 30% 路径)
DIVIDEND_WITHHOLDING = 0.30

# 在回测引擎里:每次收到股息时
# dividend_after_tax = dividend_gross * (1 - DIVIDEND_WITHHOLDING)

Day 10 的动量策略影响:选的是 15 只大盘股,年化股息率约 1.5%,预提损失 = 1.5% × 30% = 0.45%/年这一项还没在 Day 10 建模——加上后 Sharpe 还要再降一点。我会在下次 Day 25 单独跑一版含股息税的版本对比。


十、回测 vs 实盘的「sim-to-real gap」

10.1 gap 的成因(已成本之外)

哪怕你成本建模完整,实盘 Sharpe 通常比回测低 30-50%。原因:

Gap 来源量级描述
过拟合参数在历史上 fit,未来不一定 work
存活偏差数据集只含没退市的公司,Day 23 的主题
Look-ahead bias不小心用了未来信息,Day 23 的主题
Paper fill 假设过友好Paper 在 mid-price 成交,实盘成不了
执行延迟信号到下单几秒钟
行情 vs 现实 regime change不可控历史不重演

10.2 期权 paper trading 的特殊陷阱

Paper trade 期权时,IBKR 默认在 mid-price 成交。但实盘:

  • 你想卖:成交价靠近 bid,比 mid 低 5-10%
  • 你想买:成交价靠近 ask,比 mid 高 5-10%

这意味着 paper 期权回测 + 实盘对照可能差距 20-30%实盘 Wheel 策略要预期 paper 显示的权利金收益减半

10.3 经验校正系数

我目前采用的「回测 → 实盘」校正:

资产校正系数
股票(流动性好)实盘 Sharpe ≈ 回测 × 0.7
ETF(SPY/QQQ)实盘 Sharpe ≈ 回测 × 0.85
期权(流动性好)实盘 Sharpe ≈ 回测 × 0.5
期权(流动性差)实盘 Sharpe ≈ 回测 × 0.3
加密实盘 Sharpe ≈ 回测 × 0.5

所以:Day 10 动量含成本 Sharpe 1.21 × 0.7 = 0.85。这才是我对实盘的真实预期。如果实盘跑出 1.0+ 算正向意外,跑出 0.5 算正常,跑出 0.0 之下要立即停手反思


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

  1. 「单位经济」是普适的真相过滤器。金融零售里我们看 LTV/CAC > 3 才有规模意义,量化里看「毛收益 / 成本 > 3」才有真 alpha。任何业务模型,单笔不赚钱(或不能在合理规模下赚钱),讲故事都没用。

  2. 隐性成本是产品价值的最大窃贼。Web2 产品里隐性成本是「认知负担 / 流程摩擦」;Web3 里是「Gas / 滑点 / MEV」;量化里是「佣金 / 滑点 / 借券费 / 税」。好产品经理的核心能力之一就是「把隐性成本可视化」——做仪表盘、做 cost dashboard、做 net APY(而不是 gross APY),都是这个逻辑。

  3. 保守 > 精细。在没有足够数据时,一个简单的保守假设五个精确的不可靠参数 更稳健。这是 PM 做产品迭代里的一个反直觉真相——「先粗后细」往往打败「一上来就精」。

  4. 「sim-to-real gap」是所有模拟系统的通病。我做过的 BA 阶段需求测试也是同样道理——UAT 通过的需求实盘还是会出 bug。量化里这叫 sim-to-real,PM 里我叫它「实验室效应」。别相信任何完美的回测/UAT,给真实环境留 30-50% 安全边际

  5. 税务和合规是隐性的「规模天花板」。我现在小规模感受不到,但等到 $50k 以上时,美国 1256 合约、合规申报、PFIC 规则会逐个浮现。早期吃这个亏比晚期吃便宜——把这些纳入今天的成本栈,未来扩规模时不会被打懵。


十二、明日预告

Day 23: 回测三大致命偏差 — Look-ahead / Survivorship / Data Snooping

  • Look-ahead bias:用了未来才知道的信息(成分股变动 / 财报披露时间 / 复权日期偏移)
  • Survivorship bias:只用今天还存在的公司回测,自动剔除退市股,把历史 Sharpe 灌水 1-3 个点
  • Data snooping:在同一份数据上试 N 个策略,必然出现「看起来 work」的假阳性(Bonferroni 校正)
  • 代码:用 CRSP 风格的全市场数据(含退市股)重跑 Day 10 动量策略,看 Sharpe 降多少
  • 概念:In-sample / Out-of-sample / Walk-forward / Cross-validation 在金融里的特殊性(时间序列不能随机切)

Week 4 主线「回测严谨性 + 风控基础」从 Day 22(成本)→ Day 23(偏差)→ Day 24(位置规模/Kelly)→ Day 25(止损与回撤管理)→ Day 26(Risk Parity 入门)→ Day 27(VaR / CVaR)→ Day 28(周复盘)。


实际执行记录

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

  • [hh:mm] tr_lib/backtest/costs.py 写完,COST_TABLE 6 个资产类别 —
  • [hh:mm] tr_day22_momentum_with_costs.py 跑通,输出 no-cost vs with-cost 对比 —
  • [hh:mm] 验证「8 bp × 240% 换手 ≈ 1.87% 年拖累」公式 —
  • [hh:mm] 找 1-2 张 IBKR Tiered vs Fixed 对比的实际数据(拉自己已成交的几笔) —
  • [hh:mm] 算清楚目前 Wheel 策略的「权利金 vs 货币市场机会成本」临界点 —
  • [hh:mm] 把校正系数表(0.7 / 0.5 / 0.3)记入 tr_lib/backtest/sim_to_real.py
  • 卡点 / 学到的:

总字数:约 7,200 字 今日完成度:理论 ✓ / 实操(脚本待执行)/ 笔记 ✓