😇

ドルコスト平均法でインデックス投資って本当にいいのか?

2022/06/12に公開約6,300字

最近円安で米国株投資に非常に抵抗を感じます。一般的に花にも考えずにドルコスト平均法で淡々とインデックス指標連動のETFを積み上げるのが良いとされていますが、2022年6月時点で長期積立家はどういう戦略を取るべきか考えます。ドルコスト平均法による積立に忠実に従うのであれば、投資対象が割高であっても、ルールに従い定期的に積み立てることになります。このルールを守るよりも、割安なタイミングを探すほうがいいのでは、ということを感じる人は多いのではないでしょうか?それについて考えてみます。

ドルコスト平均法と一括投資の比較

様々な銘柄がありますが、ここでは、S&P 500に連動するETFであるSPYに投資することを考えます。すべての株は円ベースで計算するとします。つまり、為替変動も考慮して計算するとします。また現金化のタイミングですがあるタイミングぴったりで現金化するわけではなく、現金化タイミングで資産価値が前年より20%下落していた場合、最大2年待つこととします。

まず、一括ですべての額を投資してn年寝かせた場合を計算します。その場合のn年後のbox plotは次のとおりです。横軸は投資年数、縦軸はリターンレートです。このグラフを見ると50%の人は10年で約2倍の資産になります。また、25%の人は約3倍以上の資産となり、幅はありますが、かなり高いリターンが得られることがわかります。ただ、おそらく30%くらいの人は元本割れしてしまい、現金がたくさんあっても、すぐに投資するのはリターンもリスクも高い選択となってしまうことがわかります。

次によく言われるドルコスト平均法と呼ばれる投資法で1ヶ月に一回積立をする場合のn年後リターンを計算します。この場合、一括投資とY軸を合わせたグラフだと、次のようになり、一括投資と比べると得られるリターンのばらつきが減ることがわかります。特に元本割れする人の人数は30%から20%くらいに変わっていて、若干リスクが下がっていることがわかります。ただ、すべてのお金がn年間投資されているわけではないので、一括投資と比べると期待できるリターンが下がっていることがわかります。

これは、Y軸を変えただけのグラフですが、50%の人は、10年で資産が1.6倍くらいになるのですが、25%くらいの人は元本割れするということがわかります。10年で1.6倍というのは年利換算すると5%で結構高い額です。これを見ると、外貨建ての米国株はリスクを背負う代わりにリターンが得られる商品なんだなーという実感が得られます。

結論

これを考えると、日本円の収入をベースに投資している人は、単にドルコスト平均法で投資するのではなく、価格変動をすこし気にして取引をしなければ、元本割れのリスクがあるようです。これ面白いことに、ドルベースでドルコスト平均法をやった場合、下記のグラフのように元本割れのリスクはありません。為替変動があるせいで元本割れのリスクがあるのです。

対策としてはドルベースで給与をもらえる企業で働く、何らかの投資方針を決めて投資するという方法があります。これについては、次回の記事で深く調査してみようと思います。

プログラム

本ソースコードを使用したことによる一切の損害について、開発者は責任を負いません。

import pandas_datareader.data as web
import datetime as dt

start = dt.date(1988,1,1)
end = dt.datetime.now()

spc_df = web.DataReader('SPY',"yahoo", start, end)

usd_jpy_df = web.DataReader('USDJPY=X',"yahoo", start, end)

import tqdm
from dateutil.relativedelta import relativedelta
import pandas as pd
import numpy as np

def create_range(start, end, day_unit=1):
    days = (end - start).days
    return [start + relativedelta(days=d) for d in range(0, days, day_unit)]

def create_datetime_from_date(date):
    return dt.datetime(date.year, date.month, date.day)

return_rate_lists = []

METHOD = "DOLLED_COST_AVERAGING"
# METHOD = "ONE_TIME_PAYMENT"

