4. 收益率计算与可视化:日收益率、对数收益率,以及分布图与时间序列图
各位同学,咱们今天聊点实在的。收益率计算,说白了就是量化投资的“体温计”。你策略好不好,赚没赚钱,全看它。我个人习惯,拿到数据第一件事,不是跑模型,而是先把收益率算清楚,画出来看看。这一步做扎实了,后面心里才有底。
4.1 为什么要算两种收益率?
你可能要问,算个涨跌百分比不就行了?为什么还要搞个“对数收益率”?
嗯,这里有个坑。我刚开始做量化的时候,也只用简单收益率。后来发现,做时间序列分析、算波动率的时候,简单收益率会带来偏差。举个例子:
- 简单收益率(日收益率):直观,就是今天的收盘价相对于昨天的涨跌百分比。但它有个毛病——不可加。你今天赚10%,明天亏10%,总收益不是0,而是-1%。
- 对数收益率:把价格取个对数再差分。它天然具有“可加性”。你今天对数收益率是0.1,明天是-0.1,加起来就是0。这在做多期复利计算时,非常方便。
说白了,简单收益率适合“看”,对数收益率适合“算”。
核心公式:
- 简单日收益率:R_t = (P_t - P_{t-1}) / P_{t-1}
- 对数日收益率:r_t = ln(P_t / P_{t-1})
4.2 实战:用Python计算收益率
咱们直接上代码。我用的是yfinance拉取纳斯达克100指数(QQQ)的数据。你想想看,如果连数据都拿不到,后面全是空谈。
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 下载QQQ数据
qqq = yf.download('QQQ', start='2020-01-01', end='2023-12-31')
prices = qqq['Adj Close']
# 计算简单日收益率
simple_returns = prices.pct_change().dropna()
# 计算对数日收益率
log_returns = np.log(prices / prices.shift(1)).dropna()
# 看一眼前5行
print("简单收益率:")
print(simple_returns.head())
print("\n对数收益率:")
print(log_returns.head())
这里有个细节:pct_change() 默认会返回第一个NaN值,记得用 dropna() 去掉。我曾经因为忘了这一步,导致后面画图时多了一个异常点,排查了半天。
4.3 可视化:一图胜千言
算完收益率,别急着分析。先画图。我个人经验,图形能帮你快速发现数据中的异常、趋势和分布特征。
4.3.1 收益率时间序列图
时间序列图,说白了就是看收益率随时间怎么波动。我建议把简单收益率和对数收益率叠在一起画,看看它们有多接近。
plt.figure(figsize=(12, 6))
plt.plot(simple_returns.index, simple_returns, label='简单收益率', alpha=0.7)
plt.plot(log_returns.index, log_returns, label='对数收益率', alpha=0.7)
plt.title('QQQ 日收益率时间序列 (2020-2023)')
plt.xlabel('日期')
plt.ylabel('收益率')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
你会发现,两条线几乎重合。为什么?因为当收益率很小时,ln(1+x) ≈ x。但在极端行情下(比如2020年3月),两者会有细微差别。嗯,这里要注意,对数收益率在极端值处会更“平滑”一些。
4.3.2 收益率分布直方图
分布图能告诉你收益率的“脾气”。是正态分布吗?有没有肥尾?
plt.figure(figsize=(10, 6))
plt.hist(simple_returns, bins=100, density=True, alpha=0.6, label='简单收益率')
plt.hist(log_returns, bins=100, density=True, alpha=0.6, label='对数收益率')
plt.title('QQQ 日收益率分布')
plt.xlabel('收益率')
plt.ylabel('概率密度')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
你大概率会看到:中间高、两边低,但尾巴比正态分布要厚。这就是金融数据常见的“尖峰肥尾”特征。我在做风险模型时,如果直接用正态分布假设,VaR(在险价值)会严重低估风险。所以,画图能帮你提前避坑。
4.4 知识体系与核心逻辑
为了让你更直观地理解本章的知识结构,我画了一张图。它展示了从原始价格到最终可视化分析的完整链路。
4.5 避坑指南与个人经验
讲几个我踩过的坑,希望对你有帮助。
坑1:忘记处理缺失值
我曾经在计算对数收益率时,直接用 np.log(prices / prices.shift(1)),结果第一行是inf(无穷大)。因为价格除以0了。记得用 dropna() 清理。
技巧1:用对数收益率做多期累加
如果你想计算一个月的总收益率,简单收益率需要连乘:(1+R1)*(1+R2)*...-1。而对数收益率直接求和:r1 + r2 + ...。我习惯在策略回测中全程使用对数收益率,最后再转换回简单收益率展示给客户看。
坑2:画图时忽略异常值
有一次我画分布图,发现左边尾巴特别长。一查,原来是某天数据有误,价格跳空了。所以,画图前最好先用 describe() 看一眼统计量,确认最大值、最小值是否合理。
4.6 总结一下
收益率计算和可视化,是量化投资的“基本功”。你想想看,如果连收益率的特征都不清楚,后面做因子分析、风险模型,岂不是空中楼阁?
我个人建议,每次拿到新数据,都按这个流程走一遍:
- 计算简单收益率和对数收益率
- 画时间序列图,看波动是否稳定
- 画分布直方图,看是否符合正态分布
- 记录异常点,排查数据质量
这一步花不了多少时间,但能帮你省下后面大量的调试时间。嗯,今天就到这里,代码部分你可以直接复制到Jupyter Notebook里跑一下,看看效果。