Created
January 26, 2021 03:11
-
-
Save teepark/35e19b359ae670ebcf715d16c8f40282 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package finmath | |
import ( | |
"github.com/ericlagergren/decimal" | |
"github.com/ericlagergren/decimal/math" | |
) | |
func CalculateFV(pv, pmt, i, n *decimal.Big) *decimal.Big { | |
if i.Cmp(zero) == 0 { | |
d := new(decimal.Big).Mul(pmt, n) | |
d.Add(d, pv) | |
return d.Neg(d) | |
} | |
tmp := new(decimal.Big).Add(i, one) | |
math.Pow(tmp, tmp, n) | |
d := new(decimal.Big).Sub(one, tmp) | |
d.Mul(d, pmt) | |
d.Quo(d, i) | |
tmp.Mul(tmp, pv) | |
return d.Sub(d, tmp) | |
} | |
func CalculatePV(pmt, fv, i, n *decimal.Big) *decimal.Big { | |
if i.Cmp(zero) == 0 { | |
d := new(decimal.Big).Mul(pmt, n) | |
d.Add(d, fv) | |
return d.Neg(d) | |
} | |
tmp := new(decimal.Big).Add(i, one) | |
math.Pow(tmp, tmp, n) | |
d := new(decimal.Big).Quo(pmt, i) | |
d.Quo(d, tmp) | |
tmp.Quo(fv, tmp) | |
d.Sub(d, tmp) | |
tmp.Quo(pmt, i) | |
return d.Sub(d, tmp) | |
} | |
func CalculatePMT(pv, fv, i, n *decimal.Big) *decimal.Big { | |
if i.Cmp(zero) == 0 { | |
d := new(decimal.Big).Add(pv, fv) | |
d.Quo(d, n) | |
return d.Neg(d) | |
} | |
tmp := new(decimal.Big).Add(i, one) | |
math.Pow(tmp, tmp, n) | |
d := new(decimal.Big).Mul(pv, tmp) | |
d.Add(d, fv) | |
d.Mul(d, i) | |
tmp.Neg(tmp) | |
tmp.Add(tmp, one) | |
return d.Quo(d, tmp) | |
} | |
func CalculateN(pv, pmt, fv, i *decimal.Big) *decimal.Big { | |
if i.Cmp(zero) == 0 { | |
d := new(decimal.Big).Add(pv, fv) | |
d.Quo(d, pmt) | |
return d.Neg(d) | |
} | |
tmp := new(decimal.Big).Quo(pmt, i) | |
d := new(decimal.Big).Sub(tmp, fv) | |
tmp.Add(tmp, pv) | |
d.Quo(d, tmp) | |
math.Log(d, d) | |
tmp.Add(i, one) | |
math.Log(tmp, tmp) | |
return d.Quo(d, tmp) | |
} | |
func CalculateI(pv, pmt, fv, n *decimal.Big) *decimal.Big { | |
if justCash(pv, pmt, fv, n).Cmp(zero) == 0 { | |
return zero | |
} | |
f := func(i *decimal.Big) *decimal.Big { | |
if i.Cmp(zero) == 0 { | |
d := new(decimal.Big).Mul(pmt, n) | |
d.Add(d, pv) | |
return d.Add(d, fv) | |
} | |
tmp := new(decimal.Big).Add(one, i) | |
math.Pow(tmp, tmp, n) | |
d := new(decimal.Big).Mul(pv, tmp) | |
d.Add(d, fv) | |
if pmt.Cmp(zero) == 0 { | |
return d | |
} | |
tmp.Sub(tmp, one) | |
tmp.Mul(tmp, pmt) | |
tmp.Quo(tmp, i) | |
return d.Add(d, tmp) | |
} | |
// first derivative of f | |
fPrime := func(i *decimal.Big) *decimal.Big { | |
root := new(decimal.Big).Add(i, one) | |
tmp := new(decimal.Big).Sub(n, one) | |
pow := new(decimal.Big) | |
math.Pow(pow, root, tmp) | |
d := new(decimal.Big).Mul(pow, pv) | |
d.Mul(d, n) | |
if pmt.Cmp(zero) == 0 { | |
return d | |
} | |
term := new(decimal.Big).Mul(pow, n) | |
math.Pow(tmp, root, n) | |
tmp.Sub(tmp, one) | |
tmp.Quo(tmp, i) | |
term.Sub(term, tmp) | |
term.Mul(term, pmt) | |
term.Quo(term, i) | |
d.Add(d, term) | |
return d | |
} | |
return findRootNR(f, fPrime, defaultFindRootConfig) | |
} | |
func CalculateNPV(cfs []*decimal.Big, i *decimal.Big) *decimal.Big { | |
if len(cfs) == 0 { | |
return zero | |
} | |
sum := new(decimal.Big).Copy(cfs[0]) | |
term := new(decimal.Big) | |
for j := 1; j < len(cfs); j++ { | |
if cfs[j].Cmp(zero) == 0 { | |
continue | |
} | |
term.Add(i, one) | |
math.Pow(term, term, decimal.New(int64(j), 0)) | |
term.Quo(cfs[j], term) | |
sum.Add(sum, term) | |
} | |
return sum | |
} | |
func CalculateIRR(cfs []*decimal.Big) *decimal.Big { | |
f := func(i *decimal.Big) *decimal.Big { | |
return CalculateNPV(cfs, i) | |
} | |
fPrime := func(i *decimal.Big) *decimal.Big { | |
if len(cfs) == 0 { | |
return zero | |
} | |
sum := new(decimal.Big).Copy(cfs[0]) | |
jd := new(decimal.Big) | |
tmp := new(decimal.Big) | |
for j, cf := range cfs { | |
if j == 0 { | |
continue | |
} | |
jd.SetMantScale(int64(j+1), 0) | |
tmp.Add(i, one) | |
math.Pow(tmp, tmp, jd) | |
tmp.Quo(cf, tmp) | |
jd.SetMantScale(int64(-1*j), 0) | |
tmp.Mul(tmp, jd) | |
sum.Add(sum, tmp) | |
} | |
return sum | |
} | |
return findRootNR(f, fPrime, defaultFindRootConfig) | |
} | |
func CalculateMIRR(cfs []*decimal.Big, rr *decimal.Big) *decimal.Big { | |
cf0 := cfs[0] | |
cfs[0] = zero | |
nfv := calcNetFutureValue(cfs, rr) | |
nInv := decimal.New(int64(len(cfs) - 1), 0) | |
nInv.Quo(one, nInv) | |
d := new(decimal.Big).Quo(nfv, cf0) | |
d.Neg(d) | |
math.Pow(d, d, nInv) | |
d.Sub(d, one) | |
return d | |
} | |
func calcNetFutureValue(cfs []*decimal.Big, i *decimal.Big) *decimal.Big { | |
if len(cfs) == 0 { | |
return zero | |
} | |
sum := new(decimal.Big).Copy(cfs[len(cfs)-1]) | |
tmp := new(decimal.Big) | |
pow := new(decimal.Big) | |
for j, cf := range cfs[:len(cfs)-1] { | |
tmp.Add(i, one) | |
pow.SetMantScale(int64(len(cfs) - j - 1), 0) | |
math.Pow(tmp, tmp, pow) | |
tmp.Mul(tmp, cf) | |
sum.Add(sum, tmp) | |
} | |
return sum | |
} | |
// add up cash flows ignoring any potential interest | |
func justCash(pv, pmt, fv, n *decimal.Big) *decimal.Big { | |
d := new(decimal.Big).Mul(pmt, n) | |
d.Add(d, pv) | |
return d.Add(d, fv) | |
} | |
type findRootConfig struct { | |
InitialGuess *decimal.Big | |
Tolerance *decimal.Big | |
} | |
// Newton-Raphson method for finding a function root | |
func findRootNR(f func(*decimal.Big) *decimal.Big, fPrime func(*decimal.Big) *decimal.Big, conf findRootConfig) *decimal.Big { | |
guess := new(decimal.Big).Copy(conf.InitialGuess) | |
result := f(guess) | |
tmp := new(decimal.Big) | |
for tmp.Abs(result).Cmp(conf.Tolerance) == 1 { | |
tmp.Copy(fPrime(guess)) | |
tmp.Quo(result, tmp) | |
guess.Sub(guess, tmp) | |
result = f(guess) | |
} | |
return guess | |
} | |
var ( | |
zero = decimal.New(0, 0) | |
one = decimal.New(1, 0) | |
two = decimal.New(2, 0) | |
defaultFindRootConfig = findRootConfig{ | |
InitialGuess: decimal.New(1, 1), | |
Tolerance: decimal.New(1, 10), | |
} | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment