1   /**
2    * Investor, the open-source investment library
3    *
4    * (C) Copyright 2008, by individual contributors as indicated by the @author tag.
5    *
6    * This library is free software; you can redistribute it and/or modify it
7    * under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of
9    * the License, or (at your option) any later version.
10   *
11   * This software is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this software; if not, write to the Free
18   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
20   */
21  package com.nickokiss.investor.calc;
22  
23  import java.math.BigDecimal;
24  
25  import com.nickokiss.investor.fin.element.Annuity;
26  import com.nickokiss.investor.fin.element.StreamElement;
27  import com.nickokiss.investor.fin.env.CompoundingStrategy;
28  import com.nickokiss.investor.fin.env.ConstantInterestRateStrategy;
29  import com.nickokiss.investor.fin.env.Env;
30  
31  /**
32   * <pre>
33   * P - present value
34   * A - period amount
35   * r - period interest rate
36   * n - number of periods
37   * m - periods per year
38   *
39   * P = (A/r) * (1 - (1+r/m)^(-n))
40   *
41   * @author Tomasz Koscinski <tomasz.koscinski@nickokiss.com>
42   * </pre>
43   */
44  public class AnnuityCalc {
45  
46    private MathCalc mathCalc = new MathCalc();
47    private CompoundInterestCalc intCalc = new CompoundInterestCalc();
48  
49    /**
50     * P = (A/r) * (1 - (1+r/m)^(-n))
51     */
52    public BigDecimal getPresentValue(BigDecimal annualAmount, BigDecimal interestRate, BigDecimal periodsPerYear, BigDecimal numberOfPeriods) {
53  
54      // (1+r/m)^(-n)
55      BigDecimal growth = intCalc.getGrowth(interestRate, periodsPerYear, numberOfPeriods.negate());
56      // (1 - (1+r/m)^(-n))
57      BigDecimal factor = mathCalc.ONE.subtract(growth);
58      // (A/r)
59      BigDecimal perpetualAnnuityPV = mathCalc.div(annualAmount, interestRate);
60      // (A/r) * (1 - (1+r/m)^(-n))
61      return perpetualAnnuityPV.multiply(factor);
62    }
63  
64    /**
65     * P = (A/r) * (1 - (1+r)^(-n))
66     */
67    public BigDecimal getPresentValue(BigDecimal periodAmount, BigDecimal periodInterestRate, BigDecimal numberOfPeriods) {
68  
69      // P = (A/r) * (1 - (1+r/1)^(-n))
70      return getPresentValue(periodAmount, periodInterestRate, mathCalc.ONE, numberOfPeriods);
71    }
72  
73    /**
74     * TRANSFORMATION:
75     *
76     * <pre>
77     * P = (A/r) * (1 - (1+r)^(-n))
78     * A = P * r * ((1+r)^n / (1+r)^n - 1)
79     * </pre>
80     */
81    public BigDecimal getPeriodAmount(BigDecimal presentValue, BigDecimal periodInterestRate, BigDecimal numberOfPeriods) {
82      // (1+r)^n
83      BigDecimal growth = intCalc.getGrowth(periodInterestRate, mathCalc.ONE, numberOfPeriods);
84      // ((1+r)^n / (1+r)^n - 1)
85      BigDecimal factor = mathCalc.div(growth, growth.subtract(mathCalc.ONE));
86      // P * r * ((1+r)^n / (1+r)^n - 1)
87      return presentValue.multiply(periodInterestRate).multiply(factor);
88    }
89  
90    /**
91     * TRANSFORMATION:
92     *
93     * <pre>
94     * P = (A/r) * (1 - (1+r)^(-n))
95     * (P * r) / A = 1 - (1+r)^(-n)
96     * (1+r)^(-n) = 1 - (P * r) / A
97     * -n = log[1+r](1 - (P * r) / A)
98     * n = -1 * log[1+r](1 - (P * r) / A)
99     * </pre>
100    */
101   public BigDecimal getNumberOfPeriods(BigDecimal presentValue, BigDecimal periodAmount, BigDecimal periodInterestRate) {
102 
103     // (1 - (P * r) / A)
104     BigDecimal logNumber = mathCalc.ONE.subtract(mathCalc.div(presentValue.multiply(periodInterestRate), periodAmount));
105     // (1 + r)
106     BigDecimal base = mathCalc.ONE.add(periodInterestRate);
107     // -1 * log[1+r](1 - (P * r) / A)
108     return mathCalc.log(base, logNumber).negate();
109   }
110 
111   /**
112    * <pre>
113    * P - present value
114    * A - period amount
115    * r - period interest rate
116    * n - number of periods
117    * m - periods per year
118    *
119    * P = (A/r) * (1 - (1+r/m)^(-n))
120    * P = (A/r) - (A/r)* (1 / (1 + r/m))^(t*m)
121    * P = (A/r) - (A/r)* discountFactor
122    * P = (A/r) - streamElement.getValue(A/r, t)
123    * </pre>
124    */
125   public BigDecimal getValue(Annuity annuity, BigDecimal interestRate, CompoundingStrategy cs, BigDecimal time) {
126     // Interest rate needs to be the same all the time !! (needs to be re-factored)
127     Env env = new Env();
128     env.setCompoundingStrategy(cs);
129     env.setInterestRateStrategy(new ConstantInterestRateStrategy(interestRate));
130 
131     BigDecimal elementValue = mathCalc.div(annuity.getAnnualAmount(), interestRate);
132     StreamElement annuityElement = new StreamElement();
133     annuityElement.setValue(elementValue);
134     annuityElement.setTime(annuity.getLenght());
135     return elementValue.subtract(annuityElement.getValue(env, time));
136   }
137 
138 }