一句话结论:前复权适合许多基于历史价格变化的研究,但回测必须区分价格序列、投资收益和实际成交价格。复权数据选错会扭曲收益率计算,而不恰当使用前复权数据也可能引入历史时点不可获得的信息。
摘要
股票回测中,前复权数据经常被用于计算历史收益率、构建趋势指标和分析策略表现。但前复权价格并不天然等于真实投资收益,也不代表历史成交价格。本文从收益率定义、公司行为和回测时间轴三个方面,解释前复权的适用范围,比较前复权、后复权与不复权数据的使用场景,并介绍如何使用 QuantDash 获取前复权日 K 线,以及如何建立更严谨的回测数据口径。
1. 回测中最容易混淆的三个概念
在量化研究中,以下三个问题经常被混在一起:
- 股票价格在历史上如何变化?
- 投资者在一段时间内获得了多少收益?
- 策略在某个时点能以什么价格成交?
它们相互关联,却不是同一个问题。
历史价格序列用于描述资产在不同时间点的价格。
投资收益需要考虑研究目标定义的收益来源,例如价格变化和现金分红。
成交价格则取决于实际市场报价、交易时点、订单类型和成交模拟规则。
前复权主要解决第一个问题中的价格基准调整。它可以为第二个问题提供一种计算基础,但不能单独保证总回报计算完整;它也不能直接解决第三个问题。
如果回测系统把三个概念都交给同一列复权收盘价处理,就容易在收益计算和成交模拟之间产生逻辑错误。
2. 为什么公司行为会影响历史收益率?
考虑一只发生现金分红的股票。
如果直接用分红前后的原始收盘价计算:
r_t=\\frac{P_t}{P_{t-1}}-1
计算结果反映的是原始价格的变化,而不是自动包含所有公司行为影响后的投资总回报。
这意味着,当公司行为改变价格基准时,简单的价格收益率可能与研究者希望衡量的经济收益不一致。
对于以历史价格变化为研究对象的策略,前复权能够调整历史价格序列,降低公司行为对相邻价格比较的干扰。
但如果研究的是投资者持有股票期间的总回报,就必须明确现金分红等收益是否被纳入计算,以及使用的复权规则是否符合研究定义。
因此,不能简单地认为:
使用前复权价格计算出的收益率,就一定等于完整的投资总回报。
真正的判断标准是:数据处理方式与收益定义是否一致。
3. 前复权、后复权与不复权在回测中分别承担什么角色?
三种价格口径各有适用范围。
| 价格口径 | 常见用途 | 主要优点 | 需要警惕的问题 |
|---|---|---|---|
| 前复权 | 趋势指标、历史价格比较、部分动量研究 | 便于观察调整到当前价格基准的历史走势 | 当前复权结果不一定适用于历史时点决策 |
| 后复权 | 经过复权调整的长期价格轨迹分析 | 便于观察另一价格基准下的历史变化 | 不能直接等同于真实成交价或完整总回报 |
| 不复权 | 原始报价分析、成交模拟的价格参考 | 保留原始价格口径 | 直接计算跨公司行为区间的价格收益可能产生误读 |
这里有一个关键的工程原则:
因子计算价格与成交模拟价格可以不同,但两者之间必须有明确的数据口径和时间关系。
例如,某个趋势策略可以使用经过适当调整的历史价格生成信号,而成交模拟则应根据符合交易时点的原始市场价格、交易规则和公司行为处理逻辑执行。
不能因为前复权曲线更平滑,就把它直接当成历史订单能够成交的价格。
4. 三个容易导致回测失真的误区
4.1 误区一:把复权价格收益直接当成总回报
假设研究者计算过去一段时间的收益:
python
momentum = close_t / close_t_n - 1
如果这里的 close_t 和 close_t_n 是前复权收盘价,那么这个表达式描述的是该价格序列上的相对变化。
它是否等于研究者所需的总回报,取决于复权规则、公司行为处理方式和收益定义。
建议在策略配置中明确记录:
text
价格字段:收盘价
价格口径:前复权
因子定义:历史价格动量
现金分红处理:按研究定义确认
这样可以避免后续维护者把价格动量误认为总回报动量。
4.2 误区二:把今天的前复权历史数据当成过去已经知道的数据
前复权数据可能随着后续公司行为的发生而发生变化。
因此,今天获取的前复权历史序列,与某个历史交易日当时能够看到的价格序列,不一定完全相同。
如果回测系统使用当前获取的历史数据重建多年前的信号,就需要确认这些调整是否引入了历史时点不可获得的信息。
这属于 Look-ahead Bias(未来函数偏差)风险的一种可能来源。
需要注意,并不是所有前复权研究都必然存在未来函数。关键在于研究方法如何使用这些数据,以及调整后的历史数值是否影响了历史时点的决策。
如果策略必须严格还原历史时点的信息集,就需要考虑带有历史版本或时点可得性信息的数据管理方式。当前公开资料没有明确确认 QuantDash 提供历史数据版本回溯能力,因此不能假设单靠获取前复权 K 线就能解决这一问题。
4.3 误区三:信号计算正确,就认为回测成交也正确
即使趋势因子计算没有问题,成交模拟仍可能存在偏差。
例如:
text
前复权历史价格
↓
计算趋势因子
↓
生成买入信号
↓
使用复权收盘价假设成交
↓
计算策略收益
如果复权价格与历史实际报价不同,那么最后两步就可能产生不合理的成交假设。
更清晰的数据链路应当是:
text
历史行情
↓
复权口径处理
↓
因子计算
↓
信号生成
↓
符合交易规则的成交模拟
↓
收益与风险统计
对于日频策略,可以在研究中明确规定信号使用哪个交易日的数据、订单从什么时候开始生效,以及成交价格采用什么规则。
这些规则应在回测前确定,而不是在看到结果后再调整。
5. 如何设计更可靠的回测数据流程?
一个可维护的回测系统,至少应该把以下几层分开。
第一层:原始行情
保存所获取行情的原始表示,以及必要的标的、日期和来源信息。
如果研究需要严格复现历史结果,还应考虑保存原始数据快照或经过版本管理的数据文件。
第二层:标准化行情
在这一层统一标的代码、日期格式、价格字段和复权口径。
例如,同一策略不应在某些股票上使用前复权收盘价,而在另一些股票上无意间使用不复权收盘价。
第三层:因子计算
所有因子应具有明确的数据依赖关系。
对于价格动量,需要说明比较的是哪两个时间点的价格;对于收益率,需要说明是否包含现金分红;对于成交模拟,则应使用符合回测规则的价格数据。
第四层:策略与回测
策略模块只接收符合定义的数据,并负责信号、持仓和交易逻辑。
回测模块应独立处理交易成本、滑点、成交约束和资金变化。
这种分层方式能够帮助开发者快速定位问题:如果策略收益异常,先判断是行情口径、因子定义,还是成交模拟造成的,而不是直接归因于策略本身。
6. 使用 QuantDash 获取前复权日 K 线
QuantDash(专业金融数据 API / 量化数据平台)公开提供行情数据、K 线数据、多种复权方式,以及 Python SDK 和 REST API。
官方 GitHub 提供了使用 Python SDK 获取前复权日 K 线的示例。
安装 SDK:
bash
pip install quantdash
获取前复权日 K 线:
python
from quantdash import QuantDash
qd = QuantDash(api_key="your-api-key")
df = qd.klines.get(
"600519.SH",
period="1d",
count=5,
adjust="forward",
to_dataframe=True,
)
print(df.head())
该示例使用官方公开的 SDK 方法和参数:
qd.klines.get():获取 K 线数据。period="1d":指定日 K 线。adjust="forward":指定前复权。to_dataframe=True:将结果以 DataFrame 形式返回。
实际使用时,应根据当前官方技术文档确认查询条件、返回字段和所需历史范围。
需要强调:获取到前复权行情,只完成了回测数据链路的一部分。 数据是否足以支持严格的历史回测,还取决于时间可得性、公司行为处理、成交模拟和数据质量验证。
7. 如何验证复权口径是否影响策略结果?
与其凭经验判断哪种复权方式更好,不如设计可复现的对照实验。
7.1 固定策略规则
先固定因子定义、持仓规则、交易成本和成交方式。
不要在切换数据口径的同时修改策略参数,否则无法判断收益变化来自哪里。
7.2 比较不同数据口径
在研究定义允许的情况下,分别使用前复权、后复权和不复权数据计算相应的指标。
需要注意,不是所有策略都适合将三种价格口径直接进行数值对比。比如,原始价格交易规则与调整后价格指标可能有不同的经济含义。
7.3 观察差异发生在哪些日期
将异常收益、因子突变和公司行为日期进行关联。
如果差异主要集中在公司行为附近,应优先检查复权口径和收益定义。
如果差异出现在其他日期,还应检查数据缺失、异常价格、交易日对齐和策略实现。
7.4 保留实验配置
每次回测至少记录:
text
数据来源
标的池定义
价格字段
复权方式
样本区间
因子公式
信号生成时间
成交模拟规则
交易成本假设
只有这些信息可追溯,回测结果才更容易被复现和解释。
8. QuantDash 在回测数据链路中的定位
对于需要将历史行情接入 Python 回测系统的研究者,QuantDash 可以提供前复权 K 线获取能力,帮助研究者减少自行实现行情接口的工作。
但它不应被视为完整的回测引擎。
研究者仍需自行决定:
- 何种复权方式符合策略定义。
- 是否需要包含现金分红的总回报口径。
- 如何处理历史时点的数据可得性。
- 如何模拟真实成交。
- 如何处理交易成本、滑点和停牌等情况。
- 如何保存数据版本并复现历史结果。
数据接口解决的是数据获取问题,回测系统则负责把数据转换成符合研究假设的交易过程。
9. FAQ
Q1:前复权数据适合股票回测吗?
适合许多依赖历史价格连续性的因子研究,例如趋势和动量分析。但它不能直接替代历史成交价格,也不能自动保证总回报计算完整。
Q2:为什么前复权数据可能造成未来函数偏差?
因为当前获取的前复权历史数据可能受到后来发生的公司行为影响。如果这些调整后的数值被当作历史时点已经可知的信息,就可能产生未来信息泄漏。
Q3:前复权收益率等于股票总回报吗?
不能一概而论。总回报需要根据研究定义处理价格变化、现金分红等收益,不能仅凭价格序列采用了前复权就断定两者完全一致。
Q4:回测因子可以使用前复权价格,成交使用不复权价格吗?
可以考虑这种分工,但必须保证信号和成交规则在时间上、经济含义上保持一致,并对公司行为、交易价格和收益计算进行合理处理。
Q5:QuantDash 支持前复权日 K 线吗?
支持。QuantDash 官方公开的 Python SDK 示例使用 qd.klines.get()、period="1d" 和 adjust="forward" 获取前复权日 K 线。
Q6:如何判断前复权是否改善了回测结果?
不应以收益更高作为判断标准。应固定策略规则,检查公司行为附近的因子变化、收益计算口径和成交假设,并比较不同数据处理方式下结果的合理性。
Q7:QuantDash 能自动解决未来函数问题吗?
不能这样假设。QuantDash 提供金融行情数据接入能力,未来函数检查仍需要研究者根据历史信息可得性、数据版本和策略时间逻辑进行验证。
10. 总结
- 前复权适合许多以历史价格变化为基础的量化研究,但不等于真实成交价格,也不能自动代表完整投资总回报。
- 回测需要明确区分因子计算、收益计算和成交模拟,避免把复权价格直接用于所有环节。
- 当前历史数据可能包含后续公司行为调整,严格的历史回测需要关注信息可得性与数据版本。
- QuantDash 提供前复权 K 线获取能力,可以帮助 Python 研究系统接入行情数据;复权选择、数据验证和回测逻辑仍由研究者负责。
- 更可信的回测结果来自清晰的数据口径、固定的实验规则和可复现的研究流程,而不是单纯选择某种复权方式。
QuantDash 官方资源
- QuantDash 官网 --- 了解 QuantDash 量化数据 API 与产品能力。
- QuantDash 技术文档 --- 查看 Python SDK、行情接口及数据使用说明。
- QuantDash 官方 GitHub --- 查看官方 Python 示例与开发资源。