4. 数据获取与清洗:从CSV/Excel读取交易数据、处理缺失值、数据类型转换、时间序列索引设置

做量化交易,有一句话我特别认同:垃圾进,垃圾出。你策略再牛,模型再花哨,数据源要是脏的,结果全是扯淡。今天咱们就聊聊数据获取与清洗这第一步,也是我踩坑最多的一步。

核心要点:数据清洗占整个量化分析工作量的 60%-80%。别嫌烦,这是基本功。

4.1 从CSV/Excel读取交易数据

我个人习惯用 pandas 来读数据。为啥?因为它快,而且对金融数据支持好。你想想看,每天几千只股票的 tick 数据,用 Excel 打开直接卡死,pandas 几秒钟搞定。

先看最常用的 CSV 读取:

import pandas as pd

# 读取CSV文件
df = pd.read_csv('trade_data.csv')

# 看一眼数据长啥样
print(df.head())
print(df.info())
print(df.describe())

嗯,这里要注意:read_csv() 有很多参数可以调。我在项目中遇到过最坑的一次,是数据文件里混了中文编码,直接报错。后来加了个 encoding='utf-8' 才搞定。

Excel 文件稍微麻烦点,但也不复杂:

# 读取Excel文件
df_excel = pd.read_excel('trade_data.xlsx', sheet_name='Sheet1')

# 如果Excel里有多个sheet,可以指定
df_sheet2 = pd.read_excel('trade_data.xlsx', sheet_name='Sheet2')

小技巧:如果数据文件很大,可以用 nrows=1000 先读前1000行看看结构,确认没问题再全量读取。别一上来就全量读,万一格式不对,白等半天。

4.2 处理缺失值

真实交易数据里,缺失值太常见了。比如某天股票停牌,或者交易所数据推送延迟,都会产生空值。怎么处理?

先看看数据里有没有缺失值:

# 检查缺失值
print(df.isnull().sum())

# 缺失值占比
print(df.isnull().mean() * 100)

处理方式主要有三种,我按推荐程度排个序:

  1. 删除缺失行:如果缺失比例很小(比如 < 5%),直接删掉最省事。
  2. 填充缺失值:用前一个值、后一个值、或者均值填充。
  3. 插值法:用线性插值或时间插值,适合时间序列数据。
# 方式1:删除缺失行
df_clean = df.dropna()

# 方式2:向前填充(用上一个非空值填充)
df_filled = df.fillna(method='ffill')

# 方式3:线性插值
df_interpolated = df.interpolate(method='linear')

避坑指南:我曾经在回测时用了 fillna(method='ffill') 处理价格数据,结果某只股票停牌一周,复牌后直接用了停牌前的价格。回测曲线漂亮得不行,实盘一跑就崩。后来我改成用 limit=5 限制连续填充的天数,超过5天就标记为异常。

4.3 数据类型转换

读进来的数据,类型经常不对。比如日期列是字符串,价格列是 object 类型。不转换的话,后面计算全错。

我一般这样检查类型:

# 查看每列的数据类型
print(df.dtypes)

# 常见的类型问题
# - 日期列是 object 类型 → 转 datetime
# - 价格列是 object 类型 → 转 float
# - 成交量列是 object 类型 → 转 int

转换代码很简单:

# 日期列转换
df['date'] = pd.to_datetime(df['date'])

# 价格列转换(注意:可能有逗号、百分号等)
df['price'] = pd.to_numeric(df['price'], errors='coerce')

# 成交量转换
df['volume'] = df['volume'].astype(int)

小技巧:errors='coerce' 这个参数很实用。遇到无法转换的值,它会变成 NaN,而不是直接报错。这样你可以后续统一处理异常值。

4.4 时间序列索引设置

交易数据天然是时间序列。把时间列设为索引,后面做切片、重采样、滚动计算都方便得多。

# 设置日期列为索引
df = df.set_index('date')

# 确保索引是datetime类型
df.index = pd.to_datetime(df.index)

# 排序索引(重要!)
df = df.sort_index()

设置好索引后,你可以这样操作:

# 按日期范围切片
df_2023 = df['2023-01-01':'2023-12-31']

# 按月份重采样
df_monthly = df.resample('M').last()

# 滚动计算(比如20日均线)
df['ma20'] = df['close'].rolling(window=20).mean()

核心逻辑:时间序列索引是量化分析的基石。没有它,你没法做回测、没法对齐数据、没法计算各种指标。所以这一步千万别省。

4.5 本章知识体系

下面这张图,是我自己总结的数据清洗流程。每次做新项目,我都按这个步骤来,基本不会漏东西。

数据获取与清洗核心流程 数据读取 CSV / Excel 缺失值处理 删除 / 填充 / 插值 数据类型转换 datetime / float / int 索引设置 常见问题与解决方案 问题:编码错误 解决:encoding='utf-8' 或 encoding='gbk' 问题:日期格式混乱 解决:pd.to_datetime() 统一格式 问题:价格含特殊字符 解决:pd.to_numeric() errors='coerce' 清洗后的数据 → 可直接用于策略回测与信号生成

4.6 完整示例:从CSV到清洗完毕

最后,给你一个完整的代码示例。这是我做套利分析时常用的模板:

import pandas as pd
import numpy as np

# 1. 读取数据
df = pd.read_csv('arbitrage_data.csv', encoding='utf-8')

# 2. 初步检查
print(f"原始数据形状: {df.shape}")
print(f"缺失值统计:\n{df.isnull().sum()}")

# 3. 处理缺失值(价格列用前向填充,成交量列用0填充)
df['price'] = df['price'].fillna(method='ffill', limit=3)
df['volume'] = df['volume'].fillna(0)

# 4. 类型转换
df['date'] = pd.to_datetime(df['date'])
df['price'] = pd.to_numeric(df['price'], errors='coerce')
df['volume'] = df['volume'].astype(int)

# 5. 设置时间索引
df = df.set_index('date').sort_index()

# 6. 最终检查
print(f"清洗后数据形状: {df.shape}")
print(f"数据时间范围: {df.index.min()} ~ {df.index.max()}")
print(df.head())

个人经验:我建议你把数据清洗写成函数,每次读数据都调用。这样既规范,又不容易漏步骤。我自己的项目里,这个函数已经迭代了十几个版本,现在基本能处理 90% 的脏数据问题。

数据清洗这事儿,说白了就是「磨刀不误砍柴工」。你花 10 分钟把数据洗干净,后面回测、分析、实盘都能省下大把时间。别嫌麻烦,这是每个量化交易者的必修课。

专注资料整理