AIとファイナンス

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

【米国株】株価の下落局面での買い時の検証と定期積立の優位性 - S&P500 (VOO) を例に【分析】

こんにちは!最近の市場はかなり波がありましたね。特に、S&P500に追随するVOOは大きな動きを見せています。今回は、こうした不安定な市場でどのように投資戦略を立てれば良いのか、特に下落局面で追加購入をすることが有効なのか、Pythonを使って具体的な戦略を検証してみたいと思います!

いつも通り最下部にコードへのリンクを張っているので皆さん遊んでみてね!例えばオルカンでやったらどうなるか試してみてください!

使用するデータとツール

まずは、毎度おなじみ、Pythonyfinanceライブラリを使って、S&P500(VOO)の過去15年間の日足データをダウンロードします。これにより、実際の市場データを基にシミュレーションを行うことができます。

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.colors import LinearSegmentedColormap

# S&P 500 ETF (VOO) のデータをダウンロード
voo = yf.Ticker("VOO")
data = voo.history(period="15y")
# S&P 500 ETF (VOO) のデータをダウンロード
voo = yf.Ticker("VOO")
data = voo.history(period="15y")

# 初期設定
initial_investment = 1000
data['Purchase Flag'] = data.index.day == 20
data['SMA50'] = data['Close'].rolling(window=50).mean()

#ベースライン(ただ単純に積立した場合)の計算
baseline_investment = 15 * 12 * initial_investment

投資戦略の策定

ここでは、毎月20日に一定額を投資する戦略(一般的なドルコスト平均法による積立投資)と、毎月の投資のうち一定額を積み立てておいて50日間の移動平均価格が特定のパーセンテージだけ下落した場合に追加で投資する戦略を設定しています。関数simulate_investmentはこれらの条件に基づいて投資のシミュレーションを行い、最終的な利回りを計算します。もちろん$1000も投資に回せるかは別としてとりあえず毎月一定額は投資に回す戦略です。

def simulate_investment(data, percentage_regular, sma_drop):
     # 投資額を割合に応じて分配
    regular_investment = percentage_regular * initial_investment
    extra_funds = initial_investment - regular_investment
    data['Regular Investment'] = data['Purchase Flag'] * regular_investment
    data['Extra Funds'] = extra_funds

    # 株の購入
    data['Regular Shares'] = data['Regular Investment'] / data['Close']

    # 初期のSavingsを設定
    data['Savings'] = 0
    data['Extra Investment'] = 0
    # 追加投資の条件
    data['Buy More'] = (data['Close'] * (1 - sma_drop / 100.0) < data['SMA50'])

    # Savingsの計算
    for i in range(1, len(data)):
        if data['Purchase Flag'].iloc[i]:
          data['Savings'].iloc[i] = data['Savings'].iloc[i-1] + data['Extra Funds'].iloc[i]
          break

    for j in range(len(data) - 1):
        if data['Buy More'].iloc[j] and data['Savings'].iloc[j] > 99:
          data['Extra Investment'].iloc[j] = 100
          data['Savings'].iloc[j + 1] = np.maximum(data['Savings'].iloc[j] - 100, 0) + data['Purchase Flag'].iloc[j]*data['Extra Funds'].iloc[j]

        else:
          data['Savings'].iloc[j + 1] = data['Savings'].iloc[j] + data['Purchase Flag'].iloc[j]*data['Extra Funds'].iloc[j]

    # 追加投資の実行
    data['Extra Shares'] = data['Extra Investment'] / data['Close']


    # 総株数とポートフォリオバランスの計算
    data['Total Shares'] = (data['Regular Shares'] + data['Extra Shares']).cumsum()
    portfolio_balance = data['Total Shares'] * data['Close'] + data['Savings']

    # ベースラインに対する利回りを計算
    final_value = portfolio_balance.iloc[-1]
    return_rate = (final_value - baseline_investment) / baseline_investment * 100  # 利回りをパーセンテージで計算

    return return_rate
 

結果の比較と視覚化

最後に、これらの戦略に基づく資産の最終リターンをヒートマップで比較し、どの戦略がより効果的であったかを視覚的に示します。

# パラメータの範囲
percentage_regulars = np.arange(0, 1.1, 0.1)  # 0% から 100% まで
sma_drops = np.arange(-1, -21, -1)  # -1% から -20% まで

# 結果を格納する配列
results = np.zeros*1
sns.heatmap(results, xticklabels=np.round(sma_drops, 2), yticklabels=np.round(percentage_regulars, 2),
            annot=True, fmt=".1f", cmap=cmap, center=0, annot_kws={"size": 8})  # annot_kwsでテキストサイズを指定
plt.title('Return Over Baseline (%)')
plt.xlabel('SMA Drop (%)')
plt.ylabel('Percentage of Regular Investment (%)')
plt.show()

結果

ヒートマップは、定期的な投資と市場の下落に基づいた追加投資の割合を変えた際の利回りを示しています。驚くべきことに、株価の下落局面で積極的に追加購入する戦略よりも、定期的に一定額を積み立てる方が、長期的にはより良い結果をもたらすことが分かりました。この結果は、市場タイミングを計ることの難しさとリスクを示唆しており、定期的な積立がリスクを分散し安定した成長を促す可能性を示しています。まさか、株価の大幅な下落を待つよりも、一定のリズムで投資を行うことが、長期的には有効であるという結論になるとは思いませんでしたね!

今回のコードは下のリンクからどうぞ!

colab.research.google.com

*1:len(percentage_regulars), len(sma_drops)))


# シミュレーションの実行
for i, perc in enumerate(percentage_regulars):
    for j, drop in enumerate(sma_drops):
        results[i, j] = simulate_investment(data.copy(), perc, drop)

# カスタムカラーマップの定義
cmap = LinearSegmentedColormap.from_list(
    'custom_blue_white_red',
    [(0, 'blue'), (0.5, 'white'), (1, 'red')],
    N=256
)

# ヒートマップの作成
plt.figure(figsize=(10, 8