惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
小众软件
小众软件
Recent Announcements
Recent Announcements
博客园 - 三生石上(FineUI控件)
Security Archives - TechRepublic
Security Archives - TechRepublic
W
WeLiveSecurity
Cloudbric
Cloudbric
博客园 - 司徒正美
美团技术团队
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
PCI Perspectives
PCI Perspectives
宝玉的分享
宝玉的分享
H
Help Net Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
Last Week in AI
Last Week in AI
S
Schneier on Security
N
News | PayPal Newsroom
B
Blog RSS Feed
L
LINUX DO - 最新话题
T
Troy Hunt's Blog
S
Secure Thoughts
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
L
Lohrmann on Cybersecurity
G
Google Developers Blog
Microsoft Azure Blog
Microsoft Azure Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tenable Blog
S
Securelist
L
LangChain Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
InfoQ
H
Heimdal Security Blog
Cisco Talos Blog
Cisco Talos Blog
F
Full Disclosure
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
K
Kaspersky official blog
T
Tailwind CSS Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
C
Cisco Blogs

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12) Snack JSONPath 项目架构分析 Claude Code Buddy 小析:一个非核心功能,如何体现产品的细节完成度 AI新时代下的图床管理方案-Cloudflare图床+MCP+Skills方案指南 化繁为简:顺丰速运App如何通过 HarmonyOS SDK实现专业级空间测量 从零实现富文本编辑器#13-React非编辑节点的内容渲染 AI开发-python-langchain框架(3-23-OpenAI Functions风格Tool Calling智能助手) .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut PbootCMS 网站内容数量多导致访问慢?这些实用优化方案帮你提速! - 家兴网络技术工作室 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 网站漏洞怎么发现并修复?一篇实用指南(附完整流程) - 家兴网络技术工作室 开了 TUN 模式还是直连?90% 的人都踩过这个坑 Github日报|2026年04月12日 - AI一族 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术 Etsy 把 1000 个 MySQL 分片迁进 Vitess:425TB 数据背后的真正问题不是性能,而是运维规模 MicroPython LVGL基础知识和概念:底层渲染与性能优化 - FreakStudio 数据库草图算法 Python 潮流周刊#146:CPython 引入 Rust 的进展 - 豌豆花下猫 最小生成树 - mofei1116 红日靶场七:从外网入口、容器逃逸到 AD 接管的完整利用链复盘 - YouDiscovered1t 分享四款开源且实用的 Kafka 管理工具 - 追逐时光者 vLLM 权重加载机制全解析:从挑战到理想架构 LCT 学习笔记 - ACehomoxue Avalonia UI 12.0.0 正式发布:架构演进和性能飞跃 - 张善友 当 AI Agent 把调用链拉长,延迟开始成为一门生意 conhost.exe 无法显示 U+2717 - 145a 太秀了,我把自己蒸馏成了 Skill!已开源 - 程序员鱼皮 ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑 基于 Ghostty 带有分割标签页和为 Claude 编程设计的通知终端 - BugShare AI 焊死入口:教育的“操作系统级”重塑 - 郝hai 初级Java开发工程师使用sql脚本编写代码的过程是简单而且不糊涂 - CoderOilStation Claude Code通关手册(六):MCP协议完全指南 - 暮色之狐 边框灯光环绕动画特效实现指南 - Newbe36524 开源:子木蒸馏版的 SEO 审计工具 seo-audit-skill v1.0 我所理解的Python元模型 【从0到1构建一个ClaudeAgent】规划与协调-TodoWrite - 程序员Seven Claude 和 Codex 在审计 Skill 上性能差异探究 - ACai_sec AScript如何实现中文脚本引擎 - rockey627 【渗透测试】HTB Season10 Garfield 全过程wp - dynasty_chenzi Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革 树状数组正确性证明 - AC-wyr 你的 AI 焦虑,可能比 AI 本身更危险——ATM 机没有消灭银行柜员,但恐慌消灭了你的判断力 - 我没有三颗心脏 一个拉胯的分库分表方案有多绝望?整个部门都在救火! - 冰河团队 动态规划入门必学之走方格问题 - Ofnoname PostgREST 与 PostgreSQL 角色权限配置全解析(生产级实践) - SheepDog1998 使用 UEFI 图形输出协议 GOP 在屏幕上显示图像的方法 - 阿源- Claude Code通关手册(五):组建你的AI专家团队,子代理系统 - 暮色之狐 一个程序员到架构师的催婚路之感悟(整整10年后的催婚相亲感悟) - MisterLip 用 Agent Skill 自动生成工作周报 - 赵康
Python 行情数据清洗实战:Z-Score、MAD 与分位数过滤的异常值检测
Walter先生 · 2026-05-02 · via 博客园_首页

