学习记录656@python之MPT现代/均值-方差资产组合理论

理论就不多加叙述了,感兴趣的可以去查找MPT现代/均值-方差资产组合理论。
均值-方差理论的核心思想是同时考察资产组合的预期收益和风险,研究当我们有一系列可选资产的时候,应如何对其配置资金权重,从而可以得到最好的收益风险比,也就是最好的夏普比率。
在这里插入图片描述

以下模拟投资组合的预期收益率和波动情况

import pandas as pd
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('ggplot')

dates=pd.date_range(start='2010/1/1',end='2010/12/31')
df_rtn=pd.DataFrame(index=dates)
df_rtn['s1']=np.random.normal(0.1, 0.05, len(dates))
df_rtn['s2']=np.random.normal(0.2, 0.10, len(dates))
df_rtn['s3']=np.random.normal(0.3, 0.15, len(dates))
df_rtn['s4']=np.random.normal(0.4, 0.2, len(dates))
df_rtn['s5']=np.random.normal(-0.2, 0.10, len(dates))
df_rtn
s1s2s3s4s5
2010-01-010.1654960.0548650.5507570.416184-0.241358
2010-01-020.1238110.0661980.2368360.269195-0.276284
2010-01-030.0595120.3219740.1949330.144246-0.250190
2010-01-040.0566780.0036510.4996980.579080-0.235689
2010-01-050.0658280.1298300.2913510.3971760.001602
..................
2010-12-270.1046000.3355360.3034620.346302-0.243276
2010-12-280.0472310.1990150.475520-0.043252-0.236914
2010-12-290.1302820.1312290.3864820.6148670.059841
2010-12-300.0931230.2883650.4427050.326410-0.126081
2010-12-310.1186260.2132110.6014490.605520-0.116745

365 rows × 5 columns

df_rtn.plot(figsize=(10,6))
<AxesSubplot:>

在这里插入图片描述

df_rtn.mean()*252
s1     25.781191
s2     49.999966
s3     78.002242
s4    102.503923
s5    -50.197467
dtype: float64
df_rtn.cov()*252
s1s2s3s4s5
s10.677835-0.049152-0.003292-0.2645860.008482
s2-0.0491522.617314-0.1434900.3219910.113303
s3-0.003292-0.1434905.2427050.4430500.032384
s4-0.2645860.3219910.4430509.3585660.395169
s50.0084820.1133030.0323840.3951692.581551

随机一个投资组合权重

n=len(df_rtn.columns)
w=np.random.random(n)
w=w/np.sum(w)
w
array([0.21695834, 0.14631267, 0.1290212 , 0.20732207, 0.30038572])
# 投资组合收益率,s1~s5是每个资产的收益情况,每个资产的均值乘以不同的权重计算出投资组合的收益率
p_ret= np.sum(df_rtn.mean()*w)*252
p_ret
29.145739519785984
#投资组合波动率
p_vol=np.sqrt(np.dot(w.T,np.dot(df_rtn.cov()*252,w)))
p_vol
# 以上是某书籍中的波动率的衡量,感觉不太对,因为不是之前学过的标准的投资组合的风险计算方式,不知道是不是量化中特有的
0.9401632467991151

模拟多个权重的情况下,收益率和风险的变化情况,并作图,这个图就是经典的投资组合有效边界图

n=len(df_rtn.columns)

# 保存一系列权重对应的投资组合收益率
p_rets=[]

# 保存一系列权重对应的投资组合波动率
p_vols=[]

# 随机生成10000组权重
for i in range(10000):
    w=np.random.random(n)
    w/=np.sum(w)

    # # 投资组合收益率
    p_ret= np.sum(df_rtn.mean()*w)*252

    #投资组合波动率(需要注意的是,使用np生成的一维数据,实际上是N*1矩阵,而不是1*N矩阵)
    p_vol=np.sqrt(np.dot(w.T,np.dot(df_rtn.cov()*252,w)))

    p_rets.append(p_ret)
    p_vols.append(p_vol)

