量化数据基础:金融时间序列数据的特点与读取方法

各位同学,欢迎来到第二章。上一章我们聊了强化学习在量化交易中的整体框架,今天咱们把镜头拉近,聚焦在数据本身。

说实话,我见过太多人一上来就调模型、写策略,结果数据预处理没做好,模型训练出来就是个“垃圾进垃圾出”的悲剧。我自己早期也踩过这个坑——有一次跑了一个月的强化学习训练,回测曲线漂亮得不行,结果上线第一天就亏了。后来排查才发现,数据里有未来函数,我根本没注意到时间戳对齐的问题。

所以这一章,咱们把地基打牢。金融时间序列数据到底有什么“脾气”?怎么拿到靠谱的数据?怎么读进来?这些搞清楚了,后面的模型训练才能站得住脚。

金融时间序列数据的三大“怪脾气”

金融数据跟普通的时间序列数据不一样。它有三个显著特点,我称之为“三大怪脾气”。

1. 非平稳性

什么叫非平稳?说白了,就是数据的统计性质会随时间变化。均值、方差、协方差,这些都不是固定的。

你想想看,2015年股灾时的波动率和2020年疫情时的波动率,能一样吗?肯定不一样。股票价格本身更是典型的非平稳序列——它没有固定的均值,长期来看是向上走的。

我在项目中遇到过一个问题:直接用原始价格序列训练强化学习模型,结果模型学到的全是“价格涨了就买,跌了就卖”这种简单逻辑,根本捕捉不到真正的市场规律。后来改用收益率序列,效果才正常。

核心要点: 强化学习模型通常需要平稳或弱平稳的数据输入。常见的处理方式是对价格取对数收益率或差分。

2. 自相关性

今天的价格跟昨天的价格有没有关系?当然有。这就是自相关——当前时刻的值与过去时刻的值之间存在相关性。

但要注意,金融数据的自相关通常很弱,尤其是高频数据。我做过一个实验,用5分钟级别的BTC价格数据算自相关系数,滞后1阶的系数大概在0.1左右,再往后基本就接近0了。这说明什么?说明市场在短时间内是相对有效的,你很难用过去的价格简单预测未来。

不过,波动率(比如收益率平方)的自相关却很强。这就是所谓的“波动率聚集”现象——大的波动后面往往跟着大的波动,小的波动后面跟着小的波动。这个特性对强化学习来说很重要,因为它会影响风险控制模块的设计。

避坑指南: 我曾经在特征工程里直接用了原始价格作为状态输入,结果模型学到了“价格高就做空”这种错误逻辑。后来我改用收益率、波动率、成交量等衍生特征,效果才改善。记住:价格本身不是好特征,价格的变化才是。

3. 异方差性

这个词听起来有点吓人,其实意思很简单:数据的波动率不是恒定的。有时候市场风平浪静,有时候波涛汹涌。

为什么会这样?因为市场受新闻、政策、突发事件的影响。比如美联储加息消息一出,整个市场的波动率瞬间飙升。这种波动率的变化,就是异方差。

在强化学习中,异方差性会直接影响奖励函数的设计。如果你的奖励函数只考虑收益,不考虑波动,那模型可能会在波动大的时候做出极端决策。我个人习惯在奖励函数中加入波动率调整项,让模型学会在波动大的时候更谨慎。

特点 含义 对强化学习的影响
非平稳性 统计性质随时间变化 需要差分或对数变换,否则模型学不到稳定规律
自相关性 当前值与过去值相关 特征工程中需考虑滞后项,但注意不要过拟合
异方差性 波动率不恒定 奖励函数需考虑风险调整,避免极端决策

常见数据源与读取方法

数据源这块,我按使用频率排个序。新手建议从Yahoo Finance开始,做加密货币的用Binance,做回测的用本地CSV文件。

1. Yahoo Finance

Yahoo Finance 是入门首选。免费、覆盖广、历史数据长。我用它来获取美股和A股的数据。