阅读指南

  • 如果你只想要代码:直接跳转第三章,三种检测方法的完整实现可复制运行。
  • 如果你想理解方法选型:从第二章开始,有 Z-Score vs MAD vs 分位数的对比表。
  • 如果你关心生产级细节:第四章有各方法在金融数据上的误判场景和人工审核队列设计。

一、回测收益翻倍?先检查是不是数据错了

拿到 10 年历史 K 线数据后,大多数人的第一反应是直接跑策略回测。结果出来夏普比率 3.2,最大回撤仅 8%,年化收益 35%。兴奋地部署实盘,三个月后亏了 15%。

回测记录里通常有一个隐蔽的凶手:未被清洗的异常数据。

一笔真实成交价 150 元的股票,因为数据源错误记录了 1500 元——你的策略在这一天检测到“突破信号”大举买入。这笔交易在回测中贡献了 10% 的收益,但在实盘中永远不会发生。

问题在于:不是所有价格跳空都是数据错误。财报发布后的真实跳空、拆股除息带来的价格调整、流动性枯竭时的极端波动——这些是需要保留的市场信号。自动清洗的边界在于区分错误和异动。

数据清洗的核心不是“剔除所有异常”,而是区分数据错误(剔除)和真实市场异动(保留)。一条未被清洗的异常 K 线能让回测虚增 5-15% 的年化收益——但实盘无法复现。

二、三种统计方法的原理与金融数据适配性

展开之前,先给出结论速查:

方法选择速查:Z-Score 最常用,但在金融数据上误判率最高,仅适合截面比较。MAD 用中位数免疫极端值,是金融时间序列异常检测的主力方案。分位数过滤适合做第一道粗筛。

2.1 数据准备:从 API 到 DataFrame

在进入异常检测之前,先解决一个工程问题:数据怎么来。这里的重点是类型转换——很多行情 API 的价格和成交量字段返回的是字符串,不转成 float 之前做任何统计计算都会出错。

以下用 TickDB 的历史 K 线接口作为示例演示数据加载流程:

curl "https://api.tickdb.ai/v1/market/kline?symbol=700.HK&interval=1d&limit=100" \
  -H "X-API-Key: YOUR_KEY"

Pandas 加载时注意两件事:时间戳是毫秒 UTC,价格和成交量是字符串——必须在初始化 DataFrame 时显式转换类型:

import pandas as pd

def load_klines_to_df(resp_json: dict) -> pd.DataFrame:
    """将 kline 接口的原始响应转为 DataFrame"""
    df = pd.DataFrame(resp_json["data"]["klines"])
    df["time"] = pd.to_datetime(df["time"], unit="ms")
    df.set_index("time", inplace=True)
    df[["open", "high", "low", "close", "volume"]] = (
        df[["open", "high", "low", "close", "volume"]].astype(float)
    )
    return df

跳过了 String→Float 转换,MAD 函数会直接抛 TypeError。这是对接新数据源时最常见的“第一行代码报错”。

2.2 Z-Score:最常用,但最不适合金融数据

Z-Score 计算每个数据点距离均值有多少个标准差。当 |z| > 3 时,标记为异常值。

这个方法建立在正态分布假设之上。金融收益率分布是典型的厚尾分布——标准差的 3 倍之外并不是稀有事件。美股单日涨跌超过 3 个标准差的情况,每年实际发生 5-8 次,而正态分布预测的次数不到 1 次。

用 Z-Score 检测金融异常值,就像用测量体温的方式判断一个人是否在跑马拉松——马拉松选手完赛时体温超过 38°C 是正常的,不是发烧。

Z-Score 在金融数据上会碰到三个具体问题:2020 年 3 月美股单日 -12% 会被 Z=3.5 误判为异常——这是厚尾敏感。10 年涨 20 倍的股票,前 5 年的价格看起来全是“异常低值”——这是均值漂移。一条真正的错误数据会拉高均值和标准差,导致其他异常漏检——这叫掩蔽效应。

