返回交易笔记
TR Day 33

完整回测 2010-2024 + 税前/税后净值

回测期跨周期覆盖逻辑、美国对非居民的税务体系(W-8BEN 30% 股息预提 / 美籍资本利得豁免 / 美籍短长期分割)、税务对不同策略风格的差异化侵蚀

2026-06-11
Phase 2: 策略实战 + AI 信号
BacktestTaxDividendWithholdingW8BENCapitalGainsPreTaxPostTax

日期: 2026-06-11 方向: Phase 2 / 回测扩展 阶段: Phase 2: 策略实战 + AI 信号 标签: #Backtest #Tax #DividendWithholding #W8BEN #CapitalGains #PreTaxPostTax


今日目标

类型内容
学习回测期跨周期覆盖逻辑、美国对非居民的税务体系(W-8BEN 30% 股息预提 / 美籍资本利得豁免 / 美籍短长期分割)、税务对不同策略风格的差异化侵蚀
实操把 Day 28 双因子组合的回测期从 2014-2024 拉长到 2010-2024,加入逐笔股息预扣模块,跑出税前 vs 税后两条净值曲线
产出可运行的税务建模代码 + 14 年完整结果表 + 税前/税后 Sharpe 对比 + 三种 ETF 风格的差异化分析 + paper 决策

一、为什么今天必须把回测期拉长 + 加税务建模

Day 28 的 v1 跑了 2014-2024 共 10 年区间,已经横跨「2014-2019 慢牛 + 2020 COVID 急跌 + 2022 加息 + 2023-2024 反弹」四个明显环境。但 10 年这个长度仍有两个本质短板:

  1. 2010-2013 是关键空窗期。2010 年是金融危机后第一次「真复苏」起点,2011 年欧债危机、2013 年 Bernanke Taper Tantrum——这三年的市场结构(低利率 + QE 主导 + VIX 长期高位)和 2014+ 完全不同。回测里看不到这段,你的策略「在 ZIRP 早期能不能活」就是空白。
  2. 完整周期数 = 检验稳健性的真正变量。学术界共识:因子回测至少要覆盖 2 个独立周期(牛+熊+复苏 算一个),10 年区间在美股只能算 1.5 个周期。14 年(2010-2024)跨 2 个清晰周期:2010-2019 长牛 / 2020 COVID 流动性危机 / 2021 复苏 / 2022 加息熊 / 2023-2024 复苏。每个周期里因子表现的差异,比单看「最终 Sharpe」信息量大十倍。

而税务是另一个完全独立维度的"必须加"

回测论文里的 Sharpe 1.3 → 是税前
个人量化跑出来到手净值 → 永远是税后
两者差距不是 1-2%,而是 5-15%(甚至更多)

10 年 PM 经验告诉我一个朴素事实:没算清 unit economics 的产品都不能下定论。客单价 100 元但客服+物流成本 35 元的电商,跟客单价 100 元但成本 5 元的 SaaS,是两个完全不同的生意。回测同理——Sharpe = 0.9 但税后只剩 0.7 的策略,跟 Sharpe = 0.95 但税后还有 0.92 的策略,后者优于前者。今天就是把这件事搞清楚。


二、2010-2024 是一段什么样的市场

把 14 年切成 6 个 regime,对回测期选择的合理性会更直观:

区间主导环境SPY 表现VIX 中位数关键事件
2010-2011 H1后危机复苏 + QE2+20%18欧债危机萌芽
2011 H2欧债危机 + 美评级下调-10%35标普首次下调美国 AAA
2012-2014QE3 + 慢牛+60%14Bernanke 退场
2015-2018加息周期 + 中美博弈+25%14-22 震荡2018 Q4 -20% 快速下跌
2019-2021降息 + 疫情 + 流动性盛宴+75%12-82 极端波动COVID 急跌 35% / 反弹 100%+
2022-2024加息熊 + AI 反弹+20% (累计)16-332022 -25% / 2023-2024 +50%

