A股投资组合历史数据回测比较分析

0.356字数 1092阅读 2126

数据准备

选择如下几只股票,从tushare.org获取其2007-10-31到2017-10-31十年的交易数据,保存为csv文件以备后用:

  • 招商证券 600999
  • 广发证券 000776
  • 万科a 000002
  • 保利地产 600048
  • 招商银行 600036
  • 工商银行 601398
  • 恒瑞医药 600276
  • 同仁堂 600685
  • 大族激光 002008
  • 三安光电 600703
  • 中国石油 601857
  • 中国石化 600028
  • ST中富 000659
  • ST山水 600234
  • 沪深300指数
import tushare as ts
import pandas as pd

codes=[
    (u'招商证券','600999'),(u'广发证券','000776'),(u'万科a','000002'),
    (u'保利地产','600048'),(u'招商银行','600036'),(u'工商银行','601398'),
    (u'恒瑞医药','600276'),(u'同仁堂','600685'),(u'大族激光','002008'),
    (u'三安光电','600703'),(u'中国石油','601857'),(u'中国石化','600028'),
    (u'ST中富','000659'),(u'ST山水','600234'),(u'沪深300','hs300')
]

for c in codes:
    df=ts.get_hist_data(c[1],start='2007-10-31',end='2017-10-31')
    df.to_csv('assets/'+c[0]+'.csv')
    print 'now '+c[0]+' earliest='+str(df.index[-1])

实际执行结果:

now 招商证券 earliest=2014-11-03
now 广发证券 earliest=2014-11-03
now 万科a earliest=2014-11-03
now 保利地产 earliest=2014-11-03
now 招商银行 earliest=2014-11-03
now 工商银行 earliest=2014-11-03
now 恒瑞医药 earliest=2014-11-03
now 同仁堂 earliest=2014-11-03
now 大族激光 earliest=2014-11-04
now 三安光电 earliest=2014-11-03
now 中国石油 earliest=2014-11-03
now 中国石化 earliest=2014-11-03
now ST中富 earliest=2014-11-03
now ST山水 earliest=2015-01-26
now 沪深300 earliest=2014-11-03

从结果看,只能选择 2014-11-03 之后到 2017-9-26 之前的数据,因为获取到的最早和最近的数据是这样。

%matplotlib inline

import matplotlib as mpl
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties

plt.rcParams['font.sans-serif']=['SimHei']
# myfont=FontProperties(fname=u'C:\\Windows\Fonts\\幼圆.TTF')
mpl.rcParams['axes.unicode_minus']=False

# legend_font=FontProperties(fname=u'C:\\Windows\Fonts\\幼圆.TTF',size=10)

dpi = 72.
xinch = 800 / dpi
yinch = 400 / dpi
mpl.rcParams['figure.figsize']=(xinch,yinch)

stocks=[
    (u'招商证券','600999'),(u'广发证券','000776'),(u'万科a','000002'),
    (u'保利地产','600048'),(u'招商银行','600036'),(u'工商银行','601398'),
    (u'恒瑞医药','600276'),(u'同仁堂','600685'),(u'大族激光','002008'),
    (u'三安光电','600703'),(u'中国石油','601857'),(u'中国石化','600028'),
    (u'ST中富','000659'),(u'ST山水','600234'),(u'沪深300','hs300')
]

codes={}
for s in stocks:
    codes[s[0]]=s[1]

symbols=codes.keys()

def col_converter(x):
    try:
        return float(x)
    except:
        return None

prices=None
for i,s in enumerate(symbols):
    p=pd.read_csv('assets/'+s.upper()+'.csv',usecols=['close','date'],index_col='date',converters={'close':col_converter})
    if i==0:
        prices=pd.DataFrame(index=p.index)
    prices[s]=p['close']

prices.sort_index(ascending=True,inplace=True)
prices.dropna(inplace=True)

两支地产股和两支券商股的比较

股价波动情况

prices[[u'招商证券',u'广发证券',u'万科a',u'保利地产']].plot()
plt.ylabel(u'每日收盘价')    
plt.xlabel(u'日期')
plt.legend()

print '----期初股价----'
print prices[[u'招商证券',u'广发证券',u'万科a',u'保利地产']].head(1)
print '----期末股价----'
print prices[[u'招商证券',u'广发证券',u'万科a',u'保利地产']].tail(1)
----期初股价----
            招商证券  广发证券    万科a   保利地产
