注目キーワード

Pythonで米株長期保有シミュレーション!これが長期保有の効果!

長期保有シミュレーション

今回は、Pythonを使った米株(具体的にはナスダック100)の長期保有のシミュレーションを紹介します。資産の形成において、長期保有がどれだけ効いてくるのか、実際のデータを使って検証しました!

驚きの結果をご覧になれます。(投資経験を積まれた方はすでに実体験を通してご存知かもしれませんが・・・)

背景

株式を始めトレードをしている方は結構いらっしゃるのではないでしょうか?
デイトレードにスイングトレード、さらにはスキャルピングまで、短期~超短期のトレードで一喜一憂している方、結構いらっしゃるのではないでしょうか?

どうですか振り返ってみて?
投下した労力に対して収支は見合っていますか??
いつ上がるのか、いつ下がるのか? 一度ポジションを持つと、気が気でないですよね。。。

しかも、頑張って株価の未来を予測しようとしてもなかなか当たらない!
短期の株価をテクニカル分析に機械学習と先端テクノロジーを片っ端から突っ込んでも、なかなか当たらないものですよね。

そこで今回は、無作為なタイミングでトレードしても、長期保有することでしっかりと資産が成長することをシミュレーションを通して確かめたいと思います。

シミュレーションの準備

まず、シミュレーションに必要なデータを集めます。
今回は、investing.comから、ナスダック100指数のヒストリカルデータを入手してシミュレーションに使いました。

  • 入手したヒストリカルデータ(日足)を nq100.csv とします。
  • カラム名は、ご丁寧に日本語なのですが、Pythonで解析を進めるので英語に直しています。(データ操作において、日本語カラム名を指定するのは、個人的に違和感があったので…)

nq100.csv の先頭部分:次のようにカラム名を英語にしています。

"date","close","open","high","low","volume","diff"
"2020年10月16日","11,798.00","11,891.00","12,022.00","11,792.00","-","-0.64%"
"2020年10月15日","11,874.00","11,950.00","11,983.25","11,736.50","595.93K","-0.84%"
"2020年10月14日","11,974.25","12,079.75","12,178.00","11,901.75","597.20K","-0.99%"
(・・・中略・・・)

データファイルの設置場所

これ以降で示すPythonコードでは、次の場所に設置してあるデータファイルを読み込んで分析を進めるとします。
(今後他の銘柄の取引データもダウンロードする予定)

./data
   |
   +--- nq100.csv    # NASDAQ100

シミュレーション処理

それでは、ダウンロードした nq100.csv を使って、JupyterNotebook上で長期保有シミュレーション処理を実装していきます。

使用するモジュール群の読み込み

今回は、おなじみの numpy,pandas,matplotlibに加え、綺麗なグラフ描画でお世話になるseabornをインポート。

import numpy as np
import pandas as pd
import matplotlib.pylab as plt
import seaborn as sns
import matplotlib as mpl

視覚化処理の設定

視覚化処理の設定は以下の通り。

plt.style.use('default')
sns.set(font="IPAexGothic")
sns.set_style('whitegrid')
sns.set_palette('autumn')

価格データの読み込みと前処理

いったん、CSVファイルをデータフレームとして読み込んでおきます。

data_dir = "./data"
filename = "nq100.csv"
df_sample = pd.read_csv(f"{data_dir}/{filename}")

前処理

ただし、読み込んだ直後の状態では、データ型が計算処理に不適切なので、前処理をします。具体的には以下の処理を実施します。

  • カンマ桁区切りされているOHLCを補正し、float型に変換する。
  • 日本語の年月日表記の日付(date)を補正し、datetime型に変換する。
カンマ区切りのOHLCの処理
# カンマで桁区切りされているので補正する
cols = ["close","open","high","low"]
for _c in cols:
    df_sample[_c] = df_sample[_c].apply(lambda x: float(x.replace(",", "")))
日付カラムの処理

日本語表記(年月日形式)のものを、Datetime型に変換します。

def _convert_date(x):
    x = str(x).replace('年', '-').replace('月', '-').replace('日', '-')
    return x

