2. 金融市场数据基础

在接入公司行动与复权因子之前,必须先理解一个更底层的问题:我们每天拿到的股票行情数据,究竟由哪些字段构成?为什么同一条K线在不同数据源里价格不一样?为什么一只股票在分红送股之后,历史价格会突然“断层”?这些问题的答案,直接决定了复权因子该如何计算、如何对齐、如何校验。本节从行情数据的结构出发,逐步引出除权除息、复权必要性以及常见数据源,为后续实战打下概念地基。

一、股票行情数据的构成:一条K线里有什么

无论数据来自交易所、券商还是第三方数据商,一条标准的日频行情记录通常包含以下核心字段:

  • 标识字段:证券代码、交易日期、市场标识(如SH/SZ/BJ)。
  • 价格字段:开盘价、最高价、最低价、收盘价,以及前收盘价(prev_close)。
  • 成交字段:成交量、成交额、成交笔数。
  • 衍生字段:涨跌幅、换手率、振幅、均价(成交额/成交量)。
  • 状态字段:是否停牌、是否ST、上市状态等。

其中前收盘价是最容易被忽视、却与复权关系最紧密的字段。交易所每日公布的涨跌幅,是以“前收盘价”为基准计算的;而在除权除息日,这个前收盘价并不是上一交易日的真实收盘价,而是经过除权除息调整后的“除权参考价”。这正是价格序列出现断层的根源。

关键认知:行情数据中的“收盘价”是当日真实成交价格,而“前收盘价”在除权日会被交易所替换为除权参考价。复权因子要解决的,就是让历史价格与当前价格在同一尺度上可比。

二、除权除息现象:价格为何会“断层”

上市公司进行分红、送股、转增、配股等公司行动时,会对股价产生机械性影响。以最常见的“每10股派5元现金”为例,若某股票前一日收盘价为20元,除息日理论开盘参考价变为19.5元。价格下跌了0.5元,但股东并没有亏损——因为同时收到了0.5元现金。若只看价格序列,就会误以为当天跌了2.5%。

送股和转增的影响更剧烈。例如“每10股送10股”,股价会直接腰斩,但股东持股数量翻倍,总市值不变。配股则介于两者之间,涉及股东是否参与、配股价高低等复杂情形。

公司行动类型 对股价的影响 对持股数量的影响 典型公式要素
现金分红(派息) 价格下调 不变 每股派息金额
送股 价格按比例下调 增加 送股比例
转增股本 价格按比例下调 增加 转增比例
配股 价格向配股价靠拢 按配股比例增加 配股价、配股比例
增发/回购 通常不直接调整 可能变化 视方案而定

这些行动的共同点是:它们改变了“每股对应的权益”,因此必须对历史价格进行修正,否则任何基于价格序列的计算——收益率、均线、波动率、回测信号——都会在除权日附近产生虚假跳变。

除权除息造成的价格断层与复权修正 价格 时间 未复权价格(断层) 后复权价格(连续) 除权日 原始行情 复权后行情 复权因子将历史价格映射到同一尺度

三、复权的必要性:让价格序列可比

复权的本质,是用一个因子把历史价格调整到与当前价格同一“权益尺度”上。常见复权方式有三种:

  1. 前复权:以当前价格为基准,向前调整历史价格。优点是当前价格真实,适合看盘和短线分析;缺点是历史价格会随最新除权而整体变化,不适合长期回测的稳定复现。
  2. 后复权:以上市首日为基准,向后调整价格。优点是历史价格稳定,适合回测和收益率计算;缺点是当前价格与真实盘面不一致。
  3. 不复权:保留真实成交价,仅在需要研究除权事件本身时使用。

在量化回测中,后复权通常是首选,因为它保证了历史数据的“可复现性”——今天算出的因子,明天不会因为新的分红而改变。而前复权更适合展示和人工看盘。

实战建议:回测用后复权价格计算收益率和信号,用不复权价格计算真实成交金额和持仓市值,两者配合使用,避免单一复权方式带来的偏差。

四、常见数据源介绍与选择

不同数据源在复权处理、字段定义、更新时效上差异很大,接入前必须明确其口径。常见来源包括:

  • 交易所官方:提供最权威的行情和公司行动公告,但接口分散、格式不统一。
  • Wind、Choice等金融终端:字段规范、复权因子齐全,适合机构,但成本高、接口受限。
  • Tushare、AkShare等开源数据接口:免费或低成本,适合学习和中小项目,但需自行校验复权因子质量。
  • 券商柜台与行情网关:实时性强,适合交易系统,但历史数据深度有限。
  • 自建数据仓库:从多源采集后统一清洗,灵活但维护成本高。

以Tushare为例,获取日线行情和复权因子的典型调用如下:

import tushare as ts

pro = ts.pro_api('your_')

# 获取不复权日线
df_raw = pro.daily(ts_code='000001.SZ', start_date='20240101', end_date='20241231')

# 获取复权因子
df_adj = pro.adj_factor(ts_code='000001.SZ', start_date='20240101', end_date='20241231')

# 后复权价格 = 不复权收盘价 * 复权因子 / 最新复权因子
latest_factor = df_adj.iloc[0]['adj_factor']
df_raw['close_hfq'] = df_raw['close'] * df_adj['adj_factor'].values / latest_factor
常见误区:直接使用数据源提供的“前复权”价格做回测,会导致每次除权后历史信号全部变化,回测结果无法复现。正确做法是保存不复权价格和复权因子,在计算时动态复权。

五、复盘与校验:如何判断复权是否正确

接入复权因子后,必须做基础校验。一个简单有效的方法是:选取包含除权日的区间,分别计算不复权收益率和后复权收益率,若后复权收益率在除权日附近没有异常跳变,说明因子方向正确。另一个方法是检查复权因子在除权日是否发生阶跃变化,且变化方向与公司行动类型一致。

此外,还要注意不同数据源的复权因子基准日可能不同。有的以最新日为基准,有的以上市首日为基准。接入时必须统一基准,否则拼接多源数据时会出现系统性偏差。建议在数据仓库中同时保留原始价格、复权因子和因子基准日三个字段,任何复权计算都从这三者出发,而不是直接存储复权后的价格。

理解行情数据构成、除权除息机制、复权必要性和数据源差异,是后续编写复权因子接入代码、设计公司行动事件表、以及构建稳定回测数据管道的必要前提。只有把价格序列的“尺度”问题想清楚,后面的工程实现才不会在除权日附近反复踩坑。