为什么是 14 年而不是 20 年

  • 拉到 2005-2024 会包含 2008 金融危机,但 2008 之前是「散户/做市商主导 + 没有 HFT 主流化」的市场结构,跟 2010 后差异太大,混在一起反而会增加 regime mismatch
  • 拉到 1990-2024 数据稀缺 + 大量 ETF/期权品种当时不存在,回测样本里大量是「合成数据」,可信度打折
  • 2010 作为起点的一个朴素理由:多数关键 ETF(QQQ、SCHD、VXUS、VYM)在 2010 都已经成熟存在,不需要回填模拟数据。

三、税务建模:美国对非居民个人的核心规则

3.1 三类美国税与中国大陆居民

税种美籍居民中国大陆居民(W-8BEN 已签)备注
股息预提税0%(按个人所得税申报)30% 当场扣中美无税收协定优惠适用于 IBKR HK/SG 个人户
短期资本利得(持有 <365 天)个人所得税率 10-37%0%(豁免)IRS Pub 515 / IBKR 不扣
长期资本利得(持有 ≥365 天)0% / 15% / 20%0%(豁免)同上
利息收入(国债/现金存款)普通收入税率一般 0%(Portfolio Interest 豁免)部分公司债另算
期权 premium收入或资本利得0%(资本利得豁免)但 dividend equivalent 仍 30%

关键认知:中国大陆居民通过 IBKR HK/SG 开户做美股,最大且唯一的税务漏洞就是 30% 股息预提。资本利得(无论持有多久)IRS 都不向非居民征收——这是中国大陆量化 PM 相对美籍同行最大的"结构性税务红利"。

但有两个坑:

  • 国内申报义务:年度海外资产 ≥$50k 等值理论上要在国内年度自评税务申报里报告(CRS 信息交换在跑),但资本利得在国内目前实操上不征(个人境外投资所得征税细则尚未细化)。这块的实操灰度较大,请勿当法律建议。
  • 税收协定 10% 优惠去哪了:中美 1984 年税收协定确实规定股息预扣 10%,但协定的适用主体是中国大陆税务居民身份 + 通过 IRS 表 W-8BEN 内的"治理条款(claim treaty benefit)"勾选绝大多数中国大陆个人通过 IBKR HK/SG 开户,W-8BEN 上是默认按 30% 处理——除非你能向 IBKR 提供中国大陆税务居民证明(CR 表 / 居民身份证明),否则就是 30%。我们按 30% 建模

3.2 W-8BEN 实操细节

  • W-8BEN 表有效期 3 年,过期需重签——IBKR 会提前邮件提醒,没续签会被默认按 24% backup withholding(更糟)。
  • 表上有一栏叫 "Article 10 (dividends)" 可以勾选 claim treaty benefit 10%,但中国大陆居民通过香港或新加坡实体开户时这条不被 IBKR 默认接受,得走 paper 申诉流程,多数人最后还是 30%
  • REIT(如 SPG、O)的分红里 ROC(Return of Capital)部分理论上不预扣,但 IBKR 通常先按 30% 扣,年底统一通过 1042-S 表退还多扣部分。这块如果做 REIT 策略会复杂,我们的双因子组合不持有 REIT。

3.3 不同 ETF 风格受税务影响的差异

把市面主流 ETF 按"每年分红率"分三档,做一个粗略的"税务摩擦"测算:

ETF 类型代表标的年化股息率30% 预扣后净收益损失总收益里股息占比税后 Sharpe 衰减
高股息SCHD, VYM, HDV3.5-4.5%~1.2%/年40-50%-15% ~ -20%
中等股息SPY, VTI, IVV1.4-1.7%~0.5%/年15-20%-5% ~ -8%
低股息QQQ, VUG0.5-0.7%~0.2%/年5-10%-2% ~ -4%
零股息TSLA, GOOG0%0%0%0%
期权 premiumWheel 策略0% (premium 非 dividend)0%0%0%

这张表直接决定了你做不同策略的"税后吸引力排序"。一个 PM 直觉是:中国大陆量化做美股,最有结构性优势的是低股息 / 高换手但短持有 / 期权 premium 类策略;最被税务侵蚀的是高股息(SCHD 类)和 dividend reinvestment 复利策略


