第二章:数据准备——外汇数据源选择与清洗
做回测这么多年,我踩过最大的坑,就是数据问题。你策略再牛,数据是垃圾,结果就是垃圾。说白了,回测的起点就是数据准备。这一章,我带你搞定外汇数据源的选择、清洗和对齐。
2.1 外汇数据源怎么选?
外汇市场没有统一的交易所,数据分散在各个流动性提供商手里。我个人习惯用下面几个数据源,各有优劣。
| 数据源 | 数据类型 | 优点 | 缺点 |
|---|---|---|---|
| Dukascopy | Tick、1分钟、5分钟等 | 免费、历史数据长(2003年起)、Tick级精度 | 偶尔有跳空、需自行清洗 |
| TrueFX | Tick、1分钟 | 机构级数据、质量高 | 需注册、部分数据收费 |
| HistData.com | 各种周期 | 整理好的CSV、直接可用 | 付费、更新频率一般 |
| OANDA | 1分钟、日线 | API接口方便、实时数据 | 历史数据有限 |
我的建议:新手先用Dukascopy的免费数据练手。等策略成熟了,再考虑TrueFX或付费数据。我在项目中遇到过,用免费数据回测赚了30%,换到机构数据直接变亏——数据质量差异就这么大。
2.2 数据清洗——别让脏数据毁了你的策略
数据拿到手,第一件事不是跑策略,而是清洗。你想想看,外汇数据里常见的坑有哪些?
2.2.1 去除重复数据
同一个时间戳出现两次,这在Dukascopy的数据里偶尔会发生。我一般这样处理:
import pandas as pd
# 假设df是原始数据,包含'DateTime', 'Open', 'High', 'Low', 'Close', 'Volume'
df = df.drop_duplicates(subset='DateTime', keep='first')
df = df.sort_values('DateTime').reset_index(drop=True)
2.2.2 处理异常价格
有时候会出现价格瞬间暴涨暴跌,比如EUR/USD突然跳到1.5000,明显是数据错误。我的经验是:用3倍标准差过滤。
# 计算价格变化率
df['return'] = df['Close'].pct_change()
# 过滤掉超过3倍标准差的异常点
mean = df['return'].mean()
std = df['return'].std()
df = df[(df['return'] > mean - 3*std) & (df['return'] < mean + 3*std)]
注意:别把正常的市场波动当异常过滤了。比如2015年瑞郎黑天鹅事件,那是真实的市场行为,不是数据错误。我曾经因为过滤太狠,把那次大行情给洗掉了,回测结果完全失真。
2.3 数据对齐——不同时间框架的整合
做多品种回测时,数据对齐是个大问题。比如你要同时交易EUR/USD和GBP/JPY,它们的Tick数据时间戳可能不完全对齐。怎么办?
我的做法是:统一用1分钟K线作为基准。把所有数据都重采样到1分钟,然后做inner join。
# 重采样到1分钟
df_1min = df.resample('1T', on='DateTime').agg({
'Open': 'first',
'High': 'max',
'Low': 'min',
'Close': 'last',
'Volume': 'sum'
}).dropna()
# 多品种对齐
merged = pd.merge(df_eur_1min, df_gbp_1min, on='DateTime', suffixes=('_eur', '_gbp'))
2.4 处理跳空与缺失值
外汇市场周末休市,周一开盘经常有跳空。还有节假日、数据源故障,都会导致缺失。嗯,这里要注意,不同策略对跳空的敏感度不一样。
2.4.1 跳空处理
我个人习惯分情况处理:
- 趋势跟踪策略:保留跳空,因为跳空本身就是市场信息
- 均值回归策略:建议填充跳空,否则容易触发假信号
- 高频策略:必须用Tick数据,跳空区域直接跳过
# 检测跳空(价格变化超过正常范围)
df['gap'] = abs(df['Open'] - df['Close'].shift(1)) / df['Close'].shift(1)
# 标记跳空点
df['is_gap'] = df['gap'] > 0.005 # 0.5%的跳空阈值
2.4.2 缺失值填充
缺失值处理,我推荐三种方法:
| 方法 | 适用场景 | 代码示例 |
|---|---|---|
| 前向填充 | 短期缺失(几分钟) | df.fillna(method='ffill') |
| 线性插值 | 中期缺失(几小时) | df.interpolate(method='linear') |
| 删除 | 长期缺失(超过一天) | df.dropna() |
核心原则:缺失值处理没有银弹。我的经验是——先可视化看看缺失模式,再决定用哪种方法。别一上来就全删,有时候缺失本身就是一个信号。
2.5 知识体系总览
下面这张图,是我做数据准备时的完整流程。你可以把它当作一个检查清单。
避坑指南:我曾经因为偷懒,没做数据对齐,直接拿不同时间戳的数据跑回测。结果策略信号频繁触发,实际交易时根本执行不了。后来花了整整两天重新清洗数据,才找到问题。所以,数据准备这一步,千万别省。
好了,数据准备好了。下一步就是把这些数据喂给回测引擎。记住一句话:垃圾进,垃圾出。数据质量决定了你回测结果的可信度。