おめさんの備忘録

特定の金融商品/銘柄の売買の勧誘/推奨等を目的とするものではないです。市場全般の推奨や証券市場等の動向の上昇/下落を示唆するものでもないです。掲載の金融商品/資金運用等による投資によって生じたいかなる損失について、一切の責任を負いません。

S&P500の6銘柄を対象にMonte Carlo SimulationでMarkowitz Efficient Frontier

端的に

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を用意

 \omegaを重み行列とすると、Portfolioの

  • 期待収益率については重み付けをするだけ。
  • ボラティリティは、 \SigmaをHistoricalな共分散行列とすると、 \omega^{T}\Sigma\omega
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()

f:id:geonhee619:20211110201341p:plain

感想/展望

  • 思ったより綺麗にでてきた。