第三章 数据收集与清洗:数据源选择、采集API、清洗标准化与存储方案

做量化交易,数据就是你的石油。

这话一点不夸张。我见过太多人,策略写得天花乱坠,结果一跑实盘就崩——查到最后,往往是数据源头出了问题。今天咱们就把这块硬骨头啃下来。

3.1 数据源选择:交易所 vs OTC市场

先说说数据源怎么选。我个人习惯把数据源分成两大类:交易所数据和OTC市场数据。

交易所数据,说白了就是中心化交易所(CEX)的行情。像Binance、OKX、Coinbase这些。优点是数据规范、延迟低、历史数据完整。我在项目中遇到过最头疼的事,就是某小交易所的K线数据居然有30%的缺失——后来果断换成了主流交易所的数据源。

OTC市场数据,也就是场外交易数据。这类数据更分散,但能反映真实的大宗交易情况。比如一些机构级的OTC平台,成交数据往往比交易所的盘口更有参考价值。

我的建议:

  • 做高频策略:优先选交易所数据,延迟要控制在10ms以内
  • 做趋势策略:可以混用交易所+OTC数据,增加信号维度
  • 做套利策略:必须同时接入多个交易所的深度数据

你想想看,如果只用一个数据源,万一它出问题了呢?我曾经就因为单一数据源故障,导致一个套利策略在5分钟内亏了2%——从那以后,我所有策略都至少接两个独立数据源做交叉验证。

3.2 数据采集API使用

数据采集这块,我踩过的坑比走过的路还多。先说说最常用的几种API。

REST API:适合历史数据批量下载。比如Binance的GET /api/v3/klines,一次能拉500根K线。但要注意频率限制——我记得有一次写了个循环,忘了加sleep,结果IP直接被封了24小时。

WebSocket API:适合实时行情订阅。延迟低,但需要自己维护连接。我习惯用Python的websockets库,配合asyncio做异步处理。

下面是我常用的一个数据采集模板:

import requests
import time
from datetime import datetime

def fetch_klines(symbol, interval, limit=500):
    """
    从Binance获取K线数据
    """
    url = "https://api.binance.com/api/v3/klines"
    params = {
        "symbol": symbol,
        "interval": interval,
        "limit": limit
    }
    
    try:
        resp = requests.get(url, params=params, timeout=10)
        resp.raise_for_status()
        data = resp.json()
        
        # 清洗成标准格式
        klines = []
        for item in data:
            klines.append({
                "timestamp": item[0],
                "open": float(item[1]),
                "high": float(item[2]),
                "low": float(item[3]),
                "close": float(item[4]),
                "volume": float(item[5])
            })
        return klines
    
    except Exception as e:
        print(f"采集失败: {e}")
        return None

# 使用示例
data = fetch_klines("BTCUSDT", "1h")
if data:
    print(f"成功获取 {len(data)} 根K线")

避坑指南:

我曾经在采集深度数据时,没注意交易所的限频规则,结果被永久封禁了API权限。后来我养成了一个习惯:每个API请求之间至少间隔100ms,并且用retry机制处理临时故障。

3.3 数据清洗与标准化

数据采集回来,千万别直接用。我敢说,90%的原始数据都有问题。

常见问题:

  • 时间戳不统一:有的用毫秒,有的用秒,有的甚至用字符串
  • 缺失值:比如某根K线的成交量是0
  • 异常值:价格突然跳变,明显是数据错误
  • 重复数据:同一个时间点出现多条记录

我的清洗流程是这样的:

  1. 时间标准化:统一转为UTC时间戳(毫秒级)
  2. 去重:按时间戳去重,保留最后一条
  3. 缺失值处理:用前一根K线的收盘价填充,或者直接删除
  4. 异常值检测:用3-sigma法则剔除明显错误的数据
  5. 格式统一:所有数值转为float,时间戳转为int

举个例子,我曾经处理过一组数据,其中某根K线的最高价居然是最低价的10倍——明显是交易所的bug。如果不剔除,策略回测结果会完全失真。

import pandas as pd
import numpy as np

def clean_klines(df):
    """
    清洗K线数据
    """
    # 1. 时间戳标准化
    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
    
    # 2. 去重
    df = df.drop_duplicates(subset=['timestamp'], keep='last')
    
    # 3. 缺失值处理
    df = df.fillna(method='ffill')
    
    # 4. 异常值检测(3-sigma)
    for col in ['open', 'high', 'low', 'close']:
        mean = df[col].mean()
        std = df[col].std()
        df = df[(df[col] > mean - 3*std) & (df[col] < mean + 3*std)]
    
    # 5. 排序
    df = df.sort_values('timestamp')
    
    return df

# 使用示例
df = pd.DataFrame(data)
df_clean = clean_klines(df)
print(f"清洗前: {len(df)} 行, 清洗后: {len(df_clean)} 行")

注意:

清洗不是越狠越好。我见过有人把正常波动也当异常剔除了,结果策略信号全没了。建议先做可视化检查,确认异常值的分布情况再动手。

3.4 数据存储方案

数据存哪里?这是个好问题。我试过很多方案,最后总结出三条路。

存储方案 适用场景 优点 缺点
CSV文件 小规模数据(<1GB) 简单、易读、方便调试 查询慢、不支持并发
SQLite 中等规模(1-10GB) 轻量、支持SQL查询 写入性能一般
ClickHouse 大规模数据(>10GB) 列式存储、查询极快 部署复杂、资源消耗大

我个人习惯:

  • 开发阶段用CSV,方便调试
  • 回测阶段用SQLite,查询灵活
  • 生产环境用ClickHouse,性能拉满

嗯,这里要注意一点:千万别把所有数据都塞到一个表里。我建议按交易对和时间分区存储。比如btcusdt_2024_01btcusdt_2024_02这样。查询时只扫需要的分区,速度能快10倍以上。

我的存储架构:

data/
├── raw/          # 原始数据,不做任何修改
│   ├── binance/
│   └── okx/
├── clean/        # 清洗后的标准数据
│   ├── btcusdt/
│   └── ethusdt/
└── features/     # 衍生特征数据
    └── indicators/
  

为什么要保留原始数据?因为清洗逻辑可能会变。我曾经改过一次清洗规则,结果发现之前清洗过的数据全得重来——幸好原始数据还在,不然就抓瞎了。

3.5 本章知识体系

下面这张图,是我做数据工程时的核心框架。你照着这个来,基本不会出大问题。

数据收集与清洗核心流程 数据源选择 交易所 / OTC市场 数据采集API REST / WebSocket 数据清洗标准化 去重 / 异常值 / 格式统一 数据存储方案 CSV / SQLite / ClickHouse 数据质量反馈 关键原则 1. 保留原始数据,永远不要修改raw目录 2. 清洗逻辑必须可复现,用脚本而非手动操作 3. 数据质量监控要自动化,每天检查缺失率和异常率

这张图的核心逻辑很简单:数据从源头来,经过采集、清洗,最后存起来。但别忘了那个虚线箭头——数据质量反馈。我每次跑完策略,都会回头检查数据质量。如果策略表现异常,90%的概率是数据出了问题。

最后说一句:

数据工程这件事,做得再好都不为过。我见过太多人花90%的时间写策略,只花10%的时间搞数据。结果策略上线后,天天被数据问题折磨。我的习惯是反过来的:60%的时间搞数据,30%的时间写策略,10%的时间优化。你试试看,效果绝对不一样。

专注资料整理