【住宅ローン×Python】ローン返済シミュレータってどんなの??

考え方と機能

基本は,借入金額と返済に関する設定,ローン口座へ振り込む金額の設定,住宅ローン控除の設定,繰り上げ返済の設定を入力すると,年間いくら支払って,完済時にはどれくらい支払ったかが一目でわかる思想とする.必要な機能は下記な感じ.

<要求機能>

  1. 元利均等でも元金均等でも計算できる
  2. 金利は返済期間中は自由に設定できる
  3. 元利均等返済@変動金利での「5年ごとの返済額見直し」「見直し前の返済額の1.25倍まで縛り」「利息に応じて未払い利息発生」を再現できる
  4. ローン口座を仮定し,そこへの入金&そこからの支払いを模擬できる
  5. 住宅ローン控除によって戻ってくるおカネを計算できる
  6. 繰り上げ返済を現実的な条件で模擬できる
  7. 計算結果をグラフでビジュアル化する
  8. 条件を変えた計算結果の比較ができるよう,前の計算結果に対する重ね描き機能を有する

以上の要求機能に対して,必要なそうな入力を網羅してみる.

<借入金額の情報>

  1. 借入金額(割賦返済分とボーナス返済分)
  2. ボーナスをアテにした”割り増し返済”を行う月

<返済に関する設定>

  1. 元利均等か元金均等かを指示
  2. 返済年数を指示
  3. ローン金利を指示
  4. ローン金利に紐づいた年月情報をYYYYMM形式で指示

<ローン口座へ振り込む金額の設定>

  1. 割賦払いの金額と,ボーナス払いの金額を指示
  2. ボーナスの入金月を指示

<住宅ローン控除の情報設定>

  1. 住宅取得価格を設定
  2. ローン控除の利率を設定
  3. ローン控除の期間を設定

<繰り上げ返済の設定>

  1. 期間短縮型か返済額低減型かを設定
  2. 繰り上げ返済を開始する年を設定
  3. 年間のうち,繰り上げ返済を実施できる回数の上限を設定※
  4. 繰り上げ返済を実施できる最低金額を設定※
  5. ローン控除で戻ってくるおカネのうち,返済に回す割合を設定

※ウチがお世話になっているH信金さんは「年1回の50万円以上なら事務手数料無料」という縛りがあるので,現実的な繰り上げ返済の条件を入力するために設けた.

フローチャートにするとこんな感じ.計算条件を設定して,月々の返済ということで完済までループを回す.完済したらその情報をcsvに出力して,グラフ化する.

~フローチャート準備中~

コード(メイン関数)

コードはこんな感じ.1日2時間×1週間くらいかかった…まあ基本プログラミングスキルもポンコツだからしょうがないのよorz

雑多ではあるけど,メイン関数の先頭のところで,すべての設定を行った.このメイン関数を実行すると,こちらに記した通りだが,毎年の支払額や完済時の支払総額などがグラフでわかる.

""""""
"""--------------------プログラムの更新履歴--------------------"""
#■更新履歴
# 230108 ver0. 住宅ローンの支払額のSim,まずは試作
# 230113 ver1. グラフ描画ありバージョン
# 230119 ver1_4. グラフ描画の日本語対応バージョン
# 230120 ver1_5. 支払最終年でのバグ修正バージョン
# 230120 ver2_1. 毎年の支払いの棒グラフについて,返済元本分を,「月々+ボーナス分」「繰り上げ返済分」で分離した&手弁当からの利息総額のグラフだけ,別Windowに拡大
#
# ■ローカルルール
# データフレームは***_DF,データリストは***_DL,
# 一次元配列(横方向に数値が並ぶヤツ)は***_Ar
# 「二次元配列=行列」でX方向にデータを持つのは***_X,Y方向にデータを持つのは***_Y
# 「二次元配列=行列}でXY両方向にデータを持つのは***_XYと記述する
"""--------------------プログラムの更新履歴,以上--------------------"""

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams["font.family"] = "MS Gothic"
import FuncGeneral  #その他一般用途の関数.今回は使わなそう.
import FuncMoney_ver1#お金関係


#---------------借入関係---------------
LoanAmount_DL=[17700000,10000000]#割賦払い分とボーナス払い分をそれぞれ記述
Month_IncreasedPay_DL=[1,7]#ボーナスによる増額支払い月を記述

Sel_LoanType=0#0なら元利均等返済,1なら元金均等返済
Period_Payout=35#支払い年数
LoanRate_DL=[0.515]#ローンの店頭金利(%)を列挙
YYYYMM_Loan_DL= [202201]#ローン金利が適用または変更されるタイミングをYYYYMMで記述.先頭は返済開始年月を書くこと

#---------------ローン口座への入金設定(空行列なら,ローンの支払いと同額分を振り込むこととして扱う)---------------
PayToAccount_DL=[100000,100000]#ローン口座への振込金額について,毎月分とボーナス分のそれぞれを記述.返済額分だけかっちり払うなら,空白リストで設定する
Month_Bonus_DL=[7,12]#ボーナスの振込月を記述

#---------------住宅ローン控除関係---------------
Price_House=24900000#住宅の取得価格(物件価格-すまい給付金-贈与).住宅ローン控除の計算で微妙に使う
Rate_CashBack=1#住宅ローン控除の利率(%)
Period_CashBack=13#住宅ローン控除が適用される期間(年)