df_sample['date'] = pd.to_datetime(df_sample['date'].apply(lambda x:_convert_date(x)))
前処理結果の確認

前処理後のテーブルの内容を確認します。
日付と数値がちゃんと変換されていますね。

df_sample.head()

実行結果:
テーブル冒頭部

1年間の営業日数の把握

長期保有シミュレーションをするあたって、1年間が何営業日に相当するのか把握しておきます。

  • シミュレーション処理をする際、何件先のレコードを参照して比較すれば良いのかが分かります。

算出方法

CSVファイルでは、日付が新しい順に並んでいるので、条件日付(例 2001-01-01)以降のレコードを取り出した後、最後のレコードを参照すれば良いでしょう。

各年の最初の営業日のリストアップ

以下のコードを実行して、各年の最初の営業日を取り出してみます。

for _y in range(2001, 2021):
    _y_condition = f'{_y}-01-01'

    # 各年の最初の営業日を取得
    _df_selected = df_sample[df_sample['date'] >= _y_condition]
    _first_date = _df_selected.iloc[-1]['date']
    _index_of_fd = _df_selected.index[-1]
    print(_first_date.strftime('%Y-%m-%d'), '/', _index_of_fd)

実行結果:

2001-01-02 / 5079
2002-01-01 / 4828
2003-01-02 / 4573
2004-01-02 / 4320
2005-01-03 / 4067
2006-01-03 / 3815
2007-01-02 / 3564
2008-01-02 / 3311
2009-01-02 / 3057
2010-01-04 / 2804
2011-01-03 / 2551
2012-01-03 / 2299
2013-01-02 / 2046
2014-01-01 / 1790
2015-01-01 / 1530
2016-01-04 / 1266
2017-01-02 / 1002
2018-01-01 / 739
2019-01-01 / 475
2020-01-01 / 210

年ごとに初営業日が1月1日~1月4日の間でまちまちですね。
営業日の間隔を出して見ましょう。

  • 各年の初営業日間のインデックスの差が間隔日数に相当する。
# 各年の最初の営業日のインデックス
indice_1st_bd = []

for _y in range(2001, 2021):
    _y_condition = f'{_y}-01-01'

    # 各年の最初の営業日を取得
    _df_selected = df_sample[df_sample['date'] >= _y_condition]

    # 各年最初の営業日のインデックス
    _index_of_fd = _df_selected.index[-1]
    indice_1st_bd.append(_index_of_fd)
print(indice_1st_bd)
print(pd.Series(indice_1st_bd[::-1]).diff())

実行結果:


[5079, 4828, 4573, 4320, 4067, 3815, 3564, 3311, 3057, 2804, 2551, 2299, 2046, 1790, 1530, 1266, 1002, 739, 475, 210]
0       NaN
1     265.0
2     264.0
3     263.0
4     264.0
5     264.0
6     260.0
7     256.0
8     253.0
9     252.0
10    253.0
11    253.0
12    254.0
13    253.0
14    251.0
15    252.0
16    253.0
17    253.0
18    255.0
19    251.0
dtype: float64

傾向が途中で変わっていますね。
2014年を境目に、1年の営業日数が違うことが分かります。

各レコードのインデックスを使って、単純に264営業日間隔や253営業日間隔で比較すれば良いという話ではなさそうです。

きちんとシミュレーションするには、インデックスの間隔に頼らずに1年後の営業日のレコードを参照しなければならないでしょう。

対策:経過日数の付与

ヒストリカルデータの最初の営業日からの経過日数(土日も含む)を新たにカラムに追加して、それを使うとします。
全体の最初の営業日から見た各営業日の経過日数は、Timestamp型 の差分を取れば取得することができます。

# ヒストリカルデータ全体で見たときの初営業日
total_first_date = df_sample.iloc[-1]['date']
print(total_first_date)

実行結果:

2000-01-03 00:00:00

ヒストリカルデータ全体の初営業日は 2001-01-03 です。
この日を基準に、各営業日の経過日数を求めましょう。

