老後の2000万円問題をシミュレートするWEBアプリを作ってみた

公開:2020/10/23
更新:2020/10/23
7 min読了の目安(約6400字TECH技術記事

動機

資産運用してますか?全然お金を貯められないし資産運用もできない筆者ですが、老後に突入するまで2000万円必要という話もあるしなあ、とふんわり考えていました。実際運用するとなると、運用益が発生して、それを現金にしたときに税金を取られます。お金を複利で増やしていくだけならイメージもしやすいですが、老後まで考えると引き出すことまで想定しなければいけません。じゃあ、2000万円を年5%で運用して毎月6万円ずつ引き出したらどうなるの?という質問に答えるには、暗算より数学、数学よりコードです。面倒なシミュレーションはコンピュータにやらせたい。
というわけで、資産運用を皮算用するWEBアプリを作ってみました。

環境

Python 3.8.5
WEBアプリケーションフレームワーク FLASK
デプロイ先 Heroku

コード

hikidashi.py
from flask import *
import math
import os

# Flaskオブジェクトの生成
app = Flask(__name__)

# ルート( / )へアクセスがあった時 --- (*1)
@app.route("/")
def root():
    # HTMLでWebフォームを記述 --- (*2)
    return """
    <html>
    <title>FIRE運用シミュレータ</title>
    <body>
    <form action="/kekka" method="post">
        <h1>FIRE運用シミュレータ</h1>
        <br>
        このアプリは資金を運用しつつ、毎月引き出していく際に指定年数後の残高および引き出せる最大の金額をシミュレートします。<br>
        チェックボックスを除く全ての項目に数字を入力してください。計算に含めない場合は "0" を入力してください。<br>
        <br>
        <label for="name">想定年利(単位 %,小数点以下可):</label>
        <input type="text" name="ry">
        <br>
        <label for="name">元本(単位 円,整数,カンマ入力可):</label>
        <input type="text" name="ganpon">
        <br>
        <label for="name">初年度時点での含み益(単位 円,整数,カンマ入力可):</label>
        <input type="text" name="hukumieki">
        <br>
        <label for="name">運用年数(単位 年,整数):</label>
        <input type="text" name="year">
        <br>
        <label for="name">毎月引き出したい金額(単位 円,整数,カンマ入力可):</label>
        <input type="text" name="hikidashi">
        <br>
        <label><input type="checkbox" name="tax_calc">税金を考慮しますか?</label>
        <br>
        <input type="submit" value="計算"><br>
        <br>
        #初年度時点での含み益について<br>
        例えば、現金3000万円を投入した結果、5000万円分の金融資産を持ち運用開始時点での含み益が2000万円ある場合は "20000000" もしくは "20,000,000" と入力してください。<br>
        課税対象となる金額に影響があります。<br>
        #金利について<br>
        入力された金利は月利に直され、毎月残高に含み益として加算されます。<br>
        #税金について<br>
        元本(現金投入分)については課税されません。含み益のうち引き出された金額分については課税対象として20.315%減算されます。<br>
        "税金を考慮しますか?"チェックボックスをクリックすると税金分を減算した結果を確認できます。<br>
        #最大引出し可能金額について<br>
        シミュレーションの結果、最終月に十分な残高がある場合、毎月引出したい金額を1000円単位で加算し最大引き出し可能金額を確認できます。<br>
        <br>
        ver 1.0.0 リリース<br>
        ver 1.1.0 年利に小数点入力機能を追加<br>
        ver 1.2.0 税金を考慮する機能を追加<br>
        月利を表示する機能を追加<br>
        項目、仕様についての説明を追加<br>
        ver 1.2.1 月利の表示に関するバグを修正<br>
        ver 1.3.0 金額に関連する項目についてカンマ区切りの入力機能を追加<br>
        出力結果についてカンマ区切りでの出力結果を追加<br>
    </form>
    """

# フォームの値を受け取って結果を表示 --- (*3)
@app.route("/kekka", methods=["post"])
def kekka():
    """
    HTML フォームから入力値を取得。カンマ入力がある場合はカンマを除去。
    Tax_calc では HTML から取得したon/off を True/False へ変換。
    rm にて ry(年利)から月利を算出。
    export_l で出力用のリストを作成し、一番目に月利計算結果を入力。
    """
    ry = float(request.form.get("ry"))
    rm = ((1 + ry / 100) ** (1 / 12) - 1)
    rm_percent = rm * 100
    ganpon = str(request.form.get("ganpon"))
    if ("," in ganpon) == True:
        ganpon = ganpon.replace(",", "")
        ganpon = int(ganpon)
    else:
        ganpon = int(ganpon)
    year = int(request.form.get("year"))
    month = year * 12
    hikidashi = str(request.form.get("hikidashi"))
    if ("," in hikidashi) == True:
        hikidashi = hikidashi.replace(",", "")
        hikidashi = int(hikidashi)
    else:
        hikidashi = int(hikidashi)
    zandaka = ganpon
    hukumieki = str(request.form.get("hukumieki"))
    if ("," in hukumieki) == True:
        hukumieki = hukumieki.replace(",", "")
        hukumieki = int(hukumieki)
    else:
        hukumieki = int(hukumieki)
    tax_paid_total = 0
    tax_monthly = 0
    tax_calc = str(request.form.get("tax_calc"))
    if tax_calc == "on":
        tax_calc = True
    else:
        tax_calc = False
    export_l = ["年利" + str(ry) + "%の場合は月利は" + str(rm_percent) + "%です。<br>"]
    """
    残高(zandaka)がゼロになるまで毎月の引出し額(hikidashi)を加算。各試行ごとに export_l に結果を追加。試行の最後に残高を元本に戻す。
    運用年数から算出した月数分繰り返し。
    出力用にカンマ追加処理後、計算用にカンマ削除処理。
    export_l を出力用に str へ分割、変換。
    """
    while(zandaka >0):
        zougen = (ganpon * rm - hikidashi)
        for i in range(month):
            zandaka = zandaka + zougen
            if tax_calc == True:
                hukumieki = hukumieki + (ganpon * rm)
                if hukumieki >= hikidashi:
                    tax_monthly = hikidashi * 0.20315
                    zandaka = zandaka - tax_monthly
                    tax_paid_total = tax_paid_total + tax_monthly
                elif hukumieki != ganpon * rm:
                    tax_monthly = hukumieki * 0.20315
                    zandaka = zandaka - tax_monthly
                    tax_paid_total = tax_paid_total + tax_monthly
                else:
                    tax_monthly = hukumieki * 0.20315
                    zandaka = zandaka - tax_monthly
                    tax_paid_total = tax_paid_total + tax_monthly
        zandaka = math.floor(zandaka)
        tax_paid_total = math.floor(tax_paid_total)
        ganpon = "{:,d}".format(ganpon)
        hikidashi = "{:,d}".format(hikidashi)
        zandaka = "{:,d}".format(zandaka)
        tax_paid_total = "{:,d}".format(tax_paid_total)
        if tax_calc == True:
            print("元本" + str(ganpon) + "円を" + "年利" + str(ry) + "%で運用した場合" + "毎月" + str(hikidashi) + "円引き出すと" + str(year) + "年後に" + str(zandaka) + "円の残高です。税金は" + str(tax_paid_total) + "円支払いました。")
            export_l.append("元本" + str(ganpon) + "円を" + "年利" + str(ry) + "%で運用した場合" + "毎月" + str(hikidashi) + "円引き出すと" + str(year) + "年後に" + str(zandaka) + "円の残高です。税金は" + str(tax_paid_total) + "円支払いました。")
        else:
            print("元本" + str(ganpon) + "円を" + "年利" + str(ry) + "%で運用した場合" + "毎月" + str(hikidashi) + "円引き出すと" + str(year) + "年後に" + str(zandaka) + "円の残高です。")
            export_l.append("元本" + str(ganpon) + "円を" + "年利" + str(ry) + "%で運用した場合" + "毎月" + str(hikidashi) + "円引き出すと" + str(year) + "年後に" + str(zandaka) + "円の残高です。")
        ganpon = ganpon.replace(",", "")
        ganpon = int(ganpon)
        hikidashi = hikidashi.replace(",", "")
        hikidashi = int(hikidashi)
        zandaka = zandaka.replace(",", "")
        zandaka = int(zandaka)
        tax_paid_total = tax_paid_total.replace(",", "")
        tax_paid_total = int(tax_paid_total)
        if zandaka >0:
            zandaka = ganpon
            tax_paid_total = 0
            hikidashi = hikidashi + 1000

    export_html = '<br>'.join(export_l)
    return export_html
# サーバーを起動
if __name__ == "__main__":
    port = int(os.environ.get("PORT", 5000))
    app.run(host='0.0.0.0', port=port)