金融时间序列基础:股票价格与收益率、对数收益率计算、数据可视化与平稳性检验

各位同学,欢迎来到第二章。

上一章我们聊了强化学习的基本框架。从这章开始,我们要真正接触金融数据了。你想想看,量化交易的核心是什么?是数据。没有扎实的数据处理功底,再厉害的强化学习模型也是空中楼阁。

我个人习惯,做任何量化策略之前,先花 70% 的时间在数据清洗和特征工程上。这章的内容,就是为后续的强化学习模型打地基。地基稳不稳,直接决定了你策略的上限。

2.1 股票价格数据:原始素材长什么样?

我们先看看最原始的股票价格数据。通常我们从数据源拿到的,是 OHLCV 数据:Open(开盘价)、High(最高价)、Low(最低价)、Close(收盘价)、Volume(成交量)。

这里有个细节,我刚开始做量化时踩过坑——复权问题。股票会分红、送股,导致价格出现跳空。如果你直接用原始价格做回测,结果会失真。我建议统一使用后复权价格,这样能真实反映持有期的收益。

核心要点:价格本身是非平稳的,不能直接用于建模。我们真正需要的是收益率。

2.2 收益率计算:简单收益率 vs 对数收益率

为什么要算收益率?因为价格有趋势,收益率相对稳定。我们来看两种最常见的计算方式。

2.2.1 简单收益率

公式很简单:

R_t = (P_t - P_{t-1}) / P_{t-1}

这表示你今天相对于昨天赚了或亏了多少百分比。直观,好理解。但有个问题——它不对称。举个例子:先涨 50% 再跌 50%,你亏了 25%,而不是回到原点。

2.2.2 对数收益率

我个人更偏爱对数收益率:

r_t = ln(P_t / P_{t-1})

为什么?因为对数收益率具有可加性。多期的对数收益率可以直接相加得到总收益率。这在金融建模中非常方便。而且,对数收益率的分布更接近正态分布,很多统计模型都基于这个假设。

我的经验:在强化学习的状态空间中,我几乎只用对数收益率。它让模型更容易学习到稳定的模式。

2.3 实战代码:从价格到收益率

光说不练假把式。我们直接上代码。假设你已经有了一个包含收盘价的 DataFrame。

import pandas as pd
import numpy as np

# 假设 df 包含 'Close' 列
df['simple_return'] = df['Close'].pct_change()
df['log_return'] = np.log(df['Close'] / df['Close'].shift(1))

# 去掉第一行的 NaN
df = df.dropna()

print(df[['Close', 'simple_return', 'log_return']].head())

嗯,这里要注意:pct_change() 默认计算的是简单收益率。对数收益率需要手动用 np.log 计算。我曾经在项目里直接拿简单收益率当对数收益率用,结果模型训练出来一塌糊涂。后来排查了半天才发现是这里的问题。

2.4 数据可视化:用眼睛看趋势

数据算出来了,我们得看看长什么样。可视化是量化分析的第一步,也是最重要的一步。我习惯先画价格走势,再看收益率分布。

import matplotlib.pyplot as plt

fig, axes = plt.subplots(3, 1, figsize=(12, 10))

# 价格走势
axes[0].plot(df['Close'], color='blue')
axes[0].set_title('股票收盘价走势')
axes[0].set_ylabel('价格')

# 简单收益率
axes[1].plot(df['simple_return'], color='green', alpha=0.6)
axes[1].set_title('简单收益率')
axes[1].set_ylabel('收益率')

# 对数收益率
axes[2].plot(df['log_return'], color='red', alpha=0.6)
axes[2].set_title('对数收益率')
axes[2].set_ylabel('对数收益率')

plt.tight_layout()
plt.show()

你仔细看,价格图有明显的趋势,而收益率图看起来像「白噪声」——围绕 0 上下波动。这就是我们想要的。收益率序列相对平稳,适合作为强化学习模型的输入。

避坑指南:我曾经直接用价格序列训练模型,结果模型学会了「一直做多」这种无意义的策略。因为价格有上涨趋势,模型发现做多就能赚钱,完全忽略了风险。改用收益率后,模型才开始真正学习买卖时机。

2.5 平稳性检验:你的数据合格吗?

为什么强调平稳性?因为大多数统计模型和机器学习模型都假设数据是平稳的。平稳性意味着数据的统计性质(均值、方差)不随时间变化。

检验平稳性最常用的方法是 ADF 检验(Augmented Dickey-Fuller test)。

from statsmodels.tsa.stattools import adfuller

# 对收盘价做 ADF 检验
result_price = adfuller(df['Close'])
print(f'价格序列 ADF 统计量: {result_price[0]:.4f}')
print(f'p-value: {result_price[1]:.4f}')

# 对对数收益率做 ADF 检验
result_return = adfuller(df['log_return'])
print(f'对数收益率 ADF 统计量: {result_return[0]:.4f}')
print(f'p-value: {result_return[1]:.4f}')

结果通常是这样:

序列 ADF 统计量 p-value 是否平稳
收盘价 -1.23 0.65 否(p > 0.05)
对数收益率 -15.67 0.00 是(p < 0.05)

你看,价格序列的 p-value 很大,说明不平稳。而对数收益率的 p-value 接近 0,说明序列平稳。这就是为什么我们一定要用收益率。

小技巧:如果 ADF 检验显示不平稳,可以试试一阶差分。但说实话,在金融时间序列里,对数收益率已经足够好了。我很少再做额外的差分处理。

2.6 本章知识体系

为了让你更直观地理解本章的知识结构,我画了一张流程图。

金融时间序列处理流程 原始 OHLCV 数据 后复权处理 计算对数收益率(推荐) ADF 检验确认平稳性

这张图清晰地展示了从原始数据到可用特征的完整流程。每一步都不可或缺。跳过任何一步,你的模型都可能学到错误的东西。

2.7 本章小结

好了,这一章的内容就到这里。我们讲了价格与收益率的区别,学会了计算对数收益率,用可视化验证了数据的特性,最后用 ADF 检验确认了平稳性。

这些基础操作,在后续的强化学习建模中会反复用到。你想想看,如果连输入数据都是错的,模型怎么可能做出正确的交易决策?

下一章,我们会把这些处理好的数据,真正用到强化学习的环境构建中。到时候你会发现,今天打的地基有多重要。

核心记忆点:永远用对数收益率,永远做平稳性检验。这是量化交易的铁律。


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