p_rets=np.array(p_rets)
p_vols=np.array(p_vols)
p_rets
array([37.37827321, 24.83548494, 27.16777023, ..., 51.47163399,
       26.03826012, 31.35167142])
p_vols
array([1.05508155, 0.89835554, 0.97479329, ..., 1.09094976, 0.78924616,
       0.91253653])
plt.figure(figsize=(10,6))
plt.scatter(p_vols, p_rets, c=p_rets/p_vols,marker='o')
plt.grid(True)

# x轴是风险,y轴是收益
plt.xlabel('volatility')
plt.ylabel('return')

# 夏普比率y/x的值,越黄的部分夏普比率越高,说明收益风险比最优秀
plt.colorbar(label='Sharpe ratio')
<matplotlib.colorbar.Colorbar at 0x1d6bd551588>

在这里插入图片描述

接下来求解最优投资组合权重,也就是求解最大的夏普比率下的投资组合权重

def portfolio_stat(weights):
    """获取投资组合的各种统计值

参数:
        weights:分配的资金权重

返回值:
        p_ret:投资组合的收益率
        p_vol:投资组合的波动率
        p_sr:投资组合的夏普比率

注意:rets是全局变量,在函数外部定义
    """

    w=np.array(weights)
    p_ret=np.sum(rets.mean()*w)*252
    p_vol=np.sqrt(np.dot(w.T,np.dot(rets.cov()*252,w)))
    p_sr=p_ret/p_vol

    return np.array([p_ret,p_vol,p_sr])
# 该函数返回了夏普比率的负值,换句话说,我们需要对该函数进行最小化操作。之所以使用这种形式,是因为scipy.optimization只有minimize函数(机器学习中优化目标通常是最小化损失函数),所以这里使用了这种看起来有点奇怪的形式。
def min_func_sharpe(weights):
    """优化的目标函数,最小化夏普比率的负值,即最大化夏普比率
    """
    return -portfolio_stat(weights)[2]
import scipy.optimize as sco

# 投资组合
rets=df_rtn

n=len(rets.columns)

# 优化的约束条件:资金的权重和为1,下面表示np.sum(x)-1 eq 0
cons=({'type':'eq','fun':lambda x:np.sum(x)-1})
 #参数取值范围
bnds=tuple((0,1) for x in range(n))

# 生成初始权重
w_initial=n*[1./n,]
# 计算最有权重
opts_sharpe=sco.minimize(min_func_sharpe,w_initial,method='SLSQP',bounds=bnds,constraints=cons)
opts_sharpe
     fun: -65.88146585394263
     jac: array([ 4.17900085e-03, -1.56126022e-02,  7.96985626e-03,  3.96728516e-04,
        7.72509413e+01])
 message: 'Optimization terminated successfully'
    nfev: 82
     nit: 12
    njev: 12
  status: 0
 success: True
       x: array([4.94327710e-01, 2.19114359e-01, 1.64106232e-01, 1.22451699e-01,
       1.55918074e-11])
# fun对应最优夏普比率,注意取负值,x为权重
opts_sharpe['x'].round(2)
array([0.49, 0.22, 0.16, 0.12, 0.  ])

接下来我们来实战一下,根据近一年比亚迪,长安汽车,长城汽车近一年的收益率情况,搭建投资组合,计算最有的投资权重。

