使用akshare获取股票数据,利用月度数据计算每只上证50成分股的股票历史期望收益率(年化)、收益率标准差(年化)、夏普比率、以及股票之间月收益率的相关系数,并以夏普比率、相关系数为条件筛选股票。挑选5只股票组成篮子,篮子股票必须满足下列三个条件: A)过去3年的夏普比率大于0.2; B)过去1年的夏普比率大于0.1; C)这5只股票彼此之间的相关系数之和最小。

 

目录

1 获取数据

2 根据CAPM计算股票的历史期望收益率

获取三年间每个月的无风险利率rf,选取当月末十年期国债收益率为准,若非交易日则另选一天:

获取市场平均月收益率:

 计算成分股的β(beta_list):

计算期望收益率 E(ri​)=rf+β(E(rm​)−rf​):

 画历史预期收益率曲线图:

3 计算收益率标准差(年化),夏普比率,相关系数

计算收益率标准差:

 计算夏普比率(简单认为无风险收益率为0.03):

 计算相关系数并画:

4 以夏普比率、相关系数为条件筛选五只股票

挑选5只股票组成篮子,篮子股票必须满足下列三个条件: A)过去3年的夏普比率大于0.2; B)过去1年的夏普比率大于0.1; C)这5只股票彼此之间的相关系数之和最小。


1 获取数据

import akshare as ak
SH50_codes = [600000,600028,600030,600031,600036,600048,600050,600104,600196,600276,600309,600436,600438,600519,600547,600570,600585,
            600588,600690,600745,600809,600837,600887,600893,600900,601012,601066,601088,601138,601166,601211,601288,601318,601336,
            601398,601601,601628,601633,601668,601688,601728,601857,601888,601899,601919,601995,603259,603288,603501,603986]
SH50_codes = list(map(str,SH50_codes))
SH50_quotations_dic = {}
for i in SH50_codes:
    SH50_quotations_dic[i] = ak.stock_zh_a_hist(symbol=i, period='monthly', start_date="20190101", end_date='20211231', adjust="")
SH50_quotations_dic['600000']

数据预览: 

日期开盘收盘最高最低成交量成交额振幅涨跌幅涨跌额换手率年化期望收益率
02019-01-319.7410.7310.739.5844041944.490181e+0911.739.490.931.5773.959513
12019-02-2810.8211.7412.1510.6253152996.002415e+0914.269.411.011.8993.274971
22019-03-2911.8111.2812.3810.9095307811.102880e+1012.61-3.92-0.463.3931.214703
32019-04-3011.3611.9712.2011.28120433551.408016e+108.166.120.694.2934.030901
42019-05-3111.7511.1311.9410.9595054171.080917e+108.27-7.02-0.843.38-63.805606
52019-06-2811.1711.6812.3211.1381906929.600306e+0910.694.940.552.9165.973455
62019-07-3111.8611.8712.0011.2269389768.059537e+096.681.630.192.47-4.536585
72019-08-3011.7611.2811.8510.9769563917.886875e+097.41-4.97-0.592.48-11.354091
82019-09-3011.3011.8412.1811.2467768328.007390e+098.334.960.562.418.588689
92019-10-3111.8012.5113.3311.80108746261.386751e+1012.925.660.673.8718.232174
102019-11-2912.5011.9113.1911.8981049011.004877e+1010.39-4.80-0.602.88-17.046202
112019-12-3111.9612.3712.5511.7064768287.865092e+097.143.860.462.3052.115898
122020-01-2312.4711.3512.6911.3257162966.964347e+0911.08-8.25-1.022.03-36.791364
132020-02-2810.2210.8511.3910.2281766148.916090e+0910.31-4.41-0.502.91-32.755988
142020-03-3110.9510.1511.329.8287482869.265608e+0913.82-6.45-0.703.11-40.461173
152020-04-3010.1110.6310.799.9844443444.553876e+097.984.730.481.5857.243722
162020-05-2910.4410.5710.5710.1338016283.948919e+094.14-0.56-0.061.35-16.287209
172020-06-3010.6310.5810.8710.3546114934.880872e+094.920.090.011.6443.221173
182020-07-3110.5910.3612.6910.31179038732.040465e+1022.50-2.08-0.226.3792.779477
192020-08-3110.4010.3610.9810.31105792391.114334e+106.470.000.003.7626.443089
202020-09-3010.319.3910.369.35107585271.062955e+109.75-9.36-0.973.67-28.505517
212020-10-309.449.269.939.2586597838.302952e+097.24-1.38-0.132.9513.887474
222020-11-309.2810.0610.539.22152119531.477253e+1014.158.640.805.1851.617372
232020-12-3110.089.6810.399.44108023661.063068e+109.44-3.78-0.383.6844.363546
242021-01-299.649.9610.239.52135550101.336269e+107.332.890.284.6218.379333
252021-02-269.9410.5411.129.90183407191.945517e+1012.255.820.586.2511.180857
262021-03-3110.5910.9911.2410.36172645111.866246e+108.354.270.455.88-50.170857
272021-04-3010.9910.0511.019.96103307351.084681e+109.55-8.55-0.943.52-11.142814
282021-05-319.9710.2710.439.8882081938.330746e+095.472.190.222.8040.989192
292021-06-3010.2310.0010.389.9688752548.996571e+094.09-2.63-0.273.02-35.749590
302021-07-3010.019.0310.089.0193682269.110368e+0910.70-9.70-0.973.19-91.401168
312021-08-319.039.059.398.9277479287.090572e+095.200.220.022.64-9.289064
322021-09-309.039.009.488.9483861267.700020e+095.97-0.55-0.052.8629.225518
332021-10-299.058.949.258.9351460494.658420e+093.56-0.67-0.061.7520.175154
342021-11-308.698.518.788.4459985925.155727e+093.80-4.81-0.432.04-26.740997
352021-12-318.538.538.818.5167121355.783292e+093.530.240.022.2931.048761