读取方法很简单,用 yfinance 库,几行代码搞定。

import yfinance as yf

# 下载苹果股票数据
aapl = yf.download('AAPL', start='2020-01-01', end='2023-12-31')
print(aapl.head())

嗯,这里要注意一点:yfinance 返回的是 pandas DataFrame,索引是日期,列包括 Open、High、Low、Close、Volume、Adj Close。我个人习惯用 Adjusted Close(复权收盘价),因为它考虑了分红和拆股,更真实反映价格变化。

注意: Yahoo Finance 的数据有时会有缺失值,尤其是节假日。我建议在读取后检查一下是否有 NaN,然后用前向填充或插值处理。我曾经因为没处理缺失值,导致强化学习训练时 reward 计算出现 NaN,整个训练崩溃了。

2. Binance

做加密货币的,Binance 是首选。它的 API 很完善,支持现货、合约、期权等多种数据。

读取方法可以用 python-binance 库,或者直接通过 REST API 获取。

from binance.client import Client
import pandas as pd

client = Client()
# 获取BTC/USDT的1小时K线数据
klines = client.get_klines(symbol='BTCUSDT', interval=Client.KLINE_INTERVAL_1HOUR, limit=1000)

# 解析数据
data = []
for k in klines:
    data.append({
        'timestamp': k[0],
        'open': float(k[1]),
        'high': float(k[2]),
        'low': float(k[3]),
        'close': float(k[4]),
        'volume': float(k[5])
    })

df = pd.DataFrame(data)
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
df.set_index('timestamp', inplace=True)
print(df.head())

Binance 的数据频率很灵活,从1分钟到1个月都有。我个人做高频策略时常用1分钟和5分钟数据,做趋势策略时用4小时和日线数据。

经验分享: Binance 的 API 有频率限制,每分钟最多1200次请求。如果你要下载大量历史数据,建议用异步请求或者分批次下载。我一般一次下载1000根K线,然后循环请求,中间加个0.1秒的延迟,这样不会触发限流。

3. CSV文件

本地CSV文件是最灵活的方式。很多量化平台(比如聚宽、米筐)都支持导出CSV格式的数据。我自己做回测时,也习惯把数据存成CSV,方便反复使用。

读取方法用 pandas.read_csv(),注意设置日期列为索引。

import pandas as pd

# 读取本地CSV文件
df = pd.read_csv('btc_usdt_1h.csv', parse_dates=['timestamp'], index_col='timestamp')
print(df.info())

这里有个坑:CSV文件里的日期格式可能不统一。有的用 '2023-01-01',有的用 '2023/01/01',还有的用时间戳。我建议在读取时统一用 parse_dates 参数,或者读取后用 pd.to_datetime() 转换。

数据预处理清单:
  • 检查缺失值:df.isnull().sum()
  • 处理异常值:比如价格出现负数或极端值
  • 对齐时间戳:确保数据按时间顺序排列
  • 计算衍生特征:收益率、波动率、成交量变化等

本章知识体系

下面这张图总结了本章的核心内容,方便你快速回顾。

金融时间序列数据预处理知识体系 量化数据预处理 非平稳性 自相关性 异方差性 均值/方差随时间变化 处理:对数收益率/差分 当前值与过去值相关 特征:滞后项/波动率聚集 波动率不恒定 奖励函数需风险调整 常见数据源 Yahoo Finance (yfinance) Binance (python-binance) CSV文件 (pandas)

这张图把本章的知识点串起来了。左边是数据的三大特点,右边是数据源。你学习的时候可以对照着看,理解每个特点如何影响数据源的选择和处理方式。

好了,这一章的内容就到这里。数据预处理是强化学习量化交易中最枯燥但最重要的一环。别嫌麻烦,把这一步做扎实了,后面的模型训练会顺畅很多。

公众号:蓝海数据掘金营,微信deep3321