新NISAの導入に伴い、多くの投資家がポートフォリオ構築に関心を寄せています。今日は、我が家のポートフォリオ構築の過程を共有し、同じように投資を始めたいと考えている方々に役立つ情報を提供したいと思います。
「現代ポートフォリオ理論」の概要についてはこちらのエントリーをご覧ください。
ステップ1: データの取得と分析
最初のステップとして、私たちは積み立てNISA対象のETFに関するデータを収集し、それらの相関係数を調べました。この分析は、ポートフォリオ内の資産間の動きがどの程度連動しているかを理解するのに役立ちます。今回は「積み立てNISA」の対象商品の中で上場投資信託として届け出られている商品が対象としている7指数のデータを取得しました。
ステップ2: リターンとリスク、相関関係の計算
次に、私たちは各ETFのリターンとリスク(標準偏差)を計算しました。これには、Pythonのライブラリyfinance
を活用し、過去10年間のデータを基にこれらの年間のリターンとリスク(標準偏差)を計算しています。また直近のデータの方が重要と考えて、重みづけを設定しています。
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
tickers = {
"Nikkei225": "1321.T", # 日経225
"MSCI_Emerging_Market": "EEM" # MSCI Emerging Market
}
# データフレームの作成
data = pd.DataFrame()
# 各ETFの2019年から2024年までの終値データを取得
for name, ticker in tickers.items():
data[name] = yf.download(ticker, start="2014-01-01", end="2024-01-01")["Close"]
# 年次リターンの計算
annual_returns = data.resample('Y').last().pct_change()
# NaN値の除外
annual_returns = annual_returns.dropna()
# 最新の年から過去に向かって重みを付ける(例:2023年に重み5、2019年に重み1)
weights = np.arange(1, len(annual_returns) + 1)
# 加重平均リターンとリスクの計算
weighted_avg_returns = np.average(annual_returns, weights=weights, axis=0)
weighted_avg_risk = np.average*1**2, weights=weights, axis=0)**0.5
# 結果の表示
print("Weighted Average Returns:\n", pd.Series(weighted_avg_returns, index=tickers.keys()))
print("\nWeighted Average Risk (Standard Deviation):\n", pd.Series(weighted_avg_risk, index=tickers.keys()))
correlation_matrix = data.corr()
print(correlation_matrix)
ステップ3: 平均分散法とポートフォリオの最適化
平均分散法を用いて、リスクを最小化しながらリターンを最大化するポートフォリオを構築しました。このプロセスでは、ランダムに生成された多数のポートフォリオから、最適な組み合わせを選び出しました。
# ランダムシードの設定
np.random.seed(42)
num_portfolios = 100000
weights /= np.sum(weights)
# 期待リターン
ret_arr[i] = np.sum(weights * weighted_avg_returns)
# 期待リスク
vol_arr[i] = np.sqrt(np.dot(weights.T, np.dot(correlation_matrix, weights)))
# シャープ比率
sharpe_arr[i] = ret_arr[i] / vol_arr[i]
all_weights[i, :] = weights
ステップ4: 効率的フロンティアの可視化
最後に、効率的フロンティアを可視化しました。これは、リスクとリターンのバランスを視覚的に理解するのに役立ちます。また、最大シャープ比率を持つポートフォリオを特定し、その内訳を分析しました。
# 効率的フロンティアの可視化
plt.figure(figsize=(12,8))
plt.scatter(vol_arr, ret_arr, c=sharpe_arr, cmap='viridis')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Risk(Standard Deviation)')
plt.ylabel('Return')
plt.scatter(max_sharpe_vol, max_sharpe_return, c='red', s=50) # 赤い点
plt.title('Efficient Frontier and Maximum Sharpe Ratio Portfolios')
plt.show()
for ticker, weight in zip(tickers.keys(), all_weights[max_sharpe_idx]):
print(f"{ticker}: {weight * 100:.2f}%")
結論
今回のセットアップで分析を行うと上のような結果となりました。
新NISAの導入により、個人投資家にとって投資の選択肢が大きく広がりました。ポートフォリオ構築は複雑なプロセスですが、Pythonを用いて適切なツールと分析を行えば、個人でも効率的な投資戦略を立てることが可能です。
下に今回のコードを載せています。どうぞお使いください!(ランダムシードを使っていないので毎回結果が変わると思います。)
import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
tickers = {
"Nikkei225": "1321.T", # 日経225
"MSCI_Emerging_Market": "EEM" # MSCI Emerging Market
}
# データフレームの作成
data = pd.DataFrame()
for name, ticker in tickers.items():
data[name] = yf.download(ticker, start="2014-01-01", end="2024-01-01")["Close"]
correlation_matrix = data.corr()
print(correlation_matrix)
# 年次リターンの計算
annual_returns = data.resample('Y').last().pct_change()
# NaN値の除外
annual_returns = annual_returns.dropna()
# 最新の年から過去に向かって重みを付ける(例:2023年に重み5、2019年に重み1)
weights = np.arange(1, len(annual_returns) + 1)
# 加重平均リターンとリスクの計算
weighted_avg_returns = np.average(annual_returns, weights=weights, axis=0)
weighted_avg_risk = np.average*3**2, weights=weights, axis=0)**0.5
# 結果の表示
print("Weighted Average Returns:\n", pd.Series(weighted_avg_returns, index=tickers.keys()))
print("\nWeighted Average Risk (Standard Deviation):\n", pd.Series(weighted_avg_risk, index=tickers.keys()))
num_portfolios = 100000
weights /= np.sum(weights)
# 期待リターン
ret_arr[i] = np.sum(weights * weighted_avg_returns)
# 期待リスク
vol_arr[i] = np.sqrt(np.dot(weights.T, np.dot(correlation_matrix, weights)))
# シャープ比率
sharpe_arr[i] = ret_arr[i] / vol_arr[i]
all_weights[i, :] = weights
# 効率的フロンティアの可視化
plt.figure(figsize=(12,8))
plt.scatter(vol_arr, ret_arr, c=sharpe_arr, cmap='viridis')
plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Risk(Standard Deviation)')
plt.ylabel('Return')
plt.scatter(max_sharpe_vol, max_sharpe_return, c='red', s=50) # 赤い点
plt.title('Efficient Frontier and Maximum Sharpe Ratio Portfolios')
plt.show()
for ticker, weight in zip(tickers.keys(), all_weights[max_sharpe_idx]):
print(f"{ticker}: {weight * 100:.2f}%")
ret_arr = np.zeros(num_portfolios)
vol_arr = np.zeros(num_portfolios)
sharpe_arr = np.zeros(num_portfolios)
for i in range(num_portfolios):
weights = np.random.random(len(weighted_avg_returns
ret_arr = np.zeros(num_portfolios)
vol_arr = np.zeros(num_portfolios)
sharpe_arr = np.zeros(num_portfolios)
for i in range(num_portfolios):
weights = np.random.random(len(weighted_avg_returns