端的に
S&P500構成銘柄のうち、時価総額で上位6位の銘柄の2019年の株価を使用して、Monte Carlo SimulationによるMarkowitz Efficient Frontier集合を描いてみたい。それから、Sharpe Ratioを基準にOptimal Portfolioを探してみたい。
やってみよう
ライブラリなど
import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns #!pip install yfinance --upgrade --no-cache-dir import yfinance as yf sns.set(rc={'figure.figsize':(12,8)})
ASSETS = ['AAPL', 'FB', 'GOOG', 'GOOGL', 'AMZN', 'MSFT'] ASSETS.sort() START = '2019-01-01' UNTIL = '2019-12-31' SEED = 619 np.random.seed(SEED)
df = yf.download(ASSETS, start=START, end=UNTIL)
共分散行列、期待収益率、重み
n_assets = len(ASSETS) n_portfolios = n_assets ** 8 returns = df['Adj Close'].pct_change().dropna() avg = returns.mean() * len(returns) cov = returns.cov() * len(returns) weights = np.random.random(size=(n_portfolios, n_assets)) weights /= np.sum(weights, axis=1).reshape(-1,1)
Metricsを用意
を重み行列とすると、Portfolioの
- 期待収益率については重み付けをするだけ。
- ボラティリティは、をHistoricalな共分散行列とすると、.
portfolio_returns = np.dot(weights, avg) def portfolioMetrics(_weights, _cov_matrix, _i): return np.sqrt(np.dot(_weights[_i].T, np.dot(_cov_matrix, _weights[_i]))) portfolio_volatility = [portfolioMetrics(weights, cov, i) for i in range(len(weights))] portfolio_volatility = np.array(portfolio_volatility) df = pd.DataFrame({'returns': portfolio_returns, 'volatility': portfolio_volatility}) df['sharpe_ratio'] = df.returns / df.volatility
Efficient Frontierの関数
def returnFrontier(_df, _portfolio_volatility, _n_points): frontier_returns = np.linspace(_df.returns.min(), _df.returns.max(), _n_points) frontier_volatility = [] idx = [] for i in range(_n_points): _mask = np.isclose(portfolio_returns, frontier_returns[i], atol=0.01) if _mask.sum() != 0: frontier_volatility.append(np.min(_portfolio_volatility[np.where(_mask)[0]])) idx.append(i) frontier_returns = frontier_returns[idx] return frontier_returns, frontier_volatility
プロットするだけの関数
def _plot(): _max = df.loc[df.sharpe_ratio.idxmax()] _min = df.loc[df.volatility.idxmin()] ax = sns.scatterplot(x=df.volatility, y=df.returns, hue=df.sharpe_ratio) ax.scatter(x=_max.volatility, y=_max.returns, s=300, color='yellow', marker='*') ax.scatter(x=_min.volatility, y=_min.returns, s=100, color='yellow', marker='h') ax.plot(frontier_volatility, frontier_returns, color='yellow') ax.set(title=f'Efficient Frontier of: {ASSETS}', xlabel='Volatility', ylabel='Expected Return') for i in range(n_assets): ax.scatter(x=np.sqrt(cov.values.diagonal()[i]), y=avg[i], s=100, color='black', marker='o')
結果
Sharpe Ratioが最大(272.37%)となるPortfolioの重みが求まった。
- Return:54.18%
- Volatility:19.89%
frontier_returns, frontier_volatility = returnFrontier(df, portfolio_volatility, 500)
_plot()
感想/展望
- 思ったより綺麗にでてきた。