美股日线只拆股不除权会差多少?FMP 分红复权行情(Dividend Adjusted API)与总回报率计算实战

在搭建美股中长期回测、红利因子选股或定投收益复盘管线时,许多量化开发者默认直接调用标准日线接口拉取收盘价计算收益率,却忽略了美股行情数据源的一项关键口径分工:标准日线接口通常只做拆合股调整(Split-Adjusted Only),并不扣除历次现金分红。更隐蔽的是,FMP 在 /stable/historical-price-eod/ 路径下提供了四套不同复权层级的日线端点,其中未复权端点与分红复权端点共用完全相同的字段名 adjClose,极易在跨脚本调用时发生口径混用。

我们对 10 只代表性美股与 ETF 在 5 年完整窗口(2021-10-29 至 2026-10-30)的实盘对账显示:若误用仅拆股复权日线替代分红复权日线,高股息与分红成长标的 5 年累计总回报率会少算 25 至 83 个百分点;其中威瑞森与月度派息不动产信托 Realty Income 的 5 年价格回报率分别为 -15.59% 与 -17.70%,而计入现金分红复权后的总回报率实为 +16.18% 与 +7.62%,直接导致回测盈亏方向反转。


为什么用默认日线接口(historical-price-eod/full)算高股息美股回报率会严重失真?

在 Chart(股票行情接口)大类下,开发者最常调用的 Stock Price and Volume Data(日线完整行情接口,/stable/historical-price-eod/full)返回的 close 字段,仅对历史股票拆细(Stock Split)与合股(Reverse Split)做了前复权折算,并未对季度或月度现金派息做除权下调。用该序列首尾相除算出的只是价格回报率(Price Return);只有调用专门的 Dividend Adjusted Price Chart(分红复权日K线接口,/stable/historical-price-eod/dividend-adjusted)读取 adjClose,才能得到包含股息除权调整的分红复权总回报率(Total Return)。

为直观量化两套序列在中长期回测中的偏差幅度,我们选取高股息消费/电信蓝筹、月度派息 REIT、能源与金融龙头、宽基与红利 ETF 以及分红成长股共 10 只标的,拉取 2021-10-29 至 2026-10-30(整 5 年)的实盘定盘数据,并结合 Dividends Company(公司分红派息接口,/stable/dividends)汇总区间派息次数与拆股后累计每股股息,实测对账结果如下(数据快照截至 2026-10-30):

标的 (symbol)5年派息次数与累计 adjDividend2021-10-29 仅拆股 close2021-10-29 分红复权 adjClose2026-10-30 期末收盘价5年价格回报率 (仅拆股)5年总回报率 (分红复权)5年总回报差值 (年化 CAGR 差)
奥驰亚 (MO)20 次 · 19.81 美元44.11 美元30.03 美元78.03 美元+76.90%+159.84%+82.94 个百分点 (+8.96%/年)
博通 (AVGO)20 次 · 10.54 美元53.17 美元48.67 美元326.08 美元+513.28%+569.98%+56.70 个百分点 (+2.56%/年)
埃克森美孚 (XOM)20 次 · 19.04 美元64.47 美元54.11 美元187.25 美元+190.45%+246.05%+55.61 个百分点 (+4.41%/年)
威瑞森 (VZ)20 次 · 13.40 美元52.99 美元38.50 美元44.73 美元-15.59%+16.18%+31.77 个百分点 (+6.38%/年)
红利 ETF (SCHD)20 次 · 4.76 美元25.84 美元21.62 美元35.78 美元+38.47%+65.49%+27.03 个百分点 (+3.87%/年)
可口可乐 (KO)20 次 · 9.59 美元56.37 美元48.69 美元94.08 美元+66.90%+93.22%+26.33 个百分点 (+3.29%/年)
摩根大通 (JPM)20 次 · 24.35 美元169.89 美元150.07 美元337.04 美元+98.39%+124.59%+26.20 个百分点 (+2.88%/年)
Realty Income (O)62 次 · 16.06 美元69.17 美元52.90 美元56.93 美元-17.70%+7.62%+25.31 个百分点 (+5.30%/年)
标普 500 ETF (SPY)20 次 · 34.52 美元459.25 美元429.55 美元755.72 美元+64.56%+75.93%+11.38 个百分点 (+1.49%/年)
苹果 (AAPL)20 次 · 4.90 美元149.80 美元146.11 美元344.63 美元+130.06%+135.87%+5.81 个百分点 (+0.59%/年)

