TUSHARE 金融与技术学习兴趣小组 翻译整理 | Apathy 本期编辑 | Little monster
译者简介:国防科技大学控制工程与科学专业研二在读,目前在学习Python爬虫和量化投资。 作者:Mariano Scandizzo 本文比较了两个多元投资组合的风险调整回报,研究目标如下:
① 比较两个不同投资组合的风险调整后边际回报率,一个投资组合增加新兴市场债务,另一个投资组合增加的是黄金。 ② 创建一个基本框架,利用Python分析和比较N个资产的投资组合。 ③ 基于描述性统计和蒙特卡罗概念创建易于部署的可视化和模拟。 【注】由于代码较长,本文只列出了与统计分析相关的代码,跳过了与可视化相关的代码。文末已附含完整代码的GitHub链接。 【数据集来源】雅虎财经 【周期】20130429—20180430 【数据量】262 【资产】
SPY——SPDR S&P 500 ETF
QQQ——PowerShares QQQ ETF AGG——iShares Core US Aggregate Bond ETF GLD——SPDR Gold Shares EMB——iShares JP Morgan USD Em Mkts Bd ETF import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns; sns.set() plt.style.use(‘ggplot’) semana = 52 datos = pd.read_excel(‘MasterAllocation.xlsx’,sheet_name=’Summary’,index_col=’Date’)
数据可视化是形成初始直观的重要一步。下面是整个观察周期的历史价格演变: 由于每个资产类别的初始值不同,我们很难比较它们的相对表现。如果在观察期开始时在每项资产上投入1美元,会发生什么? 为了回答上面的问题,我们需要对数据进行标准化。 normalized_series = (datos/datos.iloc[0])
下面我们用一些数字来帮助我们描述每项资产类别的行为。 为了对每种资产的回报率和内在风险有一个直观的认识,我们可以先从年化回报率和离散度的测量开始入手。 【注】因为我们用的是周数据,需要转化为年化数据,通常假设每年有52周。 datos_returns = np.log(datos/datos.shift(1)) datos_returns.dropna(inplace=True) stats = pd.DataFrame() stats[‘Annualized Returns(%)’] =datos_returns.mean() * semana *100 stats[‘Annualized Volatility(%)’] = datos_returns.std() * np.sqrt(semana)*100 stats[‘Sharpe Ratio’] = stats[‘Annualized Returns(%)’] /stats[‘Annualized Volatility(%)’] print(82*’-’) print(‘Assets Classes Annualized Statistics — full observation period’) stats.style.bar(color=[‘red’,’green’], align=’zero’)
下一层的分析由数据的三阶和四阶矩驱动,即偏度和峰度。 作为投资组合风险管理的一部分,我们需要了解回报是否比正态分布更频繁地偏向于正值或负值,即偏度。 同样重要的是,我们要知道这些资产是否比正态分布的资产更倾向于发生极端事件,这些极端事件可以是正面的,也可以是负面的,这种现象也叫肥尾效应。 下面的图表显示了每种资产回报的直方图。 为了便于比较,绘制了一个钟形曲线,其平均值和标准偏差等于所考虑的资产。 钟形外部的值表示资产行为不能通过正态假设完全描述。为便于参考,图表中包含了偏度值和峰度值。 让我们具体比较投资组合中两种固定收益资产的收益分布。 很明显,与投资级美国政府债券相比,新兴市场债券回报率的分布更分散。回报率越高,波动性越大。 到目前为止,我们只考虑了单个资产。 投资组合拥有的资产越多,在确定投资组合风险行为中,每个资产与投资组合中其他资产的相对行为就越重要。 【投资组合1】 美国投资级固定收益:30%(AGG) 美国股票:50%(SPY和QQQ) 黄金:20%(GLD) 【投资组合2】 美国投资级固定收益:30%(AGG) 美国股票:50%(SPY和QQQ) EM政府债券:20%(EMB) 让我们比较一下它们的相对表现。投资组合2的风险调整回报比投资组合1更高,表明在观察期内新兴市场债券是比黄金更好的选择。 datos_returns.corr(‘pearson’)
用全周期相关矩阵(Pearson公式),计算年化投资组合收益率和波动率。 Expected_Return_noEM = np.sum(datos_returns.mean()* allocation.No_EM)* semana Expected_Std_noEM = np.sqrt(np.dot(allocation.No_EM.T,np.dot(datos_returns.cov()*semana, allocation.No_EM))) Sharpe_noEM = Expected_Return_noEM / Expected_Std_noEM Expected_Return_EM = np.sum(datos_returns.mean()* allocation.EM)* semana Expected_Std_EM = np.sqrt(np.dot(allocation.EM.T,np.dot(datos_returns.cov()*semana, allocation.EM))) Sharpe_EM = Expected_Return_EM / Expected_Std_EM print(‘Key Stats: Portfolio with no EM Securities ‘) print(82*’=’) print(‘Annualized Returns: {:.3%}’.format(Expected_Return_noEM)) print(‘Annualized Volatility: {:.3%}’.format(Expected_Std_noEM)) print(‘Sharpe Ratio: {:.4}’.format(Sharpe_noEM)) print(82*’-’) print(‘Key Stats: Portfolio with EM Securities ‘) print(82*’=’) print(‘Annualized Returns: {:.3%}’.format(Expected_Return_EM)) print(‘Annualized Volatility: {:.3%}’.format(Expected_Std_EM)) print(‘Sharpe Ratio: {:.4}’.format(Sharpe_EM)) print(82*’-’)
采用和之前可视化单个资产类别回报的分散性相同的方法,我们观察一下这两个投资组合的收益符合怎样的正态分布。 最后,我们模拟一下马科维茨有效边界。我们不仅要计算最优投资组合,还要计算内部(次优)投资组合。 为了计算每个投资组合,我们将随机改变资产权重,同时保持每个投资组合的资产类别不变。 该练习将为每个投资组合运行2,500次模拟。此外,色标基于每个投资组合的夏普比率,通过风险调整效率的程度在视觉上区分投资组合。 pretsEM = [] pvolsEM = [] prets_noEM = [] pvols_noEM = [] [[‘AGG’,’SPY’,’QQQ’,’EMB’]] [[‘AGG’,’SPY’,’QQQ’,’GLD’]] for p in range(2500): weights = np.random.random(len(allocation)-1) weights /= np.sum(weights) pretsEM.append(np.sum(datos_returns[[‘AGG’,’SPY’,’QQQ’,’EMB’]].mean()* weights)* semana) pvolsEM.append(np.sqrt(np.dot(weights.T,np.dot(datos_returns[[‘AGG’,’SPY’,’QQQ’,’EMB’]].cov()*semana, weights)))) pretsEM = np.array(pretsEM) pvolsEM = np.array(pvolsEM) for p in range(2500): weights = np.random.random(len(allocation)-1) weights /= np.sum(weights) prets_noEM.append(np.sum(datos_returns[[‘AGG’,’SPY’,’QQQ’,’GLD’]].mean()* weights)* semana) pvols_noEM.append(np.sqrt(np.dot(weights.T,np.dot(datos_returns[[‘AGG’,’SPY’,’QQQ’,’GLD’]].cov()*semana, weights)))) prets_noEM = np.array(prets_noEM) pvols_noEM = np.array(pvols_noEM) # the charts fig8 = plt.figure(figsize = (12,16)) plt.subplots_adjust(wspace=.5) plt.subplot(211) plt.scatter(pvolsEM, pretsEM, c = pretsEM / pvolsEM, marker = ‘o’,cmap=’coolwarm’) plt.grid(True) plt.xlabel(‘expected volatility’) plt.ylabel(‘expected return’) plt.colorbar(label = ‘Sharpe Ratio’) plt.title(‘Monte Carlo Simulation Efficient Frontier with EM’) plt.subplot(212) plt.scatter(pvols_noEM, prets_noEM, c = prets_noEM / pvols_noEM, marker = ‘o’,cmap=’viridis’) plt.grid(True) plt.xlabel(‘expected volatility’) plt.ylabel(‘expected return’) plt.colorbar(label = ‘Sharpe Ratio’) plt.title(‘Monte Carlo Simulation Efficient Frontier with no EM’) plt.show(); fig8.savefig(‘frontiers.png’,dpi=fig8.dpi)
到目前为止,我们已经使用了相同的收益和相关矩阵。覆盖整个时间周期的计算,自然会使时间序列的波动性更小,从而使结果更平滑。
为了更好地描绘资产行为,让我们计算每个投资组合的3个月拖尾相关性。 你会注意到回报率和波动率的平均值会回归到历史平均水平,因此,如果你使用更短的时间周期来计算,整体结论将不会改变,而只是会增加随机性的程度。 【注】 对于那些刚开始从事金融工作的人来说,Excel可能是唯一可供选择的工具。当试图计算效率边界时,你可能可以计算出如下形状的矩阵乘法: [1x5] * [5x5] * [5x1] = Portfolio volatility 其实,只计算一次并不是很难,但如果你想在Excel里面像我们这样运行250次,嗯……不掉几根头发是搞不定的!!!这里我想强调一下numpy Tensordot的强大功能,只需一步,就可以运行250次矩阵乘法,相当惊人!!! def trailing_ret(retornos, window, weights, annualization = 52):
roll_ret = retornos.rolling(window=window).mean() roll_ret = roll_ret.dropna() roll_ret = (roll_ret * weights)* annualization roll_ret = roll_ret.sum(axis =1) roll_ret = roll_ret.to_frame() roll_ret.rename(columns ={0:’returns’}, inplace = True) return roll_ret def trailing_cov2(retornos, window, weights, annualization = 52): retornos_length = len(retornos) retornos_width = len(retornos.columns) roll_cov = retornos.rolling(window=window).cov() roll_cov_dates = np.unique(roll_cov.index.get_level_values(0).values) roll_cov_dates = roll_cov_dates[window-1:] roll_cov = roll_cov.values.reshape(retornos_length,retornos_width,retornos_width) roll_cov = roll_cov[window-1:] * annualization weights = weights.values.reshape(len(weights),1) step1 = np.tensordot(roll_cov,weights,axes=[1,0]) step2 = np.tensordot(weights,step1, axes=[0,1]) volatility = np.sqrt(step2) volatility = volatility.reshape((retornos_length-(window-1)),1)
trailing_vol = pd.DataFrame()
trailing_vol[‘date’] = roll_cov_dates trailing_vol[‘volatility’] = volatility
trailing_vol.set_index(‘date’,inplace = True)
return trailing_vol def full_analysis(retornos, window, weights, annualization = 52): volatilidad = trailing_cov2(retornos = retornos, window = window, weights = weights, annualization = annualization) retornos = trailing_ret(retornos = retornos, window = window, weights = weights, annualization = annualization) fusion = pd.merge(volatilidad, retornos, left_index=True,right_index=True) fusion[‘sharpe’] = fusion[‘returns’]/fusion[‘volatility’] return fusion
1、与黄金相比,新兴市场债券提高了投资组合风险调整后的回报率。 2、我们创建了一个灵活的模型来比较不同的资产,分别应用于单个资产和投资组合,并在进行战略资产配置时可视化风险和收益影响。 3、初步分析表明,滚动时间框架,如上面验证的完整周期和短周期3个月,不会改变战略结论。 4、该框架可作为一个初始基础来不断扩展投资组合分析,例如VAR计算、压力测试等,留给读者去利用现有的模型继续扩展这个定量方法。
|