投資組合優化

隨機投資組合與有效前沿

  • October 8, 2019

我理解有效邊界的概念,並且能夠用 Python 計算它。但即使在生成 50'000 個隨機 10 資產組合時,單個組合甚至都沒有接近有效邊界。

我看到,例如,最大夏普比率投資組合具有非常明顯的分配(10 種資產中的大部分獲得 0 分配)。

由於這項工作對我自己來說非常重要,我只是想問問社區你是否經歷過類似的行為?在生成隨機投資組合時甚至沒有一個位於有效邊界附近是否正常? 投資組合與有效前沿

請在下面找到程式碼:

def portfolio_annualised_performance(weights, mean_returns, cov_matrix):
   returns = np.sum(mean_returns*weights )
   std = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
   return std, returns

def random_portfolios(num_portfolios, mean_returns, cov_matrix, risk_free_rate):
   results = np.zeros((3,num_portfolios))
   weights_record = []
   for i in range(num_portfolios):
       weights = abs(np.random.randn(len(mean_returns)))
       weights /= np.sum(weights)
       weights_record.append(weights)
       portfolio_std_dev, portfolio_return = portfolio_annualised_performance(weights, mean_returns, cov_matrix)
       results[0,i] = portfolio_std_dev
       results[1,i] = portfolio_return
       results[2,i] = (portfolio_return - risk_free_rate) / portfolio_std_dev
   return results, weights_record

def neg_sharpe_ratio(weights, mean_returns, cov_matrix, risk_free_rate):
   p_var, p_ret = portfolio_annualised_performance(weights, mean_returns, cov_matrix)
   return -(p_ret - risk_free_rate) / p_var

def max_sharpe_ratio(mean_returns, cov_matrix, risk_free_rate):
   num_assets = len(mean_returns)
   args = (mean_returns, cov_matrix, risk_free_rate)
   constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
   bound = (0.0,1.0)
   bounds = tuple(bound for asset in range(num_assets))
   result = sco.minimize(neg_sharpe_ratio, num_assets*[1./num_assets,], args=args,
                       method='SLSQP', bounds=bounds, constraints=constraints)
   return result

def portfolio_volatility(weights, mean_returns, cov_matrix):
   return portfolio_annualised_performance(weights, mean_returns, cov_matrix)[0]

def min_variance(mean_returns, cov_matrix):
   num_assets = len(mean_returns)
   args = (mean_returns, cov_matrix)
   constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
   bound = (0.0,1.0)
   bounds = tuple(bound for asset in range(num_assets))

   result = sco.minimize(portfolio_volatility, num_assets*[1./num_assets,], args=args,
                       method='SLSQP', bounds=bounds, constraints=constraints)
   return result