#---------------繰り上げ返済関係---------------
Sel_AdvancedType=0#0なら期間短縮型,1なら返済額軽減型
Year_AdvancedPaySta=2022+1#繰り上げ返済を開始する年
LimitNum_AdvancedPay=0#年間で繰り上げ返済できる上限回数.繰り上げ返済機能を殺すなら,0に設定.
LimitAmount_AdavancedPay=500000#繰り上げ返済できる下限金額
Rate_CBtoAdvancedPay=0#ローン控除でのCBのうち,繰り上げ返済に回す金額の割合(%).0なら繰り上げ返済には回さないし,100なら全額繰り上げ返済に使う

#---------------まとめ棒グラフのデータラベル---------------
BarLabel=''#空白なら,Test1,Test2…と自動割り当てする

#---------------計算結果のファイル名を指示---------------
FileName_yearly='CalData'#毎年の支払金額をまとめたファイルの名称
FileName_sum='SumData'#最終的な支払金額をまとめたファイルの名称

"""--------------------以上,設定終了--------------------"""
#LoanRate_DL=list(0.515+np.arange(0, 10.0, 0.1))#最終的な金利は適当な大きい値を入れておき,いつまで上がるかはYYYYMM_Loan_DLの定義に任せる
#YYYYMM_Loan_DL= [202201,202401,202407,202501,202507,202601,202607,202701,202707,202801,202807,202901,202907,203001,203007,203101,203107,203201,203207,203301]#ローン金利が適用または変更されるタイミングをYYYYMMで記述.先頭は返済開始年月を書くこと
#GraphRange_DL=[600000,1000000]#4枚目のグラフのY軸レンジ設定.Autoなら[]を指示する
#GraphRange_DL=[2800000,3200000]
GraphRange_DL=[]

#---------------メッセージボックスの準備---------------
from tkinter import messagebox,Tk
root=Tk()
root.geometry('300x300')
root.attributes('-topmost',True)
root.withdraw()

#SumDataが存在するかを確認する
F_AddSumData=0#SumDataへの重ね描きフラグを初期化する
SumDataNum=0#SumDataの行数を初期化する
try:
    with open(FileName_sum+'.csv') as f:
        print("SumDataがあるね!!")
        SumData_DF = pd.read_csv(FileName_sum + '.csv', encoding="shift-jis")
        print(SumData_DF)

        #ダイアログボックスで前のデータに対して新しい計算結果を追加して重ね描くかを確認する
        SelAns=messagebox.askquestion(title="重ね描き設定", message="前の計算結果に新しい計算結果を追加してグラフ化しますか?")
        if SelAns=='yes':
            #print(SelAns)
            F_AddSumData = 1
            SumDataNum=SumData_DF.shape[0]#読み込んだデータフレームの行数を取得する

except FileNotFoundError:
    print("SumDataはまだないね(^o^)ノシ")
print('F_AddSumData=',F_AddSumData)



"""--------------------処理開始.まずはパラメータの初期設定など--------------------"""
#繰り上げ返済するときの,繰り上げ返済するときの,割賦払い残高分とボーナス払い残高分への振り向け割合(%)を,借入金額の「月々分」「ローン払い分」に応じて割り振り..[100,0]なら,割賦払い分の元本に対して繰り上げる
#Rate_MonthlyVsBonus_DL=[100,0]#繰り上げ返済するときの,割賦払い残高分とボーナス払い残高分への振り向け割合(%).[100,0]なら,割賦払い分の元本に対して繰り上げる
Rate_MonthlyVsBonus_DL=[100*LoanAmount_DL[0]/sum(LoanAmount_DL),100*LoanAmount_DL[1]/sum(LoanAmount_DL)]

#データリストを横方向の一次元配列に変換
LoanAmount_Ar=np.array(LoanAmount_DL)
LoanRate_Ar=np.array(LoanRate_DL)
YYYYMM_Loan_Ar=np.array(YYYYMM_Loan_DL)
Month_IncreasedPay_Ar=np.array(Month_IncreasedPay_DL)
PayToAccount_Ar=np.array(PayToAccount_DL)
Month_Bonus_Ar=np.array(Month_Bonus_DL)
Rate_MonthlyVsBonus_Ar=np.array(Rate_MonthlyVsBonus_DL)

#支払い回数を毎月分とボーナス分で定義
N_Pay_Monthly=Period_Payout*12
N_Pay_Bonus=Period_Payout*len(Month_IncreasedPay_Ar)

#ローン残高の初期値
Balance_Loan=sum(LoanAmount_Ar)
Balance_Loan_Monthly=LoanAmount_Ar[0]
Balance_Loan_Bonus=LoanAmount_Ar[1]

TotalInterest_Accured=0#未払い利息総額を初期化

Balance_Account=0#ローン口座の残高
CntLoanDeduction=0#ローン控除の回数
Cnt_AdvancedPay=0#毎年の繰り上げ返済の実施回数

YYYYMM_IniStr=str(YYYYMM_Loan_Ar[0])
YearCnt=int(YYYYMM_IniStr[0:4])
MonthCnt=int(YYYYMM_IniStr[4:6])

YearSta=YearCnt#返済開始年(グラフのレンジ設定に使う)
YearEnd=YearSta+Period_Payout#当初計画の完済年(グラフのレンジ設定に使う)

#計算結果を格納する行列を定義
N_CalData=13
N_CalYear=Period_Payout
CalData_Ar = np.zeros(N_CalData)
CalData_XY=np.zeros((N_CalYear,N_CalData))
CalData_XY[:,:]=np.nan


