見出し画像

Pythonで遊ぶ〜妄想高利貸し(ローン)

00_はじめに

しばらく時間が空いてましたが、Pythonで久しぶりに何か作ってみようと思います。

今日は妄想で高利貸しになってみたいと思います。
でっかい金額をべらぼうな利息で貸し付けして、きっちり利息計算するような機能を作ってみましょう。

01_(適当な)業務要件定義

真面目に遊びましょう。

以下の機能を用意しようと思います。

  1. 契約締結

  2. 出金機能

  3. 返済機能

  4. 利息計算

  5. 残高シミュレーション

  6. 契約状態照会

あくまで遊びなので、一旦これくらいで行きたいと思います。

02_(適当な)システム要件定義

02_01_契約締結

ローンクラスを作成して、契約締結時に決めておく項目と、出金や返済にあたって更新される項目を全てクラス属性として定義しておくようにします。
契約締結メソッドというよりは、クラスのインスタンスを作成する行為を「契約締結」と呼びたいと思います。

なお、日付型を扱うのと、日付間の計算を行うため必要なライブラリをインポート。
また、小数点の切り上げなどもするのでmathライブラリもインポートしておきます。

class Loan:
    def __init__(self, contract_date, extreme_amount, interest_rate):
        #ローン元金残高(締結時は0円)
        self.loan_amount = 0
        #契約締結日(インスタンス作成時に定義)
        self.contract_date = dt.datetime.strptime(contract_date, "%Y%m%d").date()
        
        #極度額(インスタンス作成時に定義)
        self.extreme_amount = extreme_amount

        #利率(インスタンス作成時に定義)
        self.interest_rate = interest_rate

        #利息残高(締結時は0円)
        self.interest_amount = 0

        #出金日(締結時はブランク)
        self.withdrawal_date = ''

        #支払日(締結時はブランク)
        self.payment_date = ''

02_02_利息計算機能

次に出金機能かと思いきや・・・、実は利息計算機能を先に定義する必要があります。

出金時も、返済時も利息計算で前回の異動から今回の異動までの元金残高に対する利息計算を行う必要があるため、使いまわせるように利息計算機能を独立して定義しておきます。


    def interest_calculation(self, start_day, end_day):
       #直近異動日から最新異動日までの日数を算出
       dd = end_day - start_day
       diff_date = (end_day -start_day).days
        
       #ローン元金残高・ローン利率で日割り計算を実施して日数分計算
       interest = math.floor(self.loan_amount * (dd/td(days=1)/365) * (self.interest_rate/100))
   
       #計算した日数と利息をリストで返す
       return [diff_date, interest]

※ diff_dateの方は性数値で返したいので、ddと分けています。

02_03_出金機能

次に出金機能に行きたいと思います。

① まず、パターンとして以下を考慮しておきます。

  1. 締結後に初めて出金する場合

  2. 直近の異動が出金の場合

  3. 直近の異動が返済の場合

・1の場合は、利息は実質発生しないはずなので実態あまり考慮は不要ですが、2,3と分けるために定義しておくもの。

・2の場合は、前回の出金日から今回の出金日までの日数分、出金前の残高に対して利息計算をする必要があります。

・3の場合は、前回の返済日から今回の出金日までの日数分、出金前の残高に対して利息計算をする必要があります。

② 次に、利息計算をした際は、利用後残高がいくらで、いつどんな利率でローン残高いくらに対して何日分の利息がいくら発生したか、明細を出力するようにします。

