# Paste: Financial functions

Author: Loryn Jenkins factor Fri, 5 Apr 2013 18:05:17
Plain Text |
```! Copyright (C) 2013 Loryn Jenkins.
USING: kernel accessors locals combinators shuffle sequences assocs hashtables
math math.order math.functions math.ranges arrays vectors math.constants
fry
tools.continuations prettyprint
;
IN: financial-functions

: summation ( seq -- newseq )
0 [ + ] accumulate
>vector swap suffix! ;

:: pv ( rate period payment -- pv )
rate 1 + period ^ recip payment * ;

: payback ( investment cashflows -- period )
swap '[ _ values summation [ _ >= ] find drop ] call ;

:: discounted-payback ( rate investment cashflows -- period )
cashflows values [ :> ( pmt i ) rate i 1 + pmt pv ] map-index
summation [ investment >= ] find drop ;

:: npv ( rate investment cashflows -- npv )
cashflows values [ :> ( pmt i ) rate i 1 + pmt pv ] map-index
sum investment - ;

! Newton-Raphson implementation of IRR convergence algorithm
! This is an interesting, sophisticated, and non-functional (i.e. doesn't converge) algorithm.
! I'm reasonably sure it corresponds with the original C# code.

CONSTANT: tolerance 0.00001
CONSTANT: max-iterations 5000
CONSTANT: estimated-rate-of-return-min -2147483648
CONSTANT: estimated-rate-of-return-max 2147483647

: within-bounds? ( est-rate -- ? )
dup f = not [ estimated-rate-of-return-min estimated-rate-of-return-max
between? ] when ;

: irr-polynomial-sum ( guess investment cashflows -- sum )
npv ;

: converged? ( guess investment cashflows -- ? )
irr-polynomial-sum abs tolerance <= ;

:: pv-deriv ( rate period payment -- deriv )
rate 1 + period ^ recip payment period * * ;

:: irr-derivative-sum ( guess investment cashflows -- sum )
cashflows values [ :> ( pmt i ) guess i 1 + pmt pv-deriv ] map-index
sum investment - ;

:: newton-raphson ( iterations guess investment cashflows -- result )
guess within-bounds?
[ guess investment cashflows irr-polynomial-sum
guess investment cashflows irr-derivative-sum /
guess - :> estimate
estimate investment cashflows converged?
[ estimate ]
[ iterations max-iterations <
[ iterations 1 + estimate investment cashflows newton-raphson ]
[ f ] if
] if
] [ f ] if ;

: irr-newton-raphson ( guess investment cashflows -- irr )
[ 0 ] 3dip [ 1 + neg ] 2dip newton-raphson ;

! Fixed point iteration method. Has the advantage of actually working.
! From: code.activestate.com/recipes/576686-npv-irr-payback-analysis
! NOTE: Original code failed to converge due to omitting abs in the function equivalent to fixed-point-pv-deriv.

:: fixed-point-pv-deriv ( guess investment cashflows -- result )
guess investment cashflows npv investment / abs 1 swap - guess * ;

:: irr-fixed-point ( guess investment cashflows -- results )
100 [1,b] [ ] map
guess 1 + [ drop investment cashflows fixed-point-pv-deriv ] accumulate drop ;

: irr ( guess investment cashflows -- irr )
irr-fixed-point ;

! Testing

: payback-tester ( -- periods )
7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
payback
;

: disc-tester ( -- periods )
0.09
7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
discounted-payback
;

: npv-tester ( -- periods )
0.09
7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
npv
;

: irr-tester ( -- periods )
0.09
7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
irr
;```

## Annotation: Unit test (fails!)

Author: Loryn Jenkins factor Fri, 5 Apr 2013 18:25:48
Plain Text |
```! Copyright (C) 2013 Loryn Jenkins.
USING: tools.test financial-functions ;
IN: financial-functions.tests

[ 4 ] [    7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
payback
] unit-test```

## Annotation: Unit tests (succeeds)

Author: Loryn Jenkins factor Fri, 5 Apr 2013 18:46:26
Plain Text |
```! Copyright (C) 2013 Loryn Jenkins.
USING: tools.test financial-functions hashtables math math.functions ;
IN: financial-functions.tests

[ 3 ] [    7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
payback
] unit-test

[ 4 ] [
0.09
7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
discounted-payback ] unit-test

[ 138973.0 ]  [
0.09
7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
npv 2 10^ * round ] unit-test

[ 1779.0 ]  [
0.09
7000.00
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} >hashtable
irr 4 10^ * round ] unit-test```

## Annotation: Unit tests (refactored) (fails compilation)

Author: Loryn Jenkins factor Fri, 5 Apr 2013 19:00:43
Plain Text |
```! Copyright (C) 2013 Loryn Jenkins.
USING: tools.test financial-functions hashtables math math.functions ;
IN: financial-functions.tests

: cashflow1 ( -- hashtable )
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} clone ;

[ 3 ] [ 7000.00 cashflow1 payback] unit-test

[ 4 ] [ 0.09 7000.00 cashflow1 discounted-payback ] unit-test

[ 138973.0 ]  [ 0.09 7000.00 cashflow1 npv 2 10^ * round ] unit-test

[ 1779.0 ]  [ 0.09 7000.00 cashflow1 irr 4 10^ * round ] unit-test```

## Annotation: Units tests (refactored) (succeeds)

Author: Loryn Jenkins factor Fri, 5 Apr 2013 19:24:05
Plain Text |
```! Copyright (C) 2013 Loryn Jenkins.
USING: kernel tools.test financial-functions hashtables math math.functions ;
IN: financial-functions.tests

: cashflow1 ( -- hashtable )
H{
{ 1 4000.00 }
{ 2 400.00 }
{ 3 3000.00 }
{ 4 2000.00 }
{ 5 1000.00 }
} clone ;

[ 3 ] [ 7000.00 cashflow1 payback ] unit-test

[ 4 ] [ 0.09 7000.00 cashflow1 discounted-payback ] unit-test

[ 138973.0 ]  [ 0.09 7000.00 cashflow1 npv 2 10^ * round ] unit-test

[ 1779.0 ]  [ 0.09 7000.00 cashflow1 irr 4 10^ * round ] unit-test```