3. 数据获取与预处理:用Pandas搞定金融数据清洗

做量化仿真,第一步就是跟数据打交道。我见过太多人一上来就写策略,结果数据没处理好,回测结果全是错的。说白了,数据预处理占整个量化工作的60%以上。这一章,咱们就聊聊怎么用Pandas把CSV和Excel数据收拾得服服帖帖。

3.1 读取数据:CSV和Excel的打开方式

我个人习惯用pd.read_csv()pd.read_excel()。这两个函数看着简单,但有几个参数特别关键。

import pandas as pd
import numpy as np

# 读取CSV文件
df = pd.read_csv('stock_data.csv', parse_dates=['date'], index_col='date')

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

嗯,这里要注意:parse_dates参数一定要用。我刚开始做量化时,经常忘记把日期列转成datetime格式,结果后面画图、计算时间差全乱了。你想想看,日期是字符串的话,排序都会出问题。

小技巧:如果CSV文件很大,可以用chunksize参数分块读取。我在处理A股全市场数据时,经常用这个参数,避免内存爆炸。

3.2 处理缺失值:别让NaN毁了你的策略

真实数据里,缺失值太常见了。停牌、节假日、数据源问题,都会导致NaN。我曾经因为没处理好缺失值,回测时收益率曲线直接断掉,找了半天才发现是某只股票停牌了三天。

处理缺失值,我一般分三步走:

  1. 先看看缺失情况df.isnull().sum() 快速扫描
  2. 决定处理方式:删除还是填充?
  3. 执行操作
# 查看缺失值
print(df.isnull().sum())

# 方法1:删除缺失行(慎用)
df_clean = df.dropna()

# 方法2:向前填充(金融数据常用)
df_filled = df.fillna(method='ffill')

# 方法3:用均值填充
df_mean = df.fillna(df.mean())
避坑指南:千万不要直接dropna()!我曾经在回测中删除了所有缺失行,结果把连续的时间序列搞断了,收益率计算全错。金融数据建议用前向填充(ffill),因为停牌期间的价格应该沿用上一交易日的数据。

3.3 数据标准化:让不同资产站在同一起跑线

为什么需要标准化?你想想看,茅台股价1800块,工商银行才5块,直接比较没意义。标准化就是把不同量纲的数据拉到同一个尺度上。

常用的标准化方法有两种:

方法 公式 适用场景
Z-score标准化 (x - μ) / σ 数据近似正态分布时
Min-Max标准化 (x - min) / (max - min) 数据有明确边界时
from sklearn.preprocessing import StandardScaler, MinMaxScaler

# Z-score标准化
scaler = StandardScaler()
df_zscore = pd.DataFrame(
    scaler.fit_transform(df[['close', 'volume']]),
    columns=['close_scaled', 'volume_scaled'],
    index=df.index
)

# Min-Max标准化
scaler_mm = MinMaxScaler()
df_mm = pd.DataFrame(
    scaler_mm.fit_transform(df[['close', 'volume']]),
    columns=['close_mm', 'volume_mm'],
    index=df.index
)

我个人习惯用Z-score。为什么?因为金融数据经常有极端值,Min-Max会把异常值压缩得太厉害。不过,如果你做的是波动率相关的策略,Min-Max可能更合适。

3.4 对数收益率计算:量化分析的核心指标

收益率计算有两种:简单收益率和对数收益率。做量化仿真,我强烈推荐用对数收益率。

原因有三:

  • 可加性:多期对数收益率可以直接相加
  • 对称性:涨10%和跌10%在数值上对称
  • 正态性:对数收益率更接近正态分布,方便后续建模
# 计算对数收益率
df['log_return'] = np.log(df['close'] / df['close'].shift(1))

# 计算简单收益率(对比用)
df['simple_return'] = df['close'].pct_change()

# 去掉第一行的NaN
df_returns = df.dropna()
核心要点:对数收益率 = ln(P_t / P_{t-1})。这个公式看着简单,但它是整个量化分析的基石。我做的所有策略,第一步都是算对数收益率。

嗯,这里有个坑要注意:shift(1)会产生NaN,因为第一行没有前一天的数据。记得用dropna()清理掉。

3.5 完整流程:从原始数据到可用数据

把上面这些串起来,就是一个完整的数据预处理流水线。我一般写成函数,方便复用。

def preprocess_data(filepath, date_col='date', price_col='close'):
    """
    数据预处理流水线
    """
    # 1. 读取数据
    df = pd.read_csv(filepath, parse_dates=[date_col])
    df.set_index(date_col, inplace=True)
    
    # 2. 处理缺失值(前向填充)
    df.fillna(method='ffill', inplace=True)
    
    # 3. 删除仍然存在的NaN(比如开头几行)
    df.dropna(inplace=True)
    
    # 4. 计算对数收益率
    df['log_return'] = np.log(df[price_col] / df[price_col].shift(1))
    
    # 5. 标准化(可选)
    scaler = StandardScaler()
    df['return_scaled'] = scaler.fit_transform(df[['log_return']])
    
    return df.dropna()

# 使用示例
data_clean = preprocess_data('stock_data.csv')
print(data_clean.head())

这个函数我用了好几年,从A股到美股,从股票到加密货币,基本都能直接套用。你想想看,数据预处理的核心逻辑其实是一样的,只是参数不同而已。

3.6 知识体系总览

下面这张图,把本章的核心逻辑串起来了。从原始数据到最终可用的收益率序列,每一步都有讲究。

数据预处理核心流程 数据读取 CSV / Excel 缺失值处理 前向填充 / 删除 数据标准化 Z-score / Min-Max 收益率计算 对数收益率 关键说明: • 数据读取时务必指定日期列,否则后续时间序列分析会出错 • 缺失值处理推荐前向填充(ffill),保留时间序列的连续性 • 标准化不是必须的,但多资产组合时强烈建议做 • 对数收益率是量化分析的起点,后续所有计算都基于它

这张图把整个流程串起来了。你从左边开始,一步步走到右边,就完成了从原始数据到可用数据的转换。我每次做新项目,都会先跑一遍这个流程,确保数据质量没问题。

个人经验:数据预处理没有标准答案。不同的策略、不同的市场,处理方式可能完全不同。关键是理解每一步在做什么,以及为什么这么做。我见过有人把所有缺失值都删掉,结果回测结果漂亮得不行,实盘一跑就崩——因为实盘数据里也有缺失值啊!

专注资料整理