第二章 金融数据基础:股票、期货、加密货币数据获取,数据清洗与预处理,OHLCV数据结构,时间序列基础
各位同学,欢迎来到第二章。说实话,金融数据是量化交易的「原材料」。没有干净、可靠的数据,再牛的强化学习模型也是白搭。我自己刚入行那会儿,就吃过数据不干净的亏——模型在回测里跑得飞起,一上实盘就崩,后来发现是数据里有未来函数。嗯,这种坑,咱们今天就得避开。
2.1 数据来源:股票、期货、加密货币
做量化交易,第一步就是搞到数据。不同品种的数据源,差别还挺大。我个人的习惯是:先用免费数据做原型验证,跑通了再上付费数据。
2.1.1 股票数据
A股数据,我常用 tushare 和 akshare。这两个库都是Python的,免费版够用,但要注意频率限制。比如 tushare 的免费版,每分钟只能请求500次。
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')
print(df.head())
2.1.2 期货数据
期货数据比股票复杂一些。因为有主力合约切换,还有夜盘。我建议用 ccxt 或者 vnpy 的数据服务。期货的持仓量数据很重要,这个在强化学习里可以作为状态特征。
# 使用ccxt获取期货数据(示例为币安期货)
import ccxt
exchange = ccxt.binanceusdm()
symbol = 'BTC/USDT'
timeframe = '1d'
# 获取K线数据
ohlcv = exchange.fetch_ohlcv(symbol, timeframe, limit=100)
print(ohlcv[:3])
2.1.3 加密货币数据
加密货币数据最大的特点是:7x24小时交易,数据量巨大。我推荐 ccxt 库,它支持上百个交易所。不过要注意,不同交易所的数据格式可能略有差异。
# 获取比特币现货数据
import ccxt
exchange = ccxt.binance()
symbol = 'BTC/USDT'
timeframe = '1h'
# 获取最近100根K线
data = exchange.fetch_ohlcv(symbol, timeframe, limit=100)
# 返回格式:[[timestamp, open, high, low, close, volume], ...]
2.2 OHLCV数据结构
OHLCV,说白了就是:Open(开盘价)、High(最高价)、Low(最低价)、Close(收盘价)、Volume(成交量)。这是金融时间序列最基础的数据结构。你想想看,一根K线就包含了这四个价格和一个量,信息量其实挺大的。
| 字段 | 含义 | 在强化学习中的用途 |
|---|---|---|
| Open | 开盘价 | 计算日内涨跌幅 |
| High | 最高价 | 衡量波动范围 |
| Low | 最低价 | 衡量支撑/阻力 |
| Close | 收盘价 | 最常用的价格特征 |
| Volume | 成交量 | 判断市场活跃度 |
我个人习惯把OHLCV数据存成DataFrame格式,索引是时间戳。这样后续做时间序列分析特别方便。
import pandas as pd
# 假设从交易所拿到了原始数据
raw_data = [
[1700000000000, 50000, 51000, 49000, 50500, 1000],
[1700003600000, 50500, 51500, 50000, 51200, 1200],
]
# 转换成DataFrame
df = pd.DataFrame(raw_data, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
print(df)
2.3 数据清洗与预处理
数据清洗,是量化交易里最脏最累的活,但也是最关键的。我见过太多人,模型写得花里胡哨,结果数据里藏着未来函数,回测曲线漂亮得不像话——一上实盘就原形毕露。
2.3.1 常见的数据问题
- 缺失值: 比如某天停牌,没有交易数据
- 异常值: 价格突然跳空,或者成交量异常放大
- 未来函数: 用了未来才知道的数据(比如用当天的收盘价做当天的交易信号)
- 时间戳不对齐: 不同交易所的时间戳格式不同
2.3.2 清洗实战
# 处理缺失值:向前填充
df.fillna(method='ffill', inplace=True)
# 检测异常值:用Z-score方法
from scipy import stats
z_scores = stats.zscore(df['close'])
df['is_outlier'] = abs(z_scores) > 3
# 剔除异常值
df_clean = df[~df['is_outlier']]
# 检查是否有未来函数(比如用close计算当日的指标)
# 正确做法:用shift(1)取前一天的close
df['return'] = df['close'].pct_change() # 这是对的
# 错误做法:df['return'] = df['close'] / df['open'] - 1 # 这个没问题,但要注意时间点
df['close'].rolling(5).mean() 作为交易信号,结果发现这个均线包含了当天的收盘价。这就是典型的未来函数!正确做法是用 shift(1) 把数据往后挪一天。
2.4 时间序列基础
金融数据本质上就是时间序列。强化学习模型要处理的,就是这些随时间变化的价格和成交量。说白了,我们要教会模型「看懂」这些数字背后的规律。
2.4.1 时间序列的三大要素
- 趋势: 价格长期向上或向下
- 季节性: 比如A股有「春季躁动」效应
- 残差: 随机波动,也就是噪声
2.4.2 常用时间序列特征
# 计算收益率
df['return'] = df['close'].pct_change()
# 计算波动率(用滚动标准差)
df['volatility'] = df['return'].rolling(window=20).std()
# 计算移动平均线
df['ma5'] = df['close'].rolling(window=5).mean()
df['ma20'] = df['close'].rolling(window=20).mean()
# 计算相对强弱指标(RSI)
def compute_rsi(data, window=14):
delta = data.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
df['rsi'] = compute_rsi(df['close'])
2.4.3 时间序列的平稳性
做量化交易,尤其是用强化学习,我建议先检查数据是否平稳。平稳的时间序列,模型学起来更容易。常用的检验方法是ADF检验。
from statsmodels.tsa.stattools import adfuller
# ADF检验
result = adfuller(df['close'].dropna())
print(f'ADF Statistic: {result[0]}')
print(f'p-value: {result[1]}')
# 如果p-value > 0.05,说明数据非平稳,需要差分
if result[1] > 0.05:
df['close_diff'] = df['close'].diff().dropna()
print('数据非平稳,已做一阶差分')
2.5 本章知识体系
下面这张图,是我自己梳理的本章知识结构。你可以把它当成一张地图,随时回来查阅。
好了,这一章的内容就到这里。数据这块儿,看起来简单,但真正做好需要大量实践。我建议你动手跑一遍上面的代码,把数据下载下来,看看清洗前后的差别。下一章我们会把这些数据喂给强化学习模型,到时候你就知道数据预处理有多重要了。