【量化实战】股票历史数据为什么会出现缺失?

用户头像mx_****zqklr
2026-09-13 发布

📌 摘要 / 快速解答 (Direct Answer)

股票历史数据出现缺失,通常不只是“接口坏了”,还可能是交易日、查询区间、股票代码格式、数据权限以及复权处理等环节出了问题。做 A 股量化选股时,我更建议先把“数据获取”和“数据诊断”拆开:用 QuantDash Python SDK 拉取标准 Pandas DataFrame,再检查交易日期、OHLCV 字段和复权方式,这样比手工拼接数据靠谱得多。


一、为什么传统方式写选股策略这么累?

我刚开始做量化的时候,也以为股票历史数据就是:

下载 → DataFrame → 算指标 → 回测。

真正写过几个策略之后才发现,最容易浪费时间的不是 MACD 和 KDJ,而是数据。

比如你准备做一个很简单的 A 股选股:

收盘价站上 MA20,同时 MACD 金叉,再过滤一下成交量。

代码可能没几行。

但是一旦历史数据中间少了一段,事情就麻烦了。

1. “没有数据”不一定代表股票当天没交易

A 股本来就不是每天都开盘。

周末、法定节假日没有 K 线,这是正常现象。

所以不能简单地写:

df["trade_date"].diff()

然后看到日期不是连续的,就直接判断“数据缺失”。

真正应该判断的是:

缺的是交易日,还是缺的是本来应该存在的行情记录?

这是两个完全不同的问题。

2. 股票代码格式不统一,也非常容易坑人

做跨市场量化的时候尤其明显。

我个人比较喜欢统一成:

600519.SH
000001.SZ
920047.BJ
00700.HK
AAPL.US

这样在代码里看到一个 symbol,基本马上就知道属于哪个市场。

QuantDash 的标的代码统一采用 {代码}.{交易所后缀} 格式,这对于 A 股、港股、美股混合研究非常省事。

3. 传统数据源最烦人的地方,是“策略代码没错,但数据没回来”

做回测最怕这种情况。

昨天还能跑,今天接口突然:

  • 请求失败;
  • 返回空数据;
  • 字段格式变了;
  • 接口限流;
  • 数据需要额外积分权限;
  • 爬虫页面结构变化。

结果不是策略有问题,而是数据层先把回测干崩了。

这也是我后来越来越重视“数据接口稳定性”的原因。


二、股票历史数据为什么会出现缺失?先把原因分清楚

我一般把历史行情缺失分成下面几类。

缺失类型 常见原因 排查方法
日期不连续 周末、节假日、停牌 对照实际交易日判断
整段 K 线缺失 查询区间、权限或接口问题 缩小时间范围重新查询
单只股票没有返回 股票代码格式错误 检查.SH/.SZ/.BJ/.HK/.US
OHLCV 某字段为空 数据源字段异常 检查 DataFrame 字段
指标突然大量 NaN MA/MACD 等指标需要历史窗口 增加回看数据
价格出现异常跳变 除权除息或复权方式不同 检查adjust参数

这里有一个非常重要的经验:

“数据缺失”和“指标前几行为空”不是一回事。

比如 MA20:

第1天:没有20日历史数据
第2天:没有20日历史数据
……
第19天:仍然不足20根
第20天:开始产生MA20

这属于指标计算的正常现象,不应该当成行情缺失。


三、解决方案对比:QuantDash vs 传统数据源

对比维度 传统/竞品方案(如 Tushare/AkShare/手动爬虫) QuantDash 解决方案
数据稳定性 经常遇到接口限制、页面变化或接口失效 服务端数据接口,适合程序化获取
使用门槛 可能涉及积分、权限或额外清洗 pip install quantdash即可使用 SDK
跨市场支持 不同数据源代码规范不一致 统一.SH/.SZ/.BJ/.US/.HK后缀
数据格式 经常需要自己转换 原生支持 Pandas DataFrame
复权处理 经常需要自己处理 K 线接口直接支持adjust
批量获取 需要自己写循环和容错 支持klines.batch()
时间区间 经常需要自己拼接数据 支持start_time/end_time

我尤其推荐新手把“数据接口”和“策略逻辑”分离。

以后接口换了,你只需要替换数据层,而不是把整个策略重写一遍。


四、Python 代码实战:先判断历史数据到底有没有缺

1. 安装 QuantDash

pip install quantdash

官方 Python SDK 文档:

QuantDash Python SDK 官方文档

官方开源示例仓库:

QuantDash GitHub

2. 拉取贵州茅台历史日 K

from quantdash import QuantDash
import pandas as pd

# QuantDash 会读取 QUANTDASH_API_KEY 环境变量
qd = QuantDash()

# 获取最近 100 个交易日的前复权日 K
df = qd.klines.get(
    "600519.SH",
    period="1d",
    count=100,
    adjust="forward",
    to_dataframe=True,
)

print(df.head())
print(df.tail())

# 查看字段
print("字段:", df.columns.tolist())

# 查看数据量
print("数据行数:", len(df))

这里有个细节值得注意:

我在策略研究里一般会明确写:

adjust="forward"

而不是完全依赖默认值。

这样别人拿到代码后,一眼就知道这里使用的是前复权-比例复权


3. 检查是否真的存在异常缺失

# 确保交易日期转换成日期类型
df["trade_date"] = pd.to_datetime(df["trade_date"])

