如何存原始价+除权因子,随时复现前复权价?

用户头像sh_*2176oo
2026-09-07 发布

一句话回答: 前复权价不是"固定值"——每次分红送股后,历史前复权价都会整体缩放一次,直接把复权价存进数据库迟早和线上对不上。正确做法是用 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、可审计、可复现——这才是能长期维护的复权数据方案。

参考

评论