CntMonthly=1#月々払いの回数のカウンタを初期化
CntBonus=1#割増払いの回数のカウンタを初期化
CntYearly=0#年々のカウンタを初期化

Amount_Repay_Monthly_pre=0#初期化(パラメータ定義しているだけ)
Principal_Repay_Monthly_pre=0#初期化(パラメータ定義しているだけ)
Amount_Repay_Bonus_pre=0#初期化(パラメータ定義しているだけ)
Principal_Repay_Bonus_pre=0#初期化(パラメータ定義しているだけ)
TotalInterest=0#利息総額を初期化

SumCB=0#ローン控除で戻ってくる金額の総額

"""--------------------月々の時間軸でループ実行--------------------"""
while Balance_Loan>=0.1:
    #if YearCnt==2027:
        #breakpoint()
    #年月を定義
    YYYYMM=YearCnt*100+MonthCnt

    #適用する金利を算出
    idx_YYYYMM_Loan=np.max(np.where(YYYYMM_Loan_Ar<=YYYYMM))
    print('YYYYMM=',YYYYMM,'LoanRate=',LoanRate_Ar[idx_YYYYMM_Loan])
    #breakpoint()
    """月々の返済総額,元金返済額,利息返済額を求める"""
    InterestRate_Monthly = LoanRate_Ar[idx_YYYYMM_Loan] / 12
    if Balance_Loan_Monthly >= 0:
        (Amount_Repay_Monthly, Principal_Repay_Monthly, Interest_Repay_Monthly, Interest_Accrued_Monthly) \
            = FuncMoney_ver1.SubSubCalRepayFin_ver1(LoanAmount_Ar[0], Balance_Loan_Monthly, N_Pay_Monthly,
                                                    InterestRate_Monthly, Sel_LoanType, CntMonthly,12,
                                                    Amount_Repay_Monthly_pre, Principal_Repay_Monthly_pre)
        # 返済総額が計算出来たら,ローン残高を更新する
        if Balance_Loan_Monthly>Principal_Repay_Monthly:
            Balance_Loan_Monthly = Balance_Loan_Monthly - Principal_Repay_Monthly  # ローン残高を更新
        else:
            Principal_Repay_Monthly=Balance_Loan_Monthly
            Balance_Loan_Monthly =0
            Amount_Repay_Monthly=Principal_Repay_Monthly+Interest_Repay_Monthly
    else:
        (Amount_Repay_Monthly, Principal_Repay_Monthly, Interest_Repay_Monthly, Interest_Accrued_Monthly) = (0, 0, 0, 0)

    """割増返済月の場合は,割増分の返済総額,元金返済額,利息返済額を求める"""
    InterestRate_Bonus = LoanRate_Ar[idx_YYYYMM_Loan] / len(Month_IncreasedPay_Ar)
    if np.any(Month_IncreasedPay_Ar == MonthCnt) == False:# 割り増し払い月以外は,単に月々の支払いを考慮する
        Amount_Repay = Amount_Repay_Monthly
        Principal_Repay = Principal_Repay_Monthly
        Interest_Repay=Interest_Repay_Monthly
        TotalInterest = TotalInterest + Interest_Repay_Monthly  # 支払い利息の総額を求める

        Interest_Accrued=Interest_Accrued_Monthly
        TotalInterest_Accured=TotalInterest_Accured+Interest_Accrued_Monthly

    else:#ボーナスをアテにした割増返済を行う月になったら…
        if Balance_Loan_Bonus >= 0:
            (Amount_Repay_Bonus, Principal_Repay_Bonus, Interest_Repay_Bonus, Interest_Accrued_Bonus) \
                = FuncMoney_ver1.SubSubCalRepayFin_ver1(LoanAmount_Ar[1], Balance_Loan_Bonus, N_Pay_Bonus,
                                                        InterestRate_Bonus, Sel_LoanType, CntBonus,len(Month_IncreasedPay_Ar),
                                                        Amount_Repay_Bonus_pre, Principal_Repay_Bonus_pre)
            # 返済総額が計算出来たら,ローン残高を更新する
            if Balance_Loan_Bonus > Principal_Repay_Bonus:
                Balance_Loan_Bonus = Balance_Loan_Bonus - Principal_Repay_Bonus  # ローン残高を更新
            else:
                Principal_Repay_Bonus = Balance_Loan_Bonus
                Balance_Loan_Bonus = 0
                Amount_Repay_Bonus = Principal_Repay_Bonus + Interest_Repay_Bonus
        else:
            (Amount_Repay_Bonus, Principal_Repay_Bonus, Interest_Repay_Bonus, Interest_Accrued_Bonus) = (0, 0, 0, 0)

        #割り増し払い月のときは,月々の支払いに割り増し分を加算する
        Amount_Repay = Amount_Repay_Monthly+Amount_Repay_Bonus#月々の返済総額に,ボーナスをアテにした割り増し分を追加する
        Principal_Repay = Principal_Repay_Monthly+Principal_Repay_Bonus
        Interest_Repay = Interest_Repay_Monthly+Interest_Repay_Bonus
        TotalInterest = TotalInterest + Interest_Repay_Monthly + Interest_Repay_Bonus#支払い利息の総額を求める

        Interest_Accrued = Interest_Accrued_Monthly+Interest_Accrued_Bonus
        TotalInterest_Accured=TotalInterest_Accured+Interest_Accrued_Monthly+Interest_Accrued_Bonus

    Balance_Loan=Balance_Loan_Monthly+Balance_Loan_Bonus

    """住宅ローン控除(便宜上,控除額が1月に振り込まれるものとして扱う)と,繰り上げ返済の回数の初期化"""
    if CntMonthly!=1 and MonthCnt==1:
        Amount_CashBack = 0
        (Amount_LoanDeduction, CntLoanDeduction)=FuncMoney_ver1.SubCalLoanDeduction_ver1(Balance_Loan,CntLoanDeduction,Price_House,Rate_CashBack,Period_CashBack)
        print(YYYYMM, 'Amount_LoanDeduction=', Amount_LoanDeduction)
        SumCB=SumCB+Amount_LoanDeduction
        Cnt_AdvancedPay=0#年初めに,繰り上げ返済の回数を初期化する
    else:
        Amount_LoanDeduction=0


    """口座残高を計算する"""
    if len(PayToAccount_Ar)==0:
        Balance_Account = Balance_Account + Rate_CBtoAdvancedPay/100*Amount_LoanDeduction  # 口座残高を更新
    else:
        Balance_Account = Balance_Account + PayToAccount_Ar[0]  # 月々のローン口座への積み立て
        if np.any(Month_Bonus_Ar == MonthCnt) == True:  # ボーナス月分の積み立て
            Balance_Account = Balance_Account + PayToAccount_Ar[1]

        Balance_Account = Balance_Account - Amount_Repay +Rate_CBtoAdvancedPay/100*Amount_LoanDeduction # 口座残高を更新


    """都度の返済を終えた時に,ローン口座の残高が一定以上なら,繰り上げ返済のロジックを実装!!!"""
    if Balance_Account > LimitAmount_AdavancedPay and Cnt_AdvancedPay < LimitNum_AdvancedPay and YearCnt >= Year_AdvancedPaySta:
        #割賦払い分の元本に対し,繰り上げ返済実行
        (Amount_Advenced_Monthly,Amount_Repay_Monthly, Principal_Repay_Monthly, N_Pay_Monthly, Balance_Account1, Balance_Loan_Monthly) \
            = FuncMoney_ver1.SubCalAdvancedRepay_ver1(Sel_AdvancedType, Rate_MonthlyVsBonus_Ar[0], Sel_LoanType,
                                                      Balance_Account, Balance_Loan_Monthly, CntMonthly,
                                                      Amount_Repay_Monthly, Principal_Repay_Monthly, InterestRate_Monthly,N_Pay_Monthly)

        # ボーナス分の元本に対し,繰り上げ返済実行
        (Amount_Advenced_Bonus,Amount_Repay_Bonus, Principal_Repay_Bonus, N_Pay_Bonus, Balance_Account2, Balance_Loan_Bonus) \
            = FuncMoney_ver1.SubCalAdvancedRepay_ver1(Sel_AdvancedType, Rate_MonthlyVsBonus_Ar[1], Sel_LoanType,
                                                      Balance_Account, Balance_Loan_Bonus, CntBonus,
                                                      Amount_Repay_Bonus, Principal_Repay_Bonus,
                                                      InterestRate_Bonus, N_Pay_Bonus)

        Amount_Advenced=Amount_Advenced_Monthly+Amount_Advenced_Bonus
        print('【Ad】YYYYMM=',YYYYMM,'Amount_Advenced=',Amount_Advenced)

        Balance_Account=Balance_Account1+Balance_Account2-Balance_Account
        Cnt_AdvancedPay=Cnt_AdvancedPay+1
    else:
        Amount_Advenced_Monthly=0
        Amount_Advenced_Bonus=0
        Amount_Advenced = Amount_Advenced_Monthly + Amount_Advenced_Bonus

    print('YYYYMM=', YYYYMM,'CntMonthly=',CntMonthly,
          'Amount_Repay_Monthly=','{:,.0f}'.format(Amount_Repay_Monthly),
          'Principal_Repay_Monthly=','{:,.0f}'.format(Principal_Repay_Monthly),
          'Interest_Repay_Monthly=','{:,.0f}'.format(Interest_Repay_Monthly),
          'Balance_Loan_Monthly=','{:,.0f}'.format(Balance_Loan_Monthly),
          '未払利息=','{:,.0f}'.format(Interest_Accrued_Monthly),
          '口座残高=','{:,.0f}'.format(Balance_Account))


    Amount_Repay_Monthly_pre=Amount_Repay_Monthly
    Principal_Repay_Monthly_pre=Principal_Repay_Monthly
    Interest_Repay_Monthly_pre=Interest_Repay_Monthly
    Interest_Accrued_Monthly_pre=Interest_Accrued_Monthly

    # ボーナスをアテにした割増返済を行う月になったら…
    if np.any(Month_IncreasedPay_Ar == MonthCnt) == True:
        print('【B】', 'YYYYMM=', YYYYMM, 'CntBonus=', CntBonus,
              'Amount_Repay_Bonus=', '{:,.0f}'.format(Amount_Repay_Bonus),
              'Principal_Repay_Bonus=', '{:,.0f}'.format(Principal_Repay_Bonus),
              'Interest_Repay_Bonus=', '{:,.0f}'.format(Interest_Repay_Bonus),
              'Balance_Loan_Bonus=', '{:,.0f}'.format(Balance_Loan_Bonus),
              '未払利息=', '{:,.0f}'.format(Interest_Accrued_Bonus),
              '口座残高=', '{:,.0f}'.format(Balance_Account))

        Amount_Repay_Bonus_pre = Amount_Repay_Bonus
        Principal_Repay_Bonus_pre = Principal_Repay_Bonus
        Interest_Repay_Bonus_pre=Interest_Repay_Bonus
        Interest_Accrued_Bonus_pre=Interest_Accrued_Bonus

        CntBonus = CntBonus + 1

    #TotalInterest=TotalInterest+Interest_Repay_Monthly+Interest_Repay_Bonus

    #計算結果を格納
    # 一年間の返済総額,元金返済額.利息返済額,利率,未払い利息額,ローン残高,支払いの総利息,口座残高を順に格納する配列を初期化する
    if MonthCnt == 1:
        CalData_Ar[:] = 0
    print(CalData_Ar[1])
    CalData_Ar[0] = YearCnt
    CalData_Ar[1] = CalData_Ar[1] + Amount_Repay+Amount_Advenced_Monthly+Amount_Advenced_Bonus
    CalData_Ar[2] = CalData_Ar[2] + Principal_Repay#+Amount_Advenced_Monthly+Amount_Advenced_Bonus
    CalData_Ar[3] = CalData_Ar[3] + Interest_Repay
    CalData_Ar[4] = CalData_Ar[4] + Amount_Advenced
    CalData_Ar[5] = LoanRate_Ar[idx_YYYYMM_Loan]
    CalData_Ar[6] = CalData_Ar[6] + Interest_Accrued
    CalData_Ar[7] = Balance_Loan
    CalData_Ar[8] = Balance_Account
    CalData_Ar[9] = TotalInterest
    CalData_Ar[10] = CalData_Ar[10] + Amount_Repay_Monthly
    if np.any(Month_IncreasedPay_Ar == MonthCnt) == True:
        CalData_Ar[11] = CalData_Ar[11] + Amount_Repay_Bonus
    CalData_Ar[12] = SumCB


    CalData_XY[CntYearly, :] = CalData_Ar
    if MonthCnt == 12:
        CntYearly=CntYearly+1

    #年月の更新!
    MonthCnt=MonthCnt+1
    if MonthCnt>12:
        MonthCnt=1
        YearCnt=YearCnt+1
    CntMonthly=CntMonthly+1


