
量化交易中的数据清洗与预处理,是决定策略成败的隐形关键环节,核心工作包括六步:处理缺失值、去除重复数据、修正异常值、统一时间序列对齐、处理复权问题、以及规范数据格式与类型。这些工作看似枯燥,却直接关系到回测结果是否可信——脏数据喂出来的策略,再漂亮的曲线也是假象。本文用 pandas 演示量化数据清洗的实战流程,带你把一份"脏"的原始行情数据整理成可以放心用于回测的干净数据。
在量化的所有环节里,数据清洗大概是最不起眼、也最被新手忽视的一步。大家都想写酷炫的策略、跑漂亮的回测,谁愿意花时间去清理枯燥的数据?
但我做量化这些年最深的体会之一就是:数据质量决定了策略的上限。 有句话在圈子里流传很广——"垃圾进,垃圾出"(Garbage In, Garbage Out)。你喂给策略的数据是脏的,那不管你的策略逻辑多精妙、回测曲线多好看,结果都是不可信的假象。
所以这篇我们就来认认真真地做数据清洗。别嫌枯燥,这一步做扎实,能帮你避开大量"回测赚钱、实盘亏钱"的坑。
拿到一份原始数据,第一件事不是急着用,而是先"体检"——看看它有没有毛病。
import pandas as pd
import numpy as np
df = pd.read_csv("raw_stock_data.csv")
# 体检:看看数据的基本情况
print(df.info()) # 查看列、数据类型、非空数量
print(df.describe()) # 查看数值分布
print(df.isnull().sum()) # 查看每列缺失多少info() 看结构和缺失,describe() 看数值有没有明显异常,isnull().sum() 看缺失情况。这三行下去,数据有什么问题基本心里有数了。
缺失值是最常见的数据问题——某天的价格没记录、某个字段是空的。处理方式要根据情况来。
# 先转换日期并排序(时间序列处理的前提)
df["date"] = pd.to_datetime(df["date"])
df = df.sort_values("date").reset_index(drop=True)
# 方式一:价格缺失,用前一个有效值填充(前向填充)
df["close"] = df["close"].ffill()
# 方式二:如果缺失太多、无法填充,直接删除该行
df = df.dropna(subset=["close"])对行情数据,价格的少量缺失通常可以用前向填充(用上一个有效价格补上);但如果缺失严重,硬填反而会引入错误,不如删掉。怎么处理取决于数据的实际情况,没有万能公式。
数据源有时会给出重复的记录,比如同一天出现两条。重复数据会让回测结果失真,必须去掉。
# 按日期去重,保留第一条
df = df.drop_duplicates(subset=["date"], keep="first").reset_index(drop=True)
print("去重后数据条数:", len(df))去重时要想清楚"以什么为唯一标准"——对日线数据,通常一个日期只应有一条记录。
异常值是最隐蔽的坑——比如某天价格突然变成 0、或者是明显错误的天价。这些异常如果不处理,会严重扭曲回测。
# 检查明显异常:价格为 0 或负数
abnormal = df[df["close"] <= 0]
print("异常记录数:", len(abnormal))
# 处理:把异常价格标记为缺失,再用前向填充
df.loc[df["close"] <= 0, "close"] = np.nan
df["close"] = df["close"].ffill()
# 也可以检查涨跌幅是否异常(比如单日涨跌超过某个不合理阈值)
df["pct"] = df["close"].pct_change()
extreme = df[df["pct"].abs() > 0.5] # 单日波动超过 50% 需警惕
print("极端波动记录数:", len(extreme))异常值检测没有标准答案,需要结合业务常识判断。关键是保持警惕——一份看起来正常的数据里,可能藏着几个足以毁掉回测的异常点。
量化数据是时间序列,正确的时间处理至关重要。要确保日期格式统一、按时间排序、必要时对齐到统一的时间频率。
# 确保按时间排序(前面已做)
# 如果需要对齐到连续的交易日/自然日,可以设置日期索引
df = df.set_index("date")
# 示例:如果要重采样成规整的频率(按需,非必须)
# df = df.resample("D").ffill() # 按自然日对齐并前向填充
print(df.index.min(), "到", df.index.max())时间对齐的意义在于:当你处理多个标的、或要把不同数据源拼在一起时,时间轴必须对得上,否则计算会出错。
这是股票数据特有、也极易被新手忽略的坑。当股票发生分红、送股时,价格会"跳空",如果用未复权的价格算收益,会得到错误结果。
复权处理的核心思想是:把因为分红送股导致的价格跳变"还原",让价格序列在计算收益时保持连续性。实际操作中,通常直接获取前复权或后复权的价格数据来用。
# 如果数据源提供了复权价格列(如 adj_close),优先使用它
if "adj_close" in df.columns:
df["price_for_calc"] = df["adj_close"]
else:
df["price_for_calc"] = df["close"]
# 注意:用未复权价格算长期收益可能有误差记住:算收益率时,一定要用复权后的价格,否则分红送股当天会出现虚假的"暴跌",让回测结果完全失真。
最后,确保数据的格式和类型规范,为后续计算扫清障碍。
# 确保价格是数值类型
df["price_for_calc"] = pd.to_numeric(df["price_for_calc"], errors="coerce")
# 再检查一遍是否还有缺失
df = df.dropna(subset=["price_for_calc"])
# 保存清洗后的干净数据
df.to_csv("clean_stock_data.csv")
print("清洗完成,干净数据已保存")到这里,一份可以放心用于回测的干净数据就整理好了。
我把整个清洗流程整理成一张表:
步骤 | 处理内容 | 关键方法 |
|---|---|---|
缺失值 | 填充或删除空值 | ffill、dropna |
重复数据 | 去除重复记录 | drop_duplicates |
异常值 | 检测并修正异常 | 条件筛选、标记填充 |
时间对齐 | 排序、对齐时间轴 | sort_values、resample |
复权处理 | 还原分红送股影响 | 使用复权价格 |
格式规范 | 统一数据类型 | to_numeric |
量化数据的清洗与预处理,是一项被严重低估却极其关键的工作。处理缺失、去重、修异常、对齐时间、处理复权、规范格式——这六步做扎实了,你的回测才有可信的基础。记住那句朴素的真理:垃圾进,垃圾出。愿意在数据上多花功夫的人,往往能避开最多的坑。
当你要处理大量历史数据、或让数据抓取和清洗流程长期自动运行时,本地环境可能力不从心。这时可以把数据处理程序放到腾讯云云服务器 CVM 上运行,利用云端的稳定性和存储能力支撑长期任务。腾讯云近期上线了量化交易专题活动,可以了解云服务器如何承载数据抓取、清洗与策略回测的全流程。
风险提示:本文仅为量化交易科普与技术分享,不构成任何投资建议。文中代码仅为教学示例。金融市场存在风险,任何交易策略都可能产生亏损,请结合自身情况谨慎决策。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。