2 根据CAPM计算股票的历史期望收益率

计算期望收益率 E(ri​)=rf+β(E(rm​)−rf​)

E(ri​):股票i的预期收益率

rf: 无风险利率,

E(rm​):市场指数收益率,即市场的预期平均收益率;

β: β系数,即资产标的系统性风险。


获取三年间每个月的无风险利率rf,选取当月末十年期国债收益率为准,若非交易日则另选一天:

#获取三年间每个月的无风险利率rf,选取当月末十年期国债收益率为准,若非交易日则另选一天
bond_china_yield_df1 = ak.bond_china_yield(start_date="20190101", end_date='20191231')
bond_china_yield_df2 = ak.bond_china_yield(start_date="20200101", end_date='20201231')
bond_china_yield_df3 = ak.bond_china_yield(start_date="20210101", end_date='20211231')
bond_china_yield_df = pd.concat([bond_china_yield_df1,bond_china_yield_df2,bond_china_yield_df3])
bond_china_yield_df = bond_china_yield_df[bond_china_yield_df['曲线名称']=='中债国债收益率曲线']
bond_china_yield_df = bond_china_yield_df.drop(['3月','6月','1年','3年','5年','7年','30年'],axis=1)
bond_china_yield_df['日期'] = bond_china_yield_df['日期'].astype(str)
#riqi = bond_china_yield_df['日期'].astype(str).str.split("-",n=1).str[-1]
#bond_china_yield_df['riqi'] = riqi
choosendays = pd.DataFrame(['2019-01-31','2019-02-28','2019-03-29','2019-04-30','2019-05-31','2019-06-28','2019-07-31','2019-08-29','2019-09-30','2019-10-31','2019-11-28','2019-12-31',
'2019-12-31','2020-02-28','2020-03-31','2020-04-30','2020-05-29','2020-06-30','2020-07-31','2020-08-31','2020-09-30','2020-10-29','2020-11-30','2020-12-31',
'2021-01-29','2021-02-26','2021-03-31','2021-04-30','2021-05-31','2021-06-30','2021-07-29','2021-08-31','2021-09-30','2021-10-29','2021-11-30','2021-12-31'],columns=['日期'])
#monthend = pd.DataFrame(['01-31','02-28','03-31','04-30','05-31','06-30','07-31','08-31','09-30','10-31','11-30','12-31'],columns=['riqi'])
bond_china_yield_df = pd.merge(bond_china_yield_df, choosendays, on = ['日期'], how = 'inner').sort_values(by=['日期'] ,axis=0,ascending=True)
rf_list_year = list(bond_china_yield_df['10年'])
rf_list = []
for r in rf_list_year:
    rf_list.append(r/12)

获取市场平均月收益率:

