第三章:数据获取与清洗——数据源选择、清洗流程、处理缺失值与异常值、数据对齐与重采样

做轮动模型,最怕什么?

不是策略亏钱,而是数据出问题。

我见过太多人,策略逻辑写得漂漂亮亮,回测曲线也好看,结果一上实盘就崩。查到最后,发现是数据源有坑——前复权算错了、停牌日没处理、不同股票的数据长度不一样。嗯,这些坑我都踩过。

所以这一章,咱们把数据这块地基打牢。说白了,数据质量决定了你的回测可信度。数据脏,结果就是垃圾进垃圾出。

3.1 数据源选择:Tushare、AKShare、Yahoo Finance

我个人习惯,根据场景选数据源。做A股轮动,首选Tushare和AKShare。做全球资产配置,Yahoo Finance更方便。下面我逐个说说。

3.1.1 Tushare

国内老牌金融数据接口,数据质量高,覆盖全面。但需要注册获取token,部分接口有积分限制。

优点:数据准确、字段丰富、有复权因子

缺点:需要注册、有调用频率限制、高级接口收费

import tushare as ts

# 设置token(需提前注册获取)
ts.set_token('your_token_here')
pro = ts.pro_api()

# 获取日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20200101', end_date='20231231')
print(df.head())

3.1.2 AKShare

开源免费,接口丰富,更新快。我最近两年用得比较多。它直接爬取公开网站数据,所以不需要注册。

我的经验:AKShare的接口名经常变,建议固定版本号。我曾经因为升级版本导致回测脚本全崩,排查了半天。

import akshare as ak

# 获取股票日线数据
df = ak.stock_zh_a_hist(symbol="000001", period="daily", 
                        start_date="20200101", end_date="20231231", adjust="qfq")
print(df.head())

3.1.3 Yahoo Finance

做全球ETF轮动、美股轮动,Yahoo Finance是首选。用yfinance库就能直接拉数据,非常方便。

import yfinance as yf

# 获取SPY和QQQ数据
tickers = ['SPY', 'QQQ']
data = yf.download(tickers, start='2020-01-01', end='2023-12-31')
print(data['Adj Close'].head())

注意:Yahoo Finance在国内访问不稳定,建议搭配代理使用。另外,它返回的数据是未复权的,需要自己处理复权。

3.2 数据清洗流程

数据拿到手,别急着用。先走一遍清洗流程。我总结了一个四步法:

  1. 格式统一——日期格式、列名、数据类型
  2. 缺失值处理——填充或删除
  3. 异常值检测——涨跌幅异常、停牌日处理
  4. 数据对齐——多标的统一时间轴

你想想看,如果不同数据源的日期格式不一样,一个用'2020-01-01',一个用'20200101',合并的时候直接报错。这种低级错误我犯过不止一次。

# 标准清洗流程示例
import pandas as pd

def clean_data(df):
    # 1. 统一日期格式
    df['date'] = pd.to_datetime(df['date'])
    df = df.sort_values('date')
    
    # 2. 设置索引
    df = df.set_index('date')
    
    # 3. 只保留需要的列
    df = df[['open', 'high', 'low', 'close', 'volume']]
    
    # 4. 处理缺失值
    df = df.fillna(method='ffill')  # 前向填充
    
    # 5. 处理异常值(涨跌幅超过20%视为异常)
    df['return'] = df['close'].pct_change()
    df.loc[df['return'].abs() > 0.2, 'close'] = None
    df['close'] = df['close'].fillna(method='ffill')
    
    return df

3.3 处理缺失值与异常值

缺失值怎么处理?说白了就三种思路:

  • 删除——缺失比例小,直接dropna()
  • 填充——用前值、后值、均值填充
  • 插值——线性插值、时间插值

我个人习惯,金融时间序列用前向填充(ffill)最多。为什么?因为停牌日的数据,用前一天的价格填充最合理。你想想看,股票停牌了,价格不变,用ffill正好。

避坑指南:我曾经在回测中用了均值填充,结果把停牌日的数据填成了均价,导致回测收益虚高。后来发现,停牌日应该用前一天的收盘价,而不是均值。

# 缺失值处理实战
# 场景:某股票停牌5天,数据缺失
df = pd.DataFrame({
    'date': pd.date_range('2023-01-01', periods=10, freq='D'),
    'close': [10, 10.5, None, None, None, None, None, 11, 11.2, 11.5]
})

# 正确做法:前向填充
df['close_ffill'] = df['close'].fillna(method='ffill')
print(df)

异常值处理,我常用两种方法:

  1. 涨跌幅阈值法——超过±20%视为异常(A股涨跌停限制)
  2. Z-score法——超过3倍标准差视为异常
# 异常值检测
def detect_outliers(df, column='close', method='zscore', threshold=3):
    if method == 'zscore':
        z_scores = (df[column] - df[column].mean()) / df[column].std()
        return df[z_scores.abs() > threshold]
    elif method == 'pct_change':
        returns = df[column].pct_change()
        return df[returns.abs() > 0.2]

3.4 数据对齐与重采样

做轮动模型,最核心的一步就是数据对齐。不同股票的交易日期不一样,有的节假日停牌,有的临时停牌。如果不做对齐,回测结果就是错的。

我的经验:我习惯用沪深300的交易日期作为基准,把所有股票对齐到同一时间轴。这样能保证每个交易日都有数据。

# 多标的数据对齐
import pandas as pd

# 假设有三只股票的数据
stock_a = pd.DataFrame({'date': pd.date_range('2023-01-01', periods=5, freq='D'),
                         'close': [10, 10.5, 11, 11.2, 11.8]})
stock_b = pd.DataFrame({'date': pd.date_range('2023-01-01', periods=5, freq='D'),
                         'close': [20, 20.3, None, 20.8, 21]})
stock_c = pd.DataFrame({'date': pd.date_range('2023-01-03', periods=3, freq='D'),
                         'close': [30, 30.5, 31]})

# 合并并对齐
all_data = pd.merge(stock_a, stock_b, on='date', how='outer', suffixes=('_a', '_b'))
all_data = pd.merge(all_data, stock_c, on='date', how='outer')
all_data = all_data.set_index('date').sort_index()

# 前向填充缺失值
all_data = all_data.fillna(method='ffill')
print(all_data)

重采样也很重要。做周频轮动,需要把日线数据重采样成周线。做月频轮动,就重采样成月线。

# 日线重采样为周线
df_weekly = df.resample('W').agg({
    'open': 'first',
    'high': 'max',
    'low': 'min',
    'close': 'last',
    'volume': 'sum'
})

3.5 本章知识体系

下面这张图,是我做数据清洗时的完整流程。你可以保存下来,每次做数据预处理时对照着走一遍。

数据获取与清洗流程图 数据源选择 Tushare / AKShare / Yahoo Finance 数据获取 日线/周线/月线 · 复权处理 数据清洗 格式统一 · 缺失值处理 · 异常值检测 数据对齐 多标的时间轴统一 · 重采样 清洗完成 可用于回测的干净数据 关键要点 • 前复权 vs 后复权 • ffill填充停牌数据 • 涨跌幅阈值法 • 沪深300为基准对齐 • 周线/月线重采样 • 避免未来数据 • 检查数据完整性 • 保存清洗日志

嗯,这一章的内容就到这里。数据清洗看起来琐碎,但它是整个轮动模型的地基。地基不稳,上面盖的房子再漂亮也没用。下一章,咱们开始讲技术指标的计算,到时候这些干净数据就能派上用场了。


公众号:蓝海资料掘金营,微信deep3321