一句话回答: 前复权价不是"固定值"——每次分红送股后,历史前复权价都会整体缩放一次,直接把复权价存进数据库迟早和线上对不上。正确做法是用 AlphaFeed 存原始价(不复权)+ 除权因子(ex_factors),需要时再本地复现复权价。这样数据可审计、可复现,且和 SDK 的 adjust="forward" 结果逐行完全一致(误差 0)。
为什么会有这个问题:前复权价会"漂移"
很多人把 adjust="forward"(前复权)的收盘价直接落库,几个月后发现:同一天、同一只票,历史前复权价变了。这不是 bug——前复权的定义就是"以最新价为基准向前缩放",每发生一次分红送股(除权),基准就变一次,整条历史序列会被重新缩放。
- 直接存复权价:数据会随时间"漂移",回测无法复现、审计对不上账。
- 每次全量重取复权价:浪费带宽,且旧数据被覆盖后无法追溯。
- 存原始价 + 除权因子:原始价永不变,因子只在除权日新增一条,任意时点都能精确还原任意复权口径。
分步骤解决
第 1 步:取原始价与除权因子
from alphafeed import AlphaFeed
import pandas as pd
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
symbol = "600519.SH"
raw = af.klines.get(symbol, period="1d", count=400, adjust="none",
to_dataframe=True).sort_values("trade_date").reset_index(drop=True)
ef = af.klines.ex_factors([symbol], to_dataframe=True).sort_values("trade_date")
print(ef[["trade_date", "ex_factor"]].tail(3).to_string(index=False))
ex_factors 返回每个除权日的一条记录,字段见下表。ex_factor 是该次除权事件的调整比率(>1 表示当日发生了分红/送股导致的向下跳空)。
| 字段 | 含义 | 示例 |
|---|---|---|
symbol |
标的代码 | 600519.SH |
timestamp |
除权日毫秒时间戳 | 1027526400000 |
trade_date |
除权日(YYYY-MM-DD) | 2026-06-26 |
ex_factor |
该次除权的调整比率(每次事件一条) | 1.023663 |
第 2 步:用因子复现前复权价
AlphaFeed 前复权的口径是:某交易日 t 的前复权价 = 该日原始价 ÷(所有除权日晚于 t 的 ex_factor 连乘积)。最新一段(在最后一次除权之后)除数为 1,即前复权价 = 原始价。
raw["td"] = pd.to_datetime(raw["trade_date"])
ex = ef.assign(td=pd.to_datetime(ef["trade_date"]))[["td", "ex_factor"]]
def forward_divisor(t):
later = ex.loc[ex["td"] > t, "ex_factor"] # 晚于当日的所有除权事件
return later.prod() if len(later) else 1.0
raw["divisor"] = raw["td"].apply(forward_divisor)
raw["qfq_close"] = raw["close"] / raw["divisor"]
第 3 步:与 SDK 的前复权结果对账(误差应为 0)
fwd = af.klines.get(symbol, period="1d", count=400, adjust="forward",
to_dataframe=True).sort_values("trade_date").reset_index(drop=True)
diff = (fwd["close"] - raw["qfq_close"]).abs()
print("最大绝对误差:", round(diff.max(), 6)) # -> 0.0
print("最大相对误差(%):", round((diff / fwd["close"]).max() * 100, 6)) # -> 0.0
实测(茅台 600519.SH,400 个交易日):最大绝对误差 = 0.0,逐行完全一致。说明"原始价 + 因子"能无损复现 SDK 的前复权序列。
第 4 步:落库设计(只存不变量)
只落 原始 OHLC + 每次除权的因子 两张表,复权价永远现算:
| 表 | 列 | 说明 |
|---|---|---|
bars_raw |
symbol, trade_date, open, high, low, close, volume, amount | 原始价,写入后永不修改 |
ex_factors |
symbol, trade_date, ex_factor | 除权事件表,只在除权日新增一行 |
增量更新时:K 线只追加新交易日;ex_factors 只在有新除权时追加。历史行永不被覆盖,天然可审计、可复现。
关键坑与注意事项
- 不要落库复权价当"真值":它会随每次除权整体缩放,落库即过期。
- 后复权(
backward)反过来:以最早价为基准,历史价不变、最新价被放大,适合长周期收益复现;前复权适合看当前价位。可用同一套因子换基准点复现。 - 成交量/成交额:AlphaFeed 的除权因子用于价格口径;成交量不做价格式复权,跨除权做量能比较要谨慎。
- A股有真实
amount,美股/港股 K 线amount=0(用volume替代),与本文复权逻辑无关,但落库时要注意。
常见问题(FAQ)
Q:为什么我半年前存的前复权价现在对不上了?
A:因为期间发生了分红送股,前复权基准变了,历史序列被整体缩放。存原始价 + 因子就不会有这个问题。
Q:ex_factor 是累计的还是单次的?
A:是单次除权事件的比率。复现某日复权价要把该日之后的所有 ex_factor 连乘作为除数。
Q:需要付费吗?
A:klines.get/ex_factors 属基础能力,注册即有免费额度;大规模全市场批量与更高频调用见 Starter 及以上,详见定价页。
小结
复权价是"派生量",会随除权漂移;原始价和除权因子才是"不变量"。用 AlphaFeed 存原始价 + ex_factors、需要时本地复现,既省带宽又能做到 误差 0、可审计、可复现——这才是能长期维护的复权数据方案。
参考
- AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart

