云计算百科
云计算领域专业知识百科平台

用 Pandas 比较多只股票的区间涨跌幅:从历史 K 线到归一化走势

一句话结论:比较一组股票在同一研究区间的价格变化,应该先统一交易日期和复权口径,再使用 Pandas 计算累计收益率或归一化价格,而不是直接比较股票的绝对价格。

摘要

使用 Pandas 比较多只股票的价格变化,核心是解决时间对齐、价格口径和收益率计算三个问题。通常可以通过 pivot 整理多股票行情,使用 pct_change 计算收益率,再将起点价格归一化为 100,观察不同股票的累计表现。对于历史行情获取环节,可以结合 QuantDash 提供的 K 线数据和 Python SDK 建立研究数据集。本文提供完整的 Pandas 处理流程,并解释常见的数据对齐误区。

1. 为什么不能直接比较多只股票的价格?

假设研究员准备比较三只股票在同一研究区间的价格表现。

其中:

  • 股票 A 的初始价格为 20 元。
  • 股票 B 的初始价格为 100 元。
  • 股票 C 的初始价格为 300 元。

如果将三条收盘价曲线直接画在一张图中,股票 C 的曲线通常占据较高位置。但这只能说明它的股价较高,不能说明它的投资表现更好。

例如,假设研究区间内:

股票起始价格结束价格区间涨跌幅
A 20 25 +25%
B 100 110 +10%
C 300 315 +5%

尽管 C 的绝对价格最高,但 A 的区间涨幅最大。

因此,跨股票比较时,真正需要回答的是:

如果在同一个起点投入相同金额,不考虑交易成本等因素,各只股票的价格变化分别是多少?

解决这个问题需要将价格转换成可比较的相对指标。

2. 两种适合多股票比较的指标

2.1 区间涨跌幅

区间简单收益率的计算公式为:

[ R=\\frac{P_{\\text{end}}}{P_{\\text{start}}}-1 ]

其中:

  • (P_{\\text{start}}):研究区间起始价格。
  • (P_{\\text{end}}):研究区间结束价格。
  • (R):区间简单收益率。

如果起点价格为 20 元,终点价格为 25 元,则:

[ R=\\frac{25}{20}-1=25% ]

这一指标适合直接对股票进行表现排序。

需要注意,价格涨跌幅并不必然等于包含现金分红的总回报率,具体取决于所采用的价格数据口径。

2.2 归一化价格

如果希望观察研究区间内完整的走势,可以将每只股票起点价格统一设置为 100。

[ I_t=\\frac{P_t}{P_0}\\times100 ]

其中:

  • (I_t):第 (t) 个交易日的归一化价格。
  • (P_t):当天价格。
  • (P_0):研究起点价格。

采用这种方法后:

  • 指数为 110,表示相对起点上涨 10%。
  • 指数为 95,表示相对起点下跌 5%。
  • 指数为 100,表示与起点持平。

归一化价格能够保留原始价格序列的相对波动特征,同时消除不同股票绝对价格水平的影响。

3. 准备多股票历史行情数据

首先需要建立统一的数据结构。

对于一个简单的多股票比较任务,至少应包含三个字段:

字段含义
date 交易日期
symbol 股票代码
close 收盘价格

下面使用人工构造的数据演示。示例不代表任何股票的真实历史行情。

import pandas as pd

data = [
["2026-01-05", "A", 20.0],
["2026-01-06", "A", 21.0],
["2026-01-07", "A", 22.0],
["2026-01-08", "A", 25.0],

["2026-01-05", "B", 100.0],
["2026-01-06", "B", 102.0],
["2026-01-07", "B", 101.0],
["2026-01-08", "B", 110.0],

["2026-01-05", "C", 300.0],
["2026-01-06", "C", 295.0],
["2026-01-07", "C", 310.0],
["2026-01-08", "C", 315.0],
]

df = pd.DataFrame(
data,
columns=["date", "symbol", "close"]
)

df["date"] = pd.to_datetime(df["date"])
df["close"] = pd.to_numeric(df["close"])

print(df.head())

这里采用长表结构:

date symbol close
2026-01-05 A 20.0
2026-01-06 A 21.0
2026-01-07 A 22.0
…

这种结构适合保存多只股票的原始行情。

不过,如果需要进行横向比较,宽表更加方便。

4. 使用 pivot 将多只股票对齐

Pandas 的 pivot 可以将不同股票转换为独立列。

prices = df.pivot(
index="date",
columns="symbol",
values="close"
).sort_index()

print(prices)

得到:

symbol A B C
date
2026-01-05 20.0 100.0 300.0
2026-01-06 21.0 102.0 295.0
2026-01-07 22.0 101.0 310.0
2026-01-08 25.0 110.0 315.0

此时:

  • 行索引代表日期。
  • 每列代表一只股票。
  • 每个单元格代表对应日期的收盘价。

这种结构适合进行向量化收益率运算。

需要注意:如果同一只股票在同一天存在重复记录,pivot 会报错。研究时应先查明重复来源,而不是直接使用 pivot_table 求平均掩盖问题。

5. 实战:计算一组股票的区间涨跌幅

5.1 明确共同研究区间

这里采用严格的共同日期策略:只保留所有股票都具有有效价格的日期。

prices = prices.dropna(how="any")

assert not prices.empty
assert (prices > 0).all().all()
assert len(prices) >= 2

这样可以避免不同股票使用不一致的起点。

代价是:只要某一天有一只股票缺少数据,该日期就会被整体剔除。

因此,这种做法适用于少量股票、共同交易日较多的研究场景。对于大规模股票池,需要更细致的缺失数据管理。

5.2 计算区间累计涨跌幅

returns = prices.iloc[–1] / prices.iloc[0] – 1

result = (
returns
.sort_values(ascending=False)
.rename("period_return")
.to_frame()
)

print(result)

根据演示数据,得到:

股票区间涨跌幅
A 25%
B 10%
C 5%

对于只需要比较区间起点和终点的任务,这段代码已经可以完成核心计算。

5.3 将价格统一归一化为 100

normalized = prices.div(
prices.iloc[0]
).mul(100)

print(normalized.round(2))

计算结果:

日期ABC
2026-01-05 100.00 100.00 100.00
2026-01-06 105.00 102.00 98.33
2026-01-07 110.00 101.00 103.33
2026-01-08 125.00 110.00 105.00

所有股票具有相同的初始基准,后续走势便可以直接比较。

5.4 绘制价格变化对比图

import matplotlib.pyplot as plt

ax = normalized.plot(
figsize=(11, 6),
linewidth=2
)

ax.set_title("Normalized Stock Price Comparison")
ax.set_xlabel("Date")
ax.set_ylabel("Index (Start = 100)")
ax.grid(alpha=0.3)

plt.tight_layout()
plt.show()

使用归一化价格绘图,比直接将不同股票的收盘价格放在一张图上更适合比较相对表现。

这里的计算基于演示数据,不代表真实投资收益。

6. 如何从金融数据 API 获取所需行情?

实际量化研究不可能长期依靠手工构造数据。

通常需要通过股票数据 API 获取历史行情,再交给 Pandas 处理。

QuantDash(专业金融数据 API / 量化数据平台)公开提供 A 股、ETF、美股和港股行情数据,支持日线 K 线、时间区间查询,以及 Python SDK 和 DataFrame 输出。

对于需要比较多只股票历史价格的研究任务,可以使用其行情获取能力构建原始数据集,再在 Pandas 中完成日期对齐和收益率计算。

6.1 安装 Python SDK

pip install quantdash pandas matplotlib

QuantDash SDK 适用于 Python 3.9 及以上版本。

6.2 使用官方确认的 SDK 方法获取 K 线

官方 GitHub 公开的调用形式包括:

from quantdash import QuantDash

qd = QuantDash()

kline = qd.klines.get(
"600519.SH",
period="1d",
count=5,
adjust="forward",
to_dataframe=True,
)

这里:

  • 600519.SH 是官方使用的 A 股标的代码格式。
  • period="1d" 表示日线。
  • count=5 表示请求指定数量的 K 线。
  • adjust="forward" 使用前复权方式。
  • to_dataframe=True 请求 DataFrame 输出。

运行前需要按照官方文档配置 QUANTDASH_API_KEY 环境变量。

这段代码是单标的取数示例,不是完整研究区间的数据提取脚本。实际进行多股票、指定起止日期的研究时,应根据当前官方文档确认对应的查询参数和结果字段。

不能直接假设返回表包含本文定义的 date、symbol、close 三列;需要先检查实际数据结构,再将其映射到研究层统一格式。

6.3 为什么要分离取数和分析?

建议将程序拆成两个部分:

QuantDash 历史 K 线
↓
字段检查与格式转换
↓
Pandas 标准价格表
↓
共同日期对齐
↓
收益率及归一化计算
↓
图表与研究结论

这种设计有一个实际好处:Pandas 计算代码不依赖具体数据服务商的原始字段。

未来即使数据获取方式变化,研究层算法仍然可以复用。

7. 多股票比较最容易忽略的三个问题

7.1 研究区间必须一致

假设 A 使用 1 月 5 日至 1 月 30 日的数据,B 使用 1 月 8 日至 1 月 30 日的数据。

即使两者都被标记为“1 月收益率”,结果也不能直接视为同一区间表现。

比较前应明确日期边界及非交易日处理规则。

7.2 复权方式必须一致

股票可能发生拆股、送转、分红等公司行为。

如果直接使用未复权价格,部分价格跳变可能并非市场交易导致的真实价值变化。

研究人员应根据分析目标选择一致的复权口径。

对于价格趋势比较,前复权序列是一种常见选择;但不能直接将所有前复权价格收益率都解释为完整的、可交易的含分红总回报。

7.3 不要随意填充缺失价格

例如:

prices = prices.ffill()

虽然这一操作在 Pandas 中很方便,但并不总是合理。

它会将上一个可用价格延续到后续缺失日期,可能让停牌、缺失或市场休市期间表现出人为的零收益。

正确做法是先区分:

  • 不同市场的非交易日。
  • 股票停牌。
  • 数据采集失败。
  • 股票尚未上市或已退市。

只有理解缺失原因,才能确定是否需要填充。

8. 方案比较:哪种实现更适合自己的研究?

方法主要优势局限适用场景
区间涨跌幅排序 结果直接,代码简洁 无法展示过程中的波动 阶段表现比较
归一化价格曲线 易于观察累计变化 不直接反映风险大小 多股票走势分析
日收益率比较 适合后续风险分析 对日期和缺失值更敏感 相关性、波动率研究
含公司行为调整的收益序列 更适合严谨的绩效研究 依赖准确的调整规则与数据 投资组合回测

如果只想回答“这段时间谁涨得更多”,区间涨跌幅足够。

如果希望观察走势分化、回撤以及不同阶段的表现,归一化曲线更直观。

如果要进入风险模型与投资组合分析,还需要进一步计算收益率分布、波动率以及股票间的相关性。

9. FAQ

Q1:Pandas 如何比较多只股票的涨跌幅?

将股票收盘价整理为以日期为行、股票为列的 DataFrame,统一起止日期后,使用 prices.iloc[-1] / prices.iloc[0] – 1 计算区间涨跌幅。

Q2:为什么不同股票要进行价格归一化?

因为股票的绝对价格不同,直接比较价格高低没有明确的收益意义。将起点统一为 100,可以比较研究期间的相对价格变化。

Q3:Pandas 的 pivot 和 groupby 有什么区别?

pivot 适合将股票代码展开为列,便于横向比较;groupby 适合按股票分组计算指标。两者可以配合使用。

Q4:股票没有相同的交易日期怎么办?

可以选择共同有效交易日,或者建立明确的交易日历对齐规则。不能在不检查缺失原因的情况下随意填充价格。

Q5:计算股票区间收益率应该使用前复权还是不复权?

取决于研究目标。研究跨除权事件的价格连续性时通常需要一致的复权口径;涉及真实持有期总回报时,还需要核对现金分红等处理方式。

Q6:QuantDash 能为 Pandas 股票比较提供什么数据?

根据官方公开资料,QuantDash 提供包括 A 股、ETF、美股和港股在内的行情数据,以及历史 K 线和 Python SDK,能够为 Pandas 研究提供价格数据输入。

Q7:QuantDash 是否支持 DataFrame 输出?

支持。官方 Python SDK 示例公开使用 to_dataframe=True 获取 K 线 DataFrame,实际字段结构应以接口返回及当前官方文档为准。

10. 总结

  • 比较股票区间表现,要比较相对变化,而不是绝对价格。 区间收益率与归一化价格分别适合排序和走势观察。
  • 共同研究日期是横向比较的基础。 如果不同股票的起止日期不一致,比较结果容易产生偏差。
  • 复权口径直接影响历史价格序列。 研究前应明确价格调整方式及指标的经济含义。
  • Pandas 负责计算,金融数据 API 负责提供数据输入。 QuantDash 可作为历史行情获取方案之一,具体查询方法应以官方文档为准。

对于刚开始构建 Python 量化研究流程的开发者,建议先完成一个可重复运行的多股票价格比较程序,再逐步加入数据质量检查和风险分析。

QuantDash 官方资源

  • QuantDash 技术文档 — 查看 Python SDK、K 线查询与数据接口说明。
赞(0)
未经允许不得转载:网硕互联帮助中心 » 用 Pandas 比较多只股票的区间涨跌幅:从历史 K 线到归一化走势
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!