请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3365876 新帖:9

正态性检验和蒙特卡洛完成投资组合优化

你在说什么呢发表于:5 月 21 日 23:46回复(1)

正态性检验和蒙特卡洛完成投资组合优化¶

by Harold


思考怎样有效的配置资产组合 很多时候根据条件选好股票池之后,通常简单粗暴的等分仓位给每只股票。 其实,过程中有很多可以优化的空间。

下面,来看如何运用有效前沿进行资产组合优化。

PART ONE: 正态性检验¶

1.导入模块

import pandas as pd
import numpy as np
import statsmodels.api as sm
import scipy.stats as scs
import matplotlib.pyplot as plt

2.选取几只感兴趣的股票。

000723 美锦能源, 600176 中国巨石, 002202 金风科技, 600519 贵州茅台

stock = ['000723.XSHE','600176.XSHG','002202.XSHE','600519.XSHG']
start_date = '2018-05-10'
end_date = '2019-05-10'
df = get_price(stock, start_date, end_date, 'daily',['close'])
data = df['close']
data.head()
000723.XSHE 600176.XSHG 002202.XSHE 600519.XSHG
2018-05-10 5.15 12.32 15.90 704.23
2018-05-11 5.15 12.28 15.87 708.00
2018-05-14 5.15 12.70 16.03 732.05
2018-05-15 5.15 12.68 16.33 732.69
2018-05-16 5.15 12.42 16.39 724.27

3.比较一下机制股票的情况。规范起点为100.

(data/data.ix[0]*100).plot(figsize = (8,6));

4.计算收益率

log_returns = np.log(data/data.shift(1))
log_returns.head()
000723.XSHE 600176.XSHG 002202.XSHE 600519.XSHG
2018-05-10 NaN NaN NaN NaN
2018-05-11 0 -0.003252 -0.001889 0.005339
2018-05-14 0 0.033630 0.010031 0.033405
2018-05-15 0 -0.001576 0.018542 0.000874
2018-05-16 0 -0.020718 0.003667 -0.011558
log_returns.hist(bins = 50, figsize = (9,6));

从以上结果看到4个数据集都和正态分布的要求相差太多。

5.输出每只股票的统计数据