"""--------------------計算結果を格納した行列でデータフレームを作成--------------------"""
CalData_DF=pd.DataFrame(CalData_XY,columns=['Year','Amount_Repay','Principal_Repay','Interest_Repay','Amount_Advanced',
                                            'InterestRate','Interest_Accured',
                                            'Balance_Loan','Balance_Account','TotalInterest','Amount_Repay_Monthly','Amount_Repay_Bonus','SumCB'])
CalData_DF=CalData_DF.dropna(axis=0)#NaNデータの行を削除
print('CalData_DF=',CalData_DF)
CalData_DF.to_csv(FileName_yearly+'.csv',index=False, encoding="shift-jis")

TotalInterest_Fin=TotalInterest+TotalInterest_Accured
TotalInterest_Pay=TotalInterest_Fin-SumCB

"""最終的な支払い利息のまとめを表示させる"""
print('TotalInterest=','{:,.0f}'.format(TotalInterest),
      'TotalInterest_Accured=','{:,.0f}'.format(TotalInterest_Accured),
      'TotalInterest_Fin=','{:,.0f}'.format(TotalInterest_Fin),
      'SumCB=','{:,.0f}'.format(SumCB),
      'TotalInterest_Pay=','{:,.0f}'.format(TotalInterest_Pay),)