从工程回测角度审视上述实盘对账,有两处核心机制直接决定因子有效性:

  1. 高派息防御股与 REITs 的盈亏方向逆转:威瑞森(VZ)与按月派息的商业地产信托 Realty Income(O,5 年内共除息 62 次)将绝大部分自由现金流用于现金分红。若策略误用 historical-price-eod/full 计算收益,两只标的会被判定为 5 年亏损逾 15% 的劣质资产;而在 historical-price-eod/dividend-adjusted 口径下,历次除息将 2021-10-29 的前复权成本基数分别下调至 38.50 美元与 52.90 美元,还原出 +16.18% 与 +7.62% 的正收益。
  2. 分红成长股早期除息因子的乘数放大效应:博通(AVGO)5 年累计拆股后派息 adjDividend 仅 10.54 美元(约为 2021-10-29 拆股后收盘价 53.17 美元的 19.8%),但其 5 年分红复权总回报率却比价格回报率高出 56.70 个百分点。原因在于 2021 至 2023 年博通拆股后收盘价处于 42.71 至 114.70 美元相对低位,期间每次除息产生的等比例前复权因子累积下调了期初成本基数(由 53.17 美元下调至 48.67 美元);随着 2024 至 2026 年股价涨至 326.08 美元(较 2021–2023 年价格区间放大约 2.8 至 7.6 倍),早期除息因子对区间复权总回报的贡献被同步放大。

FMP 四套日线接口(full / dividend-adjusted / non-split-adjusted / light)的字段命名与复权口径有什么区别?

FMP 在 /stable/historical-price-eod/ 路径下共提供四套股票日线端点:除前文提到的 full 与 dividend-adjusted 外,还包括 Unadjusted Stock Price(未复权日线行情接口,/stable/historical-price-eod/non-split-adjusted)与 Stock Chart Light(日线行情(精简版)接口,/stable/historical-price-eod/light)。

这四套端点最容易让初次接入的开发者踩坑的地方,在于返回 JSON 的字段名与实际复权状态并不按字面对应。我们以博通(AVGO)在 2024-06-21(处于 2024-06-24 除息日前一交易日、且处于 2024-07-15 的 1 拆 10 拆股之前)这一天的四套接口真实返回报文为例:

// 1. /stable/historical-price-eod/non-split-adjusted?symbol=AVGO(未拆股、未除权的交易所原始挂牌价,但字段名却带 adj 前缀!)
[{"symbol": "AVGO", "date": "2024-06-21", "adjOpen": 1695.7, "adjHigh": 1714, "adjLow": 1651.4, "adjClose": 1658.6, "volume": 9999900}]

// 2. /stable/historical-price-eod/full?symbol=AVGO(仅拆股前复权、不含分红除权,字段名为标准 open/high/low/close)
[{"symbol": "AVGO", "date": "2024-06-21", "open": 169.57, "high": 171.4, "low": 165.14, "close": 165.86, "volume": 99999000, "change": -3.7, "changePercent": -2.19, "vwap": 167.9925}]

// 3. /stable/historical-price-eod/light?symbol=AVGO(精简版,price 与 full 的 close 完全一致,仅拆股前复权)
[{"symbol": "AVGO", "date": "2024-06-21", "price": 165.86, "volume": 99999000}]

// 4. /stable/historical-price-eod/dividend-adjusted?symbol=AVGO(拆股 + 现金分红双重前复权,字段名同样为 adjOpen..adjClose)
[{"symbol": "AVGO", "date": "2024-06-21", "adjOpen": 165.61, "adjHigh": 167.4, "adjLow": 161.29, "adjClose": 161.99, "volume": 99999000}]

为看清这四套接口在跨越现金除息日(2024-06-24,除息 5.25 美元/拆股前)与股票拆细生效日(2024-07-15,1 股拆 10 股)时的数值跳变规律,我们将博通在四个关键交易日的实盘返回数值整理对照如下:

接口端点段返回价格与成交量字段名复权口径定义2024-06-21 (除息前一日)2024-06-24 (除息生效日)2024-07-12 (拆股前末日)2024-07-15 (1拆10首日)
non-split-adjustedadjOpen ~ adjClose · volume未复权原始挂牌价:不拆股、不除权,保留历史交易当日盘面真实报价与原始股数收 1658.60 美元 · 量 999.99 万收 1592.20 美元 · 量 585.75 万收 1700.70 美元 · 量 304.53 万收 171.42 美元 · 量 2299.92 万
fullopen ~ close · volume · vwap仅拆合股前复权:拆股前价格除以 10、成交量乘以 10;不含现金分红除权收 165.86 美元 · 量 9999.90 万收 159.22 美元 · 量 5857.54 万收 170.07 美元 · 量 3045.30 万收 171.42 美元 · 量 2299.92 万
lightprice · volume仅拆合股前复权(精简流):price 恒等于 full 接口的 close,省去开高低与涨跌字段价 165.86 美元 · 量 9999.90 万价 159.22 美元 · 量 5857.54 万价 170.07 美元 · 量 3045.30 万价 171.42 美元 · 量 2299.92 万
dividend-adjustedadjOpen ~ adjClose · volume拆合股 + 现金分红双重前复权:在 full 基础上,每跨越一次除息日向历史回溯累乘除权因子收 161.99 美元 · 量 9999.90 万收 156.00 美元 · 量 5857.54 万收 166.63 美元 · 量 3045.30 万收 167.95 美元 · 量 2299.92 万

在编写数据清洗管线时,务必重点防范以下三项接口差异:


分红复权日线(dividend-adjusted)底层的等比例前复权因子是怎么算出来的?

如果要在本地数据库中维护可增量更新的前复权因子表,或者验证第三方行情的分红复权精度,就必须弄清 Calendar(市场日历接口)大类下 Dividends Company(公司分红派息接口,/stable/dividends)与 Stock Split Details(公司拆合股明细接口,/stable/splits)是如何同日线行情勾稽的。

我们先看博通(AVGO)在 2024 年跨越 T+1 交收改革与 10 拆 1 拆股前后的真实分红记录与拆股记录:

// /stable/splits?symbol=AVGO(2024-07-15 生效 10 拆 1 正向拆股)
[{"symbol": "AVGO", "date": "2024-07-15", "numerator": 10, "denominator": 1, "splitType": "stock-split"}]

// /stable/dividends?symbol=AVGO(2024 年三期真实记录,注意对比 adjDividend、dividend、recordDate 与年化 yield)
[
  {"symbol": "AVGO", "date": "2024-09-19", "recordDate": "2024-09-19", "paymentDate": "2024-09-30", "declarationDate": "2024-09-05", "adjDividend": 0.53, "dividend": 0.53, "yield": 1.2573169274877556, "frequency": "Quarterly"},
  {"symbol": "AVGO", "date": "2024-06-24", "recordDate": "2024-06-24", "paymentDate": "2024-06-28", "declarationDate": "2024-06-12", "adjDividend": 0.525, "dividend": 5.25, "yield": 1.2781057656073358, "frequency": "Quarterly"},
  {"symbol": "AVGO", "date": "2024-03-20", "recordDate": "2024-03-21", "paymentDate": "2024-03-29", "declarationDate": "2024-03-06", "adjDividend": 0.525, "dividend": 5.25, "yield": 1.5438871473354232, "frequency": "Quarterly"}
]

从上述报文中可以提炼出三条决定回测准确性的核心工程规则:

1. 拆股前分红必须取 adjDividend,严禁将原始 dividend 与 full 日线混算

在 2024-06-24 除息日(处于 1 拆 10 之前),博通实际向每股原始流通股派发现金 dividend: 5.25 美元(对应除息前一日未拆股收盘价 1658.60 美元,单季派息率约 0.3165%;接口返回的 yield: 1.2781 为按除息日价格折算的年化股息率百分比)。而在拆股后回看历史,/stable/dividends 已将其折算为拆股后口径 adjDividend: 0.525 美元(对应 full 接口拆股复权收盘价 165.86 美元)。若误拿原始 dividend = 5.25 去除以 full 的拆股复权价 165.86 美元,单季股息率会被人为放大 10 倍变成 3.17%,计算出的前复权价格也会严重失真。

