2 · 25 分钟

市场数据基础

价格、K 线、调整后收盘价,以及策略每天消费的数据面板。

OHLCV复权面板偏差

每个策略都从市场数据开始。Quant Buffet 实验室通过 load_daily_prices() 加载流动 ETF 的日频调整后收盘价,并缓存到磁盘,使重跑既快速又可复现。

LayerWhat you getIn Quant Buffet
Exchange / vendorTick-by-tick trades, quotesNot used in lab
Yahoo Finance (yfinance)Daily OHLCVDownloaded via load_daily_prices
Quant Buffet cacheCSV per symbol + date rangebacktest/data_cache/*.csv
Your strategypd.DataFrame panelColumns = ASSETS, index = dates
IndicatorsRolling SMA, returns, z-scoresComputed inside make_on_day

核心词汇

术语含义实验室示例
代码 symbol标的标识SPYTLTBTC-USD
OHLCV开高低收量每个交易日一行
调整后收盘经拆股与分红修正的收盘价load_daily_prices 默认值
面板 panel日期 × 标的表格make_on_day 中的 prices
回看 lookback指标所需历史长度200 日 SMA 需 200 行
ready信号开始可信的首个日期make_on_day 返回

面板长什么样

面板就是一张表格:日期在行、标的在列。示意如下:

日期SPYTLTGLDXLC
2015-10-05185.42112.08108.31NaN
2015-10-06184.90112.55109.02NaN
2018-06-19268.41108.77116.9448.30
2018-06-20269.03108.32117.1048.55

注意那些 NaNXLC 在 2018 年 6 月之前并不存在,任何数据清洗都无法为它在 2015 年凭空造出价格。这不是数据缺陷,而是所有想在长历史上交易通信服务板块的策略都必须面对的真实约束。

在代码中读取价格面板

python
# 在 make_on_day 内部 —— prices 已按 ASSETS 加载完成
cols = [c for c in ASSETS if c in prices.columns]
close = prices[cols]

daily_return = close.pct_change()                       # 简单收益率
sma_200 = close.rolling(200, min_periods=200).mean()    # 趋势过滤
mom_12m = close.pct_change(252)                         # 12 个月动量
vol_20d = daily_return.rolling(20).std() * (252 ** 0.5) # 年化波动率

# 在所有标的数据齐备前不要开始交易
ready = close.dropna().index[0] if not close.dropna().empty else None

毁掉新手回测的三种偏差

1. 存活偏差

如果你用今天的赢家构建资产池,就已经悄悄删掉了所有失败者。安然、雷曼兄弟、华盛顿互惠、柯达、贝尔斯登都曾是体面的标普 500 成分股。只在「活到 2026 年」的公司上做回测,等于享受了现实投资者从未获得的顺风。SPY 这类宽基 ETF 能缓解该问题:指数本身会替换掉衰败成分,ETF 则继续存续。

2. 前视偏差

这是新手最常见、也几乎最难察觉的错误。任何在全样本上计算的统计量,都会把未来泄露进过去:

python
# 错误 —— 全样本均值包含了未来的价格
z = (close - close.mean()) / close.std()

# 正确 —— 只用截至当日可得的数据
roll_mean = close.rolling(60).mean()
roll_std = close.rolling(60).std()
z = (close - roll_mean) / roll_std

错误版本常常产出 Sharpe 大于 3、净值曲线近乎直线的结果。如果你的第一次回测就这么漂亮,请先怀疑数据泄露,再考虑庆祝。

3. 公司行动与未复权价格

苹果在 2020 年 8 月 1 拆 4;英伟达在 2024 年 6 月 1 拆 10;特斯拉在 2022 年 8 月 1 拆 3。在未复权数据上,这三次分别看起来像隔夜暴跌 75%、90% 与 67%——用未复权价格跑动量策略,会在最糟的时点把它们全部卖出。分红的累计影响同样巨大:SPY 每年分配约 1.3%–2%,在 20 年回测中忽略它,等于把年化 8% 变成约 6.5%——1 万美元的终值大约是 4.66 万对 3.52 万。

真实示例:当「价格序列」不等于「资产本身」

白名单里有两只著名的「反面教材」商品 ETF,正因为它们表现异常,才值得在 IDE 中亲自加载一次。

ETF发生了什么对回测的启示
USO2020 年 4 月 WTI 近月合约结算价转负(约 -37 美元),USO 被迫重构持仓,后续还做了反向拆股。基金的投资授权可能在历史中途改变;「油价」与「USO 价格」是两条不同的序列。
UNG长期升水(contango)使每次展期都「卖便宜、买贵」,无论现货天然气如何都在持续损耗净值。此处的长期做空结论可能是结构性成本,而非可交易的 edge。
SVXY2018 年 2 月 5 日「波动率末日」,单日跌约 90%;姊妹产品 XIV 直接清算终止。日频收盘回测看不到这种量级的盘中跳空风险。

你真的会遇到的数据起始日

标的数据实际起始后果
SPY1993 年长周期回测的可靠锚点
XLRE2015 年 10 月房地产板块回测很短
XLC2018 年 6 月无法出现在 2008 年压力测试中
BTC-USD约 2014 年 9 月任何加密策略都跨不过金融危机
ETH-USD约 2017 年 11 月仅有约两轮完整周期

进入第 3 课前 — 您应能够

  • 解释调整后收盘价,以及未复权价格为何会破坏动量信号。
  • 在一行 pandas 代码中识别前视偏差
  • 说明 ETF 资产池为何能缓解但无法消除存活偏差
  • 预测在 ASSETS 中加入新上市 ETF 后 ready 会如何变化。