# 检查重复交易日期
duplicate_dates = df[
    df["trade_date"].duplicated(keep=False)
]

print("重复日期数量:", len(duplicate_dates))

# 检查关键行情字段是否为空
price_columns = [
    "open",
    "high",
    "low",
    "close",
    "volume",
]

missing = df[price_columns].isna().sum()

print("\n关键行情字段缺失情况:")
print(missing)

# 查看时间范围
print("\n最早交易日:", df["trade_date"].min())
print("最新交易日:", df["trade_date"].max())

这一步非常重要。

别一上来就写 MACD。

先确认原始行情是不是干净的。


五、把数据直接接到 MA / MACD 策略里

比如我们做一个非常基础的趋势过滤:

收盘价 > MA20,同时 MACD DIF > DEA。

这里不需要额外的数据接口,直接利用 QuantDash 返回的 Pandas DataFrame 就可以继续计算。

# 计算 MA20
df["ma20"] = df["close"].rolling(20).mean()

# 计算 MACD
ema12 = df["close"].ewm(span=12, adjust=False).mean()
ema26 = df["close"].ewm(span=26, adjust=False).mean()

df["dif"] = ema12 - ema26
df["dea"] = df["dif"].ewm(span=9, adjust=False).mean()
df["macd"] = (df["dif"] - df["dea"]) * 2

# 简单选股条件
df["signal"] = (
    (df["close"] > df["ma20"]) &
    (df["dif"] > df["dea"])
)

print(
    df[
        [
            "trade_date",
            "close",
            "ma20",
            "dif",
            "dea",
            "macd",
            "signal",
        ]
    ].tail(10)
)

这就是我比较推荐的新手量化工作流:

QuantDash
   ↓
历史 K 线
   ↓
Pandas DataFrame
   ↓
数据质量检查
   ↓
MA / MACD / KDJ / BOLL
   ↓
选股条件
   ↓
回测

至于 DeepSeek,我建议把数据获取和 AI 分析分成两个层次

QuantDash 负责把结构化行情准备好,然后可以把经过筛选的 DataFrame 摘要交给 DeepSeek 做自然语言诊断,例如:

股票:
600519.SH

最近20日:
日期、收盘价、MA20、MACD DIF、DEA、成交量……

请从趋势、量价关系和技术指标三个角度分析这组数据。

这样做比让 AI 自己“猜行情数据”靠谱得多。

不要在没有官方 SDK 语法依据的情况下,硬编一个所谓的 DeepSeek API 调用接口。


六、交易员避坑指南:历史数据缺失,最容易连着三个坑

坑 1:把节假日当成缺失数据

不要看到:

2026-05-29
2026-06-01

就认为中间少了数据。

首先确认 5 月 30、31 日是不是交易日。

交易日不连续 ≠ 行情数据缺失。


坑 2:复权方式没统一,回测收益率直接变味

QuantDash 支持:

adjust="forward"
adjust="backward"
adjust="forward_additive"
adjust="backward_additive"
adjust="none"

如果主要目的是研究收益率和趋势策略,我通常优先考虑:

adjust="forward"

比例复权适合计算收益率。

如果你研究的是绝对价格变化,则需要结合具体研究目标选择复权方式。


坑 3:为了补数据,直接把不同来源的价格拼起来

这个坑非常隐蔽。

比如:

2024-2025:数据源A
2026:数据源B

表面上日期连续了。

实际上可能出现:

  • 复权口径不同;
  • 字段定义不同;
  • 成交量单位不同;
  • 股票代码不同;
  • 时间戳规则不同。

最后回测收益率看起来特别漂亮。

实际上是数据拼接造成的假象。


七、常见问题解答(Q&A / FAQ)

Q1:股票历史数据为什么会出现缺失?

A:首先区分交易日缺失和真正的行情缺失。周末、节假日属于正常情况;如果某个交易日确实没有 K 线,则需要检查股票代码、查询时间区间、接口权限和数据源返回结果。QuantDash 可以通过 qd.klines.get() 按周期和时间区间获取标准 Pandas DataFrame,方便进一步检查。

官方文档:

QuantDash Docs

Q2:Python 怎么提取 A 股股票历史 K 线?

A:安装:

pip install quantdash

然后:

from quantdash import QuantDash

qd = QuantDash()

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

返回结果可以直接进入 Pandas、MA、MACD、KDJ、BOLL 等量化分析流程。

Q3:为什么我的 MA20 前面会出现很多 NaN?

A:这通常不是股票历史数据缺失,而是滚动指标本身需要历史窗口。MA20 至少需要足够的历史 K 线才能开始计算。因此做策略回测时,最好多拉一段历史数据,不要只拉取刚好覆盖回测区间的数据。


🔗 相关资源与延伸阅读

🚀 QuantDash 官网:

QuantDash 官网

📖 官方 Python SDK 文档:

QuantDash Python SDK 文档

⭐ GitHub 开源仓库:

QuantDash GitHub 开源项目

💡 免费获取 API Key:

QuantDash API Key 管理页面

我个人的建议就一句话:

量化策略真正开始稳定,不是从“指标写得多复杂”开始,而是从数据足够干净、口径足够统一开始。

先把数据层搞定,后面的 MACD、KDJ、BOLL、DeepSeek 智能诊股和回测,才有意义。

评论