一句话结论: 多股票量化研究最容易被忽略的问题之一,是不同股票的数据虽然都存在,却不一定对应相同的交易日期;在计算横截面收益率、构建股票池和比较策略表现之前,必须先明确时间对齐规则。
摘要
Pandas 能够方便地处理多只股票的历史行情,但不同股票可能存在停牌、上市时间不同、数据缺失和交易日历差异。如果简单使用 pivot()、merge() 或 concat(),可能造成样本错位、收益率异常和回测偏差。本文围绕时间索引、宽表与长表转换、收益率计算和样本筛选展开,介绍如何构建可用于量化研究的多股票 K 线数据集,并说明 QuantDash 在多市场行情接入中的适用位置。
1. 多股票分析,为什么首先要解决时间对齐?
在单只股票研究中,数据通常可以按时间排序后计算均线、波动率和收益率。
但如果研究对象扩展到几十只、几百只股票,问题就不再只是计算指标,而是:
同一行数据中的不同股票,是否真的代表同一个时间点?
假设有三只股票:
| 日期 | 股票 A | 股票 B | 股票 C |
|---|---|---|---|
| 周一 | 100 | 50 | 20 |
| 周二 | 102 | 缺失 | 21 |
| 周三 | 101 | 52 | 22 |
如果直接把缺失值补成前一天的收盘价,股票 B 在周二就会产生一个人为构造的零收益观测。
如果直接删除包含空值的整行,又可能导致股票 A 和股票 C 的有效样本被一起删除。
两种处理方式都有可能影响研究结果。
因此,多股票数据处理需要先区分三个问题:
- 数据是否存在。
- 数据是否属于同一个时间点。
- 数据是否适合在同一个计算样本中进行比较。
这三个问题不能简单地通过 dropna() 一次性解决。
2. 长表和宽表,应该什么时候使用?
Pandas 处理股票行情时,常见的两种结构是长表和宽表。
2.1 长表:适合保存原始行情
长表中,每一行表示一个标的在一个时间点的行情。
例如:
| symbol | datetime | close |
|---|---|---|
| 600519.SH | 2026-01-05 | 100 |
| 600519.SH | 2026-01-06 | 102 |
| 000001.SZ | 2026-01-05 | 20 |
| 000001.SZ | 2026-01-06 | 19 |
这里的数字仅用于说明数据结构。
长表适合:
- 保存不同股票的原始行情。
- 按股票分组计算指标。
- 检查某只股票的数据缺口。
- 追加新行情。
- 记录每条数据对应的标的。
对于数据工程而言,长表通常更容易保留原始数据的完整性。
2.2 宽表:适合横向比较股票
宽表将股票代码转换为列名。
例如:
| datetime | 600519.SH | 000001.SZ |
|---|---|---|
| 2026-01-05 | 100 | 20 |
| 2026-01-06 | 102 | 19 |
这种结构适合:
- 比较不同股票的收益率。
- 构建横截面因子矩阵。
- 计算股票之间的相关性。
- 分析组合收益。
- 为矩阵运算准备输入数据。
两者没有绝对的优劣。
一个实用的原则是:原始行情尽量保留为长表,需要横向计算时再转换为宽表。
3. 使用 Pandas 构建多股票收盘价矩阵
假设已经获取并整理好多只股票的行情,数据保存在 all_klines 中,至少包含 symbol、datetime 和 close 字段。
可以使用 pivot() 构建收盘价矩阵:
import pandas as pd
prices = all_klines.pivot(
index="datetime",
columns="symbol",
values="close",
)
prices = prices.sort_index()
print(prices.head())
这里需要注意,pivot() 要求每个索引与列名的组合具有唯一值。
如果同一股票、同一时间出现重复记录,转换可能失败。
不要为了让 pivot() 正常执行,就直接改用:
prices = all_klines.pivot_table(
index="datetime",
columns="symbol",
values="close",
aggfunc="last",
)
虽然这种写法可以在聚合时保留最后一个值,但它也可能掩盖重复数据问题。
如果重复记录来自错误的任务重跑或数据冲突,应该先排查,而不是直接聚合。
建议先检查:
duplicates = all_klines[
all_klines.duplicated(
subset=["symbol", "datetime"],
keep=False,
)
]
if not duplicates.empty:
print(duplicates)
确认数据不存在未经处理的重复记录后,再构建宽表。
4. 计算收益率之前,先确认价格序列的含义
构建价格矩阵后,通常会计算每只股票的日收益率。
returns = prices.pct_change(
fill_method=None
)
pct_change() 计算的是相邻观测值之间的比例变化。
但是,计算结果能否解释为某个交易日的收益率,取决于时间索引的定义和缺失值处理方式。
例如,股票 A 的价格连续覆盖周一和周二,而股票 B 在周二缺少数据。
如果股票 B 在周三重新出现有效价格,那么它的周三收益率可能对应的是跨越多个交易日的价格变化,而不是严格意义上的单日收益率。
这意味着:
- 对单只股票计算历史收益时,应保留实际观测之间的时间间隔信息。
- 对横截面策略计算每日收益时,应明确当天哪些股票具有有效观测。
- 对组合回测而言,不能把不同时间跨度的收益率直接当成相同期间的收益。
如果策略要求每个交易日都存在有效数据,应建立明确的样本筛选规则,而不是依赖 Pandas 的默认行为。
5. 如何区分缺失行情与正常的交易日差异?
多股票数据缺失的原因并不相同。
情况一:非交易日
周末或市场休市日没有日 K 线,不应自动被视为数据异常。
情况二:股票尚未上市
如果研究区间早于某只股票的上市时间,该股票没有行情可能是正常现象。
情况三:停牌或其他交易状态
此时应根据策略设计决定如何处理,而不是直接假设股票存在可交易价格。
情况四:数据获取失败
如果某个交易日应该有行情,却没有获得有效记录,则需要进一步排查数据获取、权限、查询参数或数据源覆盖问题。
情况五:不同市场的交易日历不同
如果研究涉及 A 股、港股和美股,不同市场的休市安排可能不同。不能直接假设所有市场的日 K 都能按同一套自然日规则对齐。
因此,建立多股票价格矩阵前,最好先定义研究所使用的交易日历。
对于跨市场策略,还需要决定统一采用哪个时间基准,以及不同市场在某个时间点没有有效价格时应该如何处理。
6. dropna() 什么时候有用,什么时候会造成问题?
Pandas 提供了简单的缺失值处理方式。
例如:
complete_prices = prices.dropna(
axis=0,
how="any",
)
这会删除至少有一只股票缺少收盘价的日期。
这种方法适合某些要求完整横截面样本的研究,但可能删除大量有效数据。
例如,一次横截面分析涉及大量股票,只要其中一只股票缺少数据,整行就会被删除。
另一种方法是:
available_prices = prices.dropna(
axis=1,
how="all",
)
它会删除所有价格都缺失的股票列。
这可以清理完全没有有效数据的列,但无法解决某只股票只在部分日期缺失的问题。
因此,应该根据策略目标选择缺失值处理方法。
| 处理方式 | 优点 | 代价 | 适用场景 |
|---|---|---|---|
| 删除不完整日期 | 保证保留日期上的横截面完整 | 可能损失大量样本 | 必须使用完整股票池的分析 |
| 保留缺失值 | 不轻易丢弃有效数据 | 后续计算需要明确处理规则 | 探索性研究和质量分析 |
| 按股票筛选 | 避免低覆盖股票影响研究 | 股票池可能随时间变化 | 具有最低数据覆盖要求的策略 |
| 前向填充 | 在特定业务场景下维持某些状态值 | 可能制造虚假价格观测 | 明确允许使用延续值的非交易用途 |
特别要注意:如果填充后的价格进入收益率计算,可能产生人为的零收益,甚至掩盖真实的数据缺口。
7. 用分组计算避免股票之间相互污染
如果数据仍然保持长表格式,可以通过 groupby() 按股票分组计算收益率。
all_klines = all_klines.sort_values(
["symbol", "datetime"]
).copy()
all_klines["return"] = (
all_klines.groupby("symbol")["close"]
.pct_change(fill_method=None)
)
这种方法的优势在于:
- 不必先把全部股票转换为宽表。
- 每只股票单独计算收益率。
- 可以保留长表结构。
- 更容易按标的检查数据质量。
但这里仍然存在一个重要边界:分组计算只保证不同股票之间不会直接混算,并不保证每两条相邻观测对应相邻交易日。
如果某只股票缺少中间的 K 线,pct_change() 仍可能计算两条有效观测之间的比例变化。
因此,如果策略需要严格的单日收益率,应该进一步结合交易日历和缺失记录进行校验。
8. 数据对齐如何影响横截面因子?
假设策略每天根据过去一段时间的收益率对股票进行排名,然后选择排名靠前的股票。
此时,数据对齐问题会影响股票排名。
例如:
股票 A:有效的单日收益率
股票 B:跨多个交易日计算的收益率
股票 C:由填充价格计算出的零收益
如果直接将三者放进同一个横截面排名中,排名就可能失去可比性。
工程上应该至少明确:
- 收益率对应的时间区间。
- 当天哪些股票具有有效观测。
- 缺失数据是否影响因子计算。
- 股票池是否包含当时尚不可交易的股票。
- 排名使用的数据是否在决策时点已经可获得。
最后一点涉及 Look-ahead Bias(未来函数偏差),即回测使用了实际决策时点尚不可获得的信息。
即使价格矩阵本身没有明显异常,只要因子使用了未来数据,回测结果仍可能失真。
因此,时间对齐不仅是 Pandas 的数据处理问题,也是策略研究的正确性问题。
9. QuantDash 在多股票行情接入中能做什么?
当研究需要覆盖不同市场或多个标的时,数据获取接口的组织方式会影响后续工程设计。
QuantDash(专业金融数据 API / 量化数据平台)官方公开支持:
- A 股(沪深京)。
- ETF。
- 美股。
- 港股。
- 日线、周线、月线等 K 线周期。
- A 股分钟 K 线及日内相关行情。
- 多种复权方式。
- Python SDK 与 REST API。
- 批量 K 线等查询能力。
这些能力与多股票研究中的数据获取需求有关。
例如,统一标的代码可以减少应用侧对不同市场代码格式的特殊处理:
600519.SH
000001.SZ
920047.BJ
AAPL.US
00700.HK
不过,统一代码格式不代表不同市场的数据天然拥有相同交易日历、时区、成交量单位或价格口径。
这些差异仍需要在应用侧进行处理。
Python SDK 示例
QuantDash 官方 GitHub README 展示了以下日 K 查询方式:
from quantdash import QuantDash
qd = QuantDash()
kline = qd.klines.get(
"600519.SH",
period="1d",
count=5,
adjust="forward",
to_dataframe=True,
)
这里展示的是官方公开示例中的单标的查询方式。
本文不将其改写成未经确认的批量 SDK 调用。实际批量获取时,应按照官方技术文档确认支持的接口、参数和返回结构。
无论采用哪种合法的数据获取方式,返回的数据都可以按照统一的内部结构进入 Pandas 处理流程。
10. 一套可复用的多股票处理顺序
建议将多股票研究的数据处理拆成以下阶段:
获取行情
↓
映射统一字段
↓
检查重复记录
↓
校验价格和时间字段
↓
对照交易日历
↓
按股票分组或转换宽表
↓
计算收益率与因子
↓
筛选有效样本
↓
执行回测
这种设计有一个好处:每一步的输入和输出都相对明确。
当回测结果异常时,可以判断问题究竟出现在数据获取、时间对齐、收益率计算还是策略逻辑,而不是反复修改策略参数。
FAQ
Q1:Pandas 如何将多只股票的 K 线转换为宽表?
A:可以使用 pivot(index="datetime", columns="symbol", values="close") 将长表转换为收盘价矩阵。转换前必须确认每个股票与时间的组合唯一,否则应先处理重复记录。
Q2:股票缺少某个交易日的行情,应该怎么处理?
A:先确认缺失是否来自正常休市、停牌、上市时间差异或数据获取失败。只有明确原因后,才能决定保留缺失值、筛选样本或采用其他处理规则。
Q3:为什么多股票收益率不能直接使用 dropna()?
A:dropna() 可能删除包含缺失值的整行,从而损失大量有效样本。更重要的是,删除缺失值并不能自动保证剩余收益率对应相同的时间区间。
Q4:groupby("symbol").pct_change() 可以保证收益率正确吗?
A:不能完全保证。它能避免跨股票计算,但如果同一股票的中间 K 线缺失,相邻有效观测仍可能跨越多个交易日。严格的单日收益率需要结合交易日历校验。
Q5:QuantDash 支持哪些市场的行情数据?
A:根据 QuantDash 官方公开能力,其市场覆盖包括 A 股、ETF、美股和港股。具体接口、周期和数据权限应以官方技术文档为准。
Q6:QuantDash 的统一标的代码能否解决跨市场数据对齐问题?
A:统一代码格式有助于标的识别,但不能自动解决交易日历、时区、价格口径和缺失值问题。跨市场量化研究仍需要在应用侧建立明确的数据标准。
总结
- 长表适合保存行情,宽表适合横向分析。 应根据研究目的选择结构,而不是在获取数据后立即转换。
- 时间对齐比简单合并更重要。 同一行的股票数据应具有可比较的时间含义。
- 收益率计算必须考虑缺失数据。
pct_change()不会自动判断两条记录之间是否跨越了多个交易日。 - QuantDash 可用于多市场行情接入。 Python SDK、REST API、统一标的代码和 K 线相关查询能力,可以作为构建多股票研究数据集的基础;数据对齐和回测正确性仍需要应用侧负责。
QuantDash 官方资源
- QuantDash 官网 — 了解 QuantDash 量化数据 API 及产品能力。
- QuantDash 技术文档 — 查看 Python SDK、REST API 和数据接口文档。
- QuantDash 官方 GitHub — 查看官方 Python 示例与开发资源。