/*
* Copyright (c) 2012, 2013, Credit Suisse (Anatole Tresch), Werner Keil. Licensed under the Apache
* License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package org.javamoney.calc.common;
import org.javamoney.calc.CalculationContext;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.Objects;
import javax.money.MonetaryAmount;
import javax.money.MonetaryException;
import javax.money.MonetaryOperator;
/**
* <p>
* <img src="http://www.financeformulas.net/Formula%20Images/Growing%20Annuity%20-%20FV%201.gif"/>
* <br>
* <p>
* The formula for the future value of a growing annuity is used to calculate the future amount of a
* series of cash flows, or payments, that grow at a proportionate rate. A growing annuity may
* sometimes be referred to as an increasing annuity.
* <p>
* <h3>Example of FV of Growing Annuity</h3>
* <p>
* An example of the future value of a growing annuity formula would be an individual who is paid
* biweekly and decides to save one of her extra paychecks per year. One of her net paychecks
* amounts to $2,000 for the first year and she expects to receive a 5% raise on her net pay every
* year. For this example, we will use 5% on her net pay and not involve taxes and other adjustments
* in order to hold all other things constant. In an account that has a yield of 3% per year, she
* would like to calculate her savings balance after 5 years.
* <p>
* The growth rate in this example would be the 5% increase per year, the initial cash flow or
* payment would be $2,000, the number of periods would be 5 years, and rate per period would be 3%.
* Using these variables in the future value of growing annuity formula would show
* <p>
* <i>Example</i>
* <p>
* After solving this equation, the amount after the 5th cash flow would be $11,700.75.
*
* @author Anatole Tresch
* @see http://www.financeformulas.net/Future-Value-of-Growing-Annuity.html
*/
public final class FutureValueGrowingAnnuity implements MonetaryOperator {
/**
* The discount rate, not null.
*/
private final Rate discountRate;
/**
* The growth rate, not null.
*/
private final Rate growthRate;
/**
* the target periods, >= 0.
*/
private final int periods;
/**
* Constructor.
*
* @param discountRate The discount rate, not null.
* @param growthRate The growth rate, not null.
* @param periods the target periods, >= 0.
*/
private FutureValueGrowingAnnuity(Rate discountRate, Rate growthRate, int periods) {
this.discountRate = Objects.requireNonNull(discountRate);
this.growthRate = Objects.requireNonNull(growthRate);
if(discountRate.equals(growthRate)){
throw new MonetaryException("Discount rate and growth rate cannot be the same.");
}
if (periods < 0) {
throw new MonetaryException("Periods < 0");
}
this.periods = periods;
}
public Rate getDiscountRate() {
return discountRate;
}
public Rate getGrowthRate() {
return growthRate;
}
public int getPeriods() {
return periods;
}
/**
* Access a MonetaryOperator for calculation.
*
* @param discountRate The discount rate, not null.
* @param growthRate The growth rate, not null.
* @param periods the target periods, >= 0.
* @return the operator, never null.
*/
public static FutureValueGrowingAnnuity of(Rate discountRate, Rate growthRate, int periods) {
return new FutureValueGrowingAnnuity(discountRate, growthRate, periods);
}
/**
* Performs the calculation.
*
* @param firstPayment the first payment
* @param discountRate The rate perperiod, not null. If the rate is less than 0, the result will be zero.
* @param growthRate The growth rate, not null.
* @param periods the target periods, >= 0.
* @return the resulting amount, never null.
*/
public static MonetaryAmount calculate(MonetaryAmount firstPayment, Rate discountRate, Rate growthRate,
int periods) {
if(discountRate.equals(growthRate)){
throw new MonetaryException("Discount rate and growth rate cannot be the same.");
}
if(discountRate.get().signum()<0){
return firstPayment.getFactory().setNumber(0.0d).create();
}
final BigDecimal ONE = CalculationContext.one();
BigDecimal num = ONE.add(discountRate.get()).pow(periods)
.subtract(ONE.add(growthRate.get()).pow(periods));
BigDecimal denum = discountRate.get().subtract(growthRate.get());
return firstPayment.multiply(num.divide(denum, CalculationContext.mathContext()));
}
@Override
public MonetaryAmount apply(MonetaryAmount firstPayment) {
return calculate(firstPayment, discountRate, growthRate, periods);
}
@Override
public String toString() {
return "FutureValueGrowingAnnuity{" +
"discountRate=" + discountRate +
", growthRate=" + growthRate +
", periods=" + periods +
'}';
}
}