オルカン他投資信託の約定日(月初~月末)別リターンの分析

毎月投資をしていると、月初の株高などが気になることがあります。

以前、日経平均とS&P500が積み立て投資日によってリターンが違う、というのをX(旧Twitter)で見かけました。 これは、以下が出どころのようです。

www.nikkei.com

「月初の株高」 投信積み立てのベストタイミングは? - 日本経済新聞より引用

この記事には、日経平均とS&P500だけでなく、つみたてNISA対象のいくつかの投資信託についても分析されており、投資信託によって傾向が異なることが示されています。

じゃあ、よく投資対象として挙げられるオルカンはどうなのか、また他の投資信託は?といったところが気になったので、計算してみました。

本記事の計算内容はPythonで記述しており、以下のGitHubからダウンロードできます。 Windows 10 + Python 3.11.7で動作を確認しています。

github.com

本記事の目次です。結果だけ見るには、4. 計算結果まで飛んでください。

1. 分析対象の投資信託

分析対象の投資信託は、SBI証券のNISA積立設定の月間ランキングの上位5位としました。オルカンはもとより、S&P500やNASDAQなどのファンドが含まれます。

NISA月間積立設定金額ランキング(SBI証券HPより引用)

分析対象の投資信託と設定年一覧が以下になります。

No. ファンド名 設定日
1 三菱UFJ-eMAXIS Slim 全世界株式(オール・カントリー) 2018/10/31
2 三菱UFJ-eMAXIS Slim 米国株式(S&P500) 2018/07/03
3 SBI-SBI・V・S&P500インデックス・ファンド 2019/09/26
4 ニッセイ-<購入・換金手数料なし>ニッセイNASDAQ100インデックスファンド 2023/3/31
5 大和-iFreeNEXT FANG+インデックス) 2018/01/31

2. データ取得

投資信託のデータは、大体投資信託HPで設定来データが公開されていますので、それをダウンロードしてきます。ダウンロード元は先ほどの表に記載した通りです。

データはCSVなのですが、会社によってフォーマットが異なるので、前処理をして保存しなおします。

# 株価データの整理------------------------------------
file = "eMAXIS Slim 全世界株式(オール・カントリー).csv"
df = pd.read_csv(file, encoding="shift-jis", header=1, index_col=0)
df.index = pd.to_datetime(df.index)
df.to_csv(os.path.splitext(file)[0]+"_修正.csv", encoding="utf-8-sig")

file = "eMAXIS Slim 米国株式(S&P500).csv"
df = pd.read_csv(file, encoding="shift-jis", header=1, index_col=0)
df.index = pd.to_datetime(df.index)
df.to_csv(os.path.splitext(file)[0]+"_修正.csv", encoding="utf-8-sig")

file = "<購入・換金手数料なし>ニッセイNASDAQ100インデックスファンド.csv"
df = pd.read_csv(file, encoding="shift-jis", header=0, index_col=0)
df.index = pd.to_datetime(df.index, format="%Y年%m月%d日")
df = df.drop(df.columns[0], axis=1)
df = df.sort_index()
df.to_csv(os.path.splitext(file)[0]+"_修正.csv", encoding="utf-8-sig")

file = "SBI・V・S&P500インデックス・ファンド.csv"
df = pd.read_csv(file, encoding="shift-jis", header=0, index_col=0)
df.index = pd.to_datetime(df.index, format="%Y%m%d")
df.to_csv(os.path.splitext(file)[0]+"_修正.csv", encoding="utf-8-sig")

file = "iFreeNEXT FANG+インデックス.csv"
df = pd.read_csv(file, encoding="shift-jis", header=0, index_col=0)
df.index = pd.to_datetime(df.index, format="%Y%m%d")
df.to_csv(os.path.splitext(file)[0]+"_修正.csv", encoding="utf-8-sig")

処理の内容は以下のようなものです。

  • エンコードをShift-JIS→UTF-8(BOM)に変更
  • SBIと大和のファンドは0~1列目が日付・基準価格となっているのでそれをそのまま取得
  • eMAXISは1行目にファンド名が含まれるのでそれをスキップ
  • ニッセイは1列目にファンド名があるのでそれをスキップし、新しい順になっているので並び替え

3. リターンの計算

毎月固定日に投資した時の、設定来のトータルリターンを以下の条件で計算します。

  • 設定日より最初の月初から、2023/12月末までのデータを利用
    • どれも大体5年前後データがあります
    • NASDAQ100だけ設定日が1年以内と短めのデータなので、ちょっと信頼性が低いです
  • 土日祝日は翌営業日買い付け、2月の29日・30日は2月末に買い付け

