【プログラミングのお話】
妄信しているドルコスト平均法のアンチパターンについてシミュレーションしたドルコスト平均法が万能だと何となく思っていたのですが、そうじゃないかも!と問題提起されたので自分で試しました。
pythonで簡単にスクリプトを書くだけでも、色々と考察できて非常に有意義でしたので、その過程をまとめました。
コトの発端
下記の本にドルコスト平均法について問題提起をする一節があったので、改めて考えてみました。
もともとは、チャートの読み方を学びたくて読んでいたんですが、S&P500とかの投資信託でも結局は投資タイミングが重要ですよ~ってことを解説してくれて、
非常に勉強になりました。
実際、本記事の他のアンチパターンについても例が示されていて面白いです。
シミュレーションしてみる
証券会社も色んな積立投資シミュレーションのサイトを展開しているんだけど、途中で金利を変えたりすることができない。なので、自分もプログラマの端くれなのでシミュレーションしてみる。
シミュレーションの条件は老後資金のための長期積立投資を想定して下記のような感じ。
- 投資期間:20年間
- 平均利回り:7%
- 月額:8万円(年額96万円)
計算した結果、20年後に4211万円になる。

とりあえず下記サイトのシミュレーション結果と答え合わせしてみる。

大体合ってる。
自身で記述したのは、年初一括投資にしているので、そのくらいの差分かな。
よくyoutuberが紹介している直線も大体こんな感じですね。
さらに細かい指標を見ると、 * 基準価額:3.86倍 * 投資リターン: 2.19 倍 という感じです。
つまり、一括投資しておけば、3.86倍だったものが2.19倍しか得られていないことになる。
右肩上がりになることを想定するなら、一括投資すべきというのはこの辺りになるのでしょうね。
2倍になるというのはすごいことなんだけど、最適解ではなさそうです。
問題はここからで、上記の積立投資期間後に暴落が来た場合を考えてみる。
暴落が来た場合
ITバブルの崩壊とかリーマンショックのようなものが来ると50%くらい落ちるらしい。
今回は、1年ごとにしか計算しないので瞬間的な暴落ではなく、-10%で7年間長期で受けたとする。
同じように計算してみました。
今回から、最初を100として基準価額も同じように表示してます。
暴落開始5年後の20年目で元本を割り込んでます。
この場合、
元本:1920万円
投資結果:1878万円
基準価額:162
順調に15年間上がって積み上げてきたものがわずか5年でくずれ落ちてしまう。
実際には、5年も長期的には落ちることは少ないんでしょうが、それでもちょっとあっけない。
なお、一括投資の場合は基準価額が162なので、1.6倍に増えています。
その意味でも、ドルコスト平均法が有効なのかと疑問になります。
逆パターンなら勝てる?
ドルコスト平均法の有効性がイマイチ分からなくなってきたので、逆パターンを考えてみる。 15年間7%下落して、5年間10%上昇する場合を考えてみる。

結果を計算すると下になりました。 * 元本:1920万円 * 投資結果:2007万円 * 基準価額:54
おお!ようやくドルコスト平均法に軍配が上がりました!。
一括投資の場合は、半分になっているにも関わらず、最終的には80万円ほどリターンも出ている。
長期的な下落が起きる場合は、ドルコスト平均法が適していそうです。
結論
ここまでの結論をいったんまとめます。 * 7%平均として勝ち続けるなら、ドルコスト平均法よりも一括投資が有効 * 取り崩すタイミングで暴落があるとドルコスト平均法は元本毀損の可能性が一括投資よりも高い * 長期的に落ちていて、取り崩すタイミングで上昇していると一括投資よりもリターンが高い
こうしてみると、ドルコスト平均法が有効なタイミングってかなりせまいような気がする。
結局投資する人を増やすために、取り組み始めやすい方式として紹介しているにすぎないんじゃないかなって思う。
少なくとも、S&P500は平均利回りが7%でこれとドルコスト平均法を組み合わせるのが最適!みたいなのは根拠がないんじゃないかと。
一括投資の方がリターン出ますから。
平均的なリターンが望める投資商品の場合であっても、タイミング良く一括投資するのがベスト!という気がする。
投資商品と投資手法の組み合わせとしては微妙という結論です。
20年後にちょうど暴落がきたときに正しい行動を取れるのか
仮にドルコスト平均法を採用して20年後に暴落が来ていたとして、そのあと正しい行動がどれるかについて考えてみる。
正直言って無理じゃない?って思う。
自分には自信が無い。
正直言って今回の暴落も耐えられるんじゃない?ってなって持ち続ける。20年間耐えられたんだから今回も耐えればいいやってなる。
絶対になる。
去年だしておけば、もっと得してたのに~とかなって引き出せない。
その結果、-10%の不況が5年続いたら終わるって言うのが儚いというかメンタルに来そう。
逃げ遅れた結果について改めてシミュレーションした結果が下の図。