import tushare as ts
ts.set_token('d56646c5b067c6715534aff6a6487d736a545e525bbb3bd5abf7f9ed')
df_byd = ts.pro_bar(ts_code='002594.SZ', adj='qfq', start_date='20220427', end_date='20220627')
df_byd.sort_values(by="trade_date",inplace=True)
df_byd
ts_codetrade_dateopenhighlowclosepre_closechangepct_chgvolamount
39002594.SZ20220427226.61235.81218.81235.01231.813.201.3804254341.675.794106e+06
38002594.SZ20220428234.60235.50230.00231.98235.01-3.03-1.2893146372.133.405250e+06
37002594.SZ20220429233.80243.83230.75243.00231.9811.024.7504248163.105.912448e+06
36002594.SZ20220505246.20252.39241.77248.80243.005.802.3868253258.796.282898e+06
35002594.SZ20220506241.02246.00240.10242.08248.80-6.72-2.7010145488.663.530097e+06
34002594.SZ20220509231.00236.89231.00232.73242.08-9.35-3.8624175116.354.086614e+06
33002594.SZ20220510224.10233.66222.28233.33232.730.600.2578209696.644.792959e+06
32002594.SZ20220511232.00256.66231.50252.70233.3319.378.3015459525.711.148248e+07
31002594.SZ20220512249.99257.70248.71254.09252.701.390.5501241156.066.107995e+06
30002594.SZ20220513259.80271.50255.91270.08254.0915.996.2930367120.619.733352e+06
29002594.SZ20220516271.11273.00265.30267.12270.08-2.96-1.0960202368.805.441552e+06
28002594.SZ20220517268.98285.00268.50282.50267.1215.385.7577293489.038.193619e+06
27002594.SZ20220518282.50288.00279.00281.42282.50-1.08-0.3823205302.705.812001e+06
26002594.SZ20220519275.31285.35274.30284.51281.423.091.0980173636.514.879757e+06
25002594.SZ20220520287.01291.61281.20287.70284.513.191.1212178683.365.125700e+06
24002594.SZ20220523292.70298.00287.81295.20287.707.502.6069255588.167.496178e+06
23002594.SZ20220524290.00292.96281.00281.00295.20-14.20-4.8103290496.538.303004e+06
22002594.SZ20220525281.26288.65278.03287.20281.006.202.2064230857.736.563212e+06
21002594.SZ20220526287.37291.22282.50286.64287.20-0.56-0.1950143094.994.106312e+06
20002594.SZ20220527292.95297.61286.65288.88286.642.240.7815153186.854.467796e+06
19002594.SZ20220530292.00299.70291.08298.12288.889.243.1986218523.506.485732e+06
18002594.SZ20220531300.00300.96293.51295.91298.12-2.21-0.7413177011.195.242211e+06
17002594.SZ20220601298.00304.80293.12299.98295.914.071.3754216911.756.501902e+06
16002594.SZ20220602296.00305.00295.22301.02299.981.040.3467177727.775.355774e+06
15002594.SZ20220606304.00320.47302.55318.23301.0217.215.7172294757.299.260503e+06
14002594.SZ20220607318.00319.87311.11314.89318.23-3.34-1.0496200220.896.296538e+06
13002594.SZ20220608318.60328.06313.01327.41314.8912.523.9760290513.409.321933e+06
12002594.SZ20220609334.03334.80321.00322.41327.41-5.00-1.5271307541.721.005064e+07
11002594.SZ20220610321.50350.00321.06348.80322.4126.398.1852438249.001.479062e+07
10002594.SZ20220613346.00358.86340.60348.80348.800.000.0000360681.321.257353e+07
9002594.SZ20220614342.00344.79327.23343.59348.80-5.21-1.4937404789.341.363462e+07
8002594.SZ20220615343.97346.23332.52335.00343.59-8.59-2.5001303947.871.028586e+07
7002594.SZ20220616335.50342.70333.41336.00335.001.000.2985190172.456.410739e+06
6002594.SZ20220617330.10341.20327.07338.00336.002.000.5952262147.598.830739e+06
5002594.SZ20220620338.08348.66335.02342.18338.004.181.2367245415.578.405362e+06
4002594.SZ20220621339.76341.80332.20341.00342.18-1.18-0.3448197827.396.666970e+06
3002594.SZ20220622342.42347.63338.00339.88341.00-1.12-0.3284186787.836.412052e+06
2002594.SZ20220623344.70354.05338.00353.50339.8813.624.0073323142.261.125178e+07
1002594.SZ20220624352.99356.28346.01351.05353.50-2.45-0.6931235983.558.273168e+06
0002594.SZ20220627352.07357.00348.33349.69351.05-1.36-0.3874217155.247.630685e+06
df_ca = ts.pro_bar(ts_code='000625.SZ', adj='qfq', start_date='20220427', end_date='20220627')
df_ca.sort_values(by="trade_date",inplace=True)
df_ca
ts_codetrade_dateopenhighlowclosepre_closechangepct_chgvolamount
39000625.SZ202204276.69757.03996.50726.97156.88770.08381.2167958948.858.507858e+05
38000625.SZ202204287.01717.35206.94107.24546.97150.27393.92891002657.739.455602e+05
37000625.SZ202204297.96857.96857.96857.96857.24540.72319.9801298727.483.127677e+05
36000625.SZ202205058.60028.76768.42518.76767.96850.799110.02821115967.051.268370e+06
35000625.SZ202205069.05689.39178.84378.96558.76760.19792.25723547775.544.247638e+06
34000625.SZ202205098.96559.12538.52408.66108.9655-0.3045-3.39642144091.462.474558e+06
33000625.SZ202205108.42518.69158.12078.58498.6610-0.0761-0.87871858279.402.059471e+06
32000625.SZ202205118.56219.44498.53939.30808.58490.72318.42293463359.414.132147e+06
31000625.SZ202205129.04169.28518.93509.10259.3080-0.2055-2.20782401654.432.867413e+06
30000625.SZ202205139.102510.01589.011110.01589.10250.913310.03353869386.934.886108e+06
29000625.SZ2022051610.061410.14519.74189.810310.0158-0.2055-2.05183231046.574.195913e+06
28000625.SZ202205179.718910.63989.703710.28219.81030.47184.80923594912.974.840887e+06
27000625.SZ2022051810.282111.00519.977710.175610.2821-0.1065-1.03583949048.205.460881e+06
26000625.SZ202205199.947310.57139.886410.510510.17560.33493.29123292688.104.464683e+06
25000625.SZ2022052010.388710.480010.160410.266910.5105-0.2436-2.31772470887.873.342778e+06
24000625.SZ2022052310.266910.632210.137510.434310.26690.16741.63052488559.943.394024e+06
23000625.SZ2022052410.708311.340010.655110.715910.43430.28162.69884130361.135.962842e+06
22000625.SZ2022052510.540911.294410.274511.066010.71590.35013.26713396696.864.816627e+06
21000625.SZ2022052610.883410.944310.578910.738811.0660-0.3272-2.95681916191.312.708203e+06
20000625.SZ2022052710.822511.111710.548510.769210.73880.03040.28311781256.442.532129e+06
19000625.SZ2022053010.891010.944310.578910.761610.7692-0.0076-0.07061570927.162.223216e+06
18000625.SZ2022053110.700710.708310.358210.617010.7616-0.1446-1.34371892289.812.621740e+06
17000625.SZ2022060110.883411.302010.845311.050810.61700.43384.08592930464.924.245724e+06
16000625.SZ2022060210.837712.085910.807311.804311.05080.75356.81853655846.095.568282e+06
15000625.SZ2022060611.735812.162011.568311.918411.80430.11410.96662941937.334.578257e+06
14000625.SZ2022060711.758611.781411.400911.629211.9184-0.2892-2.42651913290.762.911673e+06
13000625.SZ2022060811.621612.025011.492211.941311.62920.31212.68382254946.823.489635e+06
12000625.SZ2022060911.834711.834711.446611.492211.9413-0.4491-3.76091467712.922.230526e+06
11000625.SZ2022061011.340012.025011.294411.872811.49220.38063.31181876558.322.895509e+06
10000625.SZ2022061311.796713.052411.712912.930711.87281.05798.91033696010.896.177846e+06
9000625.SZ2022061412.687114.224512.641514.224512.93071.293810.00564249183.957.488059e+06
8000625.SZ2022061514.384315.221514.171214.171214.2245-0.0533-0.37474727900.379.067054e+06
7000625.SZ2022061614.171214.688813.996214.140814.1712-0.0304-0.21452970896.595.601199e+06
6000625.SZ2022061714.079914.460413.821114.399514.14080.25871.82952463060.394.609731e+06
5000625.SZ2022062014.841015.031214.338714.567014.39950.16751.16322629303.355.064239e+06
4000625.SZ2022062114.308215.449814.293015.373714.56700.80675.53793363875.716.578857e+06
3000625.SZ2022062215.388915.586814.993215.031215.3737-0.3425-2.22782619720.995.236025e+06
2000625.SZ2022062315.221516.538215.069316.538215.03121.507010.02583512679.337.302952e+06
1000625.SZ2022062416.705617.413416.226117.010016.53820.47182.85283504568.617.700924e+06
0000625.SZ2022062717.500018.710016.710018.500017.01001.49008.75965753928.731.027395e+07
df_cc = ts.pro_bar(ts_code='601633.SH', adj='qfq', start_date='20220427', end_date='20220627')
df_cc.sort_values(by="trade_date",inplace=True)
df_cc
ts_codetrade_dateopenhighlowclosepre_closechangepct_chgvolamount
39601633.SH2022042721.933722.931621.305122.772022.26300.50902.2863341470.02757962.925
38601633.SH2022042822.552423.241022.452622.931622.77200.15960.7009235284.09538410.271
37601633.SH2022042923.610224.927423.081324.767722.93161.83618.0069417307.701005261.810
36601633.SH2022050524.248825.117023.979424.707924.7677-0.0598-0.2414376912.00927626.444
35601633.SH2022050623.899624.009323.610223.759924.7079-0.9480-3.8368227978.49544080.521
34601633.SH2022050923.450523.819723.021423.280923.7599-0.4790-2.0160167211.01390822.373
33601633.SH2022051022.452623.171122.153323.001523.2809-0.2794-1.2001252818.28574889.646
32601633.SH2022051123.001525.306623.001525.306623.00152.305110.0215312912.35770334.450
31601633.SH2022051225.745727.521925.396426.823425.30661.51685.99371049395.342777063.170
30601633.SH2022051326.494129.507726.244629.507726.82342.684310.00731133286.693169086.473
29601633.SH2022051630.316030.316028.519828.938929.5077-0.5688-1.9276929327.242710805.031
28601633.SH2022051728.699531.832828.559731.832828.93892.893910.0000857405.122633159.103
27601633.SH2022051831.932635.016131.184233.599131.83281.76635.54871370972.184593804.484
26601633.SH2022051932.810836.922132.461535.684733.59912.08566.20731127528.773805122.945
25601633.SH2022052034.904535.844334.414634.524535.6844-1.1599-3.2504866565.333026872.497
24601633.SH2022052334.384635.614433.154834.004634.5245-0.5199-1.50591033487.103530852.856
23601633.SH2022052435.194435.964333.674734.364634.00460.36001.05871154648.954062426.732
22601633.SH2022052533.524737.694032.794837.094134.36462.72957.94281149982.274056564.929
21601633.SH2022052636.114336.594235.014535.224437.0941-1.8697-5.0404843588.023010855.738
20601633.SH2022052735.024536.654234.034634.844535.2244-0.3799-1.0785778321.462748631.008
19601633.SH2022053034.920035.300033.500034.020034.8500-0.8300-2.3816685725.842339470.648
18601633.SH2022053134.030034.040032.140033.200034.0200-0.8200-2.4103763013.822518375.766
17601633.SH2022060134.000034.800033.690033.790033.20000.59001.7771766761.942619704.685
16601633.SH2022060233.240036.490033.220035.470033.79001.68004.9719907016.543196874.468
15601633.SH2022060634.820037.240034.800036.360035.47000.89002.5092891169.063198770.206
14601633.SH2022060736.000036.160034.580035.090036.3600-1.2700-3.4928579871.202045914.414
13601633.SH2022060835.050037.500034.850037.390035.09002.30006.5546873766.343183634.937
12601633.SH2022060937.250037.250035.550035.580037.3900-1.8100-4.8409638815.972302870.364
11601633.SH2022061035.180037.200034.950036.520035.58000.94002.6419720066.722617492.454
10601633.SH2022061335.900038.970035.500038.440036.52001.92005.2574864491.143286654.568
9601633.SH2022061437.600041.990036.990041.230038.44002.79007.2581923584.903606184.652
8601633.SH2022061541.000044.520039.400039.440041.2300-1.7900-4.34151122114.774677419.539
7601633.SH2022061639.140040.260038.360038.610039.4400-0.8300-2.1045591751.332318071.822
6601633.SH2022061738.110039.790038.000039.100038.61000.49001.2691506979.991981230.766
5601633.SH2022062039.100039.710038.400038.940039.1000-0.1600-0.4092410093.721600731.789
4601633.SH2022062138.760038.920037.080038.320038.9400-0.6200-1.5922514513.571956043.991
3601633.SH2022062238.310039.280037.890037.990038.3200-0.3300-0.8612382944.071475721.830
2601633.SH2022062338.500040.490038.500040.410037.99002.42006.3701737531.292922807.528
1601633.SH2022062440.800041.190039.490039.910040.4100-0.5000-1.2373594487.882390130.687
0601633.SH2022062739.950042.330039.060040.950039.91001.04002.6059722113.372927615.901
# 计算收益率并且合并数据
df_byd['r_byd']=(df_byd['close'] - df_byd['close'].shift(1)) / df_byd['close'].shift(1)
df_ca['r_ca']=(df_ca['close'] - df_ca['close'].shift(1)) / df_ca['close'].shift(1)
df_cc['r_cc']=(df_cc['close'] - df_cc['close'].shift(1)) / df_cc['close'].shift(1)
df_byd=df_byd.dropna()
df_ca=df_ca.dropna()
df_cc=df_cc.dropna()

