forked from ChizhovVadim/xirr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
xirr.go
87 lines (76 loc) · 2.03 KB
/
xirr.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
package xirr
import (
"errors"
"math"
"time"
)
var errInvalidPayments = errors.New("negative and positive payments are required")
var errXirr = errors.New("xirr cannot be calculated")
type Payment struct {
Date time.Time
Amount float64
}
type cashflow struct {
Years float64
Amount float64
}
func XIRR(payments []Payment) (float64, error) {
var yearCashflows = convertToCashflows(payments)
const decimals = 3
var precision = math.Pow(10, -decimals)
var minRate = precision
const maxRate = 10000000
return calculateXirr(yearCashflows, minRate, maxRate, precision, decimals)
}
func findMinDate(payments []Payment) time.Time {
var result = payments[0].Date
for i := 1; i < len(payments); i++ {
var d = payments[i].Date
if d.Before(result) {
result = d
}
}
return result
}
func convertToCashflows(payments []Payment) []cashflow {
var minDate = findMinDate(payments)
var result = make([]cashflow, len(payments))
for i, x := range payments {
result[i] = cashflow{
Years: yearsBetween(minDate, x.Date),
Amount: x.Amount,
}
}
return result
}
func yearsBetween(start, finish time.Time) float64 {
return float64(finish.Sub(start)/(24*time.Hour)) / 365.25
}
func calcEquation(cashflows []cashflow, interestRate float64) float64 {
var sum float64
for _, x := range cashflows {
sum += x.Amount * math.Pow(interestRate, -x.Years)
}
return sum
}
func calculateXirr(cashFlows []cashflow, lowRate, highRate, precision float64, decimals int) (float64, error) {
var lowResult = calcEquation(cashFlows, lowRate)
var highResult = calcEquation(cashFlows, highRate)
for {
if math.Signbit(lowResult) == math.Signbit(highResult) {
return 0, errXirr
}
var middleRate = 0.5 * (lowRate + highRate)
var middleResult = calcEquation(cashFlows, middleRate)
if math.Signbit(middleResult) == math.Signbit(lowResult) {
lowRate = middleRate
lowResult = middleResult
} else {
highRate = middleRate
highResult = middleResult
}
if math.Abs(middleResult) <= precision {
return 0.5 * (highRate + lowRate), nil
}
}
}