package org.rr.commons.utils; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public final class FinancialMathUtils implements Serializable { private static final long serialVersionUID = -1841958159779826300L; private FinancialMathUtils() {}; private static final int BOOLEAN_TRUE = 1; private static final int BOOLEAN_FALSE = 0; /** * Calculates the actual cash value of an annuity * * @param rate * The interest rate for a period * @param nper * The total time of the periods * @param pmt * The payment of one period * @param fv * The future value that has to be reached after the last payment * @param type * The value which specifies when a payment has to be done * @return A double value which stands for the actual cash value of an * annuity <br> * <br> * rate and nper must be assigned in the same time unit <br> * if nper is given in month the rate has to be given in month too */ public static double pv(double rate, int nper, double pmt, double fv, int type) { if (rate != 0) { double a = Math.pow(1 + rate, nper); double b = pmt * (1 + rate * type) * ((Math.pow(1 + rate, nper) - 1) / rate) + fv; return -b / a; } else { return -((pmt * nper) + fv); } } /** * Calculates the capital share of a payment * * @param rate * The interest rate for a period * @param per * The period for which the ppmt has to be calculate * @param nper * The total time of the periods * @param pv * The actual cash value of the payment in the future * @param fv * The future value that has to bee reached after the last * payment * @param type * The value which specifies when a payment has to be done * @return A double value which stands for the capital share of a payment * <br> * <br> * rate and nper must be assigned in the same time unit <br> * if nper is given in month the rate has to be given in month too */ public static double ppmt(double rate, int per, int nper, double pv, double fv, int type) { if (per < 1 || per > nper) { throw new RuntimeException("Wrong per argument " + per); } return pmt(rate, nper, pv, fv, type) - ipmt(rate, per, nper, pv, fv, type); } /** * Calculates the payoff of an annuity * * @param rate * The interest rate for a period * @param nper * nper The total time of the periods * @param pv * The actual cash value of the payment in the future * @param fv * The future value that has to bee reached after the last * payment * @param type * The value which specifies when a payment has to be done * @return A double value which stands for the payoff of an annuity <br> * <br> * rate and nper must be assigned in the same time unit <br> * if nper is given in month the rate has to be given in month too */ public static double pmt(double rate, int nper, double pv, double fv, int type) { if (rate != 0) { double a = rate * (pv * Math.pow(1 + rate, nper) + fv); double b = (1 + rate * type) * ((Math.pow(1 + rate, nper) - 1)); double result = -a / b; return result; } else { return -((pv + fv) / nper); } } /** * Calculates the actual net cash value of a payment * * @param rate * The interest rate for a period * @param values * Cash flow values <br> * at least one positive and one negative value must be in the * array * @return A double value which stands for the actual net cash value of a * payment * */ public static double npv(double rate, double[] values) { List<Double> positiveValues = new ArrayList<>(values.length); List<Double> negativeValues = new ArrayList<>(values.length); for (int i = 0; i < values.length; i++) { if (values[i] < 0) { negativeValues.add(Double.valueOf(values[i])); } else { positiveValues.add(Double.valueOf(values[i])); } } // if (positiveValues.size() == 0) { // throw (new // ExpressionInterpreter.InterpreterException(Bundle.getString("VBFinancialMath.err.NoPositiveValuesInArray"))); // } else if (negativeValues.size() == 0) { // throw (new // ExpressionInterpreter.InterpreterException(Bundle.getString("VBFinancialMath.err.NoNegativeValuesInArray"))); // } return calculateNpvValue(rate, values); } private static double calculateNpvValue(double rate, double[] values) { double result = 0; for (int i = 0; i < values.length; i++) { result = result + (values[i] / Math.pow(1 + rate, i + 1)); } return result; } /** * Calculates the total time of the periods of one payment * * @param rate * The interest rate for a period * @param pmt * The payment of one period * @param pv * The actual cash value of the payment in the future * @param fv * The future value that has to bee reached after the last * payment * @param type * The value which specifies when a payment has to be done * @return A double value which stands for the total time of the periods */ public static double nper(double rate, double pmt, double pv, double fv, int type) { if (rate != 0) { double a = pmt * (1 + rate * type) + (-1 / rate) * fv; double b = pv * rate + pmt * (1 + rate * type); double c = 1 + rate; return Math.log(a / b) / Math.log(c)*100; } else { return -((pv + fv) / pmt); } } /** * Calculates the modified interior earning rate * * @param values * Cash flow values at least on positive and one negative value * must be in the array * @param finance_rate * The rate has to be paid at the financing of the arrangement * @param reinvest_rate * The rate that can be reached at a new arrangement of capital * @return A double value which stands for the modified interior earning * rate */ public static double mirr(double[] values, double finance_rate, double reinvest_rate) { List<Double> positiveValues = new ArrayList<>(values.length); List<Double> negativeValues = new ArrayList<>(values.length); for (int i = 0; i < values.length; i++) { if (values[i] < 0) { negativeValues.add(Double.valueOf(values[i])); } else { positiveValues.add(Double.valueOf(values[i])); } } if (positiveValues.size() == 0) { throw new RuntimeException("No positive value found in array."); } else if (negativeValues.size() == 0) { throw new RuntimeException("No negative value found in array."); } double[] pArr = new double[positiveValues.size()]; double[] nArr = new double[negativeValues.size()]; for (ListIterator<Double> i = positiveValues.listIterator(); i.hasNext();) { pArr[i.nextIndex()] = i.next().doubleValue(); } for (ListIterator<Double> i = negativeValues.listIterator(); i.hasNext();) { nArr[i.nextIndex()] = i.next().doubleValue(); } return Math.pow((-calculateNpvValue(reinvest_rate, pArr) * Math.pow(1.0 + reinvest_rate, pArr.length)) / (calculateNpvValue(finance_rate, nArr) * (1.0 + finance_rate)), 1.0 / (values.length - 1.0)) - 1.0; } /** * Calculates the future value of an annuity * * @param rate * The interest rate for a period * @param nper * The total time of the periods * @param pmt * The payment of one period * @param pv * The actual cash value of the payment in the future * @param type * The value which specifies when a payment has to be done * @return A double value which stands for the future value of an annuity * <br> * <br> * rate and nper must be assigned in the same time unit <br> * if nper is given in month the rate has to be given in month too */ public static double fv(double rate, double nper, double pmt, double pv, int type) { if (rate != 0) { return -(pv * Math.pow(1 + rate, nper) + pmt * (1 + rate * type) * ((Math.pow(1 + rate, nper) - 1) / rate)); } else { return -((pmt * nper) + pv); } } /** * Calculates the accrual asset for a period * * @param cost * The original value of the effect * @param salvage * The value of the effect at the end of the period * @param life * The expected useful life of the effect * @param period * The period for which the effect will be calculate * @param factor * The value at which the effect is reduced in a period * @return A double value which stands for the the accrual asset for a * period */ public static double ddb(double cost, double salvage, double life, double period, double factor) { double writedownForPeriod = 0; boolean reachedSalvage = false; if (life == 1) { life++; } for (; period >= 1; period--) { double oldcost = cost; writedownForPeriod = (((cost) * factor) / life); cost = cost - writedownForPeriod; if (reachedSalvage) { writedownForPeriod = 0; } else if (cost < salvage) { writedownForPeriod = oldcost - salvage; reachedSalvage = true; } } return writedownForPeriod; } /** * Calculates the payment of interest for a period * * @param rate * The interest rate for a period * @param per * The period for which the ppmt has to be calculate * @param nper * The total time of the periods * @param pv * The actual cash value of the payment in the future * @param fv * The future value that has to bee reached after the last * payment * @param type * The value which specifies when a payment has to be done * @return A double value which stands for the payment of interest for a * period <br> * <br> * rate and nper must be assigned in the same time unit <br> * if nper is given in month the rate has to be given in month too */ public static double ipmt(double rate, int per, int nper, double pv, double fv, int type) { if (per < 1 || per > nper || nper <= 0.0) { throw new RuntimeException("Wrong per argument " + per); } double pmt = pmt(rate, nper, pv, fv, type); double zins = 0.0; if (type == BOOLEAN_FALSE) { for (int i = 0; i < per; i++) { zins = pv * rate; pv = pv + pmt + zins; } } else if (type == BOOLEAN_TRUE) { for (int i = 0; i < per; i++) { pv = pv + pmt + zins; zins = pv * rate; } } return -zins; } /** * Gets the linear depreciation of a economic good for a period * * @param cost * Prime cost of the economic good * @param salvage * The value at the end of the useful life of the good * @param life * The useful life of the good * @return The linear depreciation for a period */ public static double sln(double cost, double salvage, double life) { return (cost - salvage) / life; } /** * Gets the reducing depreciation of one period * * @param cost * Prime cost of the economic good * @param salvage * The value at the end of the useful life of the good * @param life * The useful life of the good * @param period * The period of which the depreciation should be calculated * @return The depreciation of the period */ public static double syd(double cost, double salvage, double life, int period) { return ((cost - salvage) * (life - period + 1) * 2) / ((life) * (life + 1)); } }