[MPT] 经典投资组合理论的优化 RiceQuant - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
thinkingmind
V2EX    推广

[MPT] 经典投资组合理论的优化 RiceQuant

  •  
  •   thinkingmind 2016-11-18 17:43:31 +08:00 1129 次点击
    这是一个创建于 3339 天前的主题,其中的信息可能已经有所发展或是发生改变。

    新鲜出品:https://www.ricequant.com/community/topic/1568/?utm_source=v2ex?

    米筐这么厉害,你都听说过! 那你一定也很厉害!

    均值-方差投资组合理论( MPT )是金融理论的重要基础, Harry Markowitz 也凭其获得了 1990 年的诺贝尔经济学奖,可见其意义之大。这里主要介绍了如何用程序实现它,希望对大家有用。

    这里简单介绍一下均值方差理论的内容,这些基本知识也能很方便地从 wiki 上找到 。

    均值-方差理论的核心思想是:如何实现在一定风险水平下最大化资产组合的收益,或者如何在给定收益水平的条件下最小化资产组合的风险。根据其核心思想,不难倒推出其内含的假设条件,那就是投资者是理性的,并且是风险厌恶的。如果两个资产组合拥有相同的预期回报,投资者便会选择风险较小的哪一个,而也只有在获得更高预期回报的前提下,投资者才会才会承担更大的风险。

    均值方差模型最重要的概念莫过于马科维茨有效边界( Markowitz Efficient Frontier )了,其代表所有最佳投资组合的集合。什么意思呢?简言之,就是其核心思想在图表中的具体表现形式,它代表那些在给定任意一个相同预期回报条件下的风险最低的投资组合。在这一系列的最佳投资组合中,我们再选出一个更好的,那如何衡量这个更好的投资组合呢?我们使用夏普比率( Sharpe Ratio ),夏普值越大,说明单位风险内获得的收益越高。在最佳投资组合中夏普值最高的这一点所构成的资产组合便被称为市场投资组合( Market Portfolio )

    资本市场线( Capital Market Line, CML ) 马科维茨有效前沿曲线上的投资组合里并不包含无风险资产,如果将市场投资组合和无风险资产组合在一起,其便是著名的资本市场线。资本市场线上的每一点代表的投资组合比马科维茨有效边界上的投资组合更优,其能够通过改变市场投资组合和无风险资产之间任意配比而达到资本市场线上的任意一点(前提是允许卖空)。其方程可以表示为:

    我是信息的传递者

    MPT :通过分散投资实现投资组合风险最小化或者在指定风险水平下的组合收益最大化

    In [1]:

    import numpy as np import pandas as pd import matplotlib.pyplot as plt 

    In [2]:

    # 选股自选股里面的股票作为说明对象 symbols = ['600519.XSHG', '600036.XSHG', '000651.XSHE', '002007.XSHE', '600600.XSHG'] noa = len(symbols) 

    In [3]:

    startDate = '2013-01-01' endDate = '2015-06-01' data = get_price(symbols, start_date=startDate, end_date=endDate, fields='ClosingPx').dropna(axis=1, how='any') data.head() 

    In [4]:

    #规范化后的不同股票的时间序列图 (data / data.ix[0]).plot(figsize=(12, 7), linewidth=2) 

    Out[4]:<matplotlib.axes._subplots.AxesSubplot at 0x7f912261aac8>

    In [5]:

     # 均值-方差指的是不同证券(对数)收益的均值和方差,所以这里我们计算出对数收益率 rets = np.log(data).diff().dropna() # 这里默认使用 252 个交易日,从每日收益得出年化收益率 rets.mean() * 252 

    Out [5]: 600519.XSHG 0.159058 600036.XSHG 0.143368 000651.XSHE 0.409322 002007.XSHE 0.492317 600600.XSHG 0.194959 dtype: float64

    In [6]:

     # 继续计算收益率的协方差矩阵 rets.cov() * 252 

    In [7]:

     # 由于 A 股目前做空比较麻烦,这里我们只考虑进行多头操作,并且资产权重之和为 1 weights = np.random.random(noa) weights /= np.sum(weights) weights 

    Out[7]:array([ 0.24983943, 0.02577738, 0.42605211, 0.15194329, 0.14638779])

    In [8]:

     # 计算预期资产组合收益 print (np.sum(rets.mean() * weights) * 252) # 计算投资组合方差 print (np.dot(weights.T, np.dot(rets.cov() * 252, weights))) 

    0.321171021971 0.0576476704425

    In [9]:

     # 这里我们应用蒙特卡洛模拟,生成较大规模的随机投资组合权重向量。对于每一种模拟的分配,我们记录预期投资组合收益和方差 prets = [] pvols = [] for p in range(2500): weights = np.random.random(noa) weights /= np.sum(weights) prets.append(np.sum(rets.mean() * weights) * 252) pvols.append(np.sqrt(np.dot(weights.T, np.dot(rets.cov() * 252, weights)))) prets = np.array(prets) pvols = np.array(pvols) 

    In [10]:

     # 根据上面的 MC 结果,我们这里画出收益和方差的散点图 plt.figure(figsize=(12, 7)) plt.scatter(pvols, prets, c=prets/pvols, marker='o') plt.grid() plt.xlabel('Expected Volatility') plt.ylabel('Expected Returns') plt.colorbar(label='Sharpe Ratio') 

    Out[10]:<matplotlib.colorbar.Colorbar at 0x7f91176b5e48>

    In [11]:

     # 投资组合优化 def statistics(weights): ''' Returns portfolio statistics. Parameters =========== weights: array-like weights for different securities in portfolio Returns ======= pret : float expected portfolio return pvol : float expected portfolio volatility pret / pvol : float Sharpe ratio for rf = 0 ''' weights = np.array(weights) pret = np.sum(rets.mean() * weights) * 252 pvol = np.sqrt(np.dot(weights.T, np.dot(rets.cov() * 252, weights))) return np.array([pret, pvol, pret / pvol]) 

    最大化夏普比率

    In [12]:

     # 最优化投资组合的推导是一个约束最优化问题,我们使用 scipy.optimize 子库的 minimize 函数求解 # 最大化夏普值,或者说最小化夏普指数的负值,约束是所有股票的权重之和为 1 import scipy.optimize as sco def min_func_sharpe(weights): return -statistics(weights)[-1] cOns= ({'type':'eq', 'fun':lambda x: np.sum(x) - 1}) bnds = tuple((0, 1) for x in range(noa)) initGuess = noa * [1. / noa, ] opts = sco.minimize(min_func_sharpe, initGuess, method='SLSQP', bounds=bnds, cOnstraints=cons) opts 

    Out [12]: nfev: 49 fun: -1.5561085657595923 jac: array([ -7.70390034e-05, 1.89436048e-01, -2.55107880e-05, -6.16908073e-06, 1.00284815e-04, 0.00000000e+00]) status: 0 success: True x: array([ 0.03241208, 0. , 0.33520011, 0.49175377, 0.14063404]) message: 'Optimization terminated successfully.' njev: 7 nit: 7

    In [13]:

     # 找出最优投资组合的权重 print (opts['x'].round(3)) # 计算最优投资组合的收益,方差以及夏普值 print (statistics(opts['x']).round(3)) 

    [ 0.032 0. 0.335 0.492 0.141] [ 0.412 0.265 1.556]

    最小化投资组合的方差

    In [14]:

     def min_func_variance(weights): return statistics(weights)[1] ** 2 optv = sco.minimize(min_func_variance, initGuess, method='SLSQP', bounds=bnds, cOnstraints=cons) optv 

    out [14]: nfev: 42 fun: 0.041410632902538005 jac: array([ 0.08288015, 0.08279849, 0.08224446, 0.08318804, 0.08272194, 0. ]) status: 0 success: True x: array([ 0.21769845, 0.18647432, 0.04666651, 0.15638665, 0.39277407]) message: 'Optimization terminated successfully.' njev: 6 nit: 6

    In [15]:

     # 找出最优投资组合的权重 print (optv['x'].round(3)) # 计算最优投资组合的收益,方差以及夏普值 print (statistics(optv['x']).round(3)) 

    [ 0.218 0.186 0.047 0.156 0.393] [ 0.234 0.203 1.15 ]

    有效边界( Efficient Frontier )

    In [16]:

     # 有效边界 cOns= ({'type':'eq', 'fun':lambda x : statistics(x)[0] - tret}, {'type' : 'eq', 'fun':lambda x : np.sum(x) - 1}) bnds = tuple((0, 1) for x in weights) # 从 statistics 函数返回波动率的值 def min_func_port(weights): return statistics(weights)[1] trets = np.linspace(0.2, 0.5, 30) tvols = [] for tret in trets: cOns= ({'type':'eq', 'fun':lambda x : statistics(x)[0] - tret}, {'type':'eq', 'fun': lambda x : np.sum(x) - 1}) res = sco.minimize(min_func_port, initGuess, method='SLSQP', bounds=bnds, cOnstraints=cons) tvols.append(res['fun']) tvols = np.array(tvols) tvols 

    Out[16]: array([ 0.20602219, 0.20471964, 0.20388503, 0.20351338, 0.20360723, 0.20416592, 0.20518566, 0.20665984, 0.20857818, 0.21092921, 0.21369842, 0.21686981, 0.22042602, 0.22434874, 0.22861911, 0.23321803, 0.23814288, 0.24345364, 0.24913609, 0.25516538, 0.26151745, 0.26816956, 0.27509983, 0.28230594, 0.28986843, 0.2977758 , 0.31012345, 0.33066182, 0.35798614, 0.36593712])

    In [17]:

     plt.figure(figsize=(12, 7)) # Random portfolio composition plt.scatter(pvols, prets, c=prets / pvols, marker='o') # Efficient frontier plt.scatter(tvols, trets, c=trets / tvols, marker='x') # Portfolio with highest Sharpe ratio plt.plot(statistics(opts['x'])[1], statistics(opts['x'])[0], 'r*', markersize=15.0) # Minimum variance portfolio plt.plot(statistics(optv['x'])[1], statistics(optv['x'])[0], 'k*', markersize=15.0) plt.grid() plt.xlabel('Expected Volatitlity') plt.ylabel('Expected Return') plt.colorbar(label='Sharpe Ratio'); 

    资本市场线( CML)

    马科维茨有效组合本身是不包含无风险资产的,如果我们引入无风险资产构建一个新的投资组合,那么便构成了资本市场线,其在给定条件下优于有效边界上的组合(夏普值更大)。通过调整投资于风险资产组合和无风险资产的权重比例,我们可以实现任何风险---收益均衡性,这些收益位于无风险资产和有效投资组合之间的直线上

    In [18]:

     import scipy.interpolate as sci # 找出最小波动率对应的位置 ind = np.argmin(tvols) # 选取有效边界上的点进行插值 evols = tvols[ind:] erets = trets[ind:] tck = sci.splrep(evols, erets) 

    In [19]:

     # 为有效边界定义一个连续可微的函数 f(x)和对应的一阶导数函数 df(x) def f(x): '''Efficient frontier function(spline approximation)''' return sci.splev(x, tck, der=0) def df(x): '''First derivative of efficient frontier function''' return sci.splev(x, tck, der=1) # 如果这里有看不明白的 欢迎在评论区提问 def equations(p, rf=0.05): '''p=[a, b, x] 分别表示 CML 的截距,斜率以及波动率''' eq1 = rf - p[0] eq2 = rf + p[1] * p[2] - f(p[2]) eq3 = p[1] - df(p[2]) return eq1, eq2, eq3 

    In [20]:

     # 解出的无风险利率也恰好是 0.05 opt = sco.fsolve(equations, [.05, 1, .25]) opt 

    Out[20]:array([ 0.05 , 1.37419833, 0.28383029])

    In [21]:

     # 检验解出来的结果是否正确 np.round(equations(opt), 6) 

    Out[21]:array([ 0., 0., 0.])

    In [22]:

     plt.figure(figsize=(12, 7)) plt.scatter(pvols, prets, c = (prets - .05) / pvols, marker='o') plt.plot(evols, erets, 'r', lw=4) # Capital Market Line cx = np.linspace(.0, .4) plt.plot(cx, opt[0] + opt[1] * cx, lw=1.5) plt.plot(opt[2], f(opt[2]), 'r*', markersize=15.0) plt.grid() plt.xlim(0, .5) plt.xlabel('Expected Volatility') plt.ylabel('Expected Return') plt.colorbar(label='Sharpe Ratio'); 

    In [ ]:

    新鲜出品:https://www.ricequant.com/community/topic/1568/?utm_source=v2ex? 你一定知道的

    1 条回复    2016-11-18 21:36:36 +08:00
    Satan4869
        1
    Satan4869  
       2016-11-18 21:36:36 +08:00 via iPhone
    还有毛主席说过,让人民拥有土地!
    也是靠不住啊……
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2661 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 02:06 PVG 10:06 LAX 18:06 JFK 21:06
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86