AIとファイナンス

AIとファイナンスの架け橋、それがこのブログの目指すところです。兼業投資家向けに、Pythonを駆使して株やFXの分析を「自分で」行えるようになるための情報を提供します。ニューラルネットワークを活用した市場予測から、実証済みの金融理論まで、全てのコードを公開し、誰もが活用できるように!是非色々なコードで遊んでみてください!

【NISA】最適ポートフォリオの組み方とは・実践編【積み立て】

新NISAの導入に伴い、多くの投資家がポートフォリオ構築に関心を寄せています。今日は、我が家のポートフォリオ構築の過程を共有し、同じように投資を始めたいと考えている方々に役立つ情報を提供したいと思います。

「現代ポートフォリオ理論」の概要についてはこちらのエントリーをご覧ください。

 

ステップ1: データの取得と分析

最初のステップとして、私たちは積み立てNISA対象のETFに関するデータを収集し、それらの相関係数を調べました。この分析は、ポートフォリオ内の資産間の動きがどの程度連動しているかを理解するのに役立ちます。今回は「積み立てNISA」の対象商品の中で上場投資信託として届け出られている商品が対象としている7指数のデータを取得しました。

金融庁の「つみたてNISA対象商品届出一覧」より 2023/1/7現在

ステップ2: リターンとリスク、相関関係の計算

次に、私たちは各ETFのリターンとリスク(標準偏差)を計算しました。これには、Pythonのライブラリyfinanceを活用し、過去10年間のデータを基にこれらの年間のリターンとリスク(標準偏差)を計算しています。また直近のデータの方が重要と考えて、重みづけを設定しています。

import yfinance as yf
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
 

tickers = {
    "Topix": "1570.T",            # Topix
    "Nikkei225": "1321.T",        # 日経225
    "JPXNikkeiIndex400": "1592.T",# JPX日経インデックス400
    "MSCI_ACWI": "ACWI",          # MSCI ACWI
    "MSCI_World": "URTH",         # MSCI World
    "S&P500": "SPY",              # S&P 500
    "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
all_weights = np.zeros*2
    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

# 最大シャープ比率のポートフォリオ
max_sharpe_idx = sharpe_arr.argmax()
max_sharpe_return = ret_arr[max_sharpe_idx]
max_sharpe_vol = vol_arr[max_sharpe_idx]

ステップ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()


# 最大シャープ比率のポートフォリオの内訳の表示
print("最大シャープ比率のポートフォリオ内訳:\n")
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 = {
    "Topix": "1570.T",            # Topix
    "Nikkei225": "1321.T",        # 日経225
    "JPXNikkeiIndex400": "1592.T",# JPX日経インデックス400
    "MSCI_ACWI": "ACWI",          # MSCI ACWI
    "MSCI_World": "URTH",         # MSCI World
    "S&P500": "SPY",              # S&P 500
    "MSCI_Emerging_Market": "EEM" # MSCI Emerging Market
}

# データフレームの作成
data = pd.DataFrame()

# 各ETFの過去1年間の終値データを取得
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
all_weights = np.zeros*4
    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

# 最大シャープ比率のポートフォリオ
max_sharpe_idx = sharpe_arr.argmax()
max_sharpe_return = ret_arr[max_sharpe_idx]
max_sharpe_vol = vol_arr[max_sharpe_idx]

# 効率的フロンティアの可視化
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()

# 最大シャープ比率のポートフォリオの内訳の表示
print("最大シャープ比率のポートフォリオ内訳:\n")
for ticker, weight in zip(tickers.keys(), all_weights[max_sharpe_idx]):
    print(f"{ticker}: {weight * 100:.2f}%")

*1:annual_returns - annual_returns.mean(

*2:num_portfolios, 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

*3:annual_returns - annual_returns.mean(

*4:num_portfolios, 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