date                                
2015-02-03  24.9  22.3  12.96  10.15
----期末股价----
             招商证券   广发证券    万科a   保利地产
date                                  
2017-09-26  20.59  18.87  26.76  10.65
output_3_1.png

收益率比较

假定同时在2014-11-03日买入这四只股票和沪深300指数,比较一下到2017-9-26的收益率

prices_norm=prices.copy()
for symbol in symbols:
    prices_norm[symbol]=prices[symbol]/prices[symbol][0]
prices_norm[[u'招商证券',u'广发证券',u'万科a',u'保利地产']].plot()
plt.ylabel(u'收益率')    
plt.xlabel(u'日期')
plt.legend()

print prices_norm[[u'招商证券',u'广发证券',u'万科a',u'保利地产']].tail(1)
                招商证券      广发证券       万科a      保利地产
date                                              
2017-09-26  0.826908  0.846188  2.064815  1.049261
output_5_1.png

从图表来看,到2017-10-31为止,投资万科a收益率最高(是期初的206.4%),投资招商证券最低(是期初的82.70%,还在站岗中)

股价波动情况比较

再看看两支券商股和两支地产股的收盘价波动情况,同时与沪深300的收盘价波动情况比较:

prices.pct_change()[[u'招商证券',u'广发证券',u'万科a',u'保利地产',u'沪深300']].plot()
plt.ylabel(u'每日波动率')    
plt.xlabel(u'日期')
plt.legend()

print prices.pct_change()[[u'招商证券',u'广发证券',u'万科a',u'保利地产',u'沪深300']].describe()
             招商证券        广发证券         万科a        保利地产       沪深300
count  332.000000  332.000000  332.000000  332.000000  332.000000
mean     0.000319    0.000345    0.002647    0.000730    0.000556
std      0.042842    0.042965    0.030688    0.033740    0.021790
min     -0.297507   -0.313300   -0.100045   -0.229651   -0.151217
25%     -0.009038   -0.007870   -0.011612   -0.011329   -0.004597
50%      0.000000   -0.000584   -0.000400    0.000460    0.000990
75%      0.008002    0.008111    0.010071    0.010879    0.005632
max      0.470405    0.551145    0.113119    0.169173    0.196912
output_7_1.png

从图像上看广发证券外波动最大,但是从区间收盘价标准差来看,其实招商证券和广发证券都在0.043附近,风险程度相近。保利地产标准差仅0.022,风险最小。

计算股价波动的相关性

co=prices.pct_change().corr()
for symbol in symbols:
    v=co[symbol].sort_values()
    print symbol,u' | 正相关度最高: ',v.index[-2],u' 相关系数=',v[-2],u'   | 负相关度最高: ',v.index[0],u' 相关系数=',v[0]
    print ''
恒瑞医药  | 正相关度最高:  招商证券  相关系数= 0.373459451207    | 负相关度最高:  ST山水  相关系数= -0.184459943941

中国石化  | 正相关度最高:  中国石油  相关系数= 0.924324466248    | 负相关度最高:  恒瑞医药  相关系数= 0.086023084494

大族激光  | 正相关度最高:  沪深300  相关系数= 0.661414109    | 负相关度最高:  恒瑞医药  相关系数= 0.0872177335242

中国石油  | 正相关度最高:  中国石化  相关系数= 0.924324466248    | 负相关度最高:  ST山水  相关系数= 0.117857884825

沪深300  | 正相关度最高:  广发证券  相关系数= 0.860752981977    | 负相关度最高:  恒瑞医药  相关系数= 0.289424888497

三安光电  | 正相关度最高:  ST山水  相关系数= 0.621160353597    | 负相关度最高:  恒瑞医药  相关系数= 0.0579186039258

万科a  | 正相关度最高:  保利地产  相关系数= 0.575758930125    | 负相关度最高:  恒瑞医药  相关系数= 0.196663064699

工商银行  | 正相关度最高:  中国石油  相关系数= 0.782436477866    | 负相关度最高:  ST山水  相关系数= 0.0843699347197

招商证券  | 正相关度最高:  广发证券  相关系数= 0.921710029685    | 负相关度最高:  ST山水  相关系数= 0.130198313722

招商银行  | 正相关度最高:  工商银行  相关系数= 0.686388691468    | 负相关度最高:  恒瑞医药  相关系数= 0.0239442789959

