├── Protfolio ├── app.py └── app2.py └── README.md /Protfolio/app.py: -------------------------------------------------------------------------------- 1 | #coding:utf8 2 | 3 | import tushare as ts 4 | import pandas as pd 5 | import numpy as np 6 | import statsmodels.api as sm 7 | import scipy.stats as scs 8 | import matplotlib.pyplot as plt 9 | ''' 10 | 第一部分 正态性检验 11 | ''' 12 | stock = ['000001','000063','000002','600570'] 13 | 14 | data1 = ts.get_hist_data(stock[0],start='2015-05-01',end='2016-08-15') 15 | data2 = ts.get_hist_data(stock[1],start='2015-05-01',end='2016-08-15') 16 | data3 = ts.get_hist_data(stock[2],start='2015-05-01',end='2016-08-15') 17 | data4 = ts.get_hist_data(stock[3],start='2015-05-01',end='2016-08-15') 18 | 19 | #print data1['close'] 20 | df=pd.concat([data1['close'],data2['close'],data3['close'],data4['close']],axis=1) 21 | df.columns=stock 22 | (df/df.ix[0]*100).plot(figsize = (8,5)) 23 | 24 | #print df 25 | 26 | log_returns = np.log(df/df.shift(1)) 27 | #print log_returns.head() 28 | 29 | #输出每只股票的统计数据 30 | #定义print_statistics函数,为了更加易于理解的方式 31 | #输出给定(历史或者模拟)数据集均值、偏斜度或者峰度等统计数字 32 | def print_statistics(array): 33 | sta = scs.describe(array) 34 | print '%14s %15s' %('statistic','value') 35 | print 30*'-' 36 | print '%14s %15d' %('size', sta[0]) 37 | print '%14s %15.5f' %('min', sta[1][0]) 38 | print '%14s %15.5f' %('max', sta[1][1]) 39 | print '%14s %15.5f' %('mean', sta[2]) 40 | print '%14s %15.5f' %('std', np.sqrt(sta[3])) 41 | print '%14s %15.5f' %('skew', sta[4]) 42 | print '%14s %15.5f' %('kurtosis', sta[5]) 43 | 44 | for st in stock: 45 | print '\nResults for stock %s' %st 46 | print 30*'-' 47 | log_data = np.array(log_returns[st].dropna()) 48 | print_statistics(log_data) 49 | 50 | #画qq图观察数据 51 | sm.qqplot(log_returns[stock[0]].dropna(), line='s') 52 | plt.grid(True) 53 | plt.xlabel('theoretical quantiles') 54 | plt.ylabel('sample quantiles') 55 | plt.show() 56 | 57 | #进行正态性检验 58 | def normality_test(array): 59 | ''' 60 | 对给定的数据集进行正态性检验 61 | 组合了3中统计学测试 62 | 偏度测试(Skewtest)——足够接近0 63 | 峰度测试(Kurtosistest)——足够接近0 64 | 正态性测试 65 | ''' 66 | print 'Skew of data set %15.3f' % scs.skew(array) 67 | print 'Skew test p-value %14.3f' % scs.skewtest(array)[1] 68 | print 'Kurt of data set %15.3f' % scs.kurtosis(array) 69 | print 'Kurt test p-value %14.3f' % scs.kurtosistest(array)[1] 70 | print 'Norm test p-value %14.3f' % scs.normaltest(array)[1] 71 | 72 | for st in stock: 73 | print '\nResults for st %s' %st 74 | print 32*'-' 75 | log_data = np.array(log_returns[st].dropna()) 76 | normality_test(log_data) 77 | 78 | ''' 79 | 从上述测试的p值来看,否定了数据集呈正态分布的测试假设。 这说明,股票市场收益率的正态假设不成立。 80 | ''' -------------------------------------------------------------------------------- /Protfolio/app2.py: -------------------------------------------------------------------------------- 1 | #coding:utf8 2 | import tushare as ts 3 | import pandas as pd 4 | import numpy as np 5 | import statsmodels.api as sm 6 | import scipy.stats as scs 7 | import matplotlib.pyplot as plt 8 | import scipy.optimize as sco 9 | 10 | ''' 11 | PART TWO:均值-方差投资组合理论 12 | ''' 13 | 14 | #选取几只感兴趣的股票 15 | #000001中国平安 000063中兴通讯 000002万科A 600570恒生电子 16 | stock = ['000001','000063','000002','600570'] 17 | 18 | data1 = ts.get_hist_data(stock[0],start='2015-05-01',end='2016-05-01') 19 | data2 = ts.get_hist_data(stock[1],start='2015-05-01',end='2016-05-01') 20 | data3 = ts.get_hist_data(stock[2],start='2015-05-01',end='2016-05-01') 21 | data4 = ts.get_hist_data(stock[3],start='2015-05-01',end='2016-05-01') 22 | #将所有股票信息的收盘价整合成一个dataframe 23 | df=pd.concat([data1['close'],data2['close'],data3['close'],data4['close']],axis=1) 24 | #修改列名 25 | df.columns=stock 26 | #显示股价走势,比较一下几种股票的情况。规范起点为100 27 | (df/df.ix[0]*100).plot(figsize = (8,4)) 28 | 29 | #每年252个交易日,用每日收益得到年化收益。 30 | #计算投资资产的协方差是构建资产组合过程的核心部分。运用pandas内置方法生产协方差矩阵。 31 | returns = np.log(df / df.shift(1)) 32 | 33 | print "年化收益" 34 | print returns.mean()*252 35 | print 32*"-" 36 | print "协方差" 37 | print returns.cov()*252 38 | print 32*"-" 39 | returns.hist(bins = 50, figsize = (9,6)) 40 | 41 | 42 | #给不同资产随机分配初始权重 43 | #由于A股不允许建立空头头寸,所有的权重系数均在0-1之间 44 | weights = np.random.random(4) 45 | weights /= np.sum(weights) 46 | print '初始化权重',weights 47 | #计算预期组合年化收益、组合方差和组合标准差 48 | print '初始权重收益',np.sum(returns.mean()*weights)*252 49 | print '初始权重组合方差',np.dot(weights.T, np.dot(returns.cov()*252,weights)) 50 | print '初始权重组合标准差',np.sqrt(np.dot(weights.T, np.dot(returns.cov()* 252,weights))) 51 | #用蒙特卡洛模拟产生大量随机组合 52 | #进行到此,我们最想知道的是给定的一个股票池(证券组合)如何找到风险和收益平衡的位置。 53 | #下面通过一次蒙特卡洛模拟,产生大量随机的权重向量,并记录随机组合的预期收益和方差。 54 | port_returns = [] 55 | port_variance = [] 56 | for p in range(4000): 57 | weights = np.random.random(4) 58 | weights /=np.sum(weights) 59 | port_returns.append(np.sum(returns.mean()*252*weights)) 60 | port_variance.append(np.sqrt(np.dot(weights.T, np.dot(returns.cov()*252, weights)))) 61 | 62 | port_returns = np.array(port_returns) 63 | port_variance = np.array(port_variance) 64 | 65 | 66 | 67 | #投资组合优化1——sharpe最大 68 | #建立statistics函数来记录重要的投资组合统计数据(收益,方差和夏普比) 69 | #通过对约束最优问题的求解,得到最优解。其中约束是权重总和为1。 70 | print 32*"-" 71 | print "投资组合优化方法1--sharpe值最大" 72 | def statistics(weights): 73 | weights = np.array(weights) 74 | port_returns = np.sum(returns.mean()*weights)*252 75 | port_variance = np.sqrt(np.dot(weights.T, np.dot(returns.cov()*252,weights))) 76 | return np.array([port_returns, port_variance, port_returns/port_variance]) 77 | 78 | #最优化投资组合的推导是一个约束最优化问题 79 | 80 | #最小化夏普指数的负值 81 | def min_sharpe(weights): 82 | return -statistics(weights)[2] 83 | #约束是所有参数(权重)的总和为1。这可以用minimize函数的约定表达如下 84 | cons = ({'type':'eq', 'fun':lambda x: np.sum(x)-1}) 85 | #我们还将参数值(权重)限制在0和1之间。这些值以多个元组组成的一个元组形式提供给最小化函数 86 | bnds = tuple((0,1) for x in range(4)) 87 | #优化函数调用中忽略的唯一输入是起始参数列表(对权重的初始猜测)。我们简单的使用平均分布。 88 | opts = sco.minimize(min_sharpe, 4*[1./4,], method = 'SLSQP', bounds = bnds, constraints = cons) 89 | print opts 90 | print "权重",opts['x'].round(3) 91 | #预期收益率、预期波动率、最优夏普指数 92 | print '最大sharpe指数预期收益' , statistics(opts['x']).round(3) 93 | 94 | 95 | print 32*"-" 96 | print "投资组合优化方法2--方差最小" 97 | #投资组合优化2——方差最小 98 | #接下来,我们通过方差最小来选出最优投资组合。 99 | #我们定义一个函数对 方差进行最小化 100 | def min_variance(weights): 101 | return statistics(weights)[1] 102 | optv = sco.minimize(min_variance, 4*[1./4,],method = 'SLSQP', bounds = bnds, constraints = cons) 103 | print optv 104 | print "权重",optv['x'].round(3) 105 | #得到的预期收益率、波动率和夏普指数 106 | print '最小方差预期收益' , statistics(optv['x']).round(3) 107 | 108 | #组合的有效前沿 109 | #有效前沿有既定的目标收益率下方差最小的投资组合构成。 110 | #在不同目标收益率水平(target_returns)循环时,最小化的一个约束条件会变化。 111 | target_returns = np.linspace(0.0,0.5,50) 112 | target_variance = [] 113 | for tar in target_returns: 114 | cons = ({'type':'eq','fun':lambda x:statistics(x)[0]-tar},{'type':'eq','fun':lambda x:np.sum(x)-1}) 115 | res = sco.minimize(min_variance, 4*[1./4,],method = 'SLSQP', bounds = bnds, constraints = cons) 116 | target_variance.append(res['fun']) 117 | 118 | target_variance = np.array(target_variance) 119 | 120 | 121 | 122 | 123 | 124 | 125 | plt.figure(figsize=(10,6)) 126 | #圆圈:蒙特卡洛随机产生的组合分布 127 | plt.scatter(port_variance, port_returns, c = port_returns/port_variance,marker = 'o',s=5 ) 128 | #叉号:有效前沿(目标收益率下最优的投资组合) 129 | plt.scatter(target_variance,target_returns, c = target_returns/target_variance, marker = 'X',s=20) 130 | #红星:标记最高sharpe组合 131 | plt.plot(statistics(opts['x'])[1], statistics(opts['x'])[0], 'r*', markersize = 15.0) 132 | #黄星:标记最小方差组合 133 | plt.plot(statistics(optv['x'])[1], statistics(optv['x'])[0], 'y*', markersize = 15.0) 134 | plt.grid(True) 135 | plt.xlabel('expected volatility') 136 | plt.ylabel('expected return') 137 | plt.colorbar(label = 'Sharpe ratio') 138 | plt.show() 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stock-quant 2 | python量化分析股票的投资组合 3 | --------------------------------------------------------------------------------