#获取市场平均月收益率
rm_list = list(ak.index_zh_a_hist(symbol='000016', period='monthly', start_date="20190101", end_date='20211231')['涨跌幅'])

 计算成分股的β(beta_list):

#计算成分股的β(beta_list)
beta_list,alpha_list,r_value_list,p_value_list,std_err_list = [],[],[],[],[]
codetoremove = []
for code in SH50_codes:
    try:
        beta,alpha,r_value,p_value,std_err=stats.linregress(rm_list,list(SH50_quotations_dic[code]['涨跌幅']))
        beta_list.append(beta)
        alpha_list.append(alpha)
        r_value_list.append(r_value)
        p_value_list.append(p_value)
        std_err_list.append(std_err)
        print(code,"计算完毕")
    except:
        print(code,"为中途新加入的成分")
        codetoremove.append(code)
for code in codetoremove:
    SH50_codes.remove(code)
    print(code,"已从SH50_codes中移除")

输出:

600000 计算完毕
600028 计算完毕
600030 计算完毕
600031 计算完毕
600036 计算完毕
600048 计算完毕
600050 计算完毕
600104 计算完毕
600196 计算完毕
600276 计算完毕
600309 计算完毕
600436 计算完毕
600438 计算完毕
600519 计算完毕
600547 计算完毕
600570 计算完毕
600585 计算完毕
600588 计算完毕
600690 计算完毕
600745 计算完毕
600809 计算完毕
600837 计算完毕
600887 计算完毕
600893 计算完毕
600900 计算完毕
601012 计算完毕
601066 计算完毕
601088 计算完毕
601138 计算完毕
601166 计算完毕
601211 计算完毕
601288 计算完毕
601318 计算完毕
601336 计算完毕
601398 计算完毕
601601 计算完毕
601628 计算完毕
601633 计算完毕
601668 计算完毕
601688 计算完毕
601728 为中途新加入的成分
601857 计算完毕
601888 计算完毕
601899 计算完毕
601919 计算完毕
601995 为中途新加入的成分
603259 计算完毕
603288 计算完毕
603501 计算完毕
603986 计算完毕
601728 已从SH50_codes中移除
601995 已从SH50_codes中移除

计算期望收益率 E(ri​)=rf+β(E(rm​)−rf​):

#计算期望收益率 E(ri​)=rf+β(E(rm​)−rf​)
def calEr(rf,beta,rm):
    #已经将月度收益率转化为年化收益率
    return 12 * (rf + beta * (rm - rf))
for code,beta in zip(SH50_codes,beta_list):
    Er = []
    for rf,rm in zip(rf_list,rm_list):
        Er.append(calEr(rf,beta,rm))
    SH50_quotations_dic[code]['年化期望收益率'] = pd.DataFrame(Er)

 画历史预期收益率曲线图:

# 画收益率曲线图
lines = []
for code in SH50_codes:
    lines.append(go.Scatter(y=SH50_quotations_dic[code]['年化期望收益率'], x=SH50_quotations_dic[code]['日期'],mode='lines+markers', name=code))
fig = go.Figure(lines)
fig.update_layout(
    title = '年化期望收益率曲线', #定义生成的plot 的标题
    xaxis_title = '时间', #定义x坐标名称
    yaxis_title = '年化期望收益率(%)'#定义y坐标名称
)
fig.show() 

输出:

3 计算收益率标准差(年化),夏普比率,相关系数

计算收益率标准差:

#计算收益率标准差
for code in SH50_codes:
    returns = SH50_quotations_dic[code]['涨跌幅']
    print(code,"年化收益率标准差为:",empyrical.annual_volatility(returns, period='monthly', alpha=2.0, annualization=True))