# 全体の最初の営業日からの経過日数
df_sample['date'].apply(lambda x: (x - total_first_date).days)

実行結果:

0       7592
1       7591
2       7590
3       7589
4       7588
5       7585
6       7584
7       7583
8       7582
9       7581
(・・・中略・・・)
5326       7
5327       4
5328       3
5329       2
5330       1
5331       0
Name: date, Length: 5332, dtype: int64

Timestamp型 の差分は、Timedelta型 なので時間の差から経過日数(days)を取得できます。

動作を確認できました。
これなら経過日数を計算して、インデックスの代わりに使うことが出来そうですね。

カラム elapsed_days の追加

それではデータフレームに新たなカラムelapsed_daysを追加しましょう。
このカラムに全体の最初の営業日からの経過日数をセットします。

df_sample['elapsed_days'] = df_sample['date'].apply(lambda x: (x - total_first_date).days)
df_sample.head()

カラムelapsed_daysが追加できたことが確認できました。
テーブル確認

elapsed_days を使って1年前のレコードを参照する

試しに365日前のレコードを取りだしたところ、約1年前の営業日のレコードを取り出せました。

df_sample[df_sample['elapsed_days'].isin([7592, 7592 - 365])]

date の値が、2020-10-162019-10-17、間隔は1年ですね。
elapsed_daysを使って取り出す

変化率の計算

長期保有における変化率を次のように定義します。

変化率_N = \frac{N年経過後の価格}{購入時の価格} 

1年間保有する場合、次のようになります。

変化率_{1年} = \frac{1年経過後の価格}{購入時の価格} 
  • 変化率が1.0を上回っていれば、購入時点より株価が上がっているので黒字ということになります。
  • 変化率が1.0を下回っていると、購入時点より株価が下がっているので、赤字ということになります。
    • この損益率から1を引くと、利回りになりますね。
\begin{align}
利回り_{1年} &= \frac{1年経過後の価格 - 購入時の価格}{購入時の価格} \\
&= 変化率_{1年} - 1.0 
\end{align} 

変化率の分布を求める

それではシミュレーションに必要な変化率の分布を求めていきましょう。
まずは1年保有した場合の変化率の分布を求めてみます。

  • buff_profit に、各営業日ごとに計算した1年後の損益率を蓄積していきます。
  • 各営業日ごとに最大変化率最小変化率を計算し、蓄えて行きます。
  • elapsed_days を365日さかのぼって、比較するレコードを探します。
    • 見つからない場合は、そこでループ終了です。(レコードは日付降順のため)
from tqdm import trange
buff_profit = []

for _i in trange(0, len(df_sample)):
    _df_to = df_sample.iloc[_i]
    _pos_from = _df_to['elapsed_days'] - 365

    # 起点となる営業日がヒストリカルデータの範囲外になったらそこで処理終了
    if _pos_from < 0:
        break

    _df_from = df_sample[df_sample['elapsed_days'] <= _pos_from][0:1].iloc[0]

    # 最大、最小変化
    _profit_max = _df_to['high'] / _df_from['low']
    _profit_min = _df_to['low'] / _df_from['high']

    buff_profit.extend([_profit_max, _profit_min])

実行結果:

95%|███████████████████████████████████  | 5056/5332 [00:13<00:00, 444.98it/s]

計算が終わったので、分布を表示してみます。
seaborndistplot を使って、変化率の分布を視覚化します。

plt.style.use('default')
sns.set_style('whitegrid')
sns.set_palette('autumn')

# 日本語フォント指定
mpl.rcParams['font.family'] = 'IPAexGothic'
figure = plt.figure(figsize=(16,8))
sns.distplot(np.array(buff_profit), kde=False)
plt.xlabel(u'1年後の価値÷購入時の価値', fontsize=16)
plt.title('NQ100を1年保有した際の資産価値の変化', fontsize=18)
plt.axvline(x=np.median(buff_profit), color="red")

分布が出ましたね!

1年保有した際の資産の変化の分布

赤い垂直ラインは中央値で、計算すると 1.125、つまり平均的なケースでは1年で12.5%資産が増えることを表しています。

