金融数据基础:股票/期货数据结构(OHLCV)、数据源获取与清洗对齐
做量化交易,第一关就是数据。
你策略再牛,模型再花哨,数据是脏的、错的、没对齐的,结果就是白忙活。我见过太多人,上来就调参、跑回测,最后发现是数据源出了问题——嗯,我自己也踩过这个坑。
OHLCV:最基础的数据结构
说白了,OHLCV就是四个价格加一个成交量:
- Open:开盘价
- High:最高价
- Low:最低价
- Close:收盘价
- Volume:成交量
为什么是这五个字段?因为它们是市场行为的浓缩。开盘价反映隔夜情绪,最高最低价告诉你波动范围,收盘价是当天多空博弈的最终结果,成交量则是参与度。
我个人习惯,拿到数据后第一件事就是画K线图。看一眼,心里就有数了——有没有跳空、有没有异常柱、成交量是不是突然放大。这些细节,回测时都会变成信号。
重要提醒:不同交易所的OHLCV定义略有差异。比如期货的收盘价,有些是最后三分钟加权平均,有些是最后一笔成交价。做跨市场策略时,一定要确认清楚。
数据源获取:Tushare vs Baostock
国内常用的免费数据源,我主要用两个:Tushare和Baostock。
| 特性 | Tushare | Baostock |
|---|---|---|
| 数据覆盖 | 股票、期货、基金、指数等 | 股票、指数、港股 |
| 更新频率 | 日频、分钟频 | 日频为主 |
| 获取方式 | API Token认证 | 无需注册 |
| 稳定性 | 较高,但免费版有调用限制 | 稳定,但数据量有限 |
我的建议是:做研究用Baostock,做生产用Tushare。为什么?Baostock不用注册,拿来就能跑,适合快速验证想法。Tushare数据更全,但需要申请Token,而且免费版每分钟只能调200次。
我曾经在项目里直接用Tushare拉五年期货数据,结果跑到一半被限流了。后来改成分批下载,每批间隔1秒,才搞定。这个坑,你们别踩。
代码示例:用Tushare获取日线数据
import tushare as ts
# 设置Token
ts.set_token('your_token_here')
pro = ts.pro_api()
# 获取平安银行日线数据
df = pro.daily(
ts_code='000001.SZ',
start_date='20230101',
end_date='20231231'
)
# 按日期排序
df = df.sort_values('trade_date')
print(df.head())
代码示例:用Baostock获取日线数据
import baostock as bs
import pandas as pd
# 登录
lg = bs.login()
# 获取数据
rs = bs.query_history_k_data_plus(
"sh.600000",
"date,open,high,low,close,volume",
start_date='2023-01-01',
end_date='2023-12-31',
frequency="d",
adjustflag="3" # 不复权
)
# 转为DataFrame
data_list = []
while (rs.error_code == '0') & rs.next():
data_list.append(rs.get_row_data())
df = pd.DataFrame(data_list, columns=rs.fields)
# 登出
bs.logout()
print(df.head())
小技巧:下载数据时,建议一次拉取全部区间,然后存到本地CSV或数据库。别每次都从API拉,一是慢,二是容易被封。
数据清洗与对齐
数据拿到手,别急着用。先洗一洗。
常见的脏数据问题:
- 缺失值:某天停牌,成交量是0,价格是NaN
- 异常值:价格突然跳空,或者成交量比前一天大100倍
- 重复数据:同一根K线被下载了两次
- 时间戳不对:日期格式不统一,或者时区问题
我一般按这个流程处理:
- 检查缺失值,用前向填充或插值法补全
- 剔除异常值,比如价格超过5个标准差
- 去重,保留最后一条记录
- 统一时间格式,全部转为datetime类型
代码示例:数据清洗
# 检查缺失值
print(df.isnull().sum())
# 前向填充
df['close'] = df['close'].fillna(method='ffill')
# 剔除异常值(以收盘价为例)
mean = df['close'].mean()
std = df['close'].std()
df = df[(df['close'] > mean - 5*std) & (df['close'] < mean + 5*std)]
# 去重
df = df.drop_duplicates(subset=['trade_date'])
# 统一日期格式
df['trade_date'] = pd.to_datetime(df['trade_date'])
注意:前向填充在停牌时有用,但如果连续停牌很多天,填充出来的数据会失真。这时候可以考虑用行业平均或者指数收益率来补。
数据对齐,是多资产策略的关键。你想想看,如果股票A和股票B的交易时间不同,或者一个用自然日一个用交易日,那计算出来的相关性就是错的。
我的做法是:先确定一个基准时间轴,比如用沪深300的交易日历,然后把所有资产的数据都对齐到这个时间轴上。缺失的交易日,用NaN填充,或者用前一天的收盘价。
时间序列基础
金融数据本质上是时间序列。做量化,你得懂几个基本概念:
- 平稳性:序列的均值和方差不随时间变化。大多数价格序列是非平稳的,但收益率序列通常是平稳的。
- 自相关性:今天的价格和昨天的价格有没有关系?如果有,说明存在趋势或均值回归。
- 滞后:用过去的数据预测未来,滞后阶数就是看过去多少天。
我个人习惯,拿到数据后先画自相关图。如果自相关衰减很慢,说明序列有强趋势,适合用动量策略。如果自相关快速衰减到零附近,那可能是均值回归策略的菜。
代码示例:计算收益率与自相关
# 计算对数收益率
df['log_return'] = np.log(df['close'] / df['close'].shift(1))
# 计算自相关
from statsmodels.tsa.stattools import acf
autocorr = acf(df['log_return'].dropna(), nlags=20)
print(autocorr[:5])
经验之谈:做回测时,一定要用收益率而不是价格。价格序列的非平稳性会导致伪回归,你以为是发现了规律,其实是数据在骗你。
知识体系总览
下面这张图,是我自己整理的数据处理流程。每次做新策略,我都会按这个步骤走一遍。
数据是量化交易的基石。你花80%的时间在数据处理上,一点都不夸张。剩下的20%,才是策略和模型。
好了,这一章就到这里。数据搞定了,后面的事情就顺了。