2. 分红日历四个日期字段的回测时点分工

/stable/dividends 每条记录包含四个日期字段:董事会宣告日 declarationDate、除息日 date(Ex-Dividend Date)、股权登记日 recordDate 与资金到账日 paymentDate。如上方报文所示,在 2024 年 5 月 28 日美股全面实行 T+1 交收制度之前,2024-03-20 那期分红的除息日 date(2024-03-20)比股权登记日 recordDate(2024-03-21)早 1 个交易日;而自 2024-06-24 起,date 与 recordDate 已合并为同一交易日。在二级市场交易中,交易所于除息日 date 当天开盘前下调基准参考价,凡在 date 前一交易日收盘时持有股票的投资者即享有本期分红权。因此,前复权除权因子严格在除息日 date 与前一交易日之间生效。

3. 等比例累乘前复权递推公式与手算对拍

不同于部分看盘软件采用的“简单减法除权”(直接从历史股价中减去累计分红金额,回溯期较长时极易把早期低价股减成负数),FMP 的 /stable/historical-price-eod/dividend-adjusted 采用等比例累乘前复权递推法(Proportional Backward Adjustment)。设某次除息日拆股调整后每股派息为 adjDividend,除息日前一交易日在 full 接口下的拆股复权收盘价为 prev_close,则递推公式为:

(注:该因子生成的除息日复权收益为 当日收盘价 ÷ (prev_close − adjDividend) − 1,用于构建连续的含息复权价格序列,与普通现金分红下 (当日收盘价 + adjDividend) ÷ prev_close − 1 的单期持有公式存在极微小的二阶代数差异。)我们以博通截至 2026 年 10 月最近的两次除息(2026-09-21 与 2026-06-22,每次 adjDividend 均为 0.65 美元)亲手验算一轮:

  1. 最新除息日(2026-09-21)及之后:由于后续尚无新的已生效除息,累计乘数 cum_factor = 1.000000,因此 2026-09-21 当日 dividend-adjusted 的 adjClose(362.66 美元)与 full 的 close(362.66 美元)完全相等。
  2. 回溯跨越 2026-09-21 除息日:除息前最后一个交易日为 2026-09-18,当日 full 接口 close 为 357.61 美元,代入公式得单期除权因子 1 − 0.65 ÷ 357.61 ≈ 0.99818238;2026-09-18 当日的分红复权收盘价即为 357.61 × 0.99818238 ≈ 356.96 美元(与接口返回值 adjClose: 356.96 分毫不差)。
  3. 继续回溯至上一次除息日(2026-06-22):当日 full 接口 close 为 392.13 美元,乘以同一累计因子 0.99818238 得 391.42 美元,同样与接口返回值 adjClose: 391.42 精确吻合(若误按简单减法 392.13 − 0.65 = 391.48 计算,单次除息即产生 0.06 美元偏差)。
  4. 同日开高低价格同步等比例缩放:同一交易日的 adjOpen、adjHigh、adjLow 均乘以相同的累计因子 cum_factor,从而保证单根 K 线的日内振幅比例与上下影线形态在复权前后严格不变。

如何用 Python 自动对账价格回报率、分红复权总回报率与前复权因子?

在实际工程中,我们可以编写一段 Python 审计脚本,同时拉取 historical-price-eod/full、historical-price-eod/dividend-adjusted 与 dividends 三套接口,一方面批量输出各标的的价格回报率、分红复权总回报率及年化 CAGR 差值,另一方面直接用 full 的 close 配合 dividends 的 adjDividend 独立累乘复算期初前复权价 reconst_d0(乘以期末比值 ac1 / c1 对齐复权基准,使任意历史截断区间均能与官方 dadj_d0 逐分钱对拍验证)。

运行脚本前,请先在终端设置环境变量 FMP_KEY:

# Linux / macOS 终端设置环境变量
export FMP_KEY="你的密钥"

# Windows PowerShell 设置环境变量
$env:FMP_KEY="你的密钥"

完整可运行的对账与复算代码如下:

import os
import requests

BASE_URL = "https://quote-fmp.com/stable"
API_KEY = os.environ["FMP_KEY"]

