amuz.es/src/go/misc@v1.0.1/monetary/installment.go (about) 1 package monetary 2 3 import ( 4 "github.com/ericlagergren/decimal" 5 "math/big" 6 ) 7 8 // MonthlyInstallmentInterestAmount returns monthly interest amount from annual interest ratio. 9 func MonthlyInstallmentInterestAmount(balance *decimal.Big, annualInterest *big.Rat) (fee *decimal.Big) { 10 11 var ( 12 monthly big.Rat 13 feeFrac big.Rat 14 ) 15 monthly.SetFrac64(1, 12) 16 balance.Rat(&feeFrac) 17 feeFrac.Mul(&feeFrac, annualInterest) 18 feeFrac.Mul(&feeFrac, &monthly) 19 fee = decimal.New(0, 0) 20 fee.Context.RoundingMode = decimal.ToZero 21 fee.SetRat(&feeFrac) 22 fee.Quantize(0) 23 fee.Reduce() 24 return fee 25 } 26 27 // MonthlyInstallmentInfo returns monthly repayment plan. 28 func MonthlyInstallmentInfo(totalBalance *decimal.Big, modDigit, division int) (installment, installmentFractional, installmentExtra *decimal.Big) { 29 var ( 30 monthlyFee big.Rat 31 frac big.Rat 32 divisionDeci decimal.Big 33 ) 34 35 divisionDeci.SetMantScale(int64(division), 0) 36 totalBalance.Rat(&monthlyFee) 37 frac.SetFrac64(1, int64(division)) 38 monthlyFee.Mul(&monthlyFee, &frac) 39 40 installment = decimal.New(0, 0) 41 installment.Context.RoundingMode = decimal.ToZero 42 installment.SetRat(&monthlyFee) 43 installment.Quantize(-modDigit) 44 installment.Reduce() 45 46 installmentFractional = decimal.New(0, 0) 47 installmentFractional.Mul(installment, &divisionDeci) 48 installmentFractional.Sub(totalBalance, installmentFractional) 49 installmentFractional.Reduce() 50 51 installmentExtra = decimal.New(0, 0) 52 installmentExtra.Add(installment, installmentFractional) 53 installmentExtra.Reduce() 54 55 installmentTest := decimal.New(0, 0) 56 installmentTest.Mul(installment, &divisionDeci) 57 installmentTest.Add(installmentTest, installmentFractional) 58 return 59 } 60 61 // MonthlyInstallmentSchedule returns detailed monthly repayment plan 62 func MonthlyInstallmentSchedule( 63 totalBalance *decimal.Big, 64 annualInterest *decimal.Big, 65 modDigit, division int, 66 payExtraAmountEarlier bool) ( 67 installment, installmentExtra, totalInterests *decimal.Big, 68 principleBalanceBeforePayments, 69 principleBalanceAfterPayments, 70 installments, 71 interests, 72 schedules []*decimal.Big, 73 ) { 74 if division < 1 { 75 return 76 } else if totalBalance == nil { 77 return 78 } 79 var ( 80 leftBalance decimal.Big 81 annualInterestFrac big.Rat 82 ) 83 84 if annualInterest != nil { 85 annualInterest.Rat(&annualInterestFrac) 86 } 87 88 totalInterests = decimal.New(0, 0) 89 leftBalance.Copy(totalBalance) 90 installmentAmount, _, installmentAmountExtra := MonthlyInstallmentInfo(totalBalance, modDigit, division) 91 principleBalanceBeforePayments, principleBalanceAfterPayments, installments, interests, schedules = make([]*decimal.Big, 0, division), make([]*decimal.Big, 0, division), make([]*decimal.Big, 0, division), make([]*decimal.Big, 0, division), make([]*decimal.Big, 0, division) 92 for i := 0; i < division; i++ { 93 var ( 94 interestAmount = MonthlyInstallmentInterestAmount(&leftBalance, &annualInterestFrac) 95 monthlyInstallmentWithInterest decimal.Big 96 principleBalanceBeforePayment decimal.Big 97 principleBalanceAfterPayment decimal.Big 98 paymentAmount *decimal.Big 99 ) 100 if payExtraAmountEarlier { 101 if i == 0 { 102 paymentAmount = installmentAmountExtra 103 } else { 104 paymentAmount = installmentAmount 105 } 106 } else { 107 if i+1 == division { 108 paymentAmount = installmentAmountExtra 109 } else { 110 paymentAmount = installmentAmount 111 } 112 } 113 installments = append(installments, paymentAmount) 114 interests = append(interests, interestAmount) 115 totalInterests.Add(totalInterests, interestAmount) 116 principleBalanceBeforePayment.Copy(&leftBalance) 117 principleBalanceBeforePayments = append(principleBalanceBeforePayments, &principleBalanceBeforePayment) 118 monthlyInstallmentWithInterest.Add(paymentAmount, interestAmount) 119 leftBalance.Sub(&leftBalance, paymentAmount) 120 principleBalanceAfterPayment.Copy(&leftBalance) 121 principleBalanceAfterPayments = append(principleBalanceAfterPayments, &principleBalanceAfterPayment) 122 schedules = append(schedules, &monthlyInstallmentWithInterest) 123 } 124 return 125 }