Skip to content

用数组算收益率

有了收盘价,第一件要算的通常是涨了多少。本章用 NumPy 计算相邻两天的简单收益率,并计算这组收益率的均值和样本标准差。

学完后,应能不用循环算出一列价格的简单收益率,并说明 std(ddof=1) 和 std(ddof=0) 的差别。

简单收益率

第 \(t\) 天的简单收益率是

\[ r_t = \frac{P_t}{P_{t-1}} - 1 \]

四天价格 [10.0, 10.5, 10.2, 11.0] 能算出三天收益。第一天没有前一天,所以收益率比价格少一个。

import numpy as np

prices = np.array([10.0, 10.5, 10.2, 11.0])
returns = prices[1:] / prices[:-1] - 1

print(prices[1:])
print(prices[:-1])
print(returns)
[10.5 10.2 11. ]
[10.  10.5 10.2]
[ 0.05       -0.02857143  0.07843137]

prices[1:] 是从第二天起的价格,prices[:-1] 是到倒数第二天为止的价格。两段对齐之后相除,再减 1。

手工核对第一天:\(10.5 / 10.0 - 1 = 0.05\),也就是上涨 5%。第二天:\(10.2 / 10.5 - 1 \approx -0.0286\),下跌约 2.86%。

均值和标准差

print(returns.mean())
print(returns.std(ddof=1))
print(returns.std(ddof=0))
0.03328664799253037
0.05542474143737393
0.04525411188241907

mean() 是算术平均。这三天的平均简单收益率约为 0.0333,也就是 3.33%。

标准差衡量这组数散得有多开。样本标准差的分母是 \(n-1\),NumPy 里写成 ddof=1。ddof=0 的分母是 \(n\),数值会小一点。课程里报告一组历史收益的波动时,用 ddof=1。后面讲年化波动率,也沿用这个约定。

三个数的样本标准差大约是 0.0554。样本只有三天,这个数还不能当成这只证券的波动率,这里只是把公式跑通。

先写成数组,再考虑循环

同一计算用循环可以写成:

out = []
for i in range(1, len(prices)):
    out.append(prices[i] / prices[i - 1] - 1)

结果和 prices[1:] / prices[:-1] - 1 相同。数组写法短,也不容易把下标写错。数据到了几千行,数组运算通常也更快。循环留到下标规则本身就是题目的时候再用,例如“只保留成交量大于某个门槛的那些天”,那种筛选放到 Pandas 一章。

和后面章节的关系

本章的收益率是简单收益率,用在持有期收益、净值连乘。对数收益率是 \(\ln(P_t / P_{t-1})\),用在波动率和蒙特卡洛,到可视化与时间序列和收益分布与风险价值再算。两套数不要混在同一个公式里。

课堂练习

  1. 用 [10, 10, 10, 12] 计算简单收益率,说明为什么前两天的收益是 0。
  2. 把 returns.mean() 的结果乘以 100,写成百分数,保留两位小数。
  3. 对同一组收益率分别打印 std(ddof=1) 和 std(ddof=0),写出哪一个更大。

例子见 examples/fintech/returns.py。