Python金融分析(八):正态性检验

引言

统计学,统计学,统计是一门科学吗?我觉得概率论应该是,但统计不是。

经济学,经济学,经济是一门科学吗?我觉得也不是,不过金工有点那个意思。

但是,不管怎么说,经济和金融中都少不了与统计打交道,而统计又是一个太广阔的领域了,所以我们从正态分布入手一窥究竟。

正态性检验

正态分布可以说是金融中最重要的一种分布,它之所以如此重要是因为现代金融中的大量模型,尤其是重要的一些模型都有赖于某些变量服从正态分布的假设:

  • 投资组合理论(modern portfolio theory, MPT)

    当股票收益率是正态分布时,可以用标准差衡量风险,从而找到一定收益率下风险最小的投资组合。

  • 资本资产定价模型

    当股票收益率服从正态分布时,单一股票的价格可以用β与市场指数联系起来。

  • 有效市场假说

    有效市场是价格反映所有可用信息的市场,如果这个假设成立,则股票价格随机波动,回报正态分布。

  • 期权定价理论

    布朗运动是金融工具随机价格变动建模的基准;例如,Black-Scholes-Merton期权定价公式使用几何布朗运动作为股票随时间价格波动的模型,价格服从对数正态分布,收益率服从正态分布。

  • ……

美股示例

导入数据

为了检验真实的股票数据是否满足正态假设,我们选用两只科技股和两只ETF作为例子:

  • AAPL,苹果公司股票价格
  • MSFT,微软公司股票价格
  • SPY, 标普500ETF
  • GLD, SPDR黄金ETF

采用Yahoo Finance读取2010年以来的历史数据:

1
%matplotlib inline
1
2
3
4
5
6
7
8
9
10
import math
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas_datareader.data as pddata

plt.style.use('ggplot')
mpl.rcParams['figure.figsize']= [15, 9]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
stock_aapl = pddata.DataReader(name='AAPL', data_source='yahoo',
start='2010-01-01', end='2019-05-02',
retry_count=3, pause=0.1,
session=None, access_key=None)
stock_msft = pddata.DataReader(name='MSFT', data_source='yahoo',
start='2010-01-01', end='2019-05-02',
retry_count=3, pause=0.1,
session=None, access_key=None)
etf_spy = pddata.DataReader(name='SPY', data_source='yahoo',
start='2010-01-01', end='2019-05-02',
retry_count=3, pause=0.1,
session=None, access_key=None)
etf_gld = pddata.DataReader(name='GLD', data_source='yahoo',
start='2010-01-01', end='2019-05-02',
retry_count=3, pause=0.1,
session=None, access_key=None)
1
2
3
4
5
6
7
data = {'aapl': stock_aapl['Adj Close'],
'msft': stock_msft['Adj Close'],
'spy': etf_spy['Adj Close'],
'gld': etf_gld['Adj Close']
}
data = pd.DataFrame(data)
data.head()
aapl msft spy gld
Date
2009-12-31 20.073631 24.241983 92.561058 107.309998
2010-01-04 20.386072 24.615801 94.130867 109.800003
2010-01-05 20.421322 24.623755 94.380074 109.699997
2010-01-06 20.096491 24.472631 94.446495 111.510002
2010-01-07 20.059338 24.218124 94.845207 110.820000

四只股票均以2009年12月31日的已调整收盘价=100作调整,作图:

1
(data / data.iloc[0] * 100).plot(figsize=(15,9))

output_12_1

描述性统计

我们看一下四种证券对数收益率的直方图:

1
2
3
log_returns = np.log(data / data.shift(1))
log_returns.hist(bins=50, figsize=(15,9))
plt.show()

output_15_0

我个人的直观感受是微软和标普500ETF的对数收益率好像更符合正态分布一些,那么具体是不是呢?可以利用scipy.stats模块进行描述性统计,得到初步的指标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import scipy.stats as scs

def print_statistics(array):
''' Prints selected statistics.

Parameters
==========
array: ndarray
object to generate statistics on
'''
sta = scs.describe(array)
print('%14s %15s' % ('statistic', 'value'))
print(30 * '-')
print('%14s %15.5f' % ('size', sta[0]))
print('%14s %15.5f' % ('min', sta[1][0]))
print('%14s %15.5f' % ('max', sta[1][1]))
print('%14s %15.5f' % ('mean', sta[2]))
print('%14s %15.5f' % ('std', np.sqrt(sta[3])))
print('%14s %15.5f' % ('skew', sta[4]))
print('%14s %15.5f' % ('kurtosis', sta[5]))
1
2
3
4
5
for sec in data.columns:
print(f'\nResults for {sec}')
print(30*'-')
log_data = np.array(log_returns[sec].dropna())
print_statistics(log_data)
Results for aapl
------------------------------
     statistic           value