def fetch_api(endpoint: str, params: dict) -> list:
    resp = requests.get(f"{BASE_URL}/{endpoint}", params={**params, "apikey": API_KEY}, timeout=25)
    resp.raise_for_status()
    return resp.json()

def audit_dividend_total_return(symbol: str, start_date: str, end_date: str, years: float = 5.0) -> dict:
    full_rows = fetch_api("historical-price-eod/full", {"symbol": symbol, "from": start_date, "to": end_date})
    dadj_rows = fetch_api("historical-price-eod/dividend-adjusted", {"symbol": symbol, "from": start_date, "to": end_date})
    div_rows = fetch_api("dividends", {"symbol": symbol})
    full_map = {r["date"]: r["close"] for r in full_rows}
    dadj_map = {r["date"]: r["adjClose"] for r in dadj_rows}
    dates = sorted(full_map.keys())
    d0, d1 = dates[0], dates[-1]
    date_idx = {dt: i for i, dt in enumerate(dates)}

    # 提取区间内除息记录(使用拆股对齐后的 adjDividend,缺失除息日行情时显式报错)
    window_divs = [d for d in div_rows if d0 < d["date"] <= d1 and d.get("adjDividend")]
    factor = 1.0
    for d in sorted(window_divs, key=lambda x: x["date"], reverse=True):
        ex_dt = d["date"]
        if ex_dt not in date_idx or date_idx[ex_dt] == 0:
            raise ValueError(f"{symbol} 缺失除息日 {ex_dt} 或前一交易日行情,无法完整核验")
        prev_close = full_map[dates[date_idx[ex_dt] - 1]]
        factor *= (1.0 - d["adjDividend"] / prev_close)

    c0, c1 = full_map[d0], full_map[d1]
    ac0, ac1 = dadj_map[d0], dadj_map[d1]
    price_ret = (c1 / c0 - 1.0) * 100.0
    total_ret = (ac1 / ac0 - 1.0) * 100.0
    return {
        "symbol": symbol,
        "divs": len(window_divs),
        "sum_adj_div": round(sum(d["adjDividend"] for d in window_divs), 2),
        "full_d0": c0,
        "dadj_d0": ac0,
        "reconst_d0": round(c0 * factor * (ac1 / c1), 2),
        "end_close": c1,
        "price_ret_pct": round(price_ret, 2),
        "total_ret_pct": round(total_ret, 2),
        "gap_pct": round(total_ret - price_ret, 2),
        "cagr_gap_pct": round((((ac1 / ac0) ** (1.0 / years) - (c1 / c0) ** (1.0 / years)) * 100.0), 2),
    }

for sym in ["MO", "VZ", "O", "XOM", "KO", "JPM", "SPY", "SCHD", "AAPL", "AVGO"]:
    print(audit_dividend_total_return(sym, "2021-10-29", "2026-10-30"))

实盘运行上述脚本,终端输出结果如下(第一节表一即根据上述函数返回结果整理):