四、代码实现:把 Day 28 双因子升级到税务感知

4.1 模块设计

backtest/
├── data_2010_2024.py        # 数据加载 + 股息事件序列
├── factor_momentum.py        # Day 10 + Day 28 沿用
├── factor_lowvol.py          # Day 12 + Day 28 沿用
├── portfolio_engine.py       # 调仓 + 现金管理 + 成本
├── tax_engine.py             # ★ 今天新增
└── reporting.py              # 双线净值 / Sharpe / DD

4.2 数据层:把股息事件单独拉出来

# data_2010_2024.py
import pandas as pd
import yfinance as yf

UNIVERSE = ["AAPL","MSFT","GOOG","META","NVDA","JPM","XOM","KO","PG","JNJ",
            # ... S&P 100 成分股做 universe,剔除上市晚的
           ]

def load_prices(start="2010-01-01", end="2024-12-31"):
    """
    yf.download 返回 adjusted close。
    但为了把股息分开建模,我们需要 unadjusted close + dividends 序列。
    """
    raw = yf.download(UNIVERSE, start=start, end=end,
                      auto_adjust=False, actions=True)
    prices = raw['Close']            # 未复权(关键)
    dividends = raw['Dividends']     # 每个股票每天的现金分红 ($/share)
    return prices, dividends

def load_with_pit_universe(start="2010-01-01"):
    """
    Point-in-time universe: 当年 S&P 100 成分(用 Day 23 学的逻辑避免 survivorship)
    实现时建议用 CRSP / Norgate / Sharadar 的历史成分表。
    个人量化早期可以用 Wikipedia 历史快照做近似(注意误差)。
    """
    # 此处省略实现细节,但回测里 universe 必须 PIT
    pass

关键点auto_adjust=False 必须显式关掉。yfinance 默认开启会把股息直接拼到 close 价里,这样你就完全看不见分红事件了,没法做 30% 预扣

4.3 税务引擎

# tax_engine.py
from dataclasses import dataclass
from typing import Dict
import pandas as pd

@dataclass
class TaxConfig:
    dividend_withholding_rate: float = 0.30   # W-8BEN 默认
    short_term_cap_gain_rate: float = 0.00    # 中国居民豁免
    long_term_cap_gain_rate: float = 0.00     # 中国居民豁免
    short_term_threshold_days: int = 365
    # 留出美籍模式做对比
    us_resident_mode: bool = False

@dataclass
class USResidentTaxConfig(TaxConfig):
    dividend_withholding_rate: float = 0.0    # 按 qualified/ordinary 申报
    short_term_cap_gain_rate: float = 0.32    # 假设个人所得税率
    long_term_cap_gain_rate: float = 0.15
    us_resident_mode: bool = True

class TaxEngine:
    """
    逐笔处理:
    1) 每收到一笔股息,立刻扣预提(中国居民 30%)
    2) 每笔卖出,计算持有天数 + 浮盈,按短期/长期分类应计税
       注:中国居民 cap gain 率为 0,但仍跟踪以便美籍对比
    """
    def __init__(self, cfg: TaxConfig):
        self.cfg = cfg
        self.lots: Dict[str, list] = {}       # ticker -> [(qty, cost_basis, buy_date), ...]
        self.tax_paid_dividend = 0.0
        self.tax_paid_st_cg = 0.0
        self.tax_paid_lt_cg = 0.0

    def on_buy(self, ticker, qty, price, dt):
        self.lots.setdefault(ticker, []).append([qty, price, dt])

    def on_dividend(self, ticker, dividend_per_share, dt, position_qty):
        gross = dividend_per_share * position_qty
        withheld = gross * self.cfg.dividend_withholding_rate
        self.tax_paid_dividend += withheld
        net = gross - withheld
        return net   # 实际打入账户的金额

    def on_sell(self, ticker, qty, price, dt):
        """
        FIFO 出货(IBKR 默认就是 FIFO)
        返回:本笔卖出应计税额(中国居民下 = 0,但仍跟踪以便美籍对比)
        """
        tax_on_this_trade = 0.0
        remaining = qty
        while remaining > 0 and self.lots[ticker]:
            lot = self.lots[ticker][0]
            lot_qty, lot_cost, lot_dt = lot
            taken = min(remaining, lot_qty)
            pnl = (price - lot_cost) * taken
            holding_days = (dt - lot_dt).days

            if pnl > 0:
                if holding_days < self.cfg.short_term_threshold_days:
                    rate = self.cfg.short_term_cap_gain_rate
                    self.tax_paid_st_cg += pnl * rate
                    tax_on_this_trade += pnl * rate
                else:
                    rate = self.cfg.long_term_cap_gain_rate
                    self.tax_paid_lt_cg += pnl * rate
                    tax_on_this_trade += pnl * rate

            lot[0] -= taken
            if lot[0] == 0:
                self.lots[ticker].pop(0)
            remaining -= taken
        return tax_on_this_trade

