用Python分析股票的收益和风险

本文将用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 计算收益率

收益率的计算公式如下:

R_{t2} = \frac{P_{t2} - P_{t1}}{P_{t1}}

这里可理解为两天的价格差除以前一天的价格。在 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个交易日),其中 \mu 是日平均收益率。

平均年化收益率 =(1+\mu)^{252}−1

# 计算平均年化收益
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 方差

方差是对数据离散程度的度量。下图中蓝色分布比红色分布的方差大得多,其数据也更加分散。

图片来源:https://en.wikipedia.org/wiki/Standard_deviation

标准差又称均方差,是方差的算数平方根。投资回报中较高的标准差意味着较高的风险,因为数据分布离均值更远了,收益的波动幅度更大。

可使用 numpy 包中的 std() 函数计算标准差 \sigma,方差则是标准差的平方 \sigma^2.

# 计算标准差
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 偏度

偏度是数据分布偏斜方向和程度的度量,反应分布的非对称性。

下图所示的曲线分别代表了负偏态和正偏态。在金融领域,人们更倾向于正的偏度,因为这意味着高盈利的概率更大。

图片来源:https://en.wikipedia.org/wiki/Skewness

可使用 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的部分称为超值峰度。大部分金融收益都具有正的超值峰度。

kurtosis.png

使用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 的学习笔记。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容