以下の関数calc_return_by_day_of_monthに、pandas.DataFrameとして読み込んだCSVデータを渡してやると、日別リターンを計算できます。

def calc_return_by_day_of_month(df):
    return_by_day= []
    for d in np.arange(1, 31, 1):
        # 土日祝日は翌営業日買い付けのため、後ろ方向に穴埋め
        df_fill = df.asfreq("D", method="bfill") 
        # データ最初と最後の月途中のデータは削除
        first_month = df_fill.index[0].month
        first_year = df_fill.index[0].year
        last_month = df_fill.index[-1].month
        last_year = df_fill.index[-1].year
        df_fill = df_fill[(df_fill.index.year!=first_year) | (df_fill.index.month!=first_month)] # 最初に日付が1日になるまでのデータを削除
        df_fill = df_fill[(df_fill.index.year!=last_year) | (df_fill.index.month!=last_month)] # 最後の月末以降のデータを削除
        # 指定した日付に対応する価格を取得
        price_by_day = df_fill[df_fill.index.day==d][df_fill.columns[0]] # 毎月d日の基準価額
        # 2月は29日、30日が無い場合がある。この場合月末日買い付けとし、2月末日データを入れる
        if (d==29) or (d==30):
            price_by_day = price_by_day.resample("M", convention="end").ffill() # 毎月でリサンプル
            price_idx_feb = price_by_day[price_by_day.index.month==2].index # 2月末日の日付
            price_feb = [df_fill[df_fill.index==p][df_fill.columns[0]].values[0] for p in price_idx_feb] # 2月末日データをdf_fillから取得
            price_by_day[price_by_day.index.month==2]=price_feb # 2月末日データを代入
        # 総取得価格・取得株数と評価額からリターンを計算
        price_acquisition = price_by_day.sum() # 取得価格
        num_of_stocks = len(price_by_day) # 取得株数
        # print(num_of_stocks)
        price_estimate = num_of_stocks * df.tail(1)[df.columns[0]] # 評価額
        gain = (price_estimate - price_acquisition) / price_acquisition # リターン
        return_by_day.append(gain.values[0]*100)

    print(return_by_day)

files = os.listdir(os.getcwd())
csvfiles = [f for f in files if "修正.csv" in f]
for file in csvfiles:
    df = pd.read_csv(file, encoding="utf-8-sig", index_col=0, parse_dates=[0])
    calc_return_by_day_of_month(df)

4. 計算結果

結果をグラフ化したものを以下に記載します。

eMAXIS Slim 全世界株式(オール・カントリー)

eMAXIS Slim 米国株式(S&P500)

SBI・V・S&P500インデックス・ファンド

ニッセイNASDAQ100インデックスファンド

iFreeNEXT FANG+インデックス

全体の傾向としては、以下が言えそうです。

  • 約定日によるリターンの差は2%程度
    • iFreeNEXT FANG+は約5%も差があります。ただ、極端にリターンの低い29・30日を除けば3%ちょっとくらいですし、そもそもボラティリティの高い指数であるのが要因かと思います
  • 毎月1日がリターンが最も高い
    • ニッセイNASDAQ100だけ異なり、毎月8日が高くなっています。ただ、サンプル数が少ないため何とも言いづらいです。今後データが増えて行くと傾向が変わってくるかもしれません
  • 月初めに購入したほうがリターンが高く月末は低い
  • 15日~18日や25日~28日は比較的リターンが低い

なぜ月初めのほうがリターンが高いという傾向か、ちょっと考えてみます。

以下のサイトによると、基準価額は、①組入資産の変動・配当収益・為替損益、②運用費用、③分配金で変わるようです。今回対象とした投信は分配金は出ないので、①②が主要因になります。

www.am.mufg.jp

  • 月末か月初めに運用費用が計上され基準価額が下がる→月初めは低い基準価額で購入できる
  • 組入資産の配当が月中に出るので基準価額が上がる→15や25日は高い基準価額での購入になる

あたりが月初めがリターンが良いことの要因かなあと予想します。が、実際のところどうなのかはよくわかりませんでした。

おわりに

結論としては、今回分析対象の投資信託の設定来データを見ると、毎月1日に約定となるようにつみたて設定するのがいい、ということになりました。

ただ、最初の日経の記事にあるように、この傾向は今後変わる可能性が大いにありますし、2%程度と極端に大きな差にはならないので、入金日なども考慮して納得できる日に設定するのが良いかなと思います。