ST山水  | 正相关度最高:  三安光电  相关系数= 0.621160353597    | 负相关度最高:  恒瑞医药  相关系数= -0.184459943941

广发证券  | 正相关度最高:  招商证券  相关系数= 0.921710029685    | 负相关度最高:  ST山水  相关系数= 0.250817065922

同仁堂  | 正相关度最高:  沪深300  相关系数= 0.675505803837    | 负相关度最高:  恒瑞医药  相关系数= 0.11457913001

ST中富  | 正相关度最高:  广发证券  相关系数= 0.563662524852    | 负相关度最高:  招商银行  相关系数= 0.0833174028769

保利地产  | 正相关度最高:  沪深300  相关系数= 0.746992561689    | 负相关度最高:  ST山水  相关系数= 0.176521765306

结论有点意思:

  • 同属一个版块,中石油、中石化关联度很高;券商关联度也很高
  • 正相关中,沪深300 和 广发证券 出现频率最高,莫非广发证券也能作为大势研判的一个指标了,连ST的都与它正相关最高?
  • 恒瑞医药出现在负相关中的频率最高,属于独立行情,适合在资产配置加入,降低风险

聚类分析

看看基于股价波动性聚类,会是什么结果?

from sklearn.cluster import affinity_propagation

_,labels=affinity_propagation(co)
df_c=pd.DataFrame({'label':labels,'name':symbols})
g=df_c.groupby('label')
for item in g:
    print item[0],','.join(item[1]['name'].values)
    print '============='
0 恒瑞医药
=============
1 中国石化,中国石油,工商银行,招商银行
=============
2 大族激光,沪深300,三安光电,万科a,招商证券,ST山水,广发证券,同仁堂,ST中富,保利地产
=============

果然和前面的数值分析有很大的相关性! 恒瑞医疗就是独行侠,第一个归类里就只有它。石油、石化、银行混在一起,嗯,也符合它们的身份吧。

验证一下每日股价波动是否符合正态分布

prices.pct_change().hist(column=[u'恒瑞医药',u'招商银行',u'保利地产',u'三安光电'],sharex=True,sharey=True,bins=30)
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000000000CEDA320>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000000000CEF4588>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000000000D210B00>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000000000D31CAC8>]], dtype=object)
output_13_1.png

从图表来看,所选样本波动率集中度都很高,也符合正太分布。

从投资组合的角度分析

仅选两支股票的投资组合

既然前面谈到了,恒瑞医药走出独立行情,那么就设定一种投资组合,都是恒瑞医药加上另一只票。如果投资风格是追求收益风险比值是最高,那么计算一下不同的组合中,如何配比可以达到最佳值。

恒瑞医药+万科a

group_dots=np.linspace(0,1,100,endpoint=False)
hryy_wka=pd.DataFrame({
    u'回报':pd.Series([(r*prices_norm[u'恒瑞医药']+(1-r)*prices_norm[u'万科a']).pct_change().mean() for r in group_dots],index=group_dots),
    u'风险':pd.Series([(r*prices_norm[u'恒瑞医药']+(1-r)*prices_norm[u'万科a']).pct_change().std() for r in group_dots],index=group_dots)
})
ax=hryy_wka.plot(secondary_y=u'风险')
ax.set_xlabel(u'恒瑞医药占比')
ax.set_ylabel(u'单日股价波动均值')
ax.right_ax.set_ylabel(u'单日股价波动标准差')
plt.title(u'恒瑞医药+万科a 不同配比的收益风险变化图')
<matplotlib.text.Text at 0x20e0b668>
output_15_1.png

绘制这个组合的风险回报比变化曲线

reward_over_risk=hryy_wka[u'回报']/hryy_wka[u'风险']
reward_over_risk.plot()
plt.xlabel(u'恒瑞医药占比')
plt.ylabel(u'回报/风险')
print  u'恒瑞医药最佳占比:', reward_over_risk.argmax()
恒瑞医药最佳占比: 0.14
output_17_1.png

分析表明:这个组合中 15%恒瑞医药+85%万科a 时,回报和风险比最高。

恒瑞医药+招商证券