#print('CalData_XY=',CalData_XY)


"""--------------------最終的な支払い利息のまとめをDataFrameに格納&保存--------------------"""
#棒グラフのラベルを定義
if BarLabel=='':
    print('ないね')
    BarLabel='Test'+str(SumDataNum)
    print(BarLabel)
SumDataLabel_DF=pd.DataFrame([BarLabel],columns=['Label'])
print('SumDataLabel_DF=',SumDataLabel_DF)

#計算結果をDataFrameに格納
SumData_Ar=np.array([sum(LoanAmount_Ar),
           TotalInterest,TotalInterest_Accured,TotalInterest_Fin,SumCB,TotalInterest_Pay])
SumData_X=SumData_Ar.reshape((1,len(SumData_Ar)))
#print('SumData_X=',SumData_X)
SumDataTemp_DF=pd.DataFrame(SumData_X,columns=['LoanAmount','TotalInterest','TotalInterest_Accured','TotalInterest_Fin','SumCB','TotalInterest_Pay'])

#データラベルと結合
SumDataTemp_DF=pd.concat([SumDataLabel_DF,SumDataTemp_DF],axis=1)

#重ね描き設定なら,読み込んだデータフレームに新しい計算結果を追加する
if F_AddSumData==0:
    SumData_DF =SumDataTemp_DF
else:
    SumData_DF = pd.concat([SumData_DF, SumDataTemp_DF], axis=0)

print('SumData_DF=',SumData_DF)
SumData_DF.to_csv(FileName_sum+'.csv',index=False, encoding="shift-jis")


"""--------------------Graph描画--------------------"""
#Graph1-1枚目
fig,ax=plt.subplots(1,1)
ax=plt.subplot(2,1,1)
ax2 = ax.twinx()#第2軸を設定

ax.set_xlabel('Year')
ax.set_ylabel('返済額(円)')
ax2.set_ylabel('利率(%)')

