口下手エンジニアの悪あがき

自動車エンジニアのつぶやき

かゆい所に手が届く積立シミュレーションをPythonで#1

来年より新NISAが始まることもあること、また日本株がコロナ渦で深く沈んだ振り戻しにより、失われた30年を取り戻すかのように右肩上がりに進んでいることで、ちまたでは投資ブームが到来しています。

基本的に王道とされているのが、毎月定額の積み立てで投資信託を買っていく人が多いと思いますが、貯金がない人も、株価が上がって恩恵を受けている人も、これからどうやって投資をしていくのか、いろいろなパターンを想定してシミュレーションすると思います。

私も老後資金の目標を立てたときに、月々いくら積み立てて、年利何%だったら何年後にいくらになるかをいろいろ想定して来年からの新NISAを検討したいと思います。

ただ、ちまたの積み立てシミュレーションではなかなかグラフが見たいように見れなかったり、やりたいことができなかったりするので、自分でコードを作ってシミュレーションしていきたいと思います。

最終的にはいろいろな機能を織り込みたいところですが、まずは基本となる「元本」「毎月の積立額」「年利」「積立年数」から毎年いくら増えていくかを見える化できるものを作りたい。

計算の前提

入力するパラメータは以下の4つとします。 「初期投資金額」「毎月の積立額」「年利」「積立年数」

numpy金融系モジュール「numpy financial」の使用

fv — numpy-financial documentation

複利計算をするためローンの支払い計算などで使われる「numpy-financial」モジュールの「fv(future value)」関数を使用します。基本的にはローンつまり、借金の返済計算で使うことが目的のようで、現在の金額や支払額などは借金を「正数」、積立を「負数」として扱わなければいけないみたいです。

インストールしてない人は「pip」で入れましょう。

pip install numpy-financial

基本的な使い方はこんな感じ

numpy_financial.fv(rate, nper, pmt, pv, when='end')
No. 項目 内容 要否
1 rate 金利(100%なら1) 必須
2 nper 計算回数 必須
3 pmt 1回の支払い額(ローンなら正数、積立は負数) 必須
4 pv 現在の金額(ローンなら正数、積立は負数) 必須
5 when 支払い期日(期初=1,期末=0(デフォルト=0)) オプション

アウトプットイメージを決める

先にどんなものを作りたいかイメージします。
入力する変数は以下の4つの変数を入力して、毎年の資金推移をグラフ化できるようにしたい。

ただ、金融系の英語がよくわからないのでセンスのない変数名になっているのはご容赦ください。 (誰か教えて・・・)

敗者のゲーム

変数 内容
seed_money 初期投資金額
amount_accum 毎月の積立額
num_years 積立年数
annual_rate 年利

たとえば、

変数 内容
seed_money 初期投資金額 100万円
amount_accum 毎月の積立額 5万円
num_years 積立年数 10年
annual_rate 年利 5%

としたときに、こんなイメージができるグラフを出力したい。

図1.積立シミュレーショングラフ

コードを考えてみる

まずは変数の設定

seed_money = 100  # 初期投資金額(万円)
amount_accum = 5  # 毎月の投資金額(万円)
num_years = 10  # 積立期間(年)
annual_rate = 5  # 年利(%)

次にグラフ作成のために年毎の変数を格納するリストの初期値で作成

asset_now = seed_money  # 現在の資産額(asset_now)を初期投資金額で初期化
lst_seed = [seed_money]*(num_years+1)  # 初期投資金額は固定で年数分リスト化
lst_interst = [0]  # 複利による利益のリスト(lst_intrest)、初期値は「0」
lst_deposit = [0]  # 毎年の積立額をリスト化、初期値は「0」
lst_total = [seed_money]  # 毎年の資産額のリスト、初期値は初期投資金額

毎年の積立額から複利による利益を「fv関数」で計算、積立期間分ループを回す

for i in range(num_years):
    asset_now = npf.fv(annual_rate/12/100, 12, -amount_accum, -asset_now)
    lst_total.append(int(asset_now))
    lst_interst.append(lst_total[-1] - seed_money - (i+1)*amount_accum*12)
    lst_deposit.append((i+1)*amount_accum*12)

分かりやすいように棒グラフの積み上げでグラフ化

t = [i for i in range(num_years + 1)]
fig, ax = plt.subplots(figsize=(10,5), sharey=True)
ax.bar(t, lst_seed,label="initial investment")
ax.bar(t, lst_deposit,bottom=lst_seed,label="amount of deposit")
ax.bar(t,lst_interst,bottom=[i+j for i,j in zip(lst_seed,lst_deposit)],label="gain")

さらに金額がわかるようにデータラベル、軸ラベルを付けていきます

text_y1 = [0.4 * i+seed_money for i in lst_deposit]
text_y2 = [0.3 * j + i + seed_money for i,j in zip(lst_deposit,lst_interst)]
for i in range(len(lst_deposit)):
    ax.text(t[i],seed_money*0.4,seed_money, ha="center",size=9)
    ax.text(t[i],text_y1[i],lst_deposit[i], ha="center", size=9)
    ax.text(t[i],text_y2[i],lst_interst[i], ha="center", size=9)
    ax.text(t[i],lst_total[i]+lst_total[-1]/50,lst_total[i],ha="center", size=10, color="k")
plt.grid(axis='y')
plt.xlabel("Number of Years")
plt.ylabel("Amount of money(10,000yen)")

これで完成!!
グラフを見てみるとつくづく複利と時間の力を思い知らされます。計算例で出したように月々5万程度でも年利5%で10年も運用したら200万円以上の利益をもたらすことができる!!これをするとしないでは確実に投資の格差が広がっていくというものです。しっかり勉強して利益をつかんでいきたい。

これを基本として、いろいろとカスタマイズしていこうと思います。

図2.積立シミュレーション結果

投資の大原則 人生を豊かにするためのヒント

全コード

import numpy as np
import numpy_financial as npf
import matplotlib.pyplot as plt

def calc(seed_money, amount_accum=0, num_years=15, annual_rate=5):
    asset_now = seed_money
    lst_seed = [seed_money]*(num_years+1)
    lst_interst = [0]
    lst_deposit = [0]
    lst_total = [seed_money]
    
    for i in range(num_years):
        asset_now = npf.fv(annual_rate/12/100, 12, -amount_accum, -asset_now)
        lst_total.append(int(asset_now))
        lst_interst.append(lst_total[-1] - seed_money - (i+1)*amount_accum*12)
        lst_deposit.append((i+1)*amount_accum*12)
        
    t = [i for i in range(num_years + 1)]
    fig, ax = plt.subplots(figsize=(10,5), sharey=True)
    ax.bar(t, lst_seed,label="initial investment")
    ax.bar(t, lst_deposit,bottom=lst_seed,label="amount of deposit")
    ax.bar(t,lst_interst,bottom=[i+j for i,j in zip(lst_seed,lst_deposit)],label="gain")
    text_y1 = [0.4 * i+seed_money for i in lst_deposit]
    text_y2 = [0.3 * j + i + seed_money for i,j in zip(lst_deposit,lst_interst)]
    for i in range(len(lst_deposit)):
        ax.text(t[i],seed_money*0.4,seed_money, ha="center",size=9)
        ax.text(t[i],text_y1[i],lst_deposit[i], ha="center", size=9)
        ax.text(t[i],text_y2[i],lst_interst[i], ha="center", size=9)
        ax.text(t[i],lst_total[i]+lst_total[-1]/50,lst_total[i],ha="center", size=10, color="k")
    plt.grid(axis='y')
    plt.xlabel("Number of Years")
    plt.ylabel("Amount of money(10,000yen)")
    plt.legend()
    plt.show()

seed_money = 100
amount_accum = 5
num_years = 10
annual_rate = 5

calc(seed_money, amount_accum, num_years,annual_rate)