group_dots=np.linspace(0,1,100,endpoint=False)
hryy_wka=pd.DataFrame({
    u'回报':pd.Series([(r*prices_norm[u'恒瑞医药']+(1-r)*prices_norm[u'招商证券']).pct_change().mean() for r in group_dots],index=group_dots),
    u'风险':pd.Series([(r*prices_norm[u'恒瑞医药']+(1-r)*prices_norm[u'招商证券']).pct_change().std() for r in group_dots],index=group_dots)
})
ax=hryy_wka.plot(secondary_y=u'风险')
ax.set_xlabel(u'恒瑞医药占比')
ax.set_ylabel(u'单日股价波动均值')
ax.right_ax.set_ylabel(u'单日股价波动标准差')
plt.title(u'恒瑞医药+招商证券 不同配比的收益风险变化图')
<matplotlib.text.Text at 0x213725f8>
output_19_1.png
reward_over_risk=hryy_wka[u'回报']/hryy_wka[u'风险']
reward_over_risk.plot()
plt.xlabel(u'恒瑞医药占比')
plt.ylabel(u'回报/风险')
print  u'恒瑞医药最佳占比:', reward_over_risk.argmax()
恒瑞医药最佳占比: 0.99
output_20_1.png

这个组合中,恒瑞医药几乎占据全部比例时,回报/风险值最佳。

恒瑞医药+三安光电

group_dots=np.linspace(0,1,100,endpoint=False)
hryy_wka=pd.DataFrame({
    u'回报':pd.Series([(r*prices_norm[u'恒瑞医药']+(1-r)*prices_norm[u'三安光电']).pct_change().mean() for r in group_dots],index=group_dots),
    u'风险':pd.Series([(r*prices_norm[u'恒瑞医药']+(1-r)*prices_norm[u'三安光电']).pct_change().std() for r in group_dots],index=group_dots)
})
ax=hryy_wka.plot(secondary_y=u'风险')
ax.set_xlabel(u'恒瑞医药占比')
ax.set_ylabel(u'单日股价波动均值')
ax.right_ax.set_ylabel(u'单日股价波动标准差')
plt.title(u'恒瑞医药+招商证券 不同配比的收益风险变化图')
<matplotlib.text.Text at 0x2166e0b8>
output_22_1.png
reward_over_risk=hryy_wka[u'回报']/hryy_wka[u'风险']
reward_over_risk.plot()
plt.xlabel(u'恒瑞医药占比')
plt.ylabel(u'回报/风险')
print  u'恒瑞医药最佳占比:', reward_over_risk.argmax()
恒瑞医药最佳占比: 0.89
output_23_1.png

这个组合中 89%恒瑞医药+11%三安光电 时,回报和风险比最高。

多支票组合的效果对比

如果同时选择恒瑞医药+沪深300+万科a,但是根据配比不同有以下三种方案:

  • 重仓恒瑞医药,恒瑞医药、沪深300和万科a三者配比为 50%:30%:20%
  • 重仓沪深300,恒瑞医药、沪深300和万科a三者配比为 20%:50%:30%
  • 重仓万科a,恒瑞医药、沪深300和万科a三者配比为 20%:30%:50%
portfolios=pd.DataFrame({
    u'重仓恒瑞医疗':0.5*prices_norm[u'恒瑞医药']+0.3*prices_norm[u'沪深300']+0.2*prices_norm[u'万科a'],
    u'重仓沪深300':0.2*prices_norm[u'恒瑞医药']+0.50*prices_norm[u'沪深300']+0.3*prices_norm[u'万科a'],
    u'重仓万科a':0.2*prices_norm[u'恒瑞医药']+0.30*prices_norm[u'沪深300']+0.5*prices_norm[u'万科a']
})
portfolios.plot()
print portfolios.pct_change().describe()
            重仓万科a      重仓恒瑞医疗     重仓沪深300
count  332.000000  332.000000  332.000000
mean     0.001767    0.001322    0.001347
std      0.023129    0.020336    0.020824
min     -0.123201   -0.148621   -0.133951
25%     -0.009271   -0.007207   -0.007543
50%      0.000218    0.001041    0.001170
75%      0.008352    0.008673    0.008061
max      0.136271    0.134773    0.152384
output_25_1.png

结论:

  • 重仓万科a收益率最高,但是风险值也更大,股价波动率标准差为0.023
  • 重仓恒瑞医药或沪深300,收益率相近,股价波动率标准差也相近,都在 0.020附近

推荐阅读更多精彩内容