df_portfolio=pd.DataFrame([df_byd['r_byd'],df_ca['r_ca'],df_cc['r_cc']]).T
df_portfolio.head()
r_bydr_car_cc
38-0.0128930.0392890.007009
370.0475040.0998010.080069
360.0238680.100282-0.002414
35-0.0270100.022572-0.038368
34-0.038624-0.033964-0.020160
min(df_portfolio.r_byd)
-0.04810298102981026
import scipy.optimize as sco

# 投资组合
rets=df_portfolio

n=len(rets.columns)

# 优化的约束条件:资金的权重和为1,下面表示np.sum(x)-1 eq 0
cons=({'type':'eq','fun':lambda x:np.sum(x)-1})
 #参数取值范围
bnds=tuple((0,1) for x in range(n))

# 生成初始权重
w_initial=n*[1./n,]
# 计算最有权重
opts_sharpe=sco.minimize(min_func_sharpe,w_initial,method='SLSQP',bounds=bnds,constraints=cons)
opts_sharpe
     fun: -9.70554120401185
     jac: array([-6.16312027e-05,  2.01463699e-05,  1.61020207e+00])
 message: 'Optimization terminated successfully'
    nfev: 25
     nit: 6
    njev: 6
  status: 0
 success: True
       x: array([0.24684656, 0.75315344, 0.        ])
# fun对应最优夏普比率,注意取负值,x为权重
opts_sharpe['x'].round(2)
array([0.25, 0.75, 0.  ])
# 以上面投资组合计算预期收益率

rets=df_portfolio
portfolio_stat([0.25,0.75,0])
# 得到p_ret,p_vol,p_sr 预期收益率,风险,夏普比率
# 根据结果而言,这年画收益率是真的不高,我还是故意选择的这三个股票在这段时间涨的比较好的。
# 这里我没有检验各个样本是否符合正态分布,可能是组合表现不佳的原因之一,就不多加叙述了
array([5.63126075, 0.58021321, 9.70550256])
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值