4.4 主回测循环:双线净值

# portfolio_engine.py 的核心循环(简化)
def backtest(prices, dividends, signals, tax_cfg=TaxConfig()):
    tax = TaxEngine(tax_cfg)
    cash_pre  = 100_000.0       # 不扣税口径
    cash_post = 100_000.0       # 扣税口径
    pos = {}                    # ticker -> qty
    history_pre  = []
    history_post = []

    for dt in prices.index:
        # ── 1) 股息事件(每天 OPEN 前结算)─────────────────
        for tkr in pos:
            div_per_share = dividends.loc[dt, tkr]
            if div_per_share > 0:
                gross  = div_per_share * pos[tkr]
                cash_pre  += gross
                cash_post += tax.on_dividend(tkr, div_per_share, dt, pos[tkr])

        # ── 2) 调仓信号(月末)─────────────────────────
        if dt in signals.index:
            target = signals.loc[dt]   # ticker -> weight
            for tkr, w in target.items():
                price = prices.loc[dt, tkr]
                desired = (cash_post + portfolio_value(pos, prices.loc[dt])) * w / price
                trade_qty = desired - pos.get(tkr, 0)
                if trade_qty > 0:
                    # 买入
                    cost = trade_qty * price * (1 + COMMISSION + SLIPPAGE)
                    cash_pre  -= cost
                    cash_post -= cost
                    tax.on_buy(tkr, trade_qty, price, dt)
                    pos[tkr] = pos.get(tkr, 0) + trade_qty
                elif trade_qty < 0:
                    # 卖出
                    proceeds = abs(trade_qty) * price * (1 - COMMISSION - SLIPPAGE)
                    cash_pre  += proceeds
                    cg_tax = tax.on_sell(tkr, abs(trade_qty), price, dt)
                    cash_post += proceeds - cg_tax
                    pos[tkr] -= abs(trade_qty)

        # ── 3) 记账 ──────────────────────────────────
        equity_pre  = cash_pre  + portfolio_value(pos, prices.loc[dt])
        equity_post = cash_post + portfolio_value(pos, prices.loc[dt])
        history_pre.append((dt, equity_pre))
        history_post.append((dt, equity_post))

    return pd.DataFrame(history_pre, columns=["dt","equity"]).set_index("dt"), \
           pd.DataFrame(history_post, columns=["dt","equity"]).set_index("dt"), \
           tax

实现要点

  1. 两条 cash 账户并行跑——cash_pre 不扣股息税,cash_post 扣完税。这样一次回测得到两条对照净值。
  2. 股息事件在 OPEN 前结算——IBKR 实际是 ex-div 后 T+? 入账(普通股 T+1),简化为当日开盘前。
  3. FIFO 持仓批次(lots)——为美籍模式预留,便于后续做短期/长期分类。中国居民跑回测时 cap gain 率 = 0,但仍跟踪 lots 是为了未来对比 / 灰度。
  4. 现金部分——回测中现金不计利息。在 2022-2024 ZIRP 解除后这是个有意义的简化(IBKR 现金 2024 年有 4%+ 利息),但这块今天先不建模,留到 Day 34 一起处理。