考察:資産価値の変化率の分布

先ほどの分布を見ると複数の分布が重なっていることがうかがえます。
複数の分布

  • 左端の分布は、主に2000年~2002年に起きたドットコムバブル/2008年のリーマンショックの影響で生じたサンプルと考えられます。
  • 右端の分布は、暴落した時に運よく底値付近で買ったポジションで得られた利益ホクホクのケースと考えられます。

今回の資産運用シミュレーションでは、平均的な運用を想定し、変化率の中央値を元に計算をしたいと思います。
(まれに起きるブラックスワンと呼ばれる暴落現象のことを考慮すると、エントリ内容が大幅に膨れ上がりそうなので、別のエントリで扱いたいと思います)

シミュレーションの準備:保有期間ごとの資産価格変化率の分布を求める

それでは、シミュレーションに必要となる基礎データである保有期間ごとの資産価格の変化率の分布 を求めて参りましょう。
今回は、決め打ちですが1年~15年の範囲で、保有した際の資産価格の変化率を計算したいと思います。

処理の設計

  • 保有年数ごとの変化率をpl_bufferにプールする。
    • pl_buffer : 辞書+リストの階層構造
      • key : 保有年数
      • value : 各保有年数ごとの変化率を貯め込むリスト

資産価格の変化率の分布を計算する

以下のコードで資産価格の変化率を計算します。

from tqdm import tqdm

# ちゃんと計算する場合
target_years = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
pl_buffer = {_:[] for _ in target_years}
xlabels = ['{}年'.format(_) for _ in target_years]

for _year in target_years:
    print(f'保有期間の変化率の分布を計算しています:{_year}年の場合')

    # 何日離れた先を参照するか
    _days_distance = 365 * _year

    # 進捗バーの改行崩れに対応する
    for _i in tqdm(list(range(0, len(df_sample))), position=0, leave=True):
        _df_to = df_sample.iloc[_i]
        _pos_from = _df_to['elapsed_days'] - _days_distance

        # 起点となる営業日がヒストリカルデータの範囲外になったらそこで処理終了
        if _pos_from < 0:
            break

        _df_from = df_sample[df_sample['elapsed_days'] <= _pos_from][0:1].iloc[0]

        # 最大、最小変化
        _profit_max = _df_to['high'] / _df_from['low']
        _profit_min = _df_to['low'] / _df_from['high']

        pl_buffer[_year].extend([_profit_max, _profit_min])

実行結果:

保有期間の変化率の分布を計算しています:1年の場合
 95%|██████████████████████████████████▉  | 5039/5332 [00:14<00:00, 337.34it/s]
保有期間の変化率の分布を計算しています:2年の場合
 91%|█████████████████████████████████▌   | 4828/5332 [00:14<00:02, 240.55it/s]
保有期間の変化率の分布を計算しています:3年の場合
 85%|███████████████████████████████▌     | 4552/5332 [00:17<00:01, 415.69it/s]
保有期間の変化率の分布を計算しています:4年の場合
 81%|█████████████████████████████▉       | 4320/5332 [00:11<00:02, 436.92it/s]
保有期間の変化率の分布を計算しています:5年の場合
 76%|████████████████████████████▏        | 4054/5332 [00:11<00:02, 431.10it/s]
保有期間の変化率の分布を計算しています:6年の場合
 71%|██████████████████████████▍          | 3806/5332 [00:10<00:03, 427.05it/s]
保有期間の変化率の分布を計算しています:7年の場合
 66%|████████████████████████▌            | 3545/5332 [00:09<00:04, 391.49it/s]
保有期間の変化率の分布を計算しています:8年の場合
 62%|██████████████████████▊              | 3285/5332 [00:08<00:05, 351.36it/s]
保有期間の変化率の分布を計算しています:9年の場合
 57%|█████████████████████                | 3036/5332 [00:08<00:06, 362.61it/s]
保有期間の変化率の分布を計算しています:10年の場合
 52%|███████████████████▎                 | 2787/5332 [00:07<00:06, 416.98it/s]