掩蔽效应的直观理解:假设你有一组正常价格在 100 元附近波动的数据。如果混入一条 1500 元的错误数据,均值会被拉高,标准差会被撑大,Z-Score 的阈值区间随之膨胀。结果这条 1500 元的异常值本身可能刚好落在 ±3 标准差的边界内,而周围真正的异常反倒被“保护”了。

场景 正常数据均值 正常数据标准差 ±3σ 阈值区间 异常值1500的Z-Score
无异常值 100.2 2.5 [92.7, 107.7]
含一条1500 128.1 198.3 [-466.8, 723.0] 6.92 → 仍被检出
含两条1500 156.0 277.6 [-676.8, 988.8] 4.84 → 漏检边缘

随着异常值增多,掩蔽效应呈非线性恶化。这就是为什么 Z-Score 在金融数据清洗中不能单独使用。

Z-Score 的唯一适用场景是截面数据比较,不适合时间序列异常检测。

2.3 MAD:针对厚尾分布的鲁棒替代

MAD 用中位数替代均值,用中位数偏差替代标准差。0.6745 是常数,使 MAD 在正态分布下与标准差具有可比性。

为什么 MAD 更适合金融数据:中位数不受极端值影响——一条错误的高价数据不会拉偏参考基准。Modified Z-Score 的 3.5 阈值在实际测试中比 Z=3 的误判率低得多。适用于时间序列,不需要分布假设。

中位数是你的“正常参考点”,即便有一个离谱数据点,参考点纹丝不动。均值则是被极端值来回拉扯的橡皮筋。

MAD 的局限:对低流动性标的不友好。成交量偏低的股票,价格波动本身就不稳定,MAD 会标记太多“假阳性”。

2.4 分位数过滤:最简单,但需要领域知识

直接设定上下分位数阈值,超出范围即标记。

优点是不依赖任何分布假设,解释性强,适合作为其他方法的第一道粗筛。

缺点是需要人工设定阈值,缺乏自适应性。对趋势性标的失效——10 年涨 20 倍的股票,前期的正常价格会被后期抬高的分位数误判。不同价格区间的标的不能用同一套分位数。

2.5 三方法对比速查

方法 分布假设 鲁棒性 自适应性 适用场景
Z-Score 正态分布 截面排名,不做异常检测主力
Modified MAD 时间序列异常检测主力方案
分位数过滤 第一道粗筛 + 价格合法性检查

Z-Score 在金融数据上是误判率最高的方法——厚尾分布不是 bug,是 feature。生产环境推荐 MAD 做主力、分位数做粗筛的双层过滤架构。

三、生产级代码实现

3.1 理解原理:全局统计量版本(教学用途,含前视偏差)

以下为教学版实现,使用全量数据的全局中位数和分位数,便于理解方法本质。

此版本在真实回测中存在前视偏差——用后来的数据判断早期是否异常。生产环境请使用下一节的滚动窗口版本,此处仅供理解原理。

import numpy as np
import pandas as pd


def detect_by_zscore(series: pd.Series, threshold: float = 3.0) -> pd.Series:
    """Z-Score 异常检测(教学用途)。警告:对金融时间序列误判率极高。"""
    mean = series.mean()
    std = series.std()
    if std == 0:
        return pd.Series(False, index=series.index)
    z_scores = np.abs((series - mean) / std)
    return z_scores > threshold


def detect_by_mad_global(series: pd.Series, threshold: float = 3.5) -> pd.Series:
    """全局 MAD 异常检测(教学用途,含前视偏差)。"""
    median = series.median()
    mad = np.median(np.abs(series - median))
    if mad == 0:
        mad = 1e-8
    modified_z = 0.6745 * (series - median) / mad
    return np.abs(modified_z) > threshold


def detect_by_quantile(
    series: pd.Series, lower_q: float = 0.01, upper_q: float = 0.99
) -> pd.Series:
    """分位数过滤异常检测。适合作为第一道粗筛。"""
    lower = series.quantile(lower_q)
    upper = series.quantile(upper_q)
    return (series < lower) | (series > upper)

3.2 生产级实现:滚动 MAD 检测(杜绝前视偏差)