4.5 跑两个 config 拿到对比

# main_backtest.py
prices, divs = load_prices("2010-01-01","2024-12-31")
signals = build_dual_factor_signals(prices)   # Day 28 沿用

# Case A: 中国居民
eq_pre_cn, eq_post_cn, tax_cn = backtest(prices, divs, signals,
                                          tax_cfg=TaxConfig())

# Case B: 美籍参考
eq_pre_us, eq_post_us, tax_us = backtest(prices, divs, signals,
                                          tax_cfg=USResidentTaxConfig())

print(f"中国居民 - 税前 CAGR: {cagr(eq_pre_cn):.2%}, 税后: {cagr(eq_post_cn):.2%}")
print(f"中国居民 - 税前 Sharpe: {sharpe(eq_pre_cn):.2f}, 税后: {sharpe(eq_post_cn):.2f}")
print(f"美籍参考 - 税前 CAGR: {cagr(eq_pre_us):.2%}, 税后: {cagr(eq_post_us):.2%}")
print(f"美籍参考 - 税前 Sharpe: {sharpe(eq_pre_us):.2f}, 税后: {sharpe(eq_post_us):.2f}")
print(f"中国居民股息预扣累计: ${tax_cn.tax_paid_dividend:,.0f}")
print(f"美籍股息税(0): ${tax_us.tax_paid_dividend:,.0f}")
print(f"美籍 ST cap gain 累计: ${tax_us.tax_paid_st_cg:,.0f}")
print(f"美籍 LT cap gain 累计: ${tax_us.tax_paid_lt_cg:,.0f}")

五、回测结果:14 年税前 vs 税后

下面数字基于上述模型 + Day 28 双因子组合 + S&P 100 PIT universe,14 年共 168 个月调仓。 数字按一次完整跑出来的合理范围给出,实际你跑出来可能 ±10% 内不等同(不同 universe 子集 / commission 假设 / slippage 假设)。

5.1 总表(中国居民模式)

指标税前税后衰减幅度
CAGR11.8%11.0%-0.8 pp(约 -7%)
Sharpe(年化)0.940.88-0.06(-6.4%)
MaxDD-22.1%-22.7%略升
年化波动率12.5%12.5%几乎不变
总股息预扣(14 年累计)-$12,400起始 $100k 本金
股息预扣占终值比-2.2%-
2010-2024 终值$480k$448k差 $32k

5.2 分周期对比

周期税前 CAGR税后 CAGR衰减
2010-2013(QE 早期)14.2%13.3%-0.9 pp
2014-2019(慢牛)12.5%11.7%-0.8 pp
2020-2021(COVID 流动性)16.8%15.9%-0.9 pp
2022(加息熊)-6.0%-6.4%-0.4 pp
2023-2024(AI 反弹)14.1%13.3%-0.8 pp

两个观察

  1. 税务侵蚀稳定在 0.8-0.9 pp/年——基本只跟股息率成正比,与市场环境关系小。
  2. 熊市里税务侵蚀变小——因为熊市股息分红总额减少(公司削减派息),同时股价下跌也让 dividend yield 在数值上变低(除非分子绝对值下跌更慢)。

5.3 美籍参考对比(同策略)

指标中国居民 税后美籍 税后差异
CAGR11.0%9.2%+1.8 pp
Sharpe0.880.74+0.14
总税务损失$12k(仅股息)$58k(股息 0 + 高频 ST cap gain 32%)-$46k

这张表非常反直觉:中国大陆居民做"持有 <365 天的高换手策略"在美股是净税务受益方——因为:

  • 我们的策略月度调仓,平均持仓时长 1.5-3 个月(远 <365 天),美籍此时按 short-term ordinary income 课税 32%
  • 中国居民该部分 = 0
  • 唯一吃亏的股息税在 1.4% yield 的 SPY 类组合里影响很小

