-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathreducing_utils.go
292 lines (245 loc) · 9.87 KB
/
reducing_utils.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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/*
This package is a go native port of the numpy-financial package with some additional helper
functions.
The functions in this package are a scalar version of their vectorised counterparts in
the numpy-financial(https://github.com/numpy/numpy-financial) library.
Currently, only some functions are ported, the remaining will be ported soon.
*/
package gofinancial
import (
"fmt"
"math"
"github.com/razorpay/go-financial/enums/paymentperiod"
"github.com/shopspring/decimal"
)
/*
Pmt compute the fixed payment(principal + interest) against a loan amount ( fv = 0).
It can also be used to calculate the recurring payments needed to achieve a certain future value
given an initial deposit, a fixed periodically compounded interest rate, and the total number of periods.
It is obtained by solving the following equation:
fv + pv*(1 + rate)**nper + pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0
Params:
rate : rate of interest compounded once per period
nper : total number of periods to be compounded for
pv : present value (e.g., an amount borrowed)
fv : future value (e.g., 0)
when : specification of whether payment is made
at the beginning (when = 1) or the end
(when = 0) of each period
References:
[WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
Available:
http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
OpenDocument-formula-20090508.odt
*/
func Pmt(rate decimal.Decimal, nper int64, pv decimal.Decimal, fv decimal.Decimal, when paymentperiod.Type) decimal.Decimal {
one := decimal.NewFromInt(1)
minusOne := decimal.NewFromInt(-1)
dWhen := decimal.NewFromInt(when.Value())
dNper := decimal.NewFromInt(nper)
dRateWithWhen := rate.Mul(dWhen)
factor := one.Add(rate).Pow(dNper)
var secondFactor decimal.Decimal
if rate.Equal(decimal.Zero) {
secondFactor = dNper
} else {
secondFactor = factor.Sub(one).Mul(one.Add(dRateWithWhen)).Div(rate)
}
return pv.Mul(factor).Add(fv).Div(secondFactor).Mul(minusOne)
}
/*
IPmt computes interest payment for a loan under a given period.
Params:
rate : rate of interest compounded once per period
per : period under consideration
nper : total number of periods to be compounded for
pv : present value (e.g., an amount borrowed)
fv : future value (e.g., 0)
when : specification of whether payment is made
at the beginning (when = 1) or the end
(when = 0) of each period
References:
[WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
Available:
http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
OpenDocument-formula-20090508.odt
*/
func IPmt(rate decimal.Decimal, per int64, nper int64, pv decimal.Decimal, fv decimal.Decimal, when paymentperiod.Type) decimal.Decimal {
totalPmt := Pmt(rate, nper, pv, fv, when)
one := decimal.NewFromInt(1)
ipmt := rbl(rate, per, totalPmt, pv, when).Mul(rate)
if when == paymentperiod.BEGINNING {
if per == 1 {
return decimal.Zero
} else {
// paying at the beginning, so discount it.
return ipmt.Div(one.Add(rate))
}
} else {
return ipmt
}
}
/*
PPmt computes principal payment for a loan under a given period.
Params:
rate : rate of interest compounded once per period
per : period under consideration
nper : total number of periods to be compounded for
pv : present value (e.g., an amount borrowed)
fv : future value (e.g., 0)
when : specification of whether payment is made
at the beginning (when = 1) or the end
(when = 0) of each period
References:
[WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
Available:
http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
OpenDocument-formula-20090508.odt
*/
func PPmt(rate decimal.Decimal, per int64, nper int64, pv decimal.Decimal, fv decimal.Decimal, when paymentperiod.Type) decimal.Decimal {
total := Pmt(rate, nper, pv, fv, when)
ipmt := IPmt(rate, per, nper, pv, fv, when)
return total.Sub(ipmt)
}
// Rbl computes remaining balance
func rbl(rate decimal.Decimal, per int64, pmt decimal.Decimal, pv decimal.Decimal, when paymentperiod.Type) decimal.Decimal {
return Fv(rate, per-1, pmt, pv, when)
}
/*
Nper computes the number of periodic payments by solving the equation:
fv +
pv*(1 + rate)**nper +
pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) = 0
Params:
rate : an interest rate compounded once per period
pmt : a (fixed) payment, paid either
at the beginning (when = 1) or the end (when = 0) of each period
pv : a present value
when : specification of whether payment is made
at the beginning (when = 1) or the end
(when = 0) of each period
fv: a future value
when : specification of whether payment is made
at the beginning (when = 1) or the end
(when = 0) of each period
*/
func Nper(rate decimal.Decimal, pmt decimal.Decimal, pv decimal.Decimal, fv decimal.Decimal, when paymentperiod.Type) (result decimal.Decimal, err error) {
defer func() {
if r := recover(); r != nil {
result = decimal.Zero
err = fmt.Errorf("%w: %v", ErrOutOfBounds, r)
}
}()
one := decimal.NewFromInt(1)
minusOne := decimal.NewFromInt(-1)
dWhen := decimal.NewFromInt(when.Value())
dRateWithWhen := rate.Mul(dWhen)
z := pmt.Mul(one.Add(dRateWithWhen)).Div(rate)
numerator := minusOne.Mul(fv).Add(z).Div(pv.Add(z))
denominator := one.Add(rate)
floatNumerator, _ := numerator.BigFloat().Float64()
floatDenominator, _ := denominator.BigFloat().Float64()
logNumerator := math.Log(floatNumerator)
logDenominator := math.Log(floatDenominator)
dlogDenominator := decimal.NewFromFloat(logDenominator)
result = decimal.NewFromFloat(logNumerator).Div(dlogDenominator)
return result, nil
}
/*
Fv computes future value at the end of some periods(nper) by solving the following equation:
fv +
pv*(1+rate)**nper +
pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0
Params:
pv : a present value
rate : an interest rate compounded once per period
nper : total number of periods
pmt : a (fixed) payment, paid either
at the beginning (when = 1) or the end (when = 0) of each period
when : specification of whether payment is made
at the beginning (when = 1) or the end
(when = 0) of each period
References:
[WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
Available:
http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
OpenDocument-formula-20090508.odt
*/
func Fv(rate decimal.Decimal, nper int64, pmt decimal.Decimal, pv decimal.Decimal, when paymentperiod.Type) decimal.Decimal {
one := decimal.NewFromInt(1)
minusOne := decimal.NewFromInt(-1)
dWhen := decimal.NewFromInt(when.Value())
dRateWithWhen := rate.Mul(dWhen)
dNper := decimal.NewFromInt(nper)
factor := one.Add(rate).Pow(dNper)
secondFactor := factor.Sub(one).Mul(one.Add(dRateWithWhen)).Div(rate)
return pv.Mul(factor).Add(pmt.Mul(secondFactor)).Mul(minusOne)
}
/*
Pv computes present value by solving the following equation:
fv +
pv*(1+rate)**nper +
pmt*(1 + rate*when)/rate*((1 + rate)**nper - 1) == 0
Params:
fv : a future value
rate : an interest rate compounded once per period
nper : total number of periods
pmt : a (fixed) payment, paid either
at the beginning (when = 1) or the end (when = 0) of each period
when : specification of whether payment is made
at the beginning (when = 1) or the end
(when = 0) of each period
References:
[WRW] Wheeler, D. A., E. Rathke, and R. Weir (Eds.) (2009, May).
Open Document Format for Office Applications (OpenDocument)v1.2,
Part 2: Recalculated Formula (OpenFormula) Format - Annotated Version,
Pre-Draft 12. Organization for the Advancement of Structured Information
Standards (OASIS). Billerica, MA, USA. [ODT Document].
Available:
http://www.oasis-open.org/committees/documents.php?wg_abbrev=office-formula
OpenDocument-formula-20090508.odt
*/
func Pv(rate decimal.Decimal, nper int64, pmt decimal.Decimal, fv decimal.Decimal, when paymentperiod.Type) decimal.Decimal {
one := decimal.NewFromInt(1)
minusOne := decimal.NewFromInt(-1)
dWhen := decimal.NewFromInt(when.Value())
dNper := decimal.NewFromInt(nper)
dRateWithWhen := rate.Mul(dWhen)
factor := one.Add(rate).Pow(dNper)
secondFactor := factor.Sub(one).Mul(one.Add(dRateWithWhen)).Div(rate)
return fv.Add(pmt.Mul(secondFactor)).Div(factor).Mul(minusOne)
}
/*
Npv computes the Net Present Value of a cash flow series
Params:
rate : a discount rate applied once per period
values : the value of the cash flow for that time period. Values provided here must be an array of float64
References:
L. J. Gitman, “Principles of Managerial Finance, Brief,” 3rd ed., Addison-Wesley, 2003, pg. 346.
*/
func Npv(rate decimal.Decimal, values []decimal.Decimal) decimal.Decimal {
internalNpv := decimal.NewFromFloat(0.0)
currentRateT := decimal.NewFromFloat(1.0)
one := decimal.NewFromInt(1)
for _, currentVal := range values {
internalNpv = internalNpv.Add(currentVal.Div(currentRateT))
currentRateT = currentRateT.Mul(one.Add(rate))
}
return internalNpv
}