3、数据获取与清洗:交易所API使用、数据频率选择、缺失值处理、异常值检测
做量化交易,尤其是数字货币这块,有个铁律:数据质量决定策略上限。模型再牛逼,喂进去的是垃圾数据,出来的只能是更垃圾的结果。我自己刚入行那会儿,就因为没处理好一个交易所的异常K线,回测曲线漂亮得不行,实盘直接亏到怀疑人生。嗯,今天咱们就把数据获取和清洗这关彻底过一遍。
3.1 交易所API使用:别当愣头青
数字货币交易所的API,说白了就是你和市场之间的桥梁。但这座桥,不是每条都好走。
选哪个交易所?我个人习惯看三点:流动性、API稳定性、文档质量。币安、OKX、Bybit这几家算是第一梯队。我建议你至少对接两家,原因很简单——单点故障。有一次币安API挂了整整20分钟,我另一路数据从OKX走,策略照样跑,这就是冗余的好处。
REST vs WebSocket
- REST API:适合拉历史数据,或者做低频操作。请求频率别太猛,一般每秒1-2次就够了。我曾经见过有人写了个死循环去拉数据,结果IP直接被交易所封了24小时。
- WebSocket:适合实时行情。连接建立后,数据是推过来的,延迟低。但要注意心跳包,断了要重连。我一般写个自动重连机制,5秒检测一次。
核心要点:API Key别硬编码在代码里。用环境变量或者配置文件。我见过有人把Key传到GitHub上的,那画面太美不敢看。
# 一个简单的REST请求示例(Python)
import requests
import time
url = "https://api.binance.com/api/v3/klines"
params = {
"symbol": "BTCUSDT",
"interval": "1h",
"limit": 100
}
response = requests.get(url, params=params)
data = response.json()
# 注意:实际生产环境要加异常处理和限速
time.sleep(0.5) # 别太急
3.2 数据频率选择:不是越快越好
很多人一上来就选1分钟甚至秒级数据,觉得频率越高越赚钱。其实不然。你想想看,高频数据噪声大,对模型的要求也高。而且存储成本、计算成本都上去了。
我的经验法则:
- 日线/4小时:适合中长线策略,信号稳定,噪声小。
- 1小时/30分钟:适合波段交易,我大部分策略都用这个频率。
- 5分钟/1分钟:适合日内短线,但要做好滤波。
- 秒级:除非你做高频做市,否则别碰。数据量太大,处理起来头疼。
为什么会这样?因为不同频率的数据,统计特性不一样。低频数据更接近正态分布,高频数据有很强的自相关和尖峰厚尾特征。你选频率的时候,得先想清楚你的策略是抓趋势还是抓波动。
小技巧:如果你不确定选哪个频率,可以先用1小时数据做回测。如果1小时数据上策略都不赚钱,那换更细的频率大概率也是亏。
3.3 缺失值处理:别让数据「断片」
数字货币市场是7x24小时交易的,但数据缺失的情况依然常见。原因可能是交易所维护、网络波动、API限流等等。我遇到过最离谱的一次,某小交易所直接丢了3天的数据,问客服说是「硬盘坏了」。
常见的缺失值处理方法:
| 方法 | 适用场景 | 缺点 |
|---|---|---|
| 向前填充(ffill) | 价格数据,短时间缺失 | 会引入滞后 |
| 向后填充(bfill) | 成交量数据 | 会引入未来信息 |
| 线性插值 | 连续型指标 | 不适合跳跃行情 |
| 删除缺失行 | 缺失比例很小(<1%) | 会减少样本量 |
我个人习惯:对于价格数据,如果缺失不超过3根K线,用向前填充。超过3根,我会直接标记为无效数据,或者从其他交易所补数据。你想想看,如果一段行情剧烈波动时数据缺失,你用填充值去算因子,那结果能准吗?
# 缺失值处理示例(Pandas)
import pandas as pd
df = pd.read_csv('btc_1h.csv')
# 检查缺失
print(df.isnull().sum())
# 向前填充(价格列)
df['close'].fillna(method='ffill', inplace=True)
# 如果缺失太多,直接删除
if df['close'].isnull().sum() > 10:
df.dropna(subset=['close'], inplace=True)
3.4 异常值检测:把「妖怪」揪出来
异常值,说白了就是那些明显不合理的数据点。比如比特币价格突然跳到100万美元然后又跌回来,这明显是交易所的撮合错误或者数据推送bug。我曾经在回测中没处理这种异常,结果策略在「假突破」上开了仓,亏了不少。
常用的检测方法:
- 3σ原则:假设数据服从正态分布,超过均值±3倍标准差的就是异常。简单粗暴,但对金融数据不太准(因为金融数据有厚尾)。
- IQR(四分位距):用Q1-1.5*IQR和Q3+1.5*IQR作为边界。比3σ稳健一些。
- 百分比截断:直接去掉上下0.5%或1%的数据。我常用这个方法,简单有效。
- 基于波动率的动态阈值:用ATR(平均真实波幅)来设定动态阈值。适合波动率变化大的市场。
注意:不要机械地删除所有异常值。有些异常可能是真实的极端行情(比如312暴跌),这些数据对模型训练很重要。我建议先标记出来,人工判断一下。
# IQR方法检测异常值
Q1 = df['close'].quantile(0.25)
Q3 = df['close'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 标记异常
df['is_outlier'] = (df['close'] < lower_bound) | (df['close'] > upper_bound)
print(f"发现 {df['is_outlier'].sum()} 个异常值")
3.5 本章知识体系
下面这张图,是我自己梳理的数据获取与清洗流程。你照着这个走,基本不会出大问题。
嗯,数据获取与清洗这块,说白了就是「垃圾进,垃圾出」的反面——你得确保进模型的数据是干净的、可靠的。我做了这么多年,最大的体会就是:花在数据清洗上的时间,永远值得。别急着跑模型,先把数据搞明白。
最后说一句:每次拿到新数据,我都会先画个分布图,看看有没有明显异常。肉眼扫一遍,比任何算法都管用。你试试看。