Bar1_X=CalData_DF['Principal_Repay'].values
ax.bar(CalData_DF['Year'].values,Bar1_X,color="Blue",label="元本")
Bar2_X=CalData_DF['Interest_Repay'].values
ax.bar(CalData_DF['Year'].values,Bar2_X,bottom=Bar1_X,color="Red",label="利息")
Bar3_X=CalData_DF['Amount_Advanced'].values
ax.bar(CalData_DF['Year'].values,Bar3_X,bottom=Bar1_X+Bar2_X,color="Pink",label="繰上返済")
Bar4_X=CalData_DF['Interest_Accured'].values
ax.bar(CalData_DF['Year'].values,Bar4_X,bottom=Bar1_X+Bar2_X+Bar3_X,color="Gray",label="未払い利息")
ax.legend(loc='upper left', bbox_to_anchor=(1.02, 1))
#Plot
ax2.plot(CalData_DF[['Year']].values,CalData_DF[['InterestRate']].values,color="Black",label="利率")
ax2.legend(loc='upper left', bbox_to_anchor=(1.02, 0.25))
#X軸のレンジ設定
ax.set_xlim(YearSta-1,YearEnd)
#Y軸のレンジ設定
Ymax=round(1.2*np.max(CalData_DF[['Amount_Repay']].values)/10000)*10000
ax.set_ylim(0,Ymax)
#Y2軸のレンジ設定
Ymax=round(1.2*np.max(CalData_DF[['InterestRate']].values)*10)/10
ax2.set_ylim(0,Ymax)

#Graph1-2枚目
ax=plt.subplot(2,1,2)
ax2 = ax.twinx()#第2軸を設定
ax.set_xlabel('Year')
ax.set_ylabel('ローン元本(円)')
ax2.set_ylabel('支払利息(円)')
#Plot
ax.plot(CalData_DF[['Year']].values,CalData_DF[['Balance_Loan']].values,color="Black",label="ローン元本")
ax2.plot(CalData_DF[['Year']].values,CalData_DF[['TotalInterest']].values,color="Red",label="支払い利息の総額")
ax.legend(loc='upper left')
ax2.legend(loc='upper right')
#X軸のレンジ設定
ax.set_xlim(YearSta-1,YearEnd)
#Y軸のレンジ設定
Ymax=round(1.2*np.max(CalData_DF[['Balance_Loan']].values)/10**6)*10**6
ax.set_ylim(0,Ymax)
#Y2軸のレンジ設定
Ymax=round(1.2*np.max(CalData_DF[['TotalInterest']].values)/10**6)*10**6
ax2.set_ylim(0,Ymax)

#Graph2枚目を設定
fig,ax=plt.subplots(1,1)

#Graph2-1枚目
ax=plt.subplot(2,2,1)
Bar1_X=SumData_DF['LoanAmount'].values
ax.bar(SumData_DF['Label'].values,Bar1_X,color="Blue")
Bar2_X=SumData_DF['TotalInterest'].values
ax.bar(SumData_DF['Label'].values,Bar2_X,bottom=Bar1_X,color="Red")
Bar3_X=SumData_DF['TotalInterest_Accured'].values
ax.bar(SumData_DF['Label'].values,Bar3_X,bottom=Bar1_X+Bar2_X,color="Gray")
ax.set_ylabel('支払総額(円)')

#Graph2-2枚目
ax=plt.subplot(2,2,2)
Bar1_X=SumData_DF['TotalInterest'].values
ax.bar(SumData_DF['Label'].values,Bar1_X,color="Red")
ax.set_ylabel('利息総額(円)')

#Graph2-3枚目
ax=plt.subplot(2,2,3)
Bar1_X=SumData_DF['SumCB'].values
ax.bar(SumData_DF['Label'].values,Bar1_X,color="Green")
ax.set_ylabel('ローン控除総額(円)')

#Graph2-4枚目
ax=plt.subplot(2,2,4)
Bar1_X=SumData_DF['TotalInterest_Pay'].values
ax.bar(SumData_DF['Label'].values,Bar1_X,color="Orange")
ax.set_ylabel('自己資金から払った利息総額(円)')

#Graph3目を設定
fig,ax=plt.subplots(1,1)
Bar1_X=SumData_DF['TotalInterest'].values
ax.bar(SumData_DF['Label'].values,Bar1_X,color="Red")
ax.set_ylabel('利息総額(円)')
#Graph4目を設定
fig,ax=plt.subplots(1,1)
Bar1_X=SumData_DF['TotalInterest_Pay'].values
ax.bar(SumData_DF['Label'].values,Bar1_X,color="Orange")
ax.set_ylabel('自己資金から払った利息総額(円)')
if len(GraphRange_DL)!=0:
    ax.set_ylim(GraphRange_DL[0],GraphRange_DL[1])

plt.show()

コード(サブ関数)

ローンの支払額や,住宅ローン控除,繰り上げ返済の期間&返済額補正などは,サブ関数に記述し,FuncMoney_ver1.py内にまとめた.

#------------------------------------------------------------------------------------
#■更新履歴
# 230108 ver0. お金関係で必要そうな自作関数のまとめ
# 230116 ver1. 不要なライブラリや古い関数を削除
#
# ■ローカルルール
# データフレームは***_DF,データリストは***_DL,
# 一次元配列(横方向に数値が並ぶヤツ)は***_Ar
# 「二次元配列=行列」でX方向にデータを持つのは***_X,Y方向にデータを持つのは***_Y
# 「二次元配列=行列}でXY両方向にデータを持つのは***_XYと記述する
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from mpl_finance import candlestick_ohlc
import datetime