def efficient_return(mean_returns, cov_matrix, target):
   num_assets = len(mean_returns)
   args = (mean_returns, cov_matrix)

   def portfolio_return(weights):
       return portfolio_annualised_performance(weights, mean_returns, cov_matrix)[1]

   constraints = ({'type': 'eq', 'fun': lambda x: portfolio_return(x) - target},
                  {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
   bounds = tuple((0.0,1) for asset in range(num_assets))
   result = sco.minimize(portfolio_volatility, num_assets*[1./num_assets,], args=args, method='SLSQP', bounds=bounds, constraints=constraints)
   return result

def efficient_frontier(mean_returns, cov_matrix, returns_range):
   efficients = []
   for ret in returns_range:
       efficients.append(efficient_return(mean_returns, cov_matrix, ret))
   return efficients

def display_calculated_ef_with_random(mean_returns, cov_matrix, num_portfolios, risk_free_rate):
   results, _ = random_portfolios(num_portfolios,mean_returns, cov_matrix, risk_free_rate)

   max_sharpe = max_sharpe_ratio(mean_returns, cov_matrix, risk_free_rate)
   sdp, rp = portfolio_annualised_performance(max_sharpe['x'], mean_returns, cov_matrix)
   max_sharpe_allocation = pd.DataFrame(max_sharpe.x,index=curr_w_terms,columns=['allocation'])
   max_sharpe_allocation.allocation = [round(i*100,4)for i in max_sharpe_allocation.allocation]
   max_sharpe_allocation = max_sharpe_allocation.T

   min_vol = min_variance(mean_returns, cov_matrix)
   sdp_min, rp_min = portfolio_annualised_performance(min_vol['x'], mean_returns, cov_matrix)
   min_vol_allocation = pd.DataFrame(min_vol.x,index=curr_w_terms,columns=['allocation'])
   min_vol_allocation.allocation = [round(i*100,4)for i in min_vol_allocation.allocation]
   min_vol_allocation = min_vol_allocation.T

   print("-"*80)
   print("Maximum Sharpe Ratio Portfolio Allocation\n")
   print("Annualised Return:", round(rp,4))
   print("Annualised Volatility:", round(sdp,4))
   print("\n")
   print(max_sharpe_allocation)
   print("-"*80)
   print("Minimum Volatility Portfolio Allocation\n")
   print("Annualised Return:", round(rp_min,4))
   print("Annualised Volatility:", round(sdp_min,4))
   print("\n")
   print(min_vol_allocation)

   plt.figure(figsize=(10, 7))
   plt.scatter(results[0,:],results[1,:],c=results[2,:],cmap='YlGnBu', marker='o', s=10, alpha=0.3)
   plt.colorbar()
   plt.scatter(sdp,rp,marker='*',color='r',s=500, label='Maximum Sharpe ratio')
   plt.scatter(sdp_min,rp_min,marker='*',color='g',s=500, label='Minimum volatility')

   target = np.linspace(rp_min, 0.05, 20)
   efficient_portfolios = efficient_frontier(mean_returns, cov_matrix, target)
   plt.plot([p['fun'] for p in efficient_portfolios], target, linestyle='-.', color='black', label='efficient frontier')
   plt.title('Calculated Portfolio Optimization based on Efficient Frontier')
   plt.xlabel('annualised volatility')
   plt.ylabel('annualised returns')
   plt.legend(labelspacing=0.8)
   plt.ylim([-0.005,0.03])
   plt.xlim([0.0,0.05])

display_calculated_ef_with_random(log_ret, new_cov, 50000, 0)

我沒有對 Covar-Matrix 進行年化,因為我已經有了年回報估計和 covar 估計。

我的問題是:這是否合理?

編輯 由於我的隨機投資組合的權重生成過程似乎傾向於過於相似的投資組合,因此我更改了以下功能:

def random_portfolios(num_portfolios, mean_returns, cov_matrix, risk_free_rate):
   results = np.zeros((3,num_portfolios))
   weights_record = []
   for i in range(num_portfolios):
       weights = abs(np.random.randn(len(mean_returns)))
       weights[weights<1] = 0
       if sum(weights)==0:
           print("sum=0")
           indexes = np.unique(np.random.randint(0,10,3)).tolist()
           weights[indexes] = abs(np.random.randn(len(indexes)))
       weights /= np.sum(weights)
       weights_record.append(weights)
       portfolio_std_dev, portfolio_return = portfolio_annualised_performance(weights, mean_returns, cov_matrix)
       results[0,i] = portfolio_std_dev
       results[1,i] = portfolio_return
       results[2,i] = (portfolio_return - risk_free_rate) / portfolio_std_dev
   return results, weights_record

這樣做之後,投資組合的分佈會更好:

新加權方案後的投資組合

那麼,我們是否可以同意上面的程式碼做了它應該做的事情,我可以從這裡繼續?

您似乎有兩個明顯的問題:

  1. 如何生成隨機投資組合
  2. 最佳投資組合的結構

廣告 1)

模擬隨機投資組合權重的一種直接方法是使用 Dirichlet 分佈 $ Dir(\alpha_1,\ldots,\alpha_n) $ . 這是 Simplex 上的一個分佈(即在 $ S={x\in\mathbb{R}^n | \sum x_i =1, x_i\geq 0} $ ,這可以為您提供非常多樣化和非常集中的投資組合。全部設置 $ \alpha_i=1 $ 給你在單純形上的均勻分佈,使一些 $ \alpha_i $ 較小的將為您提供集中在這些資產上的投資組合 $ \alpha_i $ 更大更多樣化的分配。關於所有相關事實 $ Dir(\alpha_1,\ldots,\alpha_n) $ 可以在維基百科的文章中找到。

下面是兩個單形(空間中的三角形)的可交換狄利克雷密度的不同選擇的兩個密度圖: 在此處輸入圖像描述

廣告 2)

您的“最佳”投資組合將取決於您的優化標準和聯合資產回報。所以我懷疑任何人都可以做出重要的一般性陳述。但根據定義,最優投資組合是極端的。因此,它們是非通用的也就不足為奇了。從我的經驗來看,夏普比率確實有利於非常不平衡且幾乎沒有多元化的投資組合。

引用自:https://quant.stackexchange.com/questions/45897