-
Notifications
You must be signed in to change notification settings - Fork 0
/
installment.go
125 lines (113 loc) · 3.92 KB
/
installment.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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
}