for year in range(1,11):
    return_rates = []
    BUY_PERIOD = 30
    NUM_WAIT_YEARS = 2

    for end_date in tqdm.tqdm(create_range(dt.date(2007, 5, 10), dt.date(2022, 5, 10), day_unit=1)):
        start_date = end_date - relativedelta(years=year)

        try:
            loc = pd.Timestamp((end_date).strftime('%Y-%m-%d'))
            adj_close_final_first = spc_df.loc[loc]['Adj Close'] * usd_jpy_df.loc[loc]["Adj Close"]
            loc = pd.Timestamp((end_date - relativedelta(years=1)).strftime('%Y-%m-%d'))
            adj_close_final_previous = spc_df.loc[loc]['Adj Close'] * usd_jpy_df.loc[loc]["Adj Close"]
        except KeyError:
            all_found = False
            continue

        all_found = True
        adj_close_finals = [adj_close_final_first]
        if adj_close_final_first / adj_close_final_previous < 0.8:
            for i in range(1, NUM_WAIT_YEARS + 1):
                try:
                    loc = pd.Timestamp((end_date + relativedelta(years=i)).strftime('%Y-%m-%d'))
                    adj_close_finals.append(
                        spc_df.loc[loc]['Adj Close'] * usd_jpy_df.loc[loc]["Adj Close"],
                    )
                except KeyError:
                    all_found = False
                    continue

        if not all_found:
            continue

        adj_close_final = np.max(adj_close_finals)

        if METHOD == "DOLLED_COST_AVERAGING":
            num_total_stock = 0
            total_price = 0
            for buy_date in create_range(start_date, end_date - relativedelta(days=BUY_PERIOD), day_unit=BUY_PERIOD):
                aaa = None
                buy_date_tmp = buy_date
                while aaa is None:
                    try:
                        loc_us = pd.Timestamp(buy_date_tmp.strftime('%Y-%m-%d'))
                        aaa = spc_df.loc[loc_us]
                    except KeyError:
                        aaa = None
                        buy_date_tmp = buy_date_tmp - relativedelta(days=1)
            
                buy_date_tmp = buy_date
                usd = None
                while usd is None:
                    try:
                        loc_us = pd.Timestamp(buy_date_tmp.strftime('%Y-%m-%d'))
                        usd = usd_jpy_df.loc[loc_us]
                        
                    except KeyError:
                        usd = None
                        buy_date_tmp = buy_date_tmp - relativedelta(days=1)
                total_price += (aaa['Adj Close'] * usd['Adj Close'])
                num_total_stock += 1

            return_rates.append((adj_close_final * num_total_stock) / (total_price))
        elif METHOD == "ONE_TIME_PAYMENT":
            buy_date = start_date
            aaa = None
            buy_date_tmp = buy_date
            while aaa is None:
                try:
                    loc_us = pd.Timestamp(buy_date_tmp.strftime('%Y-%m-%d'))
                    aaa = spc_df.loc[loc_us]
                except KeyError:
                    aaa = None
                    buy_date_tmp = buy_date_tmp - relativedelta(days=1)
        
            buy_date_tmp = buy_date
            usd = None
            while usd is None:
                try:
                    loc_us = pd.Timestamp(buy_date_tmp.strftime('%Y-%m-%d'))
                    usd = usd_jpy_df.loc[loc_us]
                    
                except KeyError:
                    usd = None
                    buy_date_tmp = buy_date_tmp - relativedelta(days=1)
            return_rates.append(adj_close_final / (aaa['Adj Close'] * usd['Adj Close']))

    return_rate_lists.append(return_rates)

# %%
from scipy.stats import hmean
import matplotlib.pyplot as plt

print(len(return_rate_lists))

mean_return = hmean(np.array(return_rate_lists), axis=1)
fig, ax = plt.subplots(1,1)
# ax.set_ylim([0,6])
ax.plot(range(1, len(mean_return) + 1), mean_return)
ax.boxplot(return_rate_lists)
plt.show()
print(mean_return)

Discussion

ログインするとコメントできます