量化数据清洗应该放在数据层还是策略层?从系统架构看清职责边界

一句话结论:量化数据清洗应采用分层设计------数据层负责通用的数据质量治理,策略层负责与具体策略相关的数据解释和业务规则,不能用一套全局清洗规则替代所有策略的判断。

摘要

量化系统中的数据清洗并不是简单地删除空值、去掉重复记录。如果将所有清洗逻辑放在策略层,不同策略容易重复实现相同的数据处理规则;如果把所有异常都在数据层修正,又可能误删真实的市场变化。本文从软件架构、数据质量和策略执行链路出发,分析两层清洗职责的区别,给出可落地的分层方案,并介绍 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 官方资源

相关推荐
梅雅达编程笔记1 小时前
Day 06 · 第一个 API 接口:从 Model 到 JSON
python·django·drf·后端开发·restful api·序列化器
yi0111 小时前
DAY28: LeetCode 111. 二叉树的最小深度
人工智能·笔记·python·算法·leetcode·二叉树·递归
|_⊙1 小时前
C++ 类和对象零散知识点
开发语言·c++
Elastic 中国社区官方博客1 小时前
Elastic 线下 Meetup 将于 2026 年 12 月 19 号下午在西安举行
大数据·数据库·elasticsearch·搜索引擎·ai·数据分析·全文检索
xiangyun611 小时前
C++ list 模拟实现(二):const迭代器与反向迭代器,模板的黑魔法
开发语言·数据库·c++·算法·stl·list
Data analyse4562 小时前
Session超时与浏览器休眠的计时校验
大数据·前端·数据库·数据分析
诺伦2 小时前
从一键分发到 AI 自动运营:内容矩阵工具三代技术架构拆解 | RiseClaw玄策
python·agent·架构设计·增长运营·ai自动运营·内容营销自动化
DLite2 小时前
NLang 0.9.0发布:成为可嵌入语言
开发语言·编程语言·编译技术·nlang
有同事要进步2 小时前
LeetCode 14. 最长公共前缀:纵向扫描一次讲透
开发语言·python