第四章 收益率计算与可视化
收益率计算,是金融数据分析的起点。说实话,我见过不少新手一上来就搞复杂模型,结果连收益率算错了都不知道。嗯,今天我们就从最基础的两个概念讲起:简单收益率和对数收益率。
4.1 简单收益率 vs 对数收益率
简单收益率,就是大家平时说的涨跌幅。公式很简单:
R_simple = (P_t - P_{t-1}) / P_{t-1}
对数收益率呢?稍微绕一点:
R_log = ln(P_t / P_{t-1})
你可能会问:为什么要搞个对数收益率?我刚开始做量化的时候也觉得多此一举。直到有一次,我回测一个策略,用简单收益率算累计收益,结果发现时间序列越长,偏差越大。后来才明白——对数收益率有个很好的性质:可加性。
核心区别:
- 简单收益率:直观,但多期累加需要连乘
- 对数收益率:可加,多期累加直接求和
- 对数收益率近似服从正态分布(尤其在高频数据中)
举个例子。假设某股票连续两天涨停:第一天从100涨到110,第二天从110涨到121。简单收益率:第一天10%,第二天10%,累计是(1+10%)×(1+10%)-1=21%。对数收益率:第一天ln(110/100)=9.53%,第二天ln(121/110)=9.53%,累计就是19.06%。你看,对数收益率直接相加就行。
我的习惯:做短期分析(日频以内)用对数收益率,做长期投资分析用简单收益率。回测策略时,我一般用对数收益率计算,最后再转回简单收益率展示给客户看。
4.2 收益率分布直方图
算完收益率,第一件事就是画直方图。为什么?因为金融数据有个著名的「肥尾」特征——极端值出现的概率比正态分布预测的要高。
我曾经帮一家私募做风控模型,他们一直用正态分布假设算VaR。结果呢?2015年股灾那几天,实际亏损是模型预测的3倍多。后来我给他们画了收益率直方图,一看就明白了——尾部明显比正态分布厚。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# 假设我们有收盘价数据
prices = pd.read_csv('stock_prices.csv', index_col=0, parse_dates=True)
returns = np.log(prices / prices.shift(1)).dropna()
# 画直方图
plt.figure(figsize=(10, 6))
sns.histplot(returns, bins=50, kde=True, stat='density')
plt.title('收益率分布直方图')
plt.xlabel('对数收益率')
plt.ylabel('密度')
plt.show()
画出来之后,你会看到中间高、两边低,但两边的尾巴比正态分布要「胖」一些。这就是金融数据的典型特征。
我曾经踩过的坑:直方图的bins数量很关键。bins太少,细节丢失;bins太多,噪声太大。我一般用50-100个bins,具体看数据量。数据量大的话,100个bins比较稳妥。
4.3 Q-Q图:检验正态性
直方图只能看个大概,要严格判断收益率是否服从正态分布,得用Q-Q图。
Q-Q图的思想很简单:把数据的分位数和理论正态分布的分位数画在一起。如果数据是正态的,点应该落在45度线上。
import scipy.stats as stats
plt.figure(figsize=(8, 8))
stats.probplot(returns.values.flatten(), dist='norm', plot=plt)
plt.title('收益率Q-Q图')
plt.grid(True)
plt.show()
画出来之后,你会发现两端明显偏离直线。中间部分还好,但两头的点要么在上方(右尾更厚),要么在下方(左尾更厚)。这就是肥尾的直观证据。
解读Q-Q图的要点:
- 点落在直线上 → 数据服从正态分布
- 两端向上弯曲 → 右尾更厚(正收益极端值多)
- 两端向下弯曲 → 左尾更厚(负收益极端值多)
- S形弯曲 → 数据有偏
我记得有一次给客户做报告,对方坚持说他们的策略收益是正态的。我当场画了Q-Q图,两端的点明显偏离。客户沉默了。后来他们改用了t分布来建模,风险度量准确多了。
4.4 时间序列折线图
收益率的时间序列图,说白了就是把每天的收益率按时间顺序连起来。这个图能告诉你:波动有没有聚集性?有没有趋势?有没有异常点?
plt.figure(figsize=(14, 6))
plt.plot(returns.index, returns.values, linewidth=0.8, alpha=0.7)
plt.title('收益率时间序列')
plt.xlabel('时间')
plt.ylabel('对数收益率')
plt.axhline(y=0, color='r', linestyle='--', alpha=0.5)
plt.grid(True, alpha=0.3)
plt.show()
画出来之后,你会看到几个典型特征:
- 波动聚集性:大波动后面跟着大波动,小波动后面跟着小波动。这就是GARCH模型要捕捉的东西。
- 尖峰:偶尔出现巨大的正收益或负收益,比如财报发布、政策变动。
- 均值回归:大部分时间收益率在0附近波动,很少持续偏离。
我的经验:时间序列图一定要把y=0这条线画出来。不然你很难判断哪些是正收益、哪些是负收益。另外,我习惯把异常值用红圈标出来,方便后续排查原因。
4.5 综合案例:完整的收益率分析流程
下面我把上面所有步骤整合成一个完整的代码示例。这个流程我在实际项目中用了无数次,算是我的「标准作业流程」。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as stats
# 1. 加载数据
prices = pd.read_csv('stock_prices.csv', index_col=0, parse_dates=True)
close = prices['Close']
# 2. 计算收益率
simple_returns = close.pct_change().dropna()
log_returns = np.log(close / close.shift(1)).dropna()
# 3. 创建画布
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# 4. 时间序列图
axes[0, 0].plot(log_returns.index, log_returns.values, linewidth=0.8, alpha=0.7)
axes[0, 0].axhline(y=0, color='r', linestyle='--', alpha=0.5)
axes[0, 0].set_title('对数收益率时间序列')
axes[0, 0].grid(True, alpha=0.3)
# 5. 直方图
sns.histplot(log_returns, bins=60, kde=True, ax=axes[0, 1], stat='density')
axes[0, 1].set_title('收益率分布直方图')
# 6. Q-Q图
stats.probplot(log_returns.values.flatten(), dist='norm', plot=axes[1, 0])
axes[1, 0].set_title('Q-Q图')
axes[1, 0].grid(True, alpha=0.3)
# 7. 简单收益率 vs 对数收益率对比
axes[1, 1].scatter(simple_returns, log_returns, alpha=0.5, s=5)
axes[1, 1].plot([-0.1, 0.1], [-0.1, 0.1], 'r--', alpha=0.5)
axes[1, 1].set_xlabel('简单收益率')
axes[1, 1].set_ylabel('对数收益率')
axes[1, 1].set_title('简单收益率 vs 对数收益率')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 8. 输出统计量
print(f'均值: {log_returns.mean():.6f}')
print(f'标准差: {log_returns.std():.6f}')
print(f'偏度: {log_returns.skew():.4f}')
print(f'峰度: {log_returns.kurtosis():.4f}')
这个代码跑完之后,你会得到四张图和一组统计量。偏度看是否对称,峰度看肥尾程度。正态分布的峰度是3,金融数据通常大于3。
核心结论:
- 金融收益率不服从正态分布,有肥尾特征
- 对数收益率比简单收益率更适合建模
- 波动存在聚集性,需要GARCH类模型
- 可视化是发现问题的第一步,永远不要跳过
4.6 本章知识体系
下面这张图是我自己整理的收益率分析框架,你可以把它当作一个检查清单:
这张图把整个流程串起来了。从左到右:先算收益率,再可视化,最后得出结论。每一步都有对应的工具和检验方法。
最后说一句:可视化不是花架子,它是你理解数据的第一道防线。我见过太多人直接跑模型,结果数据有错都不知道。先画图,再建模,这个顺序别搞反了。
公众号:蓝海资料掘金营,微信deep3321