import shutil
import glob
import os


#230108 元利均等返済と元金均等返済の計算用
def SubCalRepay_ver0(BorrowingAmount,Balance_Loan,N_Pay,InterestRate,Sel_LoanType):#入力は借入金額,直前のローン残高,返済回数,利率(%),借入のタイプ(元利均等or元金均等)
    if Sel_LoanType==0:#元利均等返済の場合
        Amount_Repay=BorrowingAmount*InterestRate/100*(1+InterestRate/100)**N_Pay/((1+InterestRate/100)**N_Pay-1)#返済額
        Interest_Repay=Balance_Loan*InterestRate/100#利息返済額
        Principal_Repay=Amount_Repay-Interest_Repay
    else:
        Principal_Repay = BorrowingAmount/N_Pay
        Interest_Repay = Balance_Loan * InterestRate / 100  # 利息返済額
        Amount_Repay=Principal_Repay+Interest_Repay

    return(Amount_Repay,Principal_Repay,Interest_Repay)


#230113 返済金額の計算用,未払い利息は,その時々に発生した額とし,積算は止めた
def SubSubCalRepayFin_ver1(LoanAmount, Balance_Loan, N_Pay, InterestRate,Sel_LoanType,Cnt,NumByYear,
                           Amount_Repay_pre,Principal_Repay_pre):

    (Amount_Repay, Principal_Repay, Interest_Repay) \
        = SubCalRepay_ver0(LoanAmount, Balance_Loan, N_Pay, InterestRate,Sel_LoanType)
    Interest_Accrued=0

    if Cnt > 1:  # 2回目以降の支払いについては,条件に応じて支払総額や元金,利息が異なってくる
        # else:#2回目以降の支払い
        if Sel_LoanType == 0:  # 元利均等返済の場合
            if Cnt % (NumByYear*5) != 1:  # 5年スパン以内なら,返済総額は前月と同じ
                Amount_Repay = Amount_Repay_pre  # 前月と同じ返済増額を適用
                Principal_Repay = Amount_Repay - Interest_Repay  # 利息の変動に応じて元本分を修正

            else:
                LoanAmount_Cor = Balance_Loan  # 元本を,残高に置き換え
                N_Pay_Cor = N_Pay - Cnt + 1  # 返済回数を残り回数の置き換え
                (Amount_Repay, Principal_Repay, Interest_Repay) \
                    = SubCalRepay_ver0(LoanAmount_Cor, Balance_Loan, N_Pay_Cor,
                                                      InterestRate, Sel_LoanType)

                if Amount_Repay > 1.25 * Amount_Repay_pre:  # これまでの月々の支払総額の125%を超えるなら,125%に圧縮する
                    Amount_Repay = 1.25 * Amount_Repay_pre
                    Principal_Repay = Amount_Repay - Interest_Repay

            # 未払い利息の計算をする
            if Amount_Repay < Interest_Repay:
                Interest_Accrued = Interest_Repay - Amount_Repay

        else:  # 元金均等返済の場合
            Principal_Repay = Principal_Repay_pre
            Interest_Repay = Balance_Loan * InterestRate / 100
            Amount_Repay = Principal_Repay + Interest_Repay

    return(Amount_Repay,Principal_Repay,Interest_Repay,Interest_Accrued)

#230108 ローン控除の計算用
def SubCalLoanDeduction(Balance_Loan,CntLoanDeduction,Price_House,Rate_CashBack,Period_CashBack):
    CntLoanDeduction = CntLoanDeduction + 1

    if CntLoanDeduction<11:
    #令和3年11月30日までに契約した建売物件の場合,13年間,12月時点でのローン残高の1%がバックされる

        Amount_LoanDeduction=Balance_Loan*Rate_CashBack/100
        Amount_LoanDeduction=min(Amount_LoanDeduction,500000)#上限は長期優良住宅の場合50万円
    elif CntLoanDeduction<=Period_CashBack:
        #残高×1%または建物の取得価2%÷3のうち,小さいほう
        Amount_LoanDeduction=min(Price_House*2/100/3,Balance_Loan*Rate_CashBack/100)
    else:
        Amount_LoanDeduction=0

    return(Amount_LoanDeduction,CntLoanDeduction)

# 230118修正…取得価格とローン残高の低い方の1%が控除されるのだった
def SubCalLoanDeduction_ver1(Balance_Loan, CntLoanDeduction, Price_House, Rate_CashBack, Period_CashBack):
    CntLoanDeduction = CntLoanDeduction + 1

    if CntLoanDeduction < 11:
        # 令和3年11月30日までに契約した建売物件の場合,13年間,12月時点でのローン残高の1%がバックされる

        Amount_LoanDeduction = min(Balance_Loan,Price_House) * Rate_CashBack / 100
        Amount_LoanDeduction = min(Amount_LoanDeduction, 500000)  # 上限は長期優良住宅の場合50万円
    elif CntLoanDeduction <= Period_CashBack:
        # 残高×1%または建物の取得価2%÷3のうち,小さいほう
        Amount_LoanDeduction = min(Price_House * 2 / 100 / 3, Balance_Loan * Rate_CashBack / 100)
    else:
        Amount_LoanDeduction = 0

    return (Amount_LoanDeduction, CntLoanDeduction)