------------------------------
          size      2348.00000
           min        -0.13188
           max         0.08502
          mean         0.00100
           std         0.01642
          skew        -0.27629
      kurtosis         4.89598

Results for msft
------------------------------
     statistic           value
------------------------------
          size      2348.00000
           min        -0.12103
           max         0.09941
          mean         0.00070
           std         0.01445
          skew        -0.08792
      kurtosis         6.58734

Results for spy
------------------------------
     statistic           value
------------------------------
          size      2348.00000
           min        -0.06734
           max         0.04929
          mean         0.00049
           std         0.00936
          skew        -0.48419
      kurtosis         4.54397

Results for gld
------------------------------
     statistic           value
------------------------------
          size      2348.00000
           min        -0.09191
           max         0.04787
          mean         0.00005
           std         0.00991
          skew        -0.58841
      kurtosis         5.94489

利用分位图可以更加直观地观察。分位图,即QQ图,是指一种通过比较两个概率分布的分位数对这两个概率分布进行比较的概率图方法从而检验数据的分布情况。我们知道服从正态分布的随机变量其仿射变化仍然服从正态分布,所以如果以标准正态分布的分位数为横坐标,待检验分布的分位值为纵坐标做分位图,就可以判断样本数据是否近似服从正态分布:如果否近似地在一条直线附近,图形是直线说明是正态分布,而且该直线的斜率为标准差,截距为均值。

此外,分位图也可以提供样本偏度和峰度的粗略信息。图形中有一段是直线,在两端存在弧度,则可说明峰度的情况。如果分位图中间部分是直线,但是右边在直线下面,左边在直线上面,说明分布的峰度大于3(超值峰度大于0),存在尖峰肥尾;反之说明峰度小于3,是薄尾。此外,如果图形是曲线图,说明不对称。

1
2
3
4
5
6
7
8
9
10
11
12
import statsmodels.api as sm
f, axs = plt.subplots(2,2,figsize=(15,12))
i = 1
for sec in data.columns:
ax = plt.subplot(2,2,i)
sm.qqplot(log_returns[sec].dropna(), line='s',ax=ax)
plt.title(sec)
plt.xlabel('theoretical quantiles')
plt.ylabel('sample quantiles');
i += 1

plt.show()

output_20_0

从图上可以看出来,实际上四种证券的对数收益率都不符合正态分布,存在明显的厚尾(从前面计算的峭度也可以看出来)。

正态性检验

scipy.stats模块中有各种检验的方法,我们可以用skewtest()kurtosistest()normaltest()分别对偏度、峰度和正态性进行假设检验,此处的原假设为偏度为0、峰度为3(超值峰度为0)及服从正态分布。当p值小于给定的置信水平α\alpha时,则拒绝原假设。

1
2
3
4
5
6
7
8
9
10
11
12
13
def normality_tests(arr):
''' Tests for normality distribution of given data set.

Parameters
==========
array: ndarray
object to generate statistics on
'''
print('Skew of data set %14.3f' % scs.skew(arr))
print('Skew test p-value %14.3f' % scs.skewtest(arr)[1])
print('Kurt of data set %14.3f' % scs.kurtosis(arr))
print('Kurt test p-value %14.3f' % scs.kurtosistest(arr)[1])
print('Norm test p-value %14.3f' % scs.normaltest(arr)[1])
1
2
3
4
5
for sec in data.columns:
print(f'\nResults for {sec}')
print(30*'-')
log_data = np.array(log_returns[sec].dropna())
normality_tests(log_data)
Results for aapl
------------------------------
Skew of data set          -0.276
Skew test p-value          0.000
Kurt of data set           4.896
Kurt test p-value          0.000
Norm test p-value          0.000

Results for msft
------------------------------
Skew of data set          -0.088
Skew test p-value          0.082
Kurt of data set           6.587
Kurt test p-value          0.000
Norm test p-value          0.000

Results for spy
------------------------------
Skew of data set          -0.484
Skew test p-value          0.000
Kurt of data set           4.544
Kurt test p-value          0.000
Norm test p-value          0.000

Results for gld
------------------------------
Skew of data set          -0.588
Skew test p-value          0.000
Kurt of data set           5.945
Kurt test p-value          0.000
Norm test p-value          0.000

从上面的结果可以看出,p值均为0,全部拒绝了原假设。说明这几种资产的对数收益率不服从正态分布,相应地,其价格也不适用几何布朗运动模型。因此。我们可能需要使用厚尾分布的模型( 例如,跳跃扩散模型或具有随机波动率的模型)。