600000 年化收益率标准差为: 5.406010081381646
600028 年化收益率标准差为: 5.3025414391860926
600030 年化收益率标准差为: 10.05529988976042
600031 年化收益率标准差为: 9.71219620676653
600036 年化收益率标准差为: 6.796814724326785
600048 年化收益率标准差为: 9.456735854187373
600050 年化收益率标准差为: 5.476258426079467
600104 年化收益率标准差为: 7.457042692266534
600196 年化收益率标准差为: 19.474689416192057
600276 年化收益率标准差为: 11.208026460660108
600309 年化收益率标准差为: 11.508608051615724
600436 年化收益率标准差为: 10.85135763578376
600438 年化收益率标准差为: 18.548525950075792
600519 年化收益率标准差为: 8.891268268845083
600547 年化收益率标准差为: 10.30548965665834
600570 年化收益率标准差为: 13.404746387540712
600585 年化收益率标准差为: 8.899909309329264
600588 年化收益率标准差为: 13.983813806377306
600690 年化收益率标准差为: 8.677304218019758
600745 年化收益率标准差为: 17.355014889806256
600809 年化收益率标准差为: 14.949436194709234
600837 年化收益率标准差为: 8.705648230379579
600887 年化收益率标准差为: 7.866874064053812
600893 年化收益率标准差为: 16.575248737629423
600900 年化收益率标准差为: 5.3488282354234835
601012 年化收益率标准差为: 14.575636521263828
601066 年化收益率标准差为: 17.833851029423858
601088 年化收益率标准差为: 8.235256662763298
601138 年化收益率标准差为: 9.627002842534054
601166 年化收益率标准差为: 7.3978005901368125
601211 年化收益率标准差为: 6.930288365241148
601288 年化收益率标准差为: 3.0379813029091207
601318 年化收益率标准差为: 7.677028629061413
601336 年化收益率标准差为: 8.590617805822482
601398 年化收益率标准差为: 4.239356270390776
601601 年化收益率标准差为: 9.380441468441512
601628 年化收益率标准差为: 11.104015014599199
601633 年化收益率标准差为: 19.2355834369205
601668 年化收益率标准差为: 4.611694313855206
601688 年化收益率标准差为: 9.182674935347213
601857 年化收益率标准差为: 7.23370060511523
601888 年化收益率标准差为: 18.286591809301154
601899 年化收益率标准差为: 13.813081064413671
601919 年化收益率标准差为: 17.790006582526868
603259 年化收益率标准差为: 13.300538940626094
603288 年化收益率标准差为: 11.388728749938798
603501 年化收益率标准差为: 15.104596357106374
603986 年化收益率标准差为: 19.998498316639548

 计算夏普比率(简单认为无风险收益率为0.03):

#计算夏普比率(简单认为无风险收益率为0.03)
for code in SH50_codes:
    returns = SH50_quotations_dic[code]['涨跌幅']
    print(code,"过去三年的夏普比率为:",empyrical.sharpe_ratio(returns, risk_free=0.03, period='monthly', annualization=None))
600000 过去三年的夏普比率为: -0.17461448941356442
600028 过去三年的夏普比率为: -0.2536945995877614
600030 过去三年的夏普比率为: 0.6300614624491767
600031 过去三年的夏普比率为: 1.1596904821171
600036 过去三年的夏普比率为: 1.0387264958792537
600048 过去三年的夏普比率为: 0.4344849511592825
600050 过去三年的夏普比率为: -0.4097629935731201
600104 过去三年的夏普比率为: -0.219882711171681
600196 过去三年的夏普比率为: 0.6256827603687464
600276 过去三年的夏普比率为: 0.14217371350402006
600309 过去三年的夏普比率为: 1.267464708647196
600436 过去三年的夏普比率为: 1.6330495092973676
600438 过去三年的夏普比率为: 1.1910027707620674
600519 过去三年的夏普比率为: 1.5077796378676218
600547 过去三年的夏普比率为: -0.27236789829745867
600570 过去三年的夏普比率为: 0.36050079676320834
600585 过去三年的夏普比率为: 0.4832925096162439
600588 过去三年的夏普比率为: 0.5825601235158362
600690 过去三年的夏普比率为: 0.9941538357301389
600745 过去三年的夏普比率为: 1.2908172558333204
600809 过去三年的夏普比率为: 1.692852256758357
600837 过去三年的夏普比率为: 0.4951821961868133
600887 过去三年的夏普比率为: 0.8485112775328982
600893 过去三年的夏普比率为: 0.8720209402558546
600900 过去三年的夏普比率为: 0.7142002160096603
601012 过去三年的夏普比率为: 1.299625049271544
601066 过去三年的夏普比率为: 0.906251740515804
601088 过去三年的夏普比率为: 0.38757562379349014
601138 过去三年的夏普比率为: 0.17681734092151608
601166 过去三年的夏普比率为: 0.4272881764382275
601211 过去三年的夏普比率为: 0.31518291928363057
601288 过去三年的夏普比率为: -0.6236612225283666
601318 过去三年的夏普比率为: -0.018801228218821013
601336 过去三年的夏普比率为: 0.03763596030626062
601398 过去三年的夏普比率为: -0.2542179599328554
601601 过去三年的夏普比率为: 0.09447664762768529
601628 过去三年的夏普比率为: 0.504955491946649
601633 过去三年的夏普比率为: 1.3859788042149317
601668 过去三年的夏普比率为: -0.2186698426985567
601688 过去三年的夏普比率为: 0.2312710354141348
601857 过去三年的夏普比率为: -0.4041246690427876
601888 过去三年的夏普比率为: 0.949695617331529
601899 过去三年的夏普比率为: 0.9629414241220862
601919 过去三年的夏普比率为: 1.133334805528026
603259 过去三年的夏普比率为: 0.5435409537143506
603288 过去三年的夏普比率为: 0.5355069452832935
603501 过去三年的夏普比率为: 1.7875848483960486
603986 过去三年的夏普比率为: 0.8448231017090055

 计算相关系数并画:

#计算股票间的月收益率相关系数
#相关性分析
def my_cor_draw(data,my_method,sizex,sizey):
    """
    data: dataframe数据类型
    my_method: 检验方法,备选{‘pearson’, ‘kendall’, ‘spearman’}
    """
    
    cor_data = data.corr(method=my_method)
    #可视化
    #print(cor_data)
    import matplotlib.pyplot as mp,seaborn
    names = []
    for name ,dd in data.iteritems():
        names.append(name)
    seaborn.heatmap(cor_data, center=0, annot=True,xticklabels=names , yticklabels=names,cmap ='YlGnBu')
    mp.rcParams['figure.figsize'] = (sizex, sizey) # 设置figure_size尺寸
    mp.rcParams['font.sans-serif']=['SimHei']#黑体
    # 设置刻度字体大小
    mp.xticks(fontsize=12)
    mp.yticks(fontsize=12,wrap = True)
    # 设置坐标标签字体大小
    ax = mp.gca()
    ax.set_xlabel(..., fontsize=12)
    ax.set_ylabel(..., fontsize=12,wrap = True)
    # 设置图例字体大小
    #ax.legend(..., fontsize=20)
    mp.show()
    
    return cor_data
data_all = pd.DataFrame()
for code in SH50_codes:
    data_all[code] = pd.DataFrame(SH50_quotations_dic[code]['涨跌幅'])
cor_matrix1 = my_cor_draw(data_all,'pearson',35,30)

 输出:

4 以夏普比率、相关系数为条件筛选五只股票

条件:

挑选5只股票组成篮子,篮子股票必须满足下列三个条件: A)过去3年的夏普比率大于0.2; B)过去1年的夏普比率大于0.1; C)这5只股票彼此之间的相关系数之和最小。

#筛选满足条件1、2的股票
stockpool1 = []
for code in SH50_codes:
    returns = SH50_quotations_dic[code]['涨跌幅']
    if(empyrical.sharpe_ratio(returns, risk_free=0.03, period='monthly', annualization=None)>0.2):
        stockpool1.append(code)
stockpool2 = []
for code in stockpool1:
    returns = SH50_quotations_dic[code]['涨跌幅'][-12:-1]
    if(empyrical.sharpe_ratio(returns, risk_free=0.03, period='monthly', annualization=None)>0.1):
        stockpool2.append(code)
print("满足条件1、2的股票有:",stockpool2,"  数量为",len(stockpool2))

#筛选满足条件3的股票
def get_subset(mylist,num):
    result = []
    n =len(mylist)
    for i in combinations(mylist,num):
        result.append(list(i))
    return result
mincor = 1000
mingroup = []
for group in get_subset(stockpool2,5):
    cor_matrix2_temp = data_all[group].corr(method='pearson')
    corsum = (sum(sum(abs(cor_matrix2_temp.values)))-5)/2
    if(corsum<mincor):
        mincor = corsum
        mingroup = group
print("最终组合为:",mingroup,"相关系数之和为:",mincor)    

输出:

满足条件1、2的股票有: ['600036', '600196', '600309', '600436', '600438', '600745', '600893', '600900', '601088', '601633', '601899', '601919', '603259', '603501']   数量为 14
最终组合为: ['600436', '600745', '601088', '601633', '601899'] 相关系数之和为: 1.3162840831751845
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