#230110 繰り上げ返済の計算用
def SubCalAdvancedRepay_ver0(Sel_AdvancedType,Rate,Sel_LoanType,
                             Balance_Account,Balance_Loan,Cnt,
                             Amount_Repay, Principal_Repay,InterestRate,N_Pay):
    Amount_Advenced=Balance_Account*Rate/100
    Balance_Loan=Balance_Loan-Balance_Account*Rate/100#口座残高のすべてを以てローン支払いに宛てるので,ローン残高から口座残高を引き算する
    Balance_Account=Balance_Account*(1-Rate/100)#上記により,口座残高はスッカラカンになる
    #Cnt_AdvancedPay=Cnt_AdvancedPay+1

    if Sel_AdvancedType==0:#期間短縮型
        N_temp=round(N_Pay/2)#初期化
        for i_temp in range(1,10,1):#二分探索
            (Amount_temp, Principal_temp, Interest_temp) \
                = SubCalRepay_ver0(Balance_Loan, Balance_Loan, N_temp,
                                                  InterestRate, Sel_LoanType)
            #print('N_temp=',N_temp,'Amount_Repay=','{:,.0f}'.format(Amount_Repay),'Amount_temp=','{:,.0f}'.format(Amount_temp))
            if Amount_Repay>Amount_temp:
                N_temp=round(N_temp-N_Pay/2*(1/2)**i_temp)
            else:
                N_temp =round(N_temp+N_Pay/2*(1/2)**i_temp)
            N_temp=max(1,N_temp)
        #breakpoint()

        # 途中で金利が大幅に増え,後で返済総額が上がる場合は,今時点での返済総額で以て算出した支払回数が,初期条件の支払回数を上回ることがある
        # よって,繰り上げ返済で支払回数を低減できるとき(金利上昇がべらぼうでないとき)のみ,支払回数を上書きする
        N_Pay_Cor = N_temp + Cnt
        if N_Pay_Cor<N_Pay:
            N_Pay=N_Pay_Cor

        print('N_temp=', N_temp, 'Amount_Repay=', '{:,.0f}'.format(Amount_Repay), 'Amount_temp=','{:,.0f}'.format(Amount_temp))

    else:#返済額軽減型
        (Amount_Repay, Principal_Repay, Interest_temp) \
            = SubCalRepay_ver0(Balance_Loan, Balance_Loan, N_Pay-Cnt,InterestRate, Sel_LoanType)

    return(Amount_Advenced,Amount_Repay, Principal_Repay,N_Pay,Balance_Account,Balance_Loan)


# 230119 繰り上げ返済の計算用, ローン口座残高>ローン残高の時に実施
def SubCalAdvancedRepay_ver1(Sel_AdvancedType, Rate, Sel_LoanType,
                             Balance_Account, Balance_Loan, Cnt,
                             Amount_Repay, Principal_Repay, InterestRate, N_Pay):

    #Amount_Advenced = Balance_Account * Rate / 100
    if Balance_Account * Rate / 100 > Balance_Loan:
        Amount_Advenced=Balance_Loan
        Balance_Loan=0
        N_Pay=0
    else:
        Amount_Advenced = Balance_Account * Rate / 100
        Balance_Loan = Balance_Loan - Balance_Account * Rate / 100  # 口座残高のすべてを以てローン支払いに宛てるので,ローン残高から口座残高を引き算する
        # Cnt_AdvancedPay=Cnt_AdvancedPay+1

        if Sel_AdvancedType == 0:  # 期間短縮型
            N_temp = round(N_Pay / 2)  # 初期化
            for i_temp in range(1, 10, 1):  # 二分探索
                (Amount_temp, Principal_temp, Interest_temp) \
                    = SubCalRepay_ver0(Balance_Loan, Balance_Loan, N_temp,
                                       InterestRate, Sel_LoanType)
                # print('N_temp=',N_temp,'Amount_Repay=','{:,.0f}'.format(Amount_Repay),'Amount_temp=','{:,.0f}'.format(Amount_temp))
                if Amount_Repay > Amount_temp:
                    N_temp = round(N_temp - N_Pay / 2 * (1 / 2) ** i_temp)
                else:
                    N_temp = round(N_temp + N_Pay / 2 * (1 / 2) ** i_temp)
                N_temp = max(1, N_temp)
            # breakpoint()

            # 途中で金利が大幅に増え,後で返済総額が上がる場合は,今時点での返済総額で以て算出した支払回数が,初期条件の支払回数を上回ることがある
            # よって,繰り上げ返済で支払回数を低減できるとき(金利上昇がべらぼうでないとき)のみ,支払回数を上書きする
            N_Pay_Cor = N_temp + Cnt
            if N_Pay_Cor < N_Pay:
                N_Pay = N_Pay_Cor

            print('N_temp=', N_temp, 'Amount_Repay=', '{:,.0f}'.format(Amount_Repay), 'Amount_temp=',
                  '{:,.0f}'.format(Amount_temp))

        else:  # 返済額軽減型
            (Amount_Repay, Principal_Repay, Interest_temp) \
                = SubCalRepay_ver0(Balance_Loan, Balance_Loan, N_Pay - Cnt, InterestRate, Sel_LoanType)

    Balance_Account = Balance_Account - Amount_Advenced  # 上記により,口座残高はスッカラカンになる

    return (Amount_Advenced, Amount_Repay, Principal_Repay, N_Pay, Balance_Account, Balance_Loan)

コメント

タイトルとURLをコピーしました