go
/
misc
1
0
Fork 0
misc/monetary/installment.go

126 lines
3.9 KiB
Go
Raw Normal View History

2022-03-21 22:13:57 +09:00
package monetary
import (
"github.com/ericlagergren/decimal"
"math/big"
)
// MonthlyInstallmentInterestAmount returns monthly interest amount from annual interest ratio.
func MonthlyInstallmentInterestAmount(balance *decimal.Big, annualInterest *big.Rat) (fee *decimal.Big) {
var (
monthly big.Rat
feeFrac big.Rat
)
monthly.SetFrac64(1, 12)
balance.Rat(&feeFrac)
feeFrac.Mul(&feeFrac, annualInterest)
feeFrac.Mul(&feeFrac, &monthly)
fee = decimal.New(0, 0)
fee.Context.RoundingMode = decimal.ToZero
fee.SetRat(&feeFrac)
fee.Quantize(0)
fee.Reduce()
return fee
}
// MonthlyInstallmentInfo returns monthly repayment plan.
func MonthlyInstallmentInfo(totalBalance *decimal.Big, modDigit, division int) (installment, installmentFractional, installmentExtra *decimal.Big) {
var (
monthlyFee big.Rat
frac big.Rat
divisionDeci decimal.Big
)
divisionDeci.SetMantScale(int64(division), 0)
totalBalance.Rat(&monthlyFee)
frac.SetFrac64(1, int64(division))
monthlyFee.Mul(&monthlyFee, &frac)
installment = decimal.New(0, 0)
installment.Context.RoundingMode = decimal.ToZero
installment.SetRat(&monthlyFee)
installment.Quantize(-modDigit)
installment.Reduce()
installmentFractional = decimal.New(0, 0)
installmentFractional.Mul(installment, &divisionDeci)
installmentFractional.Sub(totalBalance, installmentFractional)
installmentFractional.Reduce()
installmentExtra = decimal.New(0, 0)
installmentExtra.Add(installment, installmentFractional)
installmentExtra.Reduce()
installmentTest := decimal.New(0, 0)
installmentTest.Mul(installment, &divisionDeci)
installmentTest.Add(installmentTest, installmentFractional)
return
}
// MonthlyInstallmentSchedule returns detailed monthly repayment plan
func MonthlyInstallmentSchedule(
totalBalance *decimal.Big,
annualInterest *decimal.Big,
modDigit, division int,
payExtraAmountEarlier bool) (
installment, installmentExtra, totalInterests *decimal.Big,
principleBalanceBeforePayments,
principleBalanceAfterPayments,
installments,
interests,
schedules []*decimal.Big,
) {
if division < 1 {
return
} else if totalBalance == nil {
return
}
var (
leftBalance decimal.Big
annualInterestFrac big.Rat
)
if annualInterest != nil {
annualInterest.Rat(&annualInterestFrac)
}
totalInterests = decimal.New(0, 0)
leftBalance.Copy(totalBalance)
installmentAmount, _, installmentAmountExtra := MonthlyInstallmentInfo(totalBalance, modDigit, division)
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)
for i := 0; i < division; i++ {
var (
interestAmount = MonthlyInstallmentInterestAmount(&leftBalance, &annualInterestFrac)
monthlyInstallmentWithInterest decimal.Big
principleBalanceBeforePayment decimal.Big
principleBalanceAfterPayment decimal.Big
paymentAmount *decimal.Big
)
if payExtraAmountEarlier {
if i == 0 {
paymentAmount = installmentAmountExtra
} else {
paymentAmount = installmentAmount
}
} else {
if i+1 == division {
paymentAmount = installmentAmountExtra
} else {
paymentAmount = installmentAmount
}
}
installments = append(installments, paymentAmount)
interests = append(interests, interestAmount)
totalInterests.Add(totalInterests, interestAmount)
principleBalanceBeforePayment.Copy(&leftBalance)
principleBalanceBeforePayments = append(principleBalanceBeforePayments, &principleBalanceBeforePayment)
monthlyInstallmentWithInterest.Add(paymentAmount, interestAmount)
leftBalance.Sub(&leftBalance, paymentAmount)
principleBalanceAfterPayment.Copy(&leftBalance)
principleBalanceAfterPayments = append(principleBalanceAfterPayments, &principleBalanceAfterPayment)
schedules = append(schedules, &monthlyInstallmentWithInterest)
}
return
}