本文将用Python编程,带你了解股票投资收益和风险的基本知识。
一、股票的收益
1.1 导入CSV时序数据
本文将分析微软2000年以来的股票交易数据(点我下载哦),它是一个 csv 格式的时间序列数据。你将使用 pandas 读取 csv 数据,并存储为DataFrame格式。
# 导入pandas包
import pandas as pd
# 读取csv文件,并将‘Date’列解析为日期时间格式,并设为索引
StockPrices = pd.read_csv('MSFTPrices.csv', parse_dates=['Date'], index_col='Date')
# 将数据按日期这一列排序(保证后续计算收益率的正确性)
StockPrices = StockPrices.sort_values(by='Date')
# 打印数据的前5行
print(StockPrices.head())
Open High Low Close Volume Adjusted
Date
2000-01-03 88.777 89.722 84.712 58.28125 53228400 38.527809
2000-01-04 85.893 88.588 84.901 56.31250 54119000 37.226345
2000-01-05 84.050 88.021 82.726 56.90625 64059600 37.618851
2000-01-06 84.853 86.130 81.970 55.00000 54976600 36.358688
2000-01-07 82.159 84.901 81.166 55.71875 62013600 36.833828
该股票数据包括了交易日期、开盘价、最高价、最低价、收盘价、调整后的收盘价以及成交量。其中调整后的收盘价最为重要,它对股票分割、股息和其他公司行为进行了标准化,能真实地反映股票随时间的回报。所以本文后续的计算都是基于调整后的收盘价(Adjusted)这一列数据。
1.2 计算收益率
收益率的计算公式如下:
这里可理解为两天的价格差除以前一天的价格。在 pandas 中,使用 .pct_change()
方法来计算收益率。
# 增加一列'Returns', 存储每日的收益率
StockPrices['Returns'] = StockPrices['Adjusted'].pct_change()
# 检查前5行数据
print(StockPrices.head())
Open High Low Close Volume Adjusted Returns
Date
2000-01-03 88.777 89.722 84.712 58.28125 53228400 38.527809 NaN
2000-01-04 85.893 88.588 84.901 56.31250 54119000 37.226345 -0.033780
2000-01-05 84.050 88.021 82.726 56.90625 64059600 37.618851 0.010544
2000-01-06 84.853 86.130 81.970 55.00000 54976600 36.358688 -0.033498
2000-01-07 82.159 84.901 81.166 55.71875 62013600 36.833828 0.013068
数据框增加了 Returns 一列,即股票的收益。注意第一天的收益率是缺失值 NaN,因为没有前一天的数据用于计算。
为了后续计算方便,我们选取 Returns 这一列,并将缺失值丢弃,存储在新的变量 clean_returns 中。使用 .dropna()
方法来删除缺失值。
clean_returns = StockPrices['Returns'].dropna()
绘制每日收益随时间变化的图。
# 导入matplotlib绘图包中的pyplot模块
import matplotlib.pyplot as plt
#绘图
clean_returns.plot()
plt.show()
1.3 收益的均值
均值是最常用的统计量,它将一串数据平均后浓缩为一个数值,但同时也丢失了数据波动性的信息。
可使用 numpy 包中的 mean()
函数计算股票历史收益的均值。
# 导入numpy包
import numpy as np
# 计算股票的日平均收益
mean_return_daily = np.mean(clean_returns)
print("日平均收益:", mean_return_daily)
日平均收益: 0.00037777546435757725
通过以下公式,将日收益率转换为年化收益率(一般假设一年252个交易日),其中 是日平均收益率。
# 计算平均年化收益
mean_return_annualized = ((1 + mean_return_daily)**252) - 1
print("平均年化收益:", mean_return_annualized)
平均年化收益: 0.09985839482858783
1.4 收益的分布
绘制收益的直方图可了解其分布情况,同时也能观察到收益中的异常值。一般在收益分布的两侧有两条长长的尾巴,在投资时一般会尽量避免左侧尾巴上的异常值,因为他们代表了较大的亏损;而分布在右侧尾巴上的异常值通常是件好事,它代表较大的盈利。
使用 matplotlib 绘图包中的 hist()
函数绘制直方图。
# 绘制直方图
plt.hist(clean_returns, bins=75)
plt.show()
上图所示的收益是个怎样的分布呢?是正态分布吗?我们将在后续揭晓答案。
二、风险的衡量
金融市场的风险是对不确定性的度量,反应在收益的波动上。一般可用以下统计量来表示:
- 方差或标准差
- 偏度
- 峰度
接下来我们将逐个计算它们。
2.1 方差
方差是对数据离散程度的度量。下图中蓝色分布比红色分布的方差大得多,其数据也更加分散。
标准差又称均方差,是方差的算数平方根。投资回报中较高的标准差意味着较高的风险,因为数据分布离均值更远了,收益的波动幅度更大。
可使用 numpy 包中的 std()
函数计算标准差 ,方差则是标准差的平方 .
# 计算标准差
sigma_daily = np.std(clean_returns)
print("标准差: ", sigma_daily)
# 计算方差
variance_daily = sigma_daily ** 2
print("方差: ", variance_daily)
标准差: 0.019341100408708328
方差: 0.0003740781650197374
以上计算的是每日的方差,我们可以将之转化成年化方差。将标准差乘以交易日数目的平方根,得到年化标准差。将年化标准差平方,就得到年化方差。
# 计算年化标准差
sigma_annualized = sigma_daily*np.sqrt(252)
print("年化标准差:", sigma_annualized)
# 计算年化方差
variance_annualized = sigma_annualized ** 2
print("年化方差:", variance_annualized)
年化标准差: 0.3070304505826317
年化方差: 0.09426769758497383
2.2 偏度
偏度是数据分布偏斜方向和程度的度量,反应分布的非对称性。
下图所示的曲线分别代表了负偏态和正偏态。在金融领域,人们更倾向于正的偏度,因为这意味着高盈利的概率更大。
可使用 scipy.stats 提供的 skew()
函数计算收益分布的偏度。
# 从 scipy.stats 导入skew函数
from scipy.stats import skew
# 计算收益分布的偏度
returns_skewness = skew(clean_returns)
print("偏度:", returns_skewness)
偏度: 0.21935459193067852
回顾之前绘制的收益分布图,乍看之下似乎是对称分布,但经过偏度的计算,我们知道它具有稍许的正偏度。
2.3 峰度
峰度表征概率密度分布曲线在平均值处峰值高低的特征数,反映了峰部的尖度。通常将样本的峰度和正态分布相比较,因为正态分布的峰度是3,所以将超出3的部分称为超值峰度。大部分金融收益都具有正的超值峰度。
使用scipy.stats提供的 kurtosis()
函数计算分布的超值峰度。
# 从 scipy.stats 导入 kurtosis 函数
from scipy.stats import kurtosis
# 计算收益分布的超值峰度
excess_kurtosis = kurtosis(clean_returns)
print("超值峰度:", excess_kurtosis)
# 计算峰度
fourth_moment = excess_kurtosis + 3
print("峰度:", fourth_moment)
超值峰度: 10.31457261802553
峰度: 13.31457261802553
上述峰度的计算结果表明,该股票收益的峰比正态分布高得多。我们也可通过下图概率密度分布的比较看出来,图中橙色代表收益的分布,而蓝色表正态分布。
# 模拟正态分布数据,其均值和标准差与文中的股票收益相同。
mu = mean_return_daily
sigma = sigma_daily
norm = np.random.normal(mu, sigma, size=10000)
# 绘制正态分布的概率密度分布图
plt. hist(norm, bins=100, alpha=0.8, density=True, label='Normal Distribution')
# 绘制收益的概率密度分布图
plt.hist(clean_returns, bins=75, alpha=0.7, density=True, label='Returns')
# 增加图例说明
plt.legend()
# 绘图
plt.show()
三、收益分布正态性检验
现在让我们回到第一部分结尾提出的问题:该股票的收益分布是正态分布吗?
我们知道正态分布是对称的,其偏度为0,而该股票收益具有正的偏度0.219。正态分布的峰度是3,而该股票收益的峰度高达13.31。从这两个统计量看出,该股票收益并不是正态分布,它稍微向右偏斜,并且具有比较尖的峰。
但是这就能让我们自信的下结论吗?为了判断股票收益分布的正态性,我们需要使用真正的统计检验方法,而不是简单地检查峰度或偏度。
这里使用 scipy.stats 提供的 shapiro()
函数,对股票收益分布进行 Shapiro-Wilk 检验。该函数有两个返回值,一个是检验的t统计量,另一个是p值。现在你并不需要知道 Shapiro-Wilk 检验到底是个什么鬼,只要知道如何使用p值判断数据的正态性:如果p值小于等于0.05,就拒绝正态性假设,得出数据非正态分布的结论。
# 从 scipy.stats 导入shapiro
from scipy.stats import shapiro
# 对股票收益进行Shapiro-Wilk检验
shapiro_results = shapiro(clean_returns)
print("Shapiro-Wilk检验结果: ", shapiro_results)
# 提取P值
p_value = shapiro_results[1]
print("P值: ", p_value)
Shapiro-Wilk检验结果: (0.9003633260726929, 0.0)
P值: 0.0
计算得到的p值非常小,在目前的精度下等于0,所以我们可以肯定地说该收益分布不是正态分布。
小结
本文用Python计算了股票的收益和风险,我们首先查看了股票的收益率及其分布,接着计算指示风险的统计量:方差、偏度和峰度,最后检验了收益分布的正态性。
另外我们还学到了以下统计函数:
import numpy as np
np.mean() # 均值
np.std() # 标准差
from scipy.stats import skew, kurtosis, shapiro
skew() # 偏度
kurtosis() # 超值峰度
shapiro() # Shapiro-Wilk检验正态性
如果你想自己下载股票数据的话,可以参考这篇文章 《如何用Python下载金融数据》。
注:本文是 DataCamp 课程 Intro to Portfolio Risk Management in Python 的学习笔记。