在生产环境中,你必须保证检测 T 日数据时,绝不使用 T+1 日及以后的任何信息。下面的滚动窗口实现严格满足这一点:

def detect_by_rolling_mad(
    series: pd.Series,
    window: int = 252,
    threshold: float = 3.5
) -> pd.Series:
    """
    生产级滚动 Modified Z-Score 异常检测。
    基于过去 window 个交易日的滚动窗口,杜绝前视偏差。

    window=252 对应约一年的交易日。窗口不足一半时返回 False,
    即需要约半年数据才开始检测——这是保守但安全的选择。
    """
    min_periods = window // 2  # 至少需要半年的数据才开始计算

    rolling_median = series.rolling(window=window, min_periods=min_periods).median()
    abs_deviation = (series - rolling_median).abs()
    rolling_mad = abs_deviation.rolling(window=window, min_periods=min_periods).median()

    # 避免零 MAD 导致除零错误
    rolling_mad = rolling_mad.replace(0, 1e-8)

    modified_z = 0.6745 * (series - rolling_median) / rolling_mad
    return modified_z.abs() > threshold

这段滚动 MAD 实现可以直接拷进你的实盘回测框架。它保证了在检测任意一天的数据时,参考基准完全由过去数据构成,不掺杂未来信息。

3.3 双层过滤架构:粗筛 + 精检

from typing import Tuple, Dict


def clean_price_data(
    df: pd.DataFrame,
    price_col: str = "close",
    volume_col: str = "volume",
    mad_window: int = 252,
    mad_threshold: float = 3.5,
    quantile_range: Tuple[float, float] = (0.005, 0.995)
) -> Tuple[pd.DataFrame, Dict]:
    """
    双层过滤:先分位数粗筛,再滚动 MAD 精检。

    返回:
    - df: 带异常标记列的 DataFrame(不删除数据)
    - report: 异常统计报告
    """
    df = df.copy()

    # 第一层:分位数粗筛(价格合法性检查)
    price_lower = df[price_col].quantile(quantile_range[0])
    price_upper = df[price_col].quantile(quantile_range[1])
    quantile_masked = (df[price_col] < price_lower) | (df[price_col] > price_upper)

    # 第二层:滚动 MAD 精检(生产级——杜绝前视偏差)
    price_mad_anomalies = detect_by_rolling_mad(
        df[price_col], window=mad_window, threshold=mad_threshold
    )
    volume_mad_anomalies = detect_by_rolling_mad(
        df[volume_col], window=mad_window, threshold=mad_threshold
    )

    # 标记但不删除——流入人工审核队列
    df["anomaly_price"] = price_mad_anomalies
    df["anomaly_volume"] = volume_mad_anomalies
    df["anomaly_quantile"] = quantile_masked
    df["anomaly_any"] = (
        df["anomaly_price"] | df["anomaly_volume"] | df["anomaly_quantile"]
    )

    report = {
        "total_rows": len(df),
        "quantile_outliers": int(quantile_masked.sum()),
        "mad_price_outliers": int(price_mad_anomalies.sum()),
        "mad_volume_outliers": int(volume_mad_anomalies.sum()),
        "total_flagged": int(df["anomaly_any"].sum()),
        "flagged_pct": round(df["anomaly_any"].sum() / len(df) * 100, 2),
        "price_range": (float(price_lower), float(price_upper)),
        "method": f"Quantile({quantile_range}) + RollingMAD(window={mad_window}, threshold={mad_threshold})"
    }
    return df, report

3.4 人工审核队列

def build_review_queue(df: pd.DataFrame) -> pd.DataFrame:
    """将标记为异常的数据点组织为人工审核队列,按置信度降序排列。"""
    if "anomaly_any" not in df.columns:
        raise ValueError("请先运行 clean_price_data 生成异常标记")

    review = df[df["anomaly_any"]].copy()

    median = df["close"].median()
    mad = np.median(np.abs(df["close"] - median))
    if mad == 0:
        mad = 1e-8
    review["confidence"] = np.abs(0.6745 * (review["close"] - median) / mad)
    review = review.sort_values("confidence", ascending=False)

    review["prev_close"] = df["close"].shift(1).loc[review.index]
    review["pct_change"] = (
        (review["close"] - review["prev_close"]) / review["prev_close"] * 100
    )

    return review[[
        "close", "prev_close", "pct_change",
        "volume", "anomaly_price", "anomaly_volume",
        "anomaly_quantile", "confidence"
    ]]

