about summary refs log tree commit diff
path: root/emacs/.emacs.d/wpc/finance.el
blob: b124061ccba3f36be824a0e6965707d458171f32 (plain) (blame)
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
;;; finance.el --- Functions to help me organize my finances -*- lexical-binding: t -*-
;; Author: William Carroll <wpcarro@gmail.com>

;;; Commentary:
;; Using functions to organize my financial thinking.

;;; Code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Dependencies
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 'prelude)
(require 'math)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Library
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defvar finance/enable-tests? t
  "When t, run the tests defined herein.")

;; TODO: Support printing an org-table of these amount in a similar format to:
;; https://keisan.casio.com/exec/system/1234231998
(cl-defun finance/future-value (amt
                                &key
                                num-years
                                (frequency 'monthly)
                                (interest-rate 0.06)
                                (payment-due-at 'beg)
                                (present-value 0))
  "Compute the Future Value of AMT.

This function assumes that the interest rate is applied annually and not
monthly.

This function will attempt to provide the following defaults:
- frequency: 'monthly
- interest-rate: 6%
- payment-due-at: 'beg
- present-value: 0.00"
  (prelude/assert (set/contains? payment-due-at (set/new 'beg 'end)))
  (prelude/assert (set/contains? frequency (set/new 'annually
                                                    'semiannually
                                                    'quarterly
                                                    'monthly)))
  (let ((pmt amt)
        (k (alist/get frequency '((annually . 1)
                                  (semiannually . 2)
                                  (quarterly . 4)
                                  (monthly . 12))))
        (r interest-rate)
        (n num-years)
        (pv present-value))
    (if (= 0 r)
        (+ pv (* pmt n k))
      (if (equal 'beg payment-due-at)
          (+ (* pv (math/exp (+ 1 (/ r k)) (* n k)))
             (* pmt
                (/ (- (math/exp (+ 1 (/ r k)) (* n k)) 1)
                   (/ r k))
                (+ 1 (/ r k))))
        (+ (* pv (math/exp (+ 1 (/ r k)) (* n k)))
           (* pmt
              (/ (- (math/exp (+ 1 (/ r k)) (* n k)) 1)
                 (/ r k))))))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tests
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(when finance/enable-tests?
  (prelude/assert
   (equal "1551.27"
          (string/format "%0.2f"
                         (finance/future-value
                          9.99
                          :interest-rate 0.05
                          :num-years 10
                          :frequency 'monthly
                          :payment-due-at 'end
                          :present-value 0))))
  (prelude/assert
   (equal "14318.34"
          (string/format "%0.2f"
                         (finance/future-value 10.0 :num-years 35))))
  (prelude/assert
   (equal "4200.00"
          (string/format "%0.2f"
                         (finance/future-value
                          10.0
                          :interest-rate 0.0
                          :num-years 35
                          :frequency 'monthly
                          :payment-due-at 'beg
                          :present-value 0))))
  (prelude/assert
   (equal "14318.34"
          (string/format "%0.2f"
                         (finance/future-value
                          10.0
                          :interest-rate 0.06
                          :num-years 35
                          :frequency 'monthly
                          :payment-due-at 'beg
                          :present-value 0))))
  (prelude/assert
   (equal "38282.77"
          (string/format "%0.2f"
                         (finance/future-value
                          10.0
                          :interest-rate 0.1
                          :num-years 35
                          :frequency 'monthly
                          :payment-due-at 'beg
                          :present-value 0)))))

(provide 'finance)
;;; finance.el ends here