2年逃げ遅れただけで、300万円規模の損失。
こうなると目も当てられない。
じゃあ、勝ってるうちに売却すればいいんじゃない?という声も聞こえるけど、これに成功した人を見たことが無い。
実際トランプ相場とかでも逃げ切れずに食らってから損切りする人が多いわけだし。
パチンコとかも一時的には勝つんだけど、負けるまで勝負しつづけるから損する人がこんなに多いのだと思ってる。
依存症とかもあるんだろうけど。
ドルコスト平均法って始めやすいけど、引き際がすごく難しい初心者殺しの手法なんじゃないかというのうが自分なりの結論です。
さらに引き際が難しいにも関わらず、ほったらかし投資とか思考停止するような考え方を展開するのは不誠実な気もして、定期的に見直して自分の頭で考えるのが大事なんだな~と再認識した良い経験でした。
引き際としては、1年目の暴落は食らうことを覚悟して、傷が浅い2年目のうちに全額引き出すのが良い気がしました。
もちろん、世の中には4%ルールみたいなものもあるので、それに従って取り崩すのもありだと思います。
付録:今回使用したソースコード
付録今回使用したソースコードを掲載しておきます。
n_yearとm_yearを編集するだけでも結構使えると思います。
# -*- coding: utf-8 -*- """ """ import plotly.express as px import plotly.graph_objs as go import numpy as np class CurrencySim: def __init__(self,): self.average_rate=1.07 self.position=np.array([]) self.result=np.array([]) self.price=100 self.list_price=np.array([]) self.invest=96 self.year=0 def sim_year(self,): self.position=np.append(self.position,self.invest) self.position=self.position*self.average_rate self.result=np.append(self.result,np.sum(self.position)) self.price=self.price*self.average_rate self.list_price=np.append(self.list_price,self.price) self.year=self.year+1 def update_averagerate(self,average_rate): self.average_rate=average_rate def show_finalresult(self,base_price): print("base price:"+str(self.price/100)) print("return ratio:"+str(self.result[-1]/(self.year*self.invest))) if __name__=="__main__": CuSim=CurrencySim() np_base=np.array([]) np_year=np.array([]) n_year=15 year=1 CuSim.update_averagerate(1.07) for i_year in range(n_year): CuSim.sim_year() np_base=np.append(np_base,year*invest) np_year=np.append(np_year,str(year)+"年目") year=year+1 m_year=7 CuSim.update_averagerate(0.9) for j_year in range(m_year): CuSim.sim_year() np_base=np.append(np_base,year*invest) np_year=np.append(np_year,str(year)+"年目") year=year+1 CuSim.show_finalresult(np_base[-1]) fig=go.Figure() trace_result=go.Scatter(x=np_year,y=CuSim.result,yaxis="y1",name="ドルコスト平均") trace_stock=go.Bar(x=np_year,y=np_base,yaxis="y1",name="元本") trace_price=go.Scatter(x=np_year,y=CuSim.list_price,mode="lines",yaxis="y2",name="基準価額") fig.add_trace(trace_result) fig.add_trace(trace_stock) fig.add_trace(trace_price) fig.update_layout(yaxis1=dict(side='left',title="投資結果"),yaxis2=dict(side='right',showgrid=False,overlaying="y",title="基準価額"),xaxis=dict(title=""),) fig.show()