{'symbol': 'MO', 'divs': 20, 'sum_adj_div': 19.81, 'full_d0': 44.11, 'dadj_d0': 30.03, 'reconst_d0': 30.04, 'end_close': 78.03, 'price_ret_pct': 76.9, 'total_ret_pct': 159.84, 'gap_pct': 82.94, 'cagr_gap_pct': 8.96}
{'symbol': 'VZ', 'divs': 20, 'sum_adj_div': 13.4, 'full_d0': 52.99, 'dadj_d0': 38.5, 'reconst_d0': 38.5, 'end_close': 44.73, 'price_ret_pct': -15.59, 'total_ret_pct': 16.18, 'gap_pct': 31.77, 'cagr_gap_pct': 6.38}
{'symbol': 'O', 'divs': 62, 'sum_adj_div': 16.06, 'full_d0': 69.17, 'dadj_d0': 52.9, 'reconst_d0': 52.9, 'end_close': 56.93, 'price_ret_pct': -17.7, 'total_ret_pct': 7.62, 'gap_pct': 25.31, 'cagr_gap_pct': 5.3}
{'symbol': 'XOM', 'divs': 20, 'sum_adj_div': 19.04, 'full_d0': 64.47, 'dadj_d0': 54.11, 'reconst_d0': 54.1, 'end_close': 187.25, 'price_ret_pct': 190.45, 'total_ret_pct': 246.05, 'gap_pct': 55.61, 'cagr_gap_pct': 4.41}
{'symbol': 'KO', 'divs': 20, 'sum_adj_div': 9.59, 'full_d0': 56.37, 'dadj_d0': 48.69, 'reconst_d0': 48.7, 'end_close': 94.08, 'price_ret_pct': 66.9, 'total_ret_pct': 93.22, 'gap_pct': 26.33, 'cagr_gap_pct': 3.29}
{'symbol': 'JPM', 'divs': 20, 'sum_adj_div': 24.35, 'full_d0': 169.89, 'dadj_d0': 150.07, 'reconst_d0': 150.07, 'end_close': 337.04, 'price_ret_pct': 98.39, 'total_ret_pct': 124.59, 'gap_pct': 26.2, 'cagr_gap_pct': 2.88}
{'symbol': 'SPY', 'divs': 20, 'sum_adj_div': 34.52, 'full_d0': 459.25, 'dadj_d0': 429.55, 'reconst_d0': 429.54, 'end_close': 755.72, 'price_ret_pct': 64.56, 'total_ret_pct': 75.93, 'gap_pct': 11.38, 'cagr_gap_pct': 1.49}
{'symbol': 'SCHD', 'divs': 20, 'sum_adj_div': 4.76, 'full_d0': 25.84, 'dadj_d0': 21.62, 'reconst_d0': 21.62, 'end_close': 35.78, 'price_ret_pct': 38.47, 'total_ret_pct': 65.49, 'gap_pct': 27.03, 'cagr_gap_pct': 3.87}
{'symbol': 'AAPL', 'divs': 20, 'sum_adj_div': 4.9, 'full_d0': 149.8, 'dadj_d0': 146.11, 'reconst_d0': 146.11, 'end_close': 344.63, 'price_ret_pct': 130.06, 'total_ret_pct': 135.87, 'gap_pct': 5.81, 'cagr_gap_pct': 0.59}
{'symbol': 'AVGO', 'divs': 20, 'sum_adj_div': 10.54, 'full_d0': 53.17, 'dadj_d0': 48.67, 'reconst_d0': 48.67, 'end_close': 326.08, 'price_ret_pct': 513.28, 'total_ret_pct': 569.98, 'gap_pct': 56.7, 'cagr_gap_pct': 2.56}

可以看到,在本次核验的 10 只标的中,用等比例累乘公式从 full 序列独立复算的 reconst_d0 与官方 dividend-adjusted 返回的 dadj_d0 在 6 只标的上完全一致(0.00 美元误差),其余 4 只标的(MO、XOM、KO、SPY)最大反推差额仅为 0.01 美元。


常见问题(FAQ):分红复权行情与批量回测中的高频疑问

1. 全市场日线批量接口(eod-bulk)返回的是拆股复权价还是分红复权价?

Eod Bulk(日线行情批量接口,/stable/eod-bulk)同时返回仅拆股的 close 与包含拆股及现金分红回溯调整的 adjClose(与 dividend-adjusted 同源等值)。仅在查询日之后尚未发生新拆股或除息时,该日 close 与 adjClose 才相等;一旦后续发生除息或拆股,历史切片的 adjClose 会被回溯下调(见 FMP Bulk 批量接口建库实战)。本地增量维护时,需结合 Dividends Calendar(分红派息日历接口,/stable/dividends-calendar)、拆股日历及历史数据修订记录同步刷新复权因子。

2. 做技术指标回测和长期多因子回测,分别该选哪套日线接口?

不能仅凭持仓周期长短指定唯一接口。若需还原历史实际成交价、订单簿挂单价或期权行权价,应使用未复权的 non-split-adjusted;计算连续技术指标或中长期多因子收益时,则取决于策略如何处理拆股与除息缺口,分别选用仅拆股的 full 或含分红复权的 dividend-adjusted。如需在本地跑通上述对账脚本,可前往 快速接入指南 申领 15 天 Ultimate 全接口试用密钥(20 RPM / 0.3 GiB,支持微信支付与人民币按月订阅)。

登录后可领取FMP Ultimate档15天试用 Key,覆盖全量接口。免费试用 15 天 →