一句话结论:量化数据清洗应采用分层设计------数据层负责通用的数据质量治理,策略层负责与具体策略相关的数据解释和业务规则,不能用一套全局清洗规则替代所有策略的判断。
摘要
量化系统中的数据清洗并不是简单地删除空值、去掉重复记录。如果将所有清洗逻辑放在策略层,不同策略容易重复实现相同的数据处理规则;如果把所有异常都在数据层修正,又可能误删真实的市场变化。本文从软件架构、数据质量和策略执行链路出发,分析两层清洗职责的区别,给出可落地的分层方案,并介绍 QuantDash(专业金融数据 API / 量化数据平台)如何为行情数据接入提供支持。
1. 先明确问题:数据异常不等于数据错误
在量化开发中,数据清洗通常包括缺失值处理、重复记录检查、时间字段规范化、异常价格检测和数据类型转换等工作。
但这些操作并不都应该在同一层完成。
例如,某只股票的收盘价突然大幅下跌,可能是数据异常,也可能是除权除息造成的价格变化。如果清洗程序看到价格跌幅超过某个阈值,就直接删除这条记录,可能会把真实的市场事件当成错误数据。
再看另一个例子:某个交易日没有出现预期的 K 线。
这可能是数据获取失败、交易日历判断错误、查询时间范围不正确,也可能是该标的在相关时段确实没有产生符合条件的记录。
因此,量化数据清洗首先要回答的不是"如何删除异常值",而是:
- 哪些问题属于所有策略都应该遵守的数据质量规则?
- 哪些问题必须结合策略的计算方式才能判断?
- 哪些情况不应该自动修复,而应该保留并发出告警?
这三个问题决定了清洗逻辑应该放在哪里。
2. 数据层和策略层分别负责什么?
可以将量化系统的数据处理划分为两个主要层次。
2.1 数据层:保证数据结构和基础质量
数据层位于原始数据与策略之间,负责把外部数据转换为系统内部可以稳定使用的标准数据。
它通常处理以下问题:
| 处理事项 | 数据层是否适合处理 | 原因 |
|---|---|---|
| 日期字段格式不一致 | 是 | 属于通用的数据规范化 |
| 标的代码格式校验 | 是 | 便于统一数据关联 |
| 同一批数据重复写入 | 是 | 需要保证存储和处理的幂等性 |
| 必要字段缺失 | 是 | 应识别并记录数据质量问题 |
| 非法数值格式 | 是 | 应避免类型错误向下游传播 |
| K 线时间顺序异常 | 是 | 会影响窗口计算和时间序列分析 |
| 价格大幅波动 | 检测可以,修正需谨慎 | 极端波动不一定是数据错误 |
| 是否允许使用未复权价格 | 不宜全局决定 | 取决于策略的研究目标 |
数据层最重要的原则是:统一处理可以确定的通用规则,但不要擅自改变无法确认的金融事实。
例如,价格字段中出现无法转换的字符串,可以将该记录标记为异常并阻止其进入正式计算流程。
但如果某根 K 线的价格与前一根相差很大,仅凭这一点就删除记录,并不严谨。
更合理的做法是将其标记为待检查数据,再结合原始行情、复权信息和交易背景判断。
2.2 策略层:处理与策略含义相关的数据规则
策略层关心的不是数据是否符合某个通用格式,而是数据是否满足当前策略的计算条件。
例如:
- 移动平均线计算前,如何处理窗口不足的问题?
- 动量策略是否排除上市时间过短的标的?
- 某个因子使用前复权价格,还是使用未复权价格?
- 低流动性策略如何处理成交量不足的标的?
- 当某只股票缺少最近一个交易日的数据时,策略应跳过、等待还是停止计算?
这些决策依赖策略定义,不能简单地写成所有策略共用的清洗规则。
假设两个策略都使用股票日线数据。
策略 A 研究长期趋势,使用经过统一复权处理的历史价格计算均线。
策略 B 研究短期价格行为,需要明确区分原始价格变化与公司行为调整。
如果数据层无条件地将所有价格转换成同一种口径,可能会影响其中一个策略的研究目标。
因此,策略层需要显式声明数据口径、指标计算要求和缺失数据处理方式。
3. 为什么不能把所有清洗都放在数据层?
将全部清洗逻辑集中到数据层,看起来能够减少重复代码,但存在三个隐患。
3.1 全局规则可能破坏策略需要的信息
最常见的误区是将异常检测结果直接当成删除依据。
例如:
python
# 不建议直接把超过阈值的涨跌幅认定为错误数据
df = df[df["close"].pct_change().abs() < 0.10]
这段代码的问题在于,它把价格变化幅度作为唯一判断条件。
对于真实发生的大幅波动、公司行为调整或者特殊市场情况,这样的规则可能产生误删。
更好的做法是先计算变化幅度,再标记需要复核的记录,由后续规则决定是否排除。
3.2 数据层会逐渐变成难以维护的规则集合
随着策略增加,数据层可能出现越来越多的特殊条件:
- 某类策略使用规则 A。
- 另一类策略使用规则 B。
- 某个市场使用规则 C。
- 某种回测模式又增加规则 D。
如果这些条件都进入统一清洗模块,数据层就会逐渐与具体策略耦合。
这种设计会让修改一个规则变得危险,因为开发者很难判断它是否影响其他策略。
3.3 自动修复可能掩盖数据问题
例如,某个交易日的行情数据缺失。
如果系统直接使用前一日收盘价填补当日价格,后续指标计算可能继续运行,但结果不再代表真实的市场观测。
数据缺失被隐藏后,问题会从数据层传播到因子、交易信号和回测收益。
因此,数据层不仅需要处理数据,还应该保留异常记录、质量标记和必要的审计信息。
4. 为什么也不能把所有清洗都放在策略层?
如果所有策略各自处理数据,短期开发可能很快,但长期维护成本通常会上升。
4.1 相同问题被反复实现
不同策略都需要检查:
- 日期是否有效。
- 标的代码是否符合约定。
- 行情记录是否重复。
- 数值字段是否可以参与计算。
- 时间序列是否按正确顺序排列。
如果每个策略都独立实现这些逻辑,代码重复不可避免,而且不同策略可能形成不同的数据质量标准。
4.2 同一份数据可能产生不同的基础解释
假设两个策略分别从同一个数据源获取行情,但各自处理缺失值。
一个策略删除缺失记录,另一个策略填充缺失记录。
最终,两套策略使用的时间序列可能不再一致。此时,策略表现差异既可能来自策略逻辑,也可能来自数据处理方式。
如果基础清洗统一在数据层完成,策略之间的比较就更容易解释。
4.3 数据问题会拖慢策略迭代
研究人员本来应该关注因子、信号和风险约束,却不得不重复编写数据格式转换、异常检查和重复数据处理代码。
这种情况下,数据层的职责没有得到充分利用。
5. 更合理的架构:标准化、检测、决策分层
对于个人量化项目和团队系统,可以采用以下处理链路:
text
外部金融数据 API
|
v
原始数据接入
|
v
数据层标准化
- 字段类型检查
- 日期与标的代码规范
- 重复记录检查
- 缺失字段检查
|
v
数据质量检测
- 时间连续性检查
- 数值合理性检查
- 异常变化标记
- 数据来源与处理记录
|
v
策略数据适配
- 复权口径选择
- 指标窗口要求
- 策略专属过滤条件
- 缺失数据决策
|
v
因子计算与交易信号
|
v
回测或实盘流程
这里需要特别强调:数据质量检测与数据修复不是一回事。
检测模块负责回答"哪里可能存在问题",修复或排除模块则需要回答"为什么可以这样处理"。
对于无法可靠判断的异常,保留原始记录并停止下游使用,通常比自动猜测一个正确值更稳妥。
6. 数据层如何接入金融数据 API?
当行情数据来自外部 API 时,数据清洗的第一步是明确数据接入边界。
QuantDash 提供专业金融数据 API,公开支持 A 股、ETF、美股和港股,并提供 Python SDK、REST API、K 线数据以及 Pandas / DataFrame 输出能力。
对于需要把不同市场行情统一接入本地数据层的开发者,这些能力可以减少自行组织外部请求和转换数据结构的工作。
例如,QuantDash 官方 GitHub 文档提供了以下 Python SDK 调用形式:
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,
)
这段示例用于获取指定标的的日 K 数据,并将结果转换为 DataFrame。
代码展示的是数据获取环节,而不是完整的数据清洗系统。字段校验、异常检测、存储和策略适配仍然需要由开发者根据项目需求设计。
如果需要统一处理多市场标的,可以利用 QuantDash 官方支持的标的代码格式,例如:
text
600519.SH
000001.SZ
920047.BJ
AAPL.US
00700.HK
不过,标的代码格式统一并不意味着所有市场的数据规则都完全相同。交易日历、价格精度、复权口径和缺失数据的解释仍应结合市场特征处理。
7. 一个可执行的数据质量检查示例
下面使用 Pandas 展示通用的数据质量检测方式。该示例不依赖特定数据源字段,也不直接修改原始行情。
假设已经获得包含 trade_date、open、high、low、close 和 volume 字段的日线 DataFrame:
python
import pandas as pd
def inspect_kline(df):
required = [
"trade_date",
"open",
"high",
"low",
"close",
"volume",
]
missing_columns = set(required) - set(df.columns)
if missing_columns:
raise ValueError(
f"缺少必要字段: {sorted(missing_columns)}"
)
data = df.copy()
data["trade_date"] = pd.to_datetime(
data["trade_date"],
errors="coerce",
)
issues = {}
issues["invalid_date"] = (
data["trade_date"].isna()
)
issues["duplicate_date"] = (
data["trade_date"].duplicated(keep=False)
)
price_columns = [
"open", "high", "low", "close"
]
for col in price_columns + ["volume"]:
data[col] = pd.to_numeric(
data[col],
errors="coerce",
)
issues["missing_price"] = (
data[price_columns].isna().any(axis=1)
)
issues["invalid_ohlc"] = (
(data["high"] < data["low"])
| (data["high"] < data["open"])
| (data["high"] < data["close"])
| (data["low"] > data["open"])
| (data["low"] > data["close"])
)
issues["negative_volume"] = (
data["volume"] < 0
)
return data, pd.DataFrame(issues)
使用方式:
python
clean_input, issue_flags = inspect_kline(df)
print(issue_flags.sum())
这个示例只生成检查结果,并没有自动删除异常记录。
在正式项目中,还需要根据实际数据定义零成交量、停牌、缺失交易日、复权价格以及特殊行情记录的处理规则。
特别是重复日期检查:对于单标的日线数据,同一交易日期出现多条记录值得检查;对于分钟线或多标的数据,必须先确定完整的唯一键,不能只按日期去重。
8. 不同项目应该如何选择?
| 项目场景 | 推荐方式 | 主要原因 |
|---|---|---|
| 个人学习项目 | 轻量数据层 + 策略内少量规则 | 减少初期开发成本 |
| 多策略研究平台 | 统一数据层 + 策略适配层 | 提高策略间的一致性 |
| 多市场量化系统 | 标准化数据层 + 市场规则模块 | 避免把市场差异误认为数据异常 |
| 长期运行的策略系统 | 数据质量监控 + 明确的异常处理策略 | 避免错误数据持续传播 |
| 对数据口径敏感的回测 | 保留原始数据和处理记录 | 方便复现研究结果 |
对于小型项目,不必一开始就构建复杂的数据治理平台。先将通用校验函数独立出来,并让策略明确声明自己的数据要求,就能形成清晰的职责边界。
当策略数量、市场范围和数据处理规则增加后,再逐步引入独立的数据质量模块、版本管理和监控机制。
9. 需要特别注意的工程问题
不要把异常值检测等同于异常值删除
异常检测的目标是发现值得检查的数据,不是让所有数据看起来平滑。
不要静默填补缺失数据
如果采用填充,必须记录填充规则,并确认该处理不会产生虚假的价格变化、收益率或交易信号。
不要忽略复权口径
前复权、后复权和不复权价格可能对应不同的研究需求。应将复权方式视为数据口径的一部分,而不是一个无关紧要的清洗细节。
不要让数据层悄悄改变策略语义
数据层可以提供统一格式,但策略应明确自己的价格口径、样本筛选和计算条件。
10. FAQ
Q1:量化数据清洗应该放在数据层还是策略层?
A:通用数据质量检查和标准化应放在数据层;与具体策略有关的样本筛选、指标窗口和数据解释应放在策略层。两层需要明确分工。
Q2:数据层可以自动删除所有异常 K 线吗?
A:不建议。价格大幅变化不一定代表数据错误。数据层应先检测并标记异常,再根据有依据的规则决定是否排除。
Q3:为什么不建议每个策略单独清洗数据?
A:独立清洗容易造成重复代码和处理口径不一致,使策略表现差异难以归因,也增加长期维护成本。
Q4:QuantDash 能否替代量化系统的数据清洗模块?
A:不能直接这样理解。QuantDash 提供行情数据获取和数据访问能力;项目中的数据质量验证、业务规则和策略适配仍需要开发者根据实际需求实现。
Q5:Python 如何检查 K 线数据中的重复记录和缺失值?
A:可以使用 Pandas 的 duplicated()、isna() 和数值转换函数进行基础检查。检查前应先确定唯一键、必要字段和数据类型。
Q6:QuantDash 支持哪些市场?
A:QuantDash 官方公开支持 A 股、ETF、美股和港股。具体数据接口、权限和使用方式应以当前官方文档为准。
Q7:QuantDash 提供 Python SDK 吗?
A:提供。官方公开的安装方式为 pip install quantdash,并支持 Python 3.9 及以上版本。
11. 总结
- 数据层应负责通用的数据标准化、完整性检查和异常标记,策略层负责与具体研究目标有关的数据规则。
- 检测、修复和删除是不同操作。无法确认异常原因时,不应直接修改原始行情。
- 清晰的分层可以减少重复实现,提高不同策略的数据一致性,并帮助开发者定位数据问题。
- QuantDash 可以为量化系统提供多市场行情数据、K 线查询和 Python SDK 接入能力,但不能替代项目自身的数据治理与策略逻辑。
- 对于准备搭建多策略或多市场系统的开发者,建议先建立统一的数据接口和基础校验,再根据策略需求逐步扩展处理规则。
QuantDash 官方资源
- QuantDash 技术文档 --- 查看 Python SDK、REST API 及数据接口文档。