DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Financial Functions

01.13.2006
| 4440 views |
  • submit to reddit
        
REBOL [
   Title:   "Financial Library"
   Date:    17-Apr-2002
   Name:    'fin
   Version: 0.0.2
   File:    %fin.r
   ;Home:    http://www.pointillistic.com/
   Author:  "Gregg Irwin"
   Email:   gregg@pointillistic.com
   ;Owner:   "Pointillistic Software"
   ;Rights:  "Copyright © Pointillistic Software 2002. All Rights Reserved."
   ;Tabs:    4
   ;Need:    0.1.4
   ;Language: 'English
   ;Charset:  'ANSI
   ;Category: [script 1]
   Purpose: {Financial module for standard library.}

   Comment: {Prototype stage.}

   History: [
      0.0.1 [13-Mar-2002 "First whack" "Gregg"]
      0.0.2 [17-Apr-2002 {Added NPV and a functional, if ugly, IRR.
      BRACKET-FN supports IRR.} "Gregg"]
   ]

   Example: {do %fin.r}
]

fin: make lib-kernel [

    ; Future Value
    fv: func [
        {Computes future value based on present value.}
        value   [number! money!] "Present value"
        rate    [number!] "Interest Rate"
        periods [number!] "Number of time periods"
    ][
        1 + rate ** periods * value
    ]
    ;lib/fin/fv 1000 .1 12
    ;lib/fin/fv 1000 .1 24
    ;lib/fin/fv 1000 .1 36


    ; Compound Interest
    interest: func [
        {Computes accumulated compound interest.}
        value   [number! money!] "Present value"
        rate    [number!] "Interest Rate"
        periods [number!] "Number of time periods"
    ][
        1 + rate ** periods - 1 * value
    ]
    ;lib/fin/interest 1000 .1 12
    ;lib/fin/interest 1000 .1 24
    ;lib/fin/interest 1000 .1 36


    ; Interest Rate
    rate: func [
        {Computes interest rate for a given future value.}
        value        [number! money!] "Present value"
        future-value [number! money!] "Future value"
        periods      [number!] "Number of time periods"
    ][
        future-value / value ** (1 / periods) - 1
    ]
    ;lib/fin/rate 1000 future-value 1000 .1 12 12
    ;lib/fin/rate 1000 future-value 1000 .1 24 24
    ;lib/fin/rate 1000 future-value 1000 .1 36 36


    ; Present Value
    pv: func [
        {Computes present value based on future value.}
        value   [number! money!] "Future value"
        rate    [number!] "Interest Rate"
        periods [number!] "Number of time periods"
    ][
        1 + rate ** (negate periods) * value
        ; - or -
        ;value / (1 + rate ** periods)
    ]
    ;lib/fin/pv future-value 1000 .1 12 .1 12
    ;lib/fin/pv future-value 1000 .1 24 .1 24
    ;lib/fin/pv future-value 1000 .1 36 .1 36

    npv: func [
        {Computes net present value based on future cash flow values
        using continuous discounting.}
        values  [any-block!] "Cash flow values"
        rate    [number!] "Interest Rate"
        /local result
    ][
        result: $0.0
        repeat i length? values [
            result: result + pv values/:i rate i
        ]
        result
    ]
    ;lib/fin/npv [1000 2000 3000] .1
    ;lib/fin/npv [-10000, 3000, 4200, 6800] .1

    ;[-$70'000 $12'000 $15'000 $18'000 $21'000 $26'000]

    ; Time Periods
    nper: func [
        {Computes the number of time periods from the present value
        to a given future value.}
        value        [number! money!] "Present value"
        future-value [number! money!] "Future value"
        rate         [number!] "Interest Rate"
    ][
        ; 2 versions. Both work.
        (log-e (future-value / value)) / (log-e (rate + 1))
        ;divide log-e divide future-value value  log-e add rate 1
    ]
    ;lib/fin/nper 1000 future-value 1000 .1 12 .1
    ;lib/fin/nper 1000 future-value 1000 .1 24 .1
    ;lib/fin/nper 1000 future-value 1000 .1 36 .1

    irr: func [
        {Computes the internal rate of return based on future cash flow values.
        The internal rate of return of a cash flow is the interest rate that
        makes the present value of a cash flow equal to zero. THIS IS A QUICKLY
        HACKED VERSION AND NEEDS TO BE CLEANED UP! HERE FOR TESTING.}
        values  [any-block!] "Cash flow values"
        /guess
            guess-val
        /local rates rate npv-result result
    ][
        tries: 50
        ACCURACY: $.000001
        range: none
        rtb: dx: x-mid: 0.0
        f-mid: $0

        if not guess [guess-val: .2]

        if range: bracket-fn :npv values 0 guess-val [
            either ((npv values range/1) < $0) [
                rtb: range/1
                dx:  range/2 - range/1
            ][
                rtb: range/2
                dx:  range/1 - range/2
            ]
            loop tries [
            	dx: dx * 0.5
            	x-mid: rtb + dx
            	f-mid: npv values x-mid
            	if (f-mid <= $0) [rtb: x-mid]
            	if any [
                    (abs f-mid < ACCURACY)
                    (abs dx < second ACCURACY)
                ] [return x-mid]
            ]
            return none
        ]
    ]

    bracket-fn: func [
        {Adapted from Numerical Recipes. Given a user supplied function and a
        given 'guess' range (x1 to x2) it expands the range geometrically until
        a root is bracketed by the returned values x1 and x2, in which case
        the range is returned as a block of those two values. If it doesn't
        succeed after 50 tries, it returns false. This was built to support
        IRR so it's designed to call NPV, which takes one arg. It should be
        generalized.}
        fn arg x1 x2
        /local factor tries f1 f2
    ][
        tries: 50
        factor: 1.6
        f1: fn arg x1
        f2: fn arg x2
        loop tries [
            ; We have to use SECOND on f2 because it's a money! value
            ; and the numeric component is returned by SECOND.
            if negative? multiply f1 second f2 [
                return reduce [x1 x2]
            ]
            either (abs f1) < (abs f2) [
                x1: x1 + (factor * (x1 - x2))
                f1: fn arg x1
            ][
                x2: x2 + (factor * (x2 - x1))
                f2: fn arg x2
            ]
        ]
        false
    ]
    ;bracket-fn get in lib/fin 'npv values 0 .2
    ;values: [-$70'000 $12'000 $15'000 $18'000 $21'000 $26'000]
    ;lib/fin/irr values
    ;lib/fin/irr copy/part values 5
    ;lib/fin/irr copy/part values 3
    ;lib/fin/irr/guess copy/part values 3 -.1
    ;values: [-10 1.8 1.8 1.8 1.8 1.8 1.8 1.8 2.8]
    ;lib/fin/irr values
    ;values: [0 -1073 -1459 -1364 -1247 -1110 31789]
    ;lib/fin/irr values

;     irr: func [
;         {Computes the internal rate of return based on future cash flow values.
;         The internal rate of return of a cash flow is the interest rate that
;         makes the present value of a cash flow equal to zero. THIS IS A QUICKLY
;         HACKED VERSION AND NEEDS TO BE CLEANED UP! HERE FOR TESTING.}
;         values  [any-block!] "Cash flow values"
;         /guess
;             guess-val
;         /local rates rate npv-result result
;     ][
;         ACCURACY: $.000001
;         tries: 50
;         range: none
;         rtb: dx: x-mid: 0.0
;         f-mid: $0
;         if not guess [guess-val: .2]
;         if range: bracket-fn :npv values 0 guess-val [
;             f: npv values range/1
;             either (f < $0) [
;                 rtb: range/1
;                 dx: range/2 - range/1
;             ][
;                 rtb: range/2
;                 dx: range/1 - range/2
;             ]
;             loop tries [
;             	dx: dx * 0.5
;             	x-mid: rtb + dx
;             	f-mid: npv values x-mid
;             	if (f-mid <= $0) [rtb: x-mid]
;             	if any [
;                     ((abs f-mid) < ACCURACY)
;                     ((abs dx) < (second ACCURACY))
;                 ] [return x-mid]
;             ]
;             return none
;         ]
;     ]
;
;     bracket-fn: func [
;         {Adapted from Numerical Recipes. Given a user supplied function and a
;         given 'guess' range (x1 to x2) it expands the range geometrically until
;         a root is bracketed by the returned values x1 and x2, in which case
;         the range is returned as a block of those two values. If it doesn't
;         succeed after 50 tries, it returns false. This was built to support
;         IRR so it's designed to call NPV, which takes one arg. It should be
;         generalized.}
;         fn arg x1 x2
;         /local factor tries f1 f2
;     ][
;         tries: 50
;         factor: 1.6
;         f1: fn arg x1
;         f2: fn arg x2
;         loop tries [
;             ; We have to use SECOND on f2 because it's a money! value
;             ; and the numeric component is returned by SECOND.
;             if negative? multiply f1 second f2 [
;                 return reduce [x1 x2]
;             ]
;             either (abs f1) < (abs f2) [
;                 x1: x1 + (factor * (x1 - x2))
;                 f1: fn arg x1
;             ][
;                 x2: x2 + (factor * (x2 - x1))
;                 f2: fn arg x2
;             ]
;         ]
;         false
;     ]

]