保有期間の変化率の分布を計算しています:11年の場合
 48%|█████████████████▌                   | 2538/5332 [00:06<00:06, 441.68it/s]
保有期間の変化率の分布を計算しています:12年の場合
 42%|███████████████▋                     | 2256/5332 [00:05<00:07, 413.93it/s]
保有期間の変化率の分布を計算しています:13年の場合
 38%|█████████████▉                       | 2008/5332 [00:04<00:07, 423.70it/s]
保有期間の変化率の分布を計算しています:14年の場合
 33%|████████████▍                        | 1785/5332 [00:04<00:08, 416.85it/s]
保有期間の変化率の分布を計算しています:15年の場合
 29%|██████████▌                          | 1529/5332 [00:03<00:08, 424.38it/s]

分布をプロットする

今回は、バイオリンプロットを用いて各保有期間ごとの分布を1つのグラフ内で並べて視覚化します。

plt.style.use('default')
sns.set_style('whitegrid')
sns.set_palette('autumn')

# SNS の初期化が終わってから指定するとワークする
plt.rcParams['font.family'] = 'IPAexGothic'

fig = plt.figure(figsize=(16,8))
ax = fig.add_subplot(1, 1, 1)

ax.violinplot([pl_buffer[_] for _ in target_years], showmedians=True)
ax.set_xticks(range(1, len(target_years)+1))
ax.set_xticklabels(xlabels)
ax.set_xlabel(u'保有期間', fontsize=16)
ax.set_ylabel(u'損益率', fontsize=16)
plt.title('保有期間による損益分布の推移: NQ100', fontsize=18)
# ax.set_ylim(0, 20)
plt.show()

バイオリンプロット

ようやく分布の全体像が見えてきました!

  • 変化率の中央値を見ると、なだらかな指数関数的なカーブを描いていることが分かります。
  • 保有期間が11年~12年において、資産価値の変化率が上ブレしていますが、恐らくこれはリーマンショックの中でポジションを買ったケース のサンプルと考えられます。
    • 長い目で見ると暴落相場は絶好の買い場ということが示されています。

資産価格の変化率の傾向を確かめる

求めた資産価格の変化率の分布から、各保有期間ごとの中央値を求めます。
ついでに、理解しやすくするため年利換算した値も求めます。

for _y in target_years:
    _median_ratio = np.median(pl_buffer[_y])
    _ratio_per_year = _median_ratio ** (1/_y) - 1.0
    print(f'保有{_y}年目 : 購入時の {"{:.03f}".format(_median_ratio)} 倍  / 年利換算 {"{:.03f}%".format(_ratio_per_year*100)}')
保有1年目 : 購入時の 1.125 倍  / 年利換算 12.506%
保有2年目 : 購入時の 1.264 倍  / 年利換算 12.446%
保有3年目 : 購入時の 1.473 倍  / 年利換算 13.775%
保有4年目 : 購入時の 1.639 倍  / 年利換算 13.149%
保有5年目 : 購入時の 1.755 倍  / 年利換算 11.909%
保有6年目 : 購入時の 1.744 倍  / 年利換算 9.713%
保有7年目 : 購入時の 1.942 倍  / 年利換算 9.948%
保有8年目 : 購入時の 2.219 倍  / 年利換算 10.476%
保有9年目 : 購入時の 2.455 倍  / 年利換算 10.495%
保有10年目 : 購入時の 2.774 倍  / 年利換算 10.742%
保有11年目 : 購入時の 3.104 倍  / 年利換算 10.846%
保有12年目 : 購入時の 3.644 倍  / 年利換算 11.378%
保有13年目 : 購入時の 3.997 倍  / 年利換算 11.248%
保有14年目 : 購入時の 4.526 倍  / 年利換算 11.387%
保有15年目 : 購入時の 5.024 倍  / 年利換算 11.361%

おしなべて、年利換算で10%を越えています。
さすが米国株式、ナスダックらしいですね。
暴れ馬のように飛び跳ねますが、振り落とされなければ素晴らしいパフォーマンスです!

