126 lines
3.9 KiB
Go
126 lines
3.9 KiB
Go
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
|
|
}
|