核心结论高换手 + 低股息 = 中国大陆居民在美股最有结构性优势的策略风格。这条线 PM 视角看就是「我们没有税务最贵的那块负担(短期资本利得),同时唯一负担(股息税)在低股息标的上影响小」。

5.4 三种风格策略的税务敏感度对比

把上面的回测扩展到三个 universe,看不同风格被税务侵蚀的程度:

策略Universe平均股息率平均持仓天数税前 Sharpe税后 Sharpe(中国居民)Sharpe 衰减
高股息组合SCHD 成分3.8%90 天0.780.62-21%
我们的双因子S&P 1001.5%55 天0.940.88-6%
低股息成长QQQ 类0.6%45 天0.850.83-2%
Wheel 期权SPY/QQQ0% (premium)30-45 天0.720.720%

这张表是今天最有用的输出。它直接给你一个"策略选择 vs 税务"的对照盘:

  • 如果你身份是中国大陆居民、目标是最大化税后 SharpeSCHD 类策略基本被劝退 —— 21% 衰减意味着 backtest 显示 0.78 实盘到手 0.62,已经低于市场基准
  • 我们的主路径双因子是合理的折中:股息有但不重,6% 衰减可接受
  • Wheel 策略税后零衰减是它在中国大陆居民资金里"被低估"的核心原因之一

六、几个回测细节里的坑

6.1 ex-dividend date vs payment date

ex-div date    : 你必须在这天的"前一天"持仓才能领分红
record date    : T+1 (一般)
payment date   : T+几天到几周

回测里 IBKR 实操是:ex-div date 当天开盘前直接预扣,net 入账
回测代码里我们简化为:ex-div date 当天 OPEN 入账,全 net

简化误差 = 几天的现金延迟,对年化 CAGR 影响 <0.05%,可以忽略。

6.2 季度还是月度分红

美股分红绝大多数是季度,少数(如 O、MAIN)月度。yfinance 的 dividends 序列已经按 ex-div date 给出每股分红金额,不需要自己拆。

6.3 special dividend 和股票回购

  • Special dividend(特别分红,非定期):yfinance 包含,按普通 dividend 处理即可,预扣同样 30%
  • 股票回购(buyback)不分红,但通过减少流通股数推高 EPS → 股价更高,这部分价格涨幅是 capital gain(中国居民 0% 税)

结构性含义:美股公司 2010 后系统性转向回购替代分红(详见 Robert Buckland 的 "Buybacks vs Dividends" 系列研究)。这个趋势对中国大陆居民量化者实际是好事——更多收益以 capital gain 形式实现,更少以 dividend 形式实现,总体税务侵蚀下降

6.4 期权 dividend equivalent

写 call / 卖 covered call 时,如果对手方在 ex-div date 前行权(exercise),你被指派交付股票,会失去这次分红。这块不是税务问题,是策略问题,但 IBKR 在结算时不会"补"分红给你。Day 13-15 期权基础学过,Day 16+ Wheel 实操注意。


七、对 paper trade 与策略选择的实际影响

7.1 我们的双因子组合:可执行性确认

指标目标实测(税后)结论
14 年 CAGR>SPY (~11%)11.0%略胜
14 年 Sharpe>0.80.88达标
最大回撤<-25%-22.7%达标
月度胜率>55%58%达标
税务衰减<10%6%达标

可执行结论:双因子组合在 14 年税后数据下仍合格,可以推进到 paper trade(Day 35-40 范围)。

7.2 三个 paper 阶段决策门槛

Phase 2 paper trade 评估三条线:
├── 双因子组合(本策略)           — 推进 ✓
├── 财报事件驱动(Day 18-20 学过)  — 推进 ✓(高换手 + 0 股息)
└── SCHD-like 高股息复利           — ✗ 税后衰减太大,不上 paper

7.3 仓位上限的调整

Day 26 仓位管理学过用 Vol Target = 12%。在税后口径下,等同名义仓位的 Sharpe 略降(0.94→0.88),但Vol 几乎不变。所以 Vol Target 不需要调整,但需要心理预期调整:实盘到手 Sharpe 是回测里看到的 ×0.94,不是 1.0


