如何比较不同复权方式下的历史价格变化?从价格序列到收益率分析 在研究一只股票的历史走势时同一段行情可能因为复权方式不同而呈现出不同的价格曲线。前复权价格可能明显低于实际历史成交价格后复权价格则可能高于当前市场价格而不复权序列会保留历史交易时的原始报价。这些差异并不一定意味着数据存在错误。对于发生过现金分红、送股、转增或拆股等事件的股票复权方式决定了如何调整历史价格使不同时点的行情具有更合理的可比性。如果希望判断一只股票在过去三年中上涨了多少、某段行情的最大回撤有多大或者不同股票的历史收益率是否具有可比性就需要先弄清楚复权价格的计算逻辑。一、先区分三种价格原始价格、前复权与后复权假设某只股票在现金分红前的收盘价为 100 元除息后收盘价为 98 元每股现金分红为 2 元。如果只比较两个交易日的原始收盘价计算结果为[R_{\text{raw}}\frac{98}{100}-1-2%]这看起来像是股票下跌了 2%。但如果投资者持有股票并获得了 2 元现金分红那么忽略税费和其他市场因素时其持有期总回报实际上为[R_{\text{total}}\frac{982}{100}-10%]这个例子说明原始价格变化不一定等于投资收益变化。复权的目的就是按照公司行为调整历史价格减少分红、送转等事件对价格序列连续性的影响。1. 不复权保留市场实际报价不复权价格保留交易发生时的原始行情。例如日期原始收盘价事件T-1100分红前T98除息日T199除息后这种数据适合研究历史成交价格、实际报价水平以及公司行为发生前后的市场表现。但如果直接使用不复权收盘价计算长期收益率就可能把除息造成的价格调整误认为投资损失。2. 前复权以当前价格尺度调整历史行情前复权通常将较早时期的历史价格按照公司行为进行调整使历史价格与当前价格处于相对一致的尺度。在上述例子中若只考虑每股 2 元现金分红前复权可以将分红前的 100 元调整为 98 元。日期不复权价格示例前复权价格T-110098T9898T19999这样可以更直观地观察剔除分红影响后的历史走势。需要注意实际数据服务可能使用不同的精度、公司行为处理规则和复权因子因此这里的数字仅用于解释原理。前复权价格不是历史上真实发生过的成交价格。如果将它用于成交金额、历史委托价格或交易执行模拟就可能产生错误。3. 后复权将历史价格调整到另一统一尺度后复权通常以较早时期的价格尺度为基础对后续价格进行调整使分红、送转等公司行为能够反映在连续的价格序列中。在相同的简化案例中如果以分红前价格为基准可以将除息后的 98 元调整为 100 元。日期不复权价格示例后复权价格T-1100100T98100T199约 101.02后复权序列有助于观察长期持有期间的累计变化但其价格可能明显高于当前市场实际报价。实际后复权序列如何确定初始基准仍取决于数据服务商的实现规则。二、比较复权方式时究竟应该比较什么如果目标是研究历史价格变化直接比较某一天的价格大小通常没有太大意义。更有效的方法是先明确研究目的再选择比较指标。比较维度适合观察的数据主要用途价格水平不复权价格、前复权价格、后复权价格理解不同价格尺度区间收益率各序列的起止价格判断某段时间的价格变化单期收益率相邻交易日的价格变化检查公司行为造成的异常跳变累计收益曲线连续收益率序列比较长期表现最大回撤基于一致口径构建的累计收益曲线观察历史下跌幅度历史成交价格不复权行情研究实际报价与交易执行这里有一个容易忽略的区别比较价格水平与比较投资收益是两个不同的问题。前复权和后复权价格可能相差很大但如果复权口径一致、区间边界一致而且采用相同的公司行为处理规则它们在某些区间的收益率可能基本一致。原因在于纯粹的比例缩放不会改变收益率[R_t\frac{P_t}{P_{t-1}}-1]若一段价格序列中的每个价格都乘以相同的常数 (c)则[R_t’\frac{cP_t}{cP_{t-1}}-1R_t]不过前复权与后复权并不一定只是对整段数据乘以同一个常数。公司行为处理、复权基准和数据更新方式都可能影响实际比较结果。因此不应仅凭两条价格曲线的绝对高度不同就认定某种复权数据错误。三、使用 QuantDash 获取不同复权口径的历史行情如果需要比较真实股票行情建议使用同一数据源、同一标的和同一时间范围获取不同复权方式的数据。QuantDash 的官方 Python 示例仓库展示了以下 K 线调用方式并列出了forward、backward、none、forward_additive和backward_additive等复权参数。具体可用市场、接口参数和返回字段应以官方技术文档为准。(GitHub)安装必要依赖pipinstallquantdash pandas matplotlib配置 API Key# macOS / LinuxexportQUANTDASH_API_KEYyour_api_key_hereWindows PowerShell$env:QUANTDASH_API_KEY your_api_key_here下面以贵州茅台600519.SH为例对比三种常用复权方式。importpandasaspdimportmatplotlib.pyplotaspltfromquantdashimportQuantDash qdQuantDash()symbol600519.SHadjust_modes{不复权:none,前复权:forward,后复权:backward,}price_series{}forlabel,adjustinadjust_modes.items():dfqd.klines.get(symbol,period1d,count750,adjustadjust,to_dataframeTrue,)ifdfisNoneordf.empty:raiseValueError(f{label}数据为空请检查标的代码、权限和请求参数。)ifclosenotindf.columns:raiseValueError(f{label}数据缺少 close 字段请根据当前 SDK 返回结构检查字段映射。)ifnotdf.index.is_unique:raiseValueError(f{label}数据存在重复索引请先检查数据结构。)closepd.to_numeric(df[close],errorscoerce).dropna()ifclose.empty:raiseValueError(f{label}数据没有可用的收盘价。)price_series[label]close# 合并不同复权方式的数据。pricespd.concat(price_series,axis1,joininner,).sort_index()ifprices.empty:raiseValueError(不同复权方式没有共同的索引请先确认返回数据的日期字段及索引格式。)# 绘制价格水平。prices.plot(figsize(12,6),titlef{symbol}不同复权方式下的历史收盘价,)plt.ylabel(价格)plt.xlabel(交易日期)plt.grid(True)plt.tight_layout()plt.show()# 将各序列统一到共同区间的首个价格。normalizedprices.div(prices.iloc[0]).mul(100)normalized.plot(figsize(12,6),titlef{symbol}不同复权方式下的归一化价格走势,)plt.ylabel(归一化价格起点 100)plt.xlabel(交易日期)plt.grid(True)plt.tight_layout()plt.show()# 比较各序列的区间收益率。returnsprices.iloc[-1].div(prices.iloc[0]).sub(1)print(区间收益率)print(returns)代码中有三个关键设计第一三种复权方式使用相同的请求条件。标的、K 线周期和请求数量保持一致有助于避免将不同时间范围造成的差异误认为复权差异。不过count750代表请求的 K 线数量并不能保证所有请求一定返回相同数量的数据。实际返回范围还需要根据数据结果核验。第二原始价格和归一化价格分别展示。第一张图展示价格尺度差异。第二张图将共同区间的首个价格设为 100便于比较相对变化而不是比较不同复权口径的绝对价格。第三代码没有预先假设三条曲线一定完全一致。如果三种口径的收益率存在明显差异应继续检查日期对齐、缺失数据、公司行为处理和复权基准而不是直接认定数据有问题。另外示例通过 DataFrame 索引对齐数据。正式使用时应先确认当前 SDK 返回的索引确实表示交易日期如果日期存储在普通列中应根据实际返回结构建立日期索引。不能仅因为两个 DataFrame 的索引可以合并就认定它们对应相同的交易日。四、如何判断不同复权序列是否具有可比性拿到数据后可以进一步检查三个方面。1. 先比较共同交易日如果某种复权方式缺少部分日期直接比较数组中相同位置的价格会产生错位。正确做法是使用交易日期作为键进行对齐并检查每种复权方式的覆盖范围。2. 检查公司行为附近的收益率对于现金分红、送股或拆股等事件应重点观察事件前后的价格变化。不复权序列可能出现明显的价格跳变而复权序列通常会减少这类机械性变化。但复权并不能消除所有真实市场波动。例如公司公布重大信息后股价下跌即使发生在除权日附近也不能简单地归因于复权处理。3. 确认研究目的与价格口径一致以下两种任务需要不同的数据口径研究长期持有收益应考虑公司行为对总回报的影响通常需要使用经过适当调整的价格或独立构建总回报序列。模拟历史成交应使用当时真实可交易的价格并在持仓和现金账户中单独处理分红、送转及拆股等事件。前复权数据可能适合技术指标研究但不意味着它可以直接替代历史成交价格。同样后复权价格也不能直接用于模拟历史订单的成交金额。五、一个重要边界复权价格不等于完整的投资回报复权序列可以帮助处理公司行为引起的价格尺度变化但并不自动解决所有投资收益计算问题。例如现金分红是否计入收益。分红是否再投资。税费如何处理。公司行为数据是否完整。复权因子后续是否发生更新。股票是否发生停牌、退市或其他特殊事件。如果研究目标是严格意义上的投资组合总回报就应明确现金流处理规则而不是把复权收盘价直接等同于完整的投资账户净值。还有一个实务细节部分数据服务会在公司行为发生后重新调整历史价格。若研究结果需要长期复现建议保存查询日期、复权口径、请求参数及数据快照以便后续解释历史结果为什么发生变化。最终判断比较不同复权方式时不要只问哪条价格曲线更高而要问当前研究需要什么价格口径、使用什么收益率定义以及不同序列是否在同一时间范围内可比。QuantDash 官方文档QuantDash 技术文档 — 查看 Python SDK、REST API 和数据接口说明。