いよいよ準備が整ったので、資産の長期保有のシミュレーションに進みたいと思います。

シミュレーション

以下の設定の下でシミュレーションをします。

  • 毎年 240万円 分のポジションを追加する。
  • 15年間のポジションの積み立てを続けた場合の資産価値の変化を計算する。

また、シミュレーションの詳細を把握できるように以下の機能を実装します。

  • 保有期間が1年経過するごとに資産価値の推移をログに書き出す。
    • その時点で保有しているポジションの内訳を書きだす。

実装コード

# 保有シミュレーション
INVEST_PER_YEAR = 240 * 10000 # 240万円

# 保有期間ごとの運用率(0.0~)
# - 損失の場合は 1.0以下になる。
return_by_holdlength = {_:np.median(pl_buffer[_]) for _ in target_years}

total_asset = 0

# シミュレーション結果を格納する
# - Dataframe の材料にする
simu_history = []

# 各年にエントリしたポジションの運用状況
asset_positions = []
for _ in range(0, 15):
    asset_positions.append({'entry_time': _, 'amount_init' : INVEST_PER_YEAR, 'amount_updated': INVEST_PER_YEAR})

    # 保有ポジションの運用状況の更新
    for _year in range(_ + 1, _ + 2):
        for _idx, _apos in enumerate(asset_positions):
            # エントリした時間からどれくらい経過しているか?
            _hold_length = _year - _apos['entry_time']
            #print(_apos)
            #print('{} -> {}'.format(_year, _hold_length))

            _return_ratio = return_by_holdlength[_hold_length]
            # 運用結果の期待値
            # 初期資産 x 経過年数に対応した運用率
            _apos.update({'amount_updated': _apos['amount_init'] * _return_ratio})

            # _apos には参照が入っているので直接更新可能...
            # print(id(_apos), id(asset_positions[_idx]) )

    # 資産額の推移を計算
    total_asset = np.sum([_['amount_updated'] for _ in asset_positions])
    print('●経過年数 {}年 : 資産額 = {:,.0f}円'.format(_ + 1, total_asset))
    print('--- 資産の内訳 ---')
    for _idx, _apos in enumerate(asset_positions):
        print('[{}] 購入年 : {}年目 購入時価値:{:,.0f}円 ⇒ 現在価値:{:,.0f}円'.format(_idx + 1, 
                                                                      _apos['entry_time'], 
                                                                      _apos['amount_init'],
                                                                      _apos['amount_updated']))

    print('-' * 15)
    print()

    simu_history.append([_idx + 1, total_asset])

# 視覚化用のデータフレームを作る
df_simu_history = pd.DataFrame(simu_history, columns=['timeline', 'total_asset'])

シミュレーション結果(ログ)

●経過年数 1年 : 資産額 = 2,700,139円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 2年 : 資産額 = 5,734,704円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 3年 : 資産額 = 9,269,366円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 4年 : 資産額 = 13,203,247円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 5年 : 資産額 = 17,415,644円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 6年 : 資産額 = 21,601,298円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 7年 : 資産額 = 26,262,734円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 8年 : 資産額 = 31,588,133円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 9年 : 資産額 = 37,480,539円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:5,892,406円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[9] 購入年 : 8年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 10年 : 資産額 = 44,138,186円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:6,657,647円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:5,892,406円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[9] 購入年 : 8年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[10] 購入年 : 9年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 11年 : 資産額 = 51,587,675円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:7,449,489円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:6,657,647円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:5,892,406円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[9] 購入年 : 8年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[10] 購入年 : 9年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[11] 購入年 : 10年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 12年 : 資産額 = 60,333,983円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:8,746,308円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:7,449,489円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:6,657,647円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:5,892,406円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[9] 購入年 : 8年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[10] 購入年 : 9年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[11] 購入年 : 10年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[12] 購入年 : 11年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 13年 : 資産額 = 69,927,807円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:9,593,823円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:8,746,308円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:7,449,489円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:6,657,647円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:5,892,406円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[9] 購入年 : 8年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[10] 購入年 : 9年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[11] 購入年 : 10年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[12] 購入年 : 11年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[13] 購入年 : 12年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 14年 : 資産額 = 80,789,209円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:10,861,403円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:9,593,823円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:8,746,308円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:7,449,489円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:6,657,647円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:5,892,406円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[9] 購入年 : 8年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[10] 購入年 : 9年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[11] 購入年 : 10年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[12] 購入年 : 11年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[13] 購入年 : 12年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[14] 購入年 : 13年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