計算根拠をちゃんと示して、言いがかりをつけられないように顧客ファーストを考えます。

    #明細出力機能は共通で使えるメソッドを作っておく
    def withdrawal_detail(self, amount_all, before_amount, diff_date, interest_amount):
        print('・出金後残高:{}\n・最新利用日:{}\n・元金残高:{}\n・利息残高:{}\n・(最新利息計算結果)\n・利用前元金残高:{}\n・経過日数:{}\n・付与利息:{}\n'
              .format(amount_all,
                      self.withdrawal_date,
                      self.loan_amount, 
                      self.interest_amount, 
                      before_amount, 
                      diff_date, 
                      interest_amount
                     )
             )
        
    #出金機能
    def withdrawal(self, withdrawal_date,withdrawal_amount):
        
        #極度オーバーは与信額を超えているため貸さない
        if self.loan_amount + withdrawal_amount > self.extreme_amount:
            print('極度額を超過しているため出金出来ません')
        
        #極度額内なら先進め
        else:
            #出金日を日付型に変換して、変数edayに格納
            eday = dt.datetime.strptime(withdrawal_date, '%Y%m%d').date()
			
			#新規出金パターン
            if self.withdrawal_date == '':
                
                #新規貸出なので、契約日を開始日とする
                sday = self.contract_date
                
                #計算対象のローン元金をセットも、使ってないので0円
                before_amount = self.loan_amount
                
                #利息計算メソッドを利用する(とはいえ0円)
                interest_amount = self.interest_calculation(sday, eday)[1]
                diff_date = self.interest_calculation(sday, eday)[0]
                
                #利息残高を更新する(元の利息残高+計算した利息額)
                self.interest_amount += interest_amount
                
                #出金日を更新する
                self.withdrawal_date = eday

                #ローン元金残高を更新する
                self.loan_amount += withdrawal_amount

                #利用後残高(ローン元金残高+利息残高)を算出する
                amount_all = self.loan_amount + self.interest_amount
                
                #明細出力機能に利用後残高、計算対象のローン元金残高、日数、計算した利息額を渡して出力
                self.withdrawal_detail(amount_all, before_amount, diff_date, interest_amount)
			
			
			#既存の最新利用日あり かつ 返済日がブランク
            elif self.withdrawal_date != '' and self.payment_date == '':
                
                #sdayは前回の出金日となる
                sday = self.withdrawal_date
                
                before_amount = self.loan_amount
                diff_date = self.interest_calculation(sday, eday)[0]
                interest_amount = self.interest_calculation(sday, eday)[1]
                self.interest_amount += interest_amount
                
                self.loan_amount += withdrawal_amount
                amount_all = self.loan_amount + self.interest_amount
                self.withdrawal_date = eday

                self.withdrawal_detail(amount_all, before_amount, diff_date, interest_amount)

			#既存の利用日あるが、出金日の方が最新
            elif self.payment_date <= self.withdrawal_date:
                sday = self.withdrawal_date
                
                before_amount = self.loan_amount
                diff_date = self.interest_calculation(sday, eday)[0]
                interest_amount = self.interest_calculation(sday, eday)[1]
                self.interest_amount += interest_amount
                
                self.loan_amount += withdrawal_amount
                amount_all = self.loan_amount + self.interest_amount
                self.withdrawal_date = eday
                print(
                    '・出金後残高:{}\n・最新利用日:{}\n・元金残高:{}\n・利息残高:{}\n・(最新利息計算結果)\n・利用前元金残高:{}\n・経過日数:{}\n・付与利息:{}\n'
                    .format(
                          amount_all,
                          self.withdrawal_date,
                          self.loan_amount, 
                          self.interest_amount, 
                          before_amount, 
                          diff_date, 
                          interest_amount
                    )
                )
                
                
			#返済日の方が最新
            else:
                #前回の返済日が起算日となる
                sday = self.payment_date
                eday = dt.datetime.strptime(withdrawal_date, '%Y%m%d').date()
				
                before_amount = self.loan_amount
                diff_date = self.interest_calculation(sday, eday)[0]
                interest_amount = self.interest_calculation(sday, eday)[1]
                self.interest_amount += interest_amount
                
                self.loan_amount += withdrawal_amount
                self.withdrawal_date = eday
                amount_all = self.loan_amount + self.interest_amount
                self.withdrawal_detail(amount_all, before_amount, diff_date, interest_amount)
            

 03_動作確認

長くなってきたので、一旦ここまでの動作確認をしてみましょう。

03_01_ローン契約ができるか

では契約締結(=インスタンス作成)をしてみます。
適当にloan_aという変数に、必要な引数を渡して作成したインスタンスを格納します。

インスタンスって何?クラスって何?という方はこちらを・・・

#インスタンスを作成(契約日・極度額・利率を引数で与える)
loan_a = Loan("20240115", 100000, 15.0)

#作成したインスタンスが保有する項目がどのように設定されているかチェック
#引数から設定する項目と、0やブランクの固定値を設定する項目が正しいかチェック
print(
    "ローン元金残高:{}\n契約日:{}\n極度額:{}\n利率:{}\n利息残高:{}\n出金日:{}\n返済日:{}"
    .format(loan_a.loan_amount, 
            loan_a.contract_date,
            loan_a.extreme_amount,
            loan_a.interest_rate,
            loan_a.interest_amount,
            loan_a.withdrawal_date,
            loan_a.payment_date
           ))

【出力結果】

インスタンスの各項目もチェックして、0円・ブランクの固定値セット項目も問題なさそうです。

ローン元金残高:0
契約日:2024-01-15
極度額:100000
利率:15.0
利息残高:0
出金日:
返済日:

03_02_初回出金のテスト

次に契約後に初回出金する時の挙動を確認します。
先ほど作成したloan_aで、作成した出金機能を使ってみます。

loan_a.withdrawal("20240202", 10000)

【出力結果】

・出金後残高:10000
・最新利用日:2024-02-02
・元金残高:10000
・利息残高:0
・(最新利息計算結果)
・利用前元金残高:0
・経過日数:18
・付与利息:0

初回出金までの間のローン元金は0円なので、18日間経過しているものの利息計算結果は0円です。

03_03_連続出金のテスト

直近の異動が出金だった場合に、出金した場合のテストです。

loan_a.withdrawal("20240210", 10000)

【出力結果】

・出金後残高:20032
・最新利用日:2024-02-10
・元金残高:20000
・利息残高:32
・(最新利息計算結果)
・利用前元金残高:10000
・経過日数:8
・付与利息:32

こちらも前回の出金日(最新利用日)から、8日分の利息を元金10000円に対して実施しているので問題無しです。

一応Pythonで計算してみると・・・

10000*0.15/365*8

【出力結果】

32.87671232876713

次回は、返済機能も作っていきたいと思います。

この記事が気に入ったらサポートをしてみませんか?