第三章 季节性数据获取:国内外数据源介绍与数据库构建
做跨品种套利,数据是命根子。
这话我常说,也常被新人当成废话。直到有一次,我花了整整两周搭建的套利模型,回测曲线漂亮得像教科书,结果一上实盘就崩了。查来查去,问题出在数据上——两个品种的结算价时间戳差了15分钟,我根本没发现。
嗯,从那以后,我对数据源的选择和清洗,再也不敢马虎了。
3.1 国内外主流数据源:各有各的脾气
先说说我这些年用过的数据源。每个都有它的长处,也都有坑。
3.1.1 Wind(万得)——国内量化标配
说实话,Wind在国内金融数据这块,地位确实稳。我个人习惯把它当作主力数据源,尤其是做商品期货跨品种套利时,Wind的日频数据质量相当不错。
- 覆盖范围:国内四大期货交易所(上期所、郑商所、大商所、中金所)全品种,包括主力合约、连续合约、指数合约
- 数据频率:Tick、分钟、日、周、月,基本够用
- 获取方式:Wind API(Python/C++/Excel),我常用WindPy库
- 坑在哪:节假日数据对齐有时会出问题。比如春节前后,国内休市但外盘还在交易,Wind的连续合约可能会跳空。我踩过这个坑,后来加了手动校验逻辑。
3.1.2 Bloomberg——国际品种的权威来源
做跨品种套利,尤其是涉及内外盘联动(比如沪铜与LME铜、豆粕与CBOT大豆),Bloomberg几乎是绕不开的。
- 优势:数据历史长、质量高、字段全。我记得有一次需要回溯2005年的LME铜库存数据,Bloomberg轻松搞定
- 获取方式:Bloomberg Terminal + API(blpapi Python库)
- 注意:Bloomberg的合约代码规则比较特殊,比如"CO1 Comdty"代表铜连续合约。刚开始用的时候,我经常搞混代码格式,后来干脆写了个映射表。
3.1.3 交易所官网——免费但费手
说实话,交易所官网的数据是免费的,但获取起来确实麻烦。我一般只在两种情况下用:一是验证商业数据源的准确性,二是做小规模研究不想花钱。
| 交易所 | 官网地址 | 可用数据 | 获取方式 |
|---|---|---|---|
| 上期所 | www.shfe.com.cn | 日行情、持仓、仓单 | 手动下载/爬虫 |
| 郑商所 | www.czce.com.cn | 日行情、仓单、交割数据 | 手动下载/爬虫 |
| 大商所 | www.dce.com.cn | 日行情、持仓、交割 | 手动下载/爬虫 |
| 中金所 | www.cffex.com.cn | 股指期货、国债期货 | 手动下载/爬虫 |
3.2 数据清洗与对齐:90%的时间花在这
你想想看,数据源拿回来,直接就能用吗?
太天真了。我做过统计,一个完整的跨品种套利项目,数据清洗和对齐至少占掉90%的时间。剩下的10%才是建模和回测。
3.2.1 常见的数据脏问题
- 缺失值:节假日、交易所故障、数据源漏发。我遇到过某天LME铜的收盘价全部为0,原因是Bloomberg那天的数据推送出了问题
- 异常值:价格跳空、成交量突变。比如某天螺纹钢的成交量突然放大10倍,后来发现是数据源把"手"和"吨"搞混了
- 时间戳不一致:国内夜盘是21:00-23:00(或次日02:30),LME是北京时间16:00-次日01:00。两个品种的收盘时间不同,直接对齐会出问题
- 合约换月:主力合约切换时,价格会有跳空。如果不处理,套利信号会失真
3.2.2 数据对齐的核心逻辑
说白了,对齐就两件事:时间对齐和价格对齐。
时间对齐:我一般用"最近可用价格"原则。比如国内夜盘收盘是23:00,LME还在交易,那就用LME 23:00的实时价。如果LME在23:00没有交易(比如休市),就用前一个有效价格。
价格对齐:跨品种套利通常用"指数合约"或"连续合约"。我个人习惯用指数合约,因为它平滑了换月跳空。但要注意,指数合约是加权计算的,不同数据源的权重算法可能不同。
3.3 构建季节性数据库:从数据到资产
数据清洗完了,怎么存?怎么用?
我建议建一个专门的季节性数据库。不是随便扔几个CSV文件就完事,而是要有结构、有索引、可复用的。
3.3.1 数据库结构设计
我常用的结构是这样的:
数据库:seasonal_arbitrage_db
├── 表1:daily_price(日行情)
│ ├── date(日期)
│ ├── symbol(品种代码)
│ ├── open(开盘价)
│ ├── high(最高价)
│ ├── low(最低价)
│ ├── close(收盘价)
│ ├── volume(成交量)
│ └── open_interest(持仓量)
├── 表2:seasonal_stats(季节性统计)
│ ├── symbol(品种代码)
│ ├── month(月份)
│ ├── avg_return(平均收益率)
│ ├── std_return(收益率标准差)
│ ├── win_rate(胜率)
│ └── sample_count(样本数)
└── 表3:pair_metrics(套利对指标)
├── pair_id(套利对ID)
├── date(日期)
├── spread(价差)
├── zscore(Z分数)
└── signal(信号:1做多/ -1做空/ 0观望)
3.3.2 数据入库流程
我一般按这个流程走:
- 数据拉取:每天定时从Wind/Bloomberg拉取增量数据
- 清洗校验:检查缺失值、异常值,标记可疑数据
- 对齐处理:按品种、按时间对齐,生成统一格式的日行情表
- 季节性计算:按月统计收益率、胜率等指标,写入seasonal_stats表
- 套利对计算:计算价差、Z分数,生成交易信号
3.3.3 一个简单的数据清洗示例
用Python写个简单的清洗函数,你感受一下:
import pandas as pd
import numpy as np
def clean_price_data(df):
"""
清洗日行情数据
df: 包含date, symbol, close, volume的DataFrame
"""
# 1. 删除缺失值超过50%的列
df = df.dropna(thresh=len(df)*0.5, axis=1)
# 2. 填充少量缺失值(用前一个交易日数据)
df['close'] = df['close'].fillna(method='ffill')
# 3. 标记异常值(价格变动超过5个标准差)
df['return'] = df['close'].pct_change()
mean_ret = df['return'].mean()
std_ret = df['return'].std()
df['outlier'] = np.abs(df['return'] - mean_ret) > 5 * std_ret
# 4. 删除异常值(或者标记后人工审核)
df_clean = df[~df['outlier']].copy()
return df_clean
3.4 本章知识体系总览
说了这么多,我画个图帮你理一理思路。
这张图把整个流程串起来了。从数据源获取,到清洗对齐,再到数据库构建,最后输出套利信号。每一步都有坑,但每一步也都有方法。
公众号:蓝海资料掘金营,微信deep3321