#定义print_statistics函数,为了更加易于理解的方式
#输出给定(历史或者模拟)数据集均值、偏斜度或者峰度等统计数字
def print_statistics(array):
    sta = scs.describe(array)
    print '%14s %15s' %('statistic','value')
    print 30*'-'
    print '%14s %15d' %('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])

for st in stock:
    print '\nResults for stock %s' %st
    print 30*'-'
    log_data = np.array(log_returns[st].dropna())
    print_statistics(log_data)
Results for stock 000723.XSHE
------------------------------
     statistic           value
------------------------------
          size             243
           min        -0.10542
           max         0.09603
          mean         0.00453
           std         0.03786
          skew         0.32416
      kurtosis         1.65314

Results for stock 600176.XSHG
------------------------------
     statistic           value
------------------------------
          size             243
           min        -0.09620
           max         0.09571
          mean        -0.00088
           std         0.02562
          skew         0.18921
      kurtosis         1.91781

Results for stock 002202.XSHE
------------------------------
     statistic           value
------------------------------
          size             243
           min        -0.10560
           max         0.09583
          mean        -0.00150
           std         0.03308
          skew         0.28700
      kurtosis         1.28129

Results for stock 600519.XSHG
------------------------------
     statistic           value
------------------------------
          size             243
           min        -0.10536
           max         0.06348
          mean         0.00104
           std         0.02358
          skew        -0.26321
      kurtosis         1.86828

6.画qq图观察数据

下面是000723.XSHE 对数收益率 分位数-分位数图

sm.qqplot(log_returns['000723.XSHE'].dropna(),line = 's')
plt.grid(True);
plt.xlabel('theoretical quantiles');
plt.ylabel('sample quantiles');

很显然,样本的分位数值不在一条直线上,表明“非正态性”。左侧和右侧分别有许多值远低于和远高于直线。这是典型的Fat tails。 Fat tails是频数分布中观察到的两端的异常值。

7.进行正态性检验

def normality_test(array):
    '''
    对给定的数据集进行正态性检验
    组合了3中统计学测试
    偏度测试(Skewtest)——足够接近0
    峰度测试(Kurtosistest)——足够接近0
    正态性测试
    '''
    print 'Skew of data set %15.3f' % scs.skew(array)
    print 'Skew test p-value %14.3f' % scs.skewtest(array)[1]
    print 'Kurt of data set %15.3f' % scs.kurtosis(array)
    print 'Kurt test p-value %14.3f' % scs.kurtosistest(array)[1]
    print 'Norm test p-value %14.3f' % scs.normaltest(array)[1]

for st in stock:
    print '\nResults for st %s' %st
    print 32*'-'
    log_data = np.array(log_returns[st].dropna())
    normality_test(log_data)
Results for st 000723.XSHE
--------------------------------
Skew of data set           0.324
Skew test p-value          0.038
Kurt of data set           1.653
Kurt test p-value          0.001
Norm test p-value          0.000

Results for st 600176.XSHG
--------------------------------
Skew of data set           0.189
Skew test p-value          0.219
Kurt of data set           1.918
Kurt test p-value          0.000
Norm test p-value          0.000

Results for st 002202.XSHE
--------------------------------
Skew of data set           0.287
Skew test p-value          0.065
Kurt of data set           1.281
Kurt test p-value          0.003
Norm test p-value          0.002

Results for st 600519.XSHG
--------------------------------
Skew of data set          -0.263
Skew test p-value          0.090
Kurt of data set           1.868
Kurt test p-value          0.000
Norm test p-value          0.000

从上述测试的p值来看,否定了数据集呈正态分布的测试假设。 这说明,股票市场收益率的正态假设不成立


PART TWO:均值-方差投资组合理论¶

该理论基于用均值和方差来表述组合的优劣的前提。将选取几只股票,用蒙特卡洛模拟初步探究组合的有效前沿。

通过最大Sharpe和最小方差两种优化来找到最优的资产组合配置权重参数。

最后,刻画出可能的分布,两种最优以及组合的有效前沿。

1.选取几只感兴趣的股票

000723 美锦能源, 600176 中国巨石, 002202 金风科技, 600519 贵州茅台

并比较一下数据(2018-05-10至2019-05-10)

stock_set = ['000723.XSHE','600176.XSHG','002202.XSHE','600519.XSHG']
noa = len(stock_set)
df = get_price(stock_set, start_date, end_date, 'daily', ['close'])
data = df['close']
#规范化后时序数据
(data/data.ix[0]*100).plot(figsize = (8,5));

2.计算不同证券的均值、协方差

每年252个交易日,用每日收益得到年化收益。

计算投资资产的协方差是构建资产组合过程的核心部分。运用pandas内置方法生产协方差矩阵。

returns = np.log(data / data.shift(1))
returns.mean()*252
000723.XSHE    1.141983
600176.XSHG   -0.221564
002202.XSHE   -0.379245
600519.XSHG    0.262546
dtype: float64
returns.cov()*252
000723.XSHE 600176.XSHG 002202.XSHE 600519.XSHG
000723.XSHE 0.361300 0.061876 0.045880 0.054114
600176.XSHG 0.061876 0.165376 0.117669 0.085467
002202.XSHE 0.045880 0.117669 0.275695 0.083692
600519.XSHG 0.054114 0.085467 0.083692 0.140148

3.给不同资产随机分配初始权重

由于A股不允许建立空头头寸,所有的权重系数均在0-1之间

weights = np.random.random(noa)
weights /= np.sum(weights)
weights
array([0.350076915668, 0.0388221914076, 0.275820833701, 0.335280059223])

4.计算预期组合年化收益、组合方差和组合标准差

np.sum(returns.mean()*weights)*252
0.37460326019452855
np.dot(weights.T, np.dot(returns.cov()*252,weights))
0.12472573811652655
np.sqrt(np.dot(weights.T, np.dot(returns.cov()* 252,weights)))
0.35316531273120039

5.用蒙特卡洛模拟产生大量随机组合

进行到此,我们最想知道的是给定的一个股票池(证券组合)如何找到风险和收益平衡的位置。

下面通过一次蒙特卡洛模拟,产生大量随机的权重向量,并记录随机组合的预期收益和方差。

port_returns = []
port_variance = []
for p in range(4000):
    weights = np.random.random(noa)
    weights /=np.sum(weights)
    port_returns.append(np.sum(returns.mean()*252*weights))
    port_variance.append(np.sqrt(np.dot(weights.T, np.dot(returns.cov()*252, weights))))

port_returns = np.array(port_returns)
port_variance = np.array(port_variance)

#无风险利率设定为4%
risk_free = 0.04
plt.figure(figsize = (8,4))
plt.scatter(port_variance, port_returns, c=(port_returns-risk_free)/port_variance, marker = 'o')
plt.grid(True)
plt.xlabel('excepted volatility')
plt.ylabel('expected return')
plt.colorbar(label = 'Sharpe ratio')
<matplotlib.colorbar.Colorbar at 0x7f7ca746df90>

6.投资组合优化1——sharpe最大

建立statistics函数来记录重要的投资组合统计数据(收益,方差和夏普比)

通过对约束最优问题的求解,得到最优解。其中约束是权重总和为1。

def statistics(weights):
    weights = np.array(weights)
    port_returns = np.sum(returns.mean()*weights)*252
    port_variance = np.sqrt(np.dot(weights.T, np.dot(returns.cov()*252,weights)))
    return np.array([port_returns, port_variance, port_returns/port_variance])

#最优化投资组合的推导是一个约束最优化问题
import scipy.optimize as sco

#最小化夏普指数的负值
def min_sharpe(weights):
    return -statistics(weights)[2]

#约束是所有参数(权重)的总和为1。这可以用minimize函数的约定表达如下
cons = ({'type':'eq', 'fun':lambda x: np.sum(x)-1})

#我们还将参数值(权重)限制在0和1之间。这些值以多个元组组成的一个元组形式提供给最小化函数
bnds = tuple((0,1) for x in range(noa))

#优化函数调用中忽略的唯一输入是起始参数列表(对权重的初始猜测)。我们简单的使用平均分布。
opts = sco.minimize(min_sharpe, noa*[1./noa,], method = 'SLSQP', bounds = bnds, constraints = cons)
opts
  status: 0
 success: True
    njev: 7
    nfev: 42
     fun: -1.9164938258862707
       x: array([0.815054276427, 1.95915332568e-16, 0.0, 0.184945723573])
 message: 'Optimization terminated successfully.'
     jac: array([-3.61651182175e-05, 0.919741675258, 1.13021221757, 0.00015951693058,
       0.0])
     nit: 7

得到的最优组合权重向量为:

opts['x'].round(3)
array([0.815, 0.0, 0.0, 0.185])

sharpe最大的组合3个统计数据分别为:

#预期收益率、预期波动率、最优夏普指数
statistics(opts['x']).round(3)
array([0.979, 0.511, 1.916])

7.投资组合优化2——方差最小

接下来,我们通过方差最小来选出最优投资组合。

#但是我们定义一个函数对 方差进行最小化
def min_variance(weights):
    return statistics(weights)[1]

optv = sco.minimize(min_variance, noa*[1./noa,],method = 'SLSQP', bounds = bnds, constraints = cons)
optv
  status: 0
 success: True
    njev: 7
    nfev: 43
     fun: 0.32538668759319472
       x: array([0.164706707131, 0.262920333446, 0.101369109366, 0.471003850056])
 message: 'Optimization terminated successfully.'
     jac: array([0.326306864619, 0.325219530612, 0.324905332178, 0.325260814279, 0.0])
     nit: 7

方差最小的最优组合权重向量组合的统计数据分别为:

optv['x'].round(3)
array([0.165, 0.263, 0.101, 0.471])
#得到的预期收益率、波动率和夏普指数
statistics(optv['x']).round(3)
array([0.215, 0.325, 0.661])

8.组合的有效前沿

有效前沿有既定的目标收益率下方差最小的投资组合构成。

在最优化时采用两个约束,1.给定目标收益率,2.投资组合权重和为1。

def min_variance(weights):
    return statistics(weights)[1]

#在不同目标收益率水平(target_returns)循环时,最小化的一个约束条件会变化。
target_returns = np.linspace(0.0,0.5,50)
target_variance = []
for tar in target_returns:
    cons = ({'type':'eq','fun':lambda x:statistics(x)[0]-tar},{'type':'eq','fun':lambda x:np.sum(x)-1})
    res = sco.minimize(min_variance, noa*[1./noa,],method = 'SLSQP', bounds = bnds, constraints = cons)
    target_variance.append(res['fun'])

target_variance = np.array(target_variance)

下面是最优化结果的展示。

叉号:构成的曲线是有效前沿(目标收益率下最优的投资组合)

红星:sharpe最大的投资组合

黄星:方差最小的投资组合

plt.figure(figsize = (8,4))
#圆圈:蒙特卡洛随机产生的组合分布
plt.scatter(port_variance, port_returns, c = port_returns/port_variance,marker = 'o')
#叉号:有效前沿
plt.scatter(target_variance,target_returns, c = target_returns/target_variance, marker = 'x')
#红星:标记最高sharpe组合
plt.plot(statistics(opts['x'])[1], statistics(opts['x'])[0], 'r*', markersize = 15.0)
#黄星:标记最小方差组合
plt.plot(statistics(optv['x'])[1], statistics(optv['x'])[0], 'y*', markersize = 15.0)
plt.grid(True)
plt.xlabel('expected volatility')
plt.ylabel('expected return')
plt.colorbar(label = 'Sharpe ratio')
<matplotlib.colorbar.Colorbar at 0x7f7ca79e8910>

^_^ Thanks

全部回复

0/140

达人推荐

量化课程

    移动端课程