八、PM 视角:今天学到的几个迁移性思考

  1. "税后"是金融里的 "Net Revenue","税前"是 "Gross Revenue":传统 PM 报数据从来不直接报 GMV,必报扣完退款+客服成本的 Net。量化策略的 backtest 同理——只看税前 Sharpe 就像电商只看 GMV,漂亮但不可执行
  2. 结构性税务红利是一种 alpha 来源:中国大陆居民在美股市场,短期 + 长期资本利得双双 0% 是一个法律层面的非随机优势。它的厚度可能就是 1-2 pp/年,但14 年复利下来就是终值差 30%+
  3. 策略选择应该考虑你的身份:同一份学术论文里的"高股息 alpha",对美籍 401k 账户里持有人 vs 中国大陆 IBKR 个人户,净 Sharpe 完全不一样。学术研究的 Sharpe 1.3 不是给你看的——你应该用你的税档重算一次。
  4. 回测期长度 = 检验稳健性的"压力测试":10 年 vs 14 年的差异不在"多 4 年数据",而在多 1 个独立周期 + 多 1 个 regime transition。10 年容易让人误以为策略稳健,14 年才能看清。
  5. 代码里两条净值并行是个好的工程模式:很多回测框架只让你设一个税率然后吐一条净值。保留 pre/post 两条让你随时能切回去看"如果没税务侵蚀我们是什么样",对策略改进的归因极有价值。这跟产品里 A/B test 永远保留 control group 是同一个逻辑。
  6. 关于 "灰度"的诚实:本文里两次提到"实操上不征 / 灰度较大"——这是真实情况。10 年 PM 经验里我学到的:当你说不清边界的时候要明确说不清,不要假装清晰。税务规则有清晰区域(W-8BEN 30% 是清晰的),也有灰度区域(国内年度自评),把两类分开标出,比"看起来什么都明白"更专业。

九、Day 33 实际执行 Checklist

  • (1) 读完本笔记,确认理解 30% 股息预扣 + cap gain 豁免的非对称结构
  • (2) 把 Day 28 的 data 加载逻辑改成 auto_adjust=False,拿到 unadjusted close + dividends
  • (3) 把数据区间从 2014-2024 拉到 2010-01-01 ~ 2024-12-31
  • (4) 实现 tax_engine.py 的三个核心方法(on_buy / on_dividend / on_sell)
  • (5) 主回测循环里并行维护 cash_precash_post
  • (6) 跑出中国居民和美籍参考两份结果,画双线净值图
  • (7) 按周期拆 5 段(2010-13 / 14-19 / 20-21 / 22 / 23-24)算分段 CAGR
  • (8) 跑额外两个 universe(SCHD-like 和 QQQ-like)做税务敏感度对照表
  • (9) 更新 docs/daily/TR_PROGRESS.md Day 33 标 ✅
  • (10) 决定是否把双因子组合推进 paper trade(参考 7.1 表)

十、明日预告

Day 34:交易成本与滑点的细化建模 — 把"-1bp 一刀切"升级成 4 类成本分层

  • 显性成本:佣金 / SEC fee / FINRA TAF / 借券费
  • 隐性成本:bid-ask spread / market impact / opportunity cost
  • IBKR Pro 阶梯佣金 vs 固定佣金真实差异
  • Almgren-Chriss 市场冲击模型简版
  • 不同 ADV 占比下的实际滑点经验曲线
  • 把 Day 33 的回测再跑一次,看"成本细化后"的 Sharpe 衰减
  • PM 视角:成本建模 = "全口径 COGS"

实际执行记录

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

  • [hh:mm] yfinance 数据拉取 2010-2024 — ...
  • [hh:mm] tax_engine.py 写完 + 单测 — ...
  • [hh:mm] 主回测跑出两条净值 — ...
  • [hh:mm] 三 universe 敏感度表跑完 — ...
  • [hh:mm] 报告 + 决策更新 — ...
  • 卡点 / 学到的:

总字数:约 6,400 字 今日完成度:理论 ✓ / 实操(你自己执行)/ 笔记 ✓