3.5 使用示例

df_cleaned, report = clean_price_data(df)

print(f"清洗报告:")
print(f"  总数据量: {report['total_rows']} 条")
print(f"  分位数异常: {report['quantile_outliers']} 条")
print(f"  MAD 价格异常: {report['mad_price_outliers']} 条")
print(f"  MAD 成交量异常: {report['mad_volume_outliers']} 条")
print(f"  总计标记: {report['total_flagged']} 条 ({report['flagged_pct']}%)")

review_queue = build_review_queue(df_cleaned)
print(f"\n前 5 条待审核异常点:")
print(review_queue.head(5))

输出示例(示意性数据):

清洗报告:
  总数据量: 2518 条
  分位数异常: 25 条
  MAD 价格异常: 18 条
  MAD 成交量异常: 32 条
  总计标记: 52 条 (2.07%)
  价格范围: (12.50, 385.00)

前 5 条待审核异常点:
            close  prev_close  pct_change    volume  confidence
2020-03-16  85.30      105.20      -18.92  48200000        5.21
2018-08-03  185.40     150.60       23.11  38100000        4.68
2019-01-14  205.10     218.30       -6.05  12100000        3.95

双层过滤标记了约 2% 的数据点为异常——在 10 年级别的原始行情数据中,这个比例是合理的。审核队列按置信度降序排列,可以从最可疑的数据开始处理。

四、踩坑记录:各方法在金融数据上的真实误判场景

问题 现象 根因 解决方案
拆股被误判为暴跌 股价“腰斩”,Z 值极高 拆股导致价格断崖式变化 先用复权因子调整价格再做检测
财报跳空被误判 跳空高开 20% 被标记 真实市场异动,不应剔除 检查当日成交量——真实异动通常放量
低流动性标的 MAD 失效 MAD 为零,除零报错 交易极不活跃,价格连续不变 跳过该标的,标记“数据不足”
成交量天然高方差 同一标的成交量波动极大 成交量分布极度右偏 取对数后再做 MAD 检测
滚动窗口初期数据不足 前 126 天无 MAD 输出 min_periods 设置过长 适当降低 min_periods 或对早期数据单独处理

你在历史数据中遇到过最离谱的异常值是什么?是价格后面多了一个零,还是成交量为负数?关于“真实异动”和“数据错误”的区分,你的策略是怎么处理的?

五、结语

数据清洗不是“把异常值删掉”,而是标记→审核→决策——自动删除的每一次操作,都可能是在删除市场真相。

本文实现了一套双层过滤架构和人工审核队列。为什么不用 Z-Score:金融数据是厚尾分布,误判率偏高,掩蔽效应会让异常值互相“保护”。为什么用滚动 MAD:中位数免疫极端值,滚动窗口杜绝未来函数,不需要正态假设。为什么不自动删除:财报跳空、拆股、流动性枯竭——这些都是需要保留的市场信号。

异常检测的误判率高度依赖数据源本身的质量。如果你使用的数据源已经做了标准化处理——比如时间戳统一为毫秒 UTC、成交量字段经过合法性校验——那么 MAD 检测的基线噪声会小很多。反之,多数据源混合或自行爬取的数据,建议在统计检测之前先做一轮字段级合法性检查:成交量不为负、最高价不低于最低价、时间戳严格递增。市面上的标准化行情接口,例如 TickDB 的 kline 接口,可以作为一个参考起点,具体字段定义可搜索官方文档了解。

扩展方向

  • 成交量对数变换:对成交量取对数后再做 MAD 检测,改善右偏分布。
df["log_volume"] = np.log(df["volume"] + 1)
volume_anomalies = detect_by_rolling_mad(df["log_volume"])
  • 多维度联合检测:结合价格 MAD、成交量 MAD、日内形态做三维异常评分。
  • 自适应窗口:根据标的流动性动态调整窗口大小——大盘股用 252,小盘股用 504。

📡 数据由 TickDB.ai 提供

本文不构成任何投资建议。异常值检测结果仅供数据清洗参考,不构成买卖依据。

工程笔记