●経過年数 15年 : 資産額 = 92,845,980円
--- 資産の内訳 ---
[1] 購入年 : 0年目 購入時価値:2,400,000円 ⇒ 現在価値:12,056,771円
[2] 購入年 : 1年目 購入時価値:2,400,000円 ⇒ 現在価値:10,861,403円
[3] 購入年 : 2年目 購入時価値:2,400,000円 ⇒ 現在価値:9,593,823円
[4] 購入年 : 3年目 購入時価値:2,400,000円 ⇒ 現在価値:8,746,308円
[5] 購入年 : 4年目 購入時価値:2,400,000円 ⇒ 現在価値:7,449,489円
[6] 購入年 : 5年目 購入時価値:2,400,000円 ⇒ 現在価値:6,657,647円
[7] 購入年 : 6年目 購入時価値:2,400,000円 ⇒ 現在価値:5,892,406円
[8] 購入年 : 7年目 購入時価値:2,400,000円 ⇒ 現在価値:5,325,399円
[9] 購入年 : 8年目 購入時価値:2,400,000円 ⇒ 現在価値:4,661,436円
[10] 購入年 : 9年目 購入時価値:2,400,000円 ⇒ 現在価値:4,185,654円
[11] 購入年 : 10年目 購入時価値:2,400,000円 ⇒ 現在価値:4,212,397円
[12] 購入年 : 11年目 購入時価値:2,400,000円 ⇒ 現在価値:3,933,881円
[13] 購入年 : 12年目 購入時価値:2,400,000円 ⇒ 現在価値:3,534,662円
[14] 購入年 : 13年目 購入時価値:2,400,000円 ⇒ 現在価値:3,034,565円
[15] 購入年 : 14年目 購入時価値:2,400,000円 ⇒ 現在価値:2,700,139円
---------------

考察

  • 15年コツコツと継続することで、資産が1億円近くになることが判明!
  • 資産を構成する各ポジションが、経過年数が増えるごとに価値が育っていることが分かります。
    • 例) 15年前に購入した240万円分のポジションが、約1,200万円に成長しています。
  • 複利の効果がどういったものか、具体的な数字、それも各ポジションごとの資産価値の推移も伴って確認することができます。
  • 投資する場合は資産価値が増えるので良いですが、これがもし有利子負債だったら大変なことに!
    • 複利で借金が増えるのは極めて恐ろしい事態ですね!

シミュレーション結果の視覚化

直感的にも把握できるように、シミュレーション結果を視覚化します。

視覚化のコード

from matplotlib.ticker import FuncFormatter

ax = sns.barplot(x="timeline", y="total_asset", data=df_simu_history, color="orange")
plt.xlabel('経過年数')
plt.ylabel('資産合計')
plt.title('毎年240万円をNQ100に投資し続けた場合の資産額推移')
ax.yaxis.set_major_formatter(FuncFormatter(
    lambda y,x:'{:,.0f}円'.format(y)))
plt.show()

視覚化した結果

資産額の推移は以下のようになりました。
緩やかな指数関数的に増え続けていますね。
長期保有のシミュレーション結果

まとめ

  • 今回は、米国株式(ナスダック)に長期間積み立て続けた場合の資産額の推移をシミュレーションしました。
  • 15年間続けると1億円近くに資産が増えることが分かりました。
    • 一喜一憂せずに長期保有すると、複利の恩恵にあずかることが出来る!
    • 年金問題の切り札となる可能性を秘めていますね!
  • 暴落相場は、長い目で見ると絶好の買い場であることが分かりました。

資金力があるほど、余裕をもって相場の荒波を乗り越えることができると思うので、時間が経過するほど資産が資産を呼ぶ複利効果が炸裂することがうかがえます。

改めてPythonでシミュレーションしてみて良かったと思えるテーマでした。
同様のシミュレーションを様々な金融商品に対して実施し、長期保有による資産の伸び方を調査して、ポートフォリオを組むと良さそうですね。

ということで、、、
資産運用は発酵食品、その心は 寝かせるほど美味しくなる!忘れた頃に旨くなる! ということで締めくくらせていただきます!

それでは皆さま、良き投資ライフを!

こちらのエントリもあわせてどうぞ!

目次 1 まじめにコーディング練習1.1 秘書問題とは1.1.1 代表的な例1.2 数学的に証明されている最適解1.3 まずは数学的な最適解を確かめる2 実験2.1 応募者サンプルデータを作る関数2.2 自然対数 e を取得する2.3 秘書問題を解く関数を実装する3 実験を実施4 採用結果の期待値( […]

目次 1 概要2 命名規則(Naming)2.1 避けるべき名前2.2 命名規則(Naming Conventions)2.2.1 Protected/private メソッド・プロパティ2.2.2 ダンダー(__)を使ったprivate化の注意点2.2.3 モジュール化:クラスや関数のまとめ方2. […]

目次 1 Python でExcelファイルをいじってみたらzip圧縮ファイルだった!2 このエントリを読むと得られるであろうもの3 実験環境についての説明4 今回使用するPythonモジュールを読み込む5 xlsxファイルをzipファイルとして読み込んでみる6 zipファイルのアーカイブからxml […]

目次 1 Pythonでtar.gzアーカイブを扱う方法2 Python の tarfile モジュール3 tarfile モジュールのインポート4 tar.gz アーカイブを読み込む5 tar.gz アーカイブを出力するコード6 まとめ等 Pythonでtar.gzアーカイブを扱う方法  備忘録& […]

目次 1 Apache Beam チュートリアル2 Apache Beam 概要2.0.1 (補足) Apache Beam のリリース状況3 概念3.1 Runner(実行環境)3.2 パイプライン処理を構成する概念4 基本的なパイプラインの開発の流れ4.1 パイプライン処理内の各工程の詳細5 動 […]

目次 1 概要2 Anacondaで開発環境を構築2.1 自然言語処理用の開発環境を conda で構築3 mecab 本体のインストール4 neologd のインストール&設定4.1 ビルド環境の整備4.2 neologd のインストール4.3 mecab の辞書を neologd に変更する4. […]

目次 1 長期保有シミュレーション1.1 背景2 シミュレーションの準備2.1 データファイルの設置場所3 シミュレーション処理3.1 使用するモジュール群の読み込み3.2 視覚化処理の設定3.3 価格データの読み込みと前処理3.3.1 前処理3.3.1.1 カンマ区切りのOHLCの処理3.3.1. […]

目次 1 秘書問題をもうちょっとだけ掘り下げる2 おさらい:シミュレーションの定義3 今回の評価基準:評価期待値4 実験5 Python コード実装!5.1 序盤定義部分5.2 応募者サンプルデータを作る関数5.3 秘書問題を解く関数を実装する6 実験6.1 実験結果を描画する7 レビューまとめ8 […]

目次 1 概要2 感染フェーズの数値化とは2.1 Phase Position の分布から分かること2.2 Phase Position 上位国の顔ぶれ3 序盤から感染爆発に襲われた欧米先進国4 感染者の重症化率から分かること4.1 重症化率の高い国は医療崩壊のリスクに晒されている5 医療崩壊リスク […]

目次 1 新型コロナ・致死率の時系列推移を分析した結果分かったこと1.1 新型コロナのニュースで頻繁に見かける国々と日本の比較:1.2 ヨーロッパにおける致死率の二極化:1.3 新型コロナ対策に成功している国?での比較 新型コロナ・致死率の時系列推移を分析した結果分かったこと  先日から使い始めたジ […]

最新情報をチェックしよう!