Browse Source

reimplemnt and test financial functions

git-svn-id: https://svn.code.sf.net/p/jackcess/code/jackcess/trunk@1208 f203690c-595d-4dc9-a70b-905162fa7fd2
tags/jackcess-2.2.1
James Ahlborn 5 years ago
parent
commit
603d0d1313

+ 6
- 9
src/main/java/com/healthmarketscience/jackcess/expr/package-info.java View File

@@ -156,19 +156,16 @@ limitations under the License.
*
* <table border="1" width="25%" cellpadding="3" cellspacing="0">
* <tr class="TableHeadingColor" align="left"><th>Function</th><th>Supported</th></tr>
* <tr class="TableRowColor"><td>DDB</td><td></td></tr>
* <tr class="TableRowColor"><td>DDB</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>FV</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>IPmt</td><td></td></tr>
* <tr class="TableRowColor"><td>IRR</td><td></td></tr>
* <tr class="TableRowColor"><td>MIRR</td><td></td></tr>
* <tr class="TableRowColor"><td>IPmt</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>NPer</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>NPV</td><td></td></tr>
* <tr class="TableRowColor"><td>Pmt</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>PPmt</td><td></td></tr>
* <tr class="TableRowColor"><td>PPmt</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>PV</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>Rate</td><td></td></tr>
* <tr class="TableRowColor"><td>SLN</td><td></td></tr>
* <tr class="TableRowColor"><td>SYD</td><td></td></tr>
* <tr class="TableRowColor"><td>Rate</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>SLN</td><td>Y</td></tr>
* <tr class="TableRowColor"><td>SYD</td><td>Y</td></tr>
* </table>
*
* <h3>Inspection</h3>

+ 0
- 8
src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultDateFunctions.java View File

@@ -378,12 +378,4 @@ public class DefaultDateFunctions
}
return firstDay;
}

private static boolean getOptionalBooleanParam(
LocaleContext ctx, Value[] params, int idx) {
if(params.length > idx) {
return params[idx].getAsBoolean(ctx);
}
return false;
}
}

+ 224
- 286
src/main/java/com/healthmarketscience/jackcess/impl/expr/DefaultFinancialFunctions.java View File

@@ -12,6 +12,14 @@ 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.

NOTICE:
Many of the financial functions have been originally copied from the Apache
POI project (Apache Software Foundation) and the UCanAccess Project. They
have been then modified and adapted so that they are integrated with Jackcess,
in a consistent manner. The Apache POI and UCanAccess projects are licensed
under Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.

*/

package com.healthmarketscience.jackcess.impl.expr;
@@ -29,11 +37,21 @@ import static com.healthmarketscience.jackcess.impl.expr.FunctionSupport.*;
*/
public class DefaultFinancialFunctions
{
// Useful Sources:
// https://brownmath.com/bsci/loan.htm
// http://financeformulas.net/Number-of-Periods-of-Annuity-from-Future-Value.html
// https://accountingexplained.com/capital/tvm/fv-annuity
// http://www.tvmcalcs.com/index.php/calculators/apps/excel_loan_amortization


/** 0 - payment end of month (default) */
private static final int PMT_END_MNTH = 0;
/** 1 - payment start of month */
private static final int PMT_BEG_MNTH = 1;

private static final int MAX_RATE_ITERATIONS = 20;
private static final double RATE_PRECISION = 0.0000001;// 1.0e-8


private DefaultFinancialFunctions() {}

@@ -48,22 +66,10 @@ public class DefaultFinancialFunctions
double rate = params[0].getAsDouble(ctx);
double pmt = params[1].getAsDouble(ctx);
double pv = params[2].getAsDouble(ctx);
double fv = getFV(ctx, params, 3);
int pmtType = getPaymentType(ctx, params, 4);

double fv = 0d;
if(params.length > 3) {
fv = params[3].getAsDouble(ctx);
}

int pmtType = PMT_END_MNTH;
if(params.length > 4) {
pmtType = params[4].getAsLongInt(ctx);
}

double result = calculateLoanPaymentPeriods(rate, pmt, pv, pmtType);

if(fv != 0d) {
result += calculateAnnuityPaymentPeriods(rate, pmt, fv, pmtType);
}
double result = calculateLoanPaymentPeriods(rate, pmt, pv, fv, pmtType);

return ValueSupport.toValue(result);
}
@@ -75,22 +81,10 @@ public class DefaultFinancialFunctions
double rate = params[0].getAsDouble(ctx);
double nper = params[1].getAsDouble(ctx);
double pmt = params[2].getAsDouble(ctx);
double pv = getOptionalDoubleParam(ctx, params, 3, 0d);
int pmtType = getPaymentType(ctx, params, 4);

double pv = 0d;
if(params.length > 3) {
pv = params[3].getAsDouble(ctx);
}

int pmtType = PMT_END_MNTH;
if(params.length > 4) {
pmtType = params[4].getAsLongInt(ctx);
}

if(pv != 0d) {
nper -= calculateLoanPaymentPeriods(rate, pmt, pv, pmtType);
}

double result = calculateFutureValue(rate, nper, pmt, pmtType);
double result = calculateFutureValue(rate, nper, pmt, pv, pmtType);

return ValueSupport.toValue(result);
}
@@ -102,22 +96,10 @@ public class DefaultFinancialFunctions
double rate = params[0].getAsDouble(ctx);
double nper = params[1].getAsDouble(ctx);
double pmt = params[2].getAsDouble(ctx);
double fv = getFV(ctx, params, 3);
int pmtType = getPaymentType(ctx, params, 4);

double fv = 0d;
if(params.length > 3) {
fv = params[3].getAsDouble(ctx);
}

int pmtType = PMT_END_MNTH;
if(params.length > 4) {
pmtType = params[4].getAsLongInt(ctx);
}

if(fv != 0d) {
nper -= calculateAnnuityPaymentPeriods(rate, pmt, fv, pmtType);
}

double result = calculatePresentValue(rate, nper, pmt, pmtType);
double result = calculatePresentValue(rate, nper, pmt, fv, pmtType);

return ValueSupport.toValue(result);
}
@@ -129,312 +111,268 @@ public class DefaultFinancialFunctions
double rate = params[0].getAsDouble(ctx);
double nper = params[1].getAsDouble(ctx);
double pv = params[2].getAsDouble(ctx);
double fv = getFV(ctx, params, 3);
int pmtType = getPaymentType(ctx, params, 4);

double fv = 0d;
if(params.length > 3) {
fv = params[3].getAsDouble(ctx);
}
double result = calculateLoanPayment(rate, nper, pv, fv, pmtType);

int pmtType = PMT_END_MNTH;
if(params.length > 4) {
pmtType = params[4].getAsLongInt(ctx);
}
return ValueSupport.toValue(result);
}
});

double result = calculateLoanPayment(rate, nper, pv, pmtType);
public static final Function IPMT = registerFunc(new FuncVar("IPmt", 4, 6) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
double rate = params[0].getAsDouble(ctx);
double per = params[1].getAsDouble(ctx);
double nper = params[2].getAsDouble(ctx);
double pv = params[3].getAsDouble(ctx);
double fv = getFV(ctx, params, 4);
int pmtType = getPaymentType(ctx, params, 5);

if(fv != 0d) {
result += calculateAnnuityPayment(rate, nper, fv, pmtType);
}
double result = calculateInterestPayment(rate, per, nper, pv, fv,
pmtType);

return ValueSupport.toValue(result);
}
});

// FIXME not working for all param combos
// public static final Function IPMT = registerFunc(new FuncVar("IPmt", 4, 6) {
// @Override
// protected Value evalVar(EvalContext ctx, Value[] params) {
// double rate = params[0].getAsDouble(ctx);
// double per = params[1].getAsDouble(ctx);
// double nper = params[2].getAsDouble(ctx);
// double pv = params[3].getAsDouble(ctx);

// double fv = 0d;
// if(params.length > 4) {
// fv = params[4].getAsDouble(ctx);
// }

// int pmtType = PMT_END_MNTH;
// if(params.length > 5) {
// pmtType = params[5].getAsLongInt(ctx);
// }

// double pmt = calculateLoanPayment(rate, nper, pv, pmtType);

// if(fv != 0d) {
// pmt += calculateAnnuityPayment(rate, nper, fv, pmtType);
// }

// double result = calculateInterestPayment(pmt, rate, per, pv, pmtType);

// return ValueSupport.toValue(result);
// }
// });

// FIXME untested
// public static final Function PPMT = registerFunc(new FuncVar("PPmt", 4, 6) {
// @Override
// protected Value evalVar(EvalContext ctx, Value[] params) {
// double rate = params[0].getAsDouble(ctx);
// double per = params[1].getAsDouble(ctx);
// double nper = params[2].getAsDouble(ctx);
// double pv = params[3].getAsDouble(ctx);

// double fv = 0d;
// if(params.length > 4) {
// fv = params[4].getAsDouble(ctx);
// }

// int pmtType = PMT_END_MNTH;
// if(params.length > 5) {
// pmtType = params[5].getAsLongInt(ctx);
// }

// double pmt = calculateLoanPayment(rate, nper, pv, pmtType);

// if(fv != 0d) {
// pmt += calculateAnnuityPayment(rate, nper, fv, pmtType);
// }

// double result = pmt - calculateInterestPayment(pmt, rate, per, pv,
// pmtType);

// return ValueSupport.toValue(result);
// }
// });

// FIXME, doesn't work for partial days
// public static final Function DDB = registerFunc(new FuncVar("DDB", 4, 5) {
// @Override
// protected Value evalVar(EvalContext ctx, Value[] params) {
// double cost = params[0].getAsDouble(ctx);
// double salvage = params[1].getAsDouble(ctx);
// double life = params[2].getAsDouble(ctx);
// double period = params[3].getAsDouble(ctx);

// double factor = 2d;
// if(params.length > 4) {
// factor = params[4].getAsDouble(ctx);
// }

// double result = 0d;

// // fractional value always rounds up to one year
// if(period < 1d) {
// period = 1d;
// }

// // FIXME? apply partial period _first_
// // double partPeriod = period % 1d;
// // if(partPeriod != 0d) {
// // result = calculateDoubleDecliningBalance(
// // cost, salvage, life, factor) * partPeriod;
// // period -= partPeriod;
// // cost -= result;
// // }
// double prevResult = 0d;
// while(period > 0d) {
// prevResult = result;
// double remPeriod = Math.min(period, 1d);
// result = calculateDoubleDecliningBalance(
// cost, salvage, life, factor);
// if(remPeriod < 1d) {
// result = (prevResult + result) / 2d;
// }
// period -= 1d;
// cost -= result;
// }

// return ValueSupport.toValue(result);
// }
// });

// FIXME, untested
// public static final Function SLN = registerFunc(new FuncVar("SLN", 3, 3) {
// @Override
// protected Value evalVar(EvalContext ctx, Value[] params) {
// double cost = params[0].getAsDouble(ctx);
// double salvage = params[1].getAsDouble(ctx);
// double life = params[2].getAsDouble(ctx);

// double result = calculateStraightLineDepreciation(cost, salvage, life);

// return ValueSupport.toValue(result);
// }
// });

// FIXME, untested
// public static final Function SYD = registerFunc(new FuncVar("SYD", 4, 4) {
// @Override
// protected Value evalVar(EvalContext ctx, Value[] params) {
// double cost = params[0].getAsDouble(ctx);
// double salvage = params[1].getAsDouble(ctx);
// double life = params[2].getAsDouble(ctx);
// double period = params[3].getAsDouble(ctx);

// double result = calculateSumOfYearsDepreciation(
// cost, salvage, life, period);

// return ValueSupport.toValue(result);
// }
// });


private static double calculateLoanPaymentPeriods(
double rate, double pmt, double pv, int pmtType) {
public static final Function PPMT = registerFunc(new FuncVar("PPmt", 4, 6) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
double rate = params[0].getAsDouble(ctx);
double per = params[1].getAsDouble(ctx);
double nper = params[2].getAsDouble(ctx);
double pv = params[3].getAsDouble(ctx);
double fv = getFV(ctx, params, 4);
int pmtType = getPaymentType(ctx, params, 5);

// https://brownmath.com/bsci/loan.htm
// http://financeformulas.net/Number-of-Periods-of-Annuity-from-Present-Value.html
double result = calculatePrincipalPayment(rate, per, nper, pv, fv,
pmtType);

if(pmtType == PMT_BEG_MNTH) {
pv += pmt;
return ValueSupport.toValue(result);
}
});

double v1 = Math.log(1d + (rate * pv / pmt));

double v2 = Math.log(1d + rate);
public static final Function DDB = registerFunc(new FuncVar("DDB", 4, 5) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
double cost = params[0].getAsDouble(ctx);
double salvage = params[1].getAsDouble(ctx);
double life = params[2].getAsDouble(ctx);
double period = params[3].getAsDouble(ctx);
double factor = getOptionalDoubleParam(ctx, params, 4, 2d);

double result = -v1 / v2;
double result = calculateDoubleDecliningBalance(
cost, salvage, life, period, factor);

if(pmtType == PMT_BEG_MNTH) {
result += 1d;
return ValueSupport.toValue(result);
}
});

return result;
}

private static double calculateAnnuityPaymentPeriods(
double rate, double pmt, double fv, int pmtType) {
public static final Function SLN = registerFunc(new FuncVar("SLN", 3, 3) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
double cost = params[0].getAsDouble(ctx);
double salvage = params[1].getAsDouble(ctx);
double life = params[2].getAsDouble(ctx);

// https://brownmath.com/bsci/loan.htm
// http://financeformulas.net/Number-of-Periods-of-Annuity-from-Future-Value.html
// https://accountingexplained.com/capital/tvm/fv-annuity
double result = calculateStraightLineDepreciation(cost, salvage, life);

if(pmtType == PMT_BEG_MNTH) {
fv *= (1d + rate);
return ValueSupport.toValue(result);
}
});

double v1 = Math.log(1d - (rate * fv / pmt));

double v2 = Math.log(1d + rate);
public static final Function SYD = registerFunc(new FuncVar("SYD", 4, 4) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
double cost = params[0].getAsDouble(ctx);
double salvage = params[1].getAsDouble(ctx);
double life = params[2].getAsDouble(ctx);
double period = params[3].getAsDouble(ctx);

double result = v1 / v2;
double result = calculateSumOfYearsDepreciation(
cost, salvage, life, period);

if(pmtType == PMT_BEG_MNTH) {
result -= 1d;
return ValueSupport.toValue(result);
}
});

return result;
}
public static final Function Rate = registerFunc(new FuncVar("Rate", 3, 6) {
@Override
protected Value evalVar(EvalContext ctx, Value[] params) {
double nper = params[0].getAsDouble(ctx);
double pmt = params[1].getAsDouble(ctx);
double pv = params[2].getAsDouble(ctx);
double fv = getFV(ctx, params, 3);
int pmtType = getPaymentType(ctx, params, 4);
double guess = getOptionalDoubleParam(ctx, params, 5, 0.1);

private static double calculateFutureValue(
double rate, double nper, double pmt, int pmtType) {
double result = calculateRate(nper, pmt, pv, fv, pmtType, guess);

double result = -pmt * ((Math.pow((1d + rate), nper) - 1d) / rate);
return ValueSupport.toValue(result);
}
});

if(pmtType == PMT_BEG_MNTH) {
result *= (1d + rate);
private static double calculateFutureValue(
double rate, double nper, double pmt, double pv, int pmtType) {
if (rate == 0d) {
return -1 * (pv + (nper * pmt));
}
double r1 = (pmtType == PMT_BEG_MNTH ? (rate + 1) : 1);
double p1 = Math.pow((rate + 1), nper);
return ((((1 - p1) * r1 * pmt) / rate) - (pv * p1));

return result;
}

private static double calculatePresentValue(
double rate, double nper, double pmt, int pmtType) {

if(pmtType == PMT_BEG_MNTH) {
nper -= 1d;
double rate, double nper, double pmt, double fv, int pmtType) {
if (rate == 0d) {
return -1 * ((nper * pmt) + fv);
}
double r1 = (pmtType == PMT_BEG_MNTH ? (rate + 1) : 1);
double p1 = Math.pow((rate + 1), nper);
return ((((1 - p1) / rate) * r1 * pmt) - fv) / p1;
}

double result = -pmt * ((1d - Math.pow((1d + rate), -nper)) / rate);
private static double calculateLoanPayment(
double rate, double nper, double pv, double fv, int pmtType) {

if(pmtType == PMT_BEG_MNTH) {
result -= pmt;
if (rate == 0d) {
return -1*(fv + pv) / nper;
}

return result;
double r1 = (pmtType == PMT_BEG_MNTH ? (rate + 1) : 1);
double p1 = Math.pow((rate + 1), nper);
return (fv + (pv * p1)) * rate / (r1 * (1 - p1));
}

private static double calculateLoanPayment(
double rate, double nper, double pv, int pmtType) {

double result = -(rate * pv) / (1d - Math.pow((1d + rate), -nper));
private static double calculateInterestPayment(
double rate, double per, double nper, double pv, double fv, int pmtType) {

if(pmtType == PMT_BEG_MNTH) {
result /= (1d + rate);
if((per == 1d) && (pmtType == PMT_BEG_MNTH)) {
// no inerest for pmt at beginning of month of 1st period
return 0d;
}

double pmt = calculateLoanPayment(rate, nper, pv, fv, pmtType);
double result = calculateFutureValue(
rate, per - 1, pmt, pv, pmtType) * rate;

if (pmtType == PMT_BEG_MNTH) {
result /= (1 + rate);
}
return result;
}

private static double calculateAnnuityPayment(
double rate, double nper, double fv, int pmtType) {
private static double calculatePrincipalPayment(
double rate, double per, double nper, double pv, double fv, int pmtType) {
double pmt = calculateLoanPayment(rate, nper, pv, fv, pmtType);
double ipmt = calculateInterestPayment(rate, per, nper, pv, fv, pmtType);
return (pmt - ipmt);
}

double result = -(fv * rate) / (Math.pow((1d + rate), nper) - 1d);
public static double calculateDoubleDecliningBalance(
double cost, double salvage, double life, double period, double factor) {
if (cost < 0 || ((life == 2d) && (period > 1d))) {
return 0;
}
if (life < 2d || ((life == 2d) && (period <= 1d))) {
return (cost - salvage);
}

if(pmtType == PMT_BEG_MNTH) {
result /= (1d + rate);
double v1 = ((factor * cost) / life);
if (period <= 1d) {
return Math.min(v1, cost - salvage);
}

double v2 = (life - factor) / life;
double v3 = Math.max(salvage - (cost * Math.pow(v2, period)), 0);
double result = (v1 * Math.pow(v2, period - 1d)) - v3;

return Math.max(result, 0);
}

private static double calculateStraightLineDepreciation(
double cost, double salvage, double life) {
return ((cost - salvage) / life);
}

private static double calculateSumOfYearsDepreciation(
double cost, double salvage, double life, double period) {
double result = ((cost - salvage) * (life - period + 1) * 2d) /
(life * (life + 1));
return result;
}

private static double calculateInterestPayment(
double pmt, double rate, double per, double pv, int pmtType) {
private static double calculateLoanPaymentPeriods(
double rate, double pmt, double pv, double fv, int pmtType) {

// http://www.tvmcalcs.com/index.php/calculators/apps/excel_loan_amortization
// http://financeformulas.net/Remaining_Balance_Formula.html
if (rate == 0d) {
return -1 * (fv + pv) / pmt;
}

double pvPer = per;
double fvPer = per;
if(pmtType == PMT_END_MNTH) {
pvPer -= 1d;
fvPer -= 1d;
double cr = ((pmtType == PMT_BEG_MNTH) ? (1 + rate) : 1) * pmt / rate;
double v1;
double v2;
if((cr - fv) < 0d) {
v1 = Math.log(fv - cr);
v2 = Math.log(-pv - cr);
} else {
pvPer -= 2d;
fvPer -= 1d;
v1 = Math.log(cr - fv);
v2 = Math.log(pv + cr);
}

double remBalance = (pv * Math.pow((1d + rate), pvPer)) -
// FIXME, always use pmtType of 0?
calculateFutureValue(rate, fvPer, pmt, PMT_END_MNTH);

double result = -(remBalance * rate);

return result;
return (v1 - v2) / Math.log(1 + rate);
}

private static double calculateDoubleDecliningBalance(
double cost, double salvage, double life, double factor) {
public static double calculateRate(double nper, double pmt, double pv,
double fv, int pmtType, double guess) {
double y, f = 0;
double rate = guess;
if (Math.abs(rate) < RATE_PRECISION) {
y = pv * (1 + nper * rate) + pmt * (1 + rate * pmtType) * nper + fv;
} else {
f = Math.exp(nper * Math.log(1 + rate));
y = pv * f + pmt * (1 / rate + pmtType) * (f - 1) + fv;
}
double y0 = pv + pmt * nper + fv;
double y1 = pv * f + pmt * (1 / rate + pmtType) * (f - 1) + fv;

// find root by Newton secant method
int i = 0;
double x0 = 0.0;
double x1 = rate;
while ((Math.abs(y0 - y1) > RATE_PRECISION) && (i < MAX_RATE_ITERATIONS)) {
rate = (y1 * x0 - y0 * x1) / (y1 - y0);
x0 = x1;
x1 = rate;

if (Math.abs(rate) < RATE_PRECISION) {
y = pv * (1 + nper * rate) + pmt * (1 + rate * pmtType) * nper + fv;
} else {
f = Math.exp(nper * Math.log(1 + rate));
y = pv * f + pmt * (1 / rate + pmtType) * (f - 1) + fv;
}

double result1 = cost * (factor/life);
double result2 = cost - salvage;
y0 = y1;
y1 = y;
++i;
}

return Math.min(result1, result2);
return rate;
}

private static double calculateStraightLineDepreciation(
double cost, double salvage, double life) {
return ((cost - salvage) / life);
private static double getFV(EvalContext ctx, Value[] params, int idx) {
return getOptionalDoubleParam(ctx, params, idx, 0d);
}

private static double calculateSumOfYearsDepreciation(
double cost, double salvage, double life, double period) {

double sumOfYears = (period * (period + 1)) / 2d;
double result = ((cost - salvage) * ((life + 1 - period) / sumOfYears));

return result;
private static int getPaymentType(EvalContext ctx, Value[] params, int idx) {
int pmtType = PMT_END_MNTH;
if(params.length > idx) {
pmtType = (params[idx].getAsLongInt(ctx) != PMT_END_MNTH) ?
PMT_BEG_MNTH : PMT_END_MNTH;
}
return pmtType;
}

}

+ 17
- 0
src/main/java/com/healthmarketscience/jackcess/impl/expr/FunctionSupport.java View File

@@ -21,6 +21,7 @@ import java.util.Arrays;
import com.healthmarketscience.jackcess.expr.EvalContext;
import com.healthmarketscience.jackcess.expr.EvalException;
import com.healthmarketscience.jackcess.expr.Function;
import com.healthmarketscience.jackcess.expr.LocaleContext;
import com.healthmarketscience.jackcess.expr.Value;

/**
@@ -238,4 +239,20 @@ public class FunctionSupport
}
}

public static boolean getOptionalBooleanParam(
LocaleContext ctx, Value[] params, int idx) {
if(params.length > idx) {
return params[idx].getAsBoolean(ctx);
}
return false;
}

public static double getOptionalDoubleParam(
EvalContext ctx, Value[] params, int idx, double defValue) {
if(params.length > idx) {
return params[idx].getAsDouble(ctx);
}
return defValue;
}

}

+ 94
- 61
src/test/java/com/healthmarketscience/jackcess/impl/expr/DefaultFunctionsTest.java View File

@@ -284,17 +284,17 @@ public class DefaultFunctionsTest extends TestCase

public void testFinancialFuncs() throws Exception
{
assertEquals("-9.57859403981317",
assertEquals("-9.57859403981306",
eval("=CStr(NPer(0.12/12,-100,-1000))"));
assertEquals("-9.48809500550583",
assertEquals("-9.48809500550578",
eval("=CStr(NPer(0.12/12,-100,-1000,0,1))"));
assertEquals("60.0821228537617",
assertEquals("60.0821228537616",
eval("=CStr(NPer(0.12/12,-100,-1000,10000))"));
assertEquals("59.6738656742946",
assertEquals("59.6738656742947",
eval("=CStr(NPer(0.12/12,-100,-1000,10000,1))"));
assertEquals("69.6607168935748",
assertEquals("69.6607168935747",
eval("=CStr(NPer(0.12/12,-100,0,10000))"));
assertEquals("69.1619606798004",
assertEquals("69.1619606798005",
eval("=CStr(NPer(0.12/12,-100,0,10000,1))"));

assertEquals("8166.96698564091",
@@ -312,7 +312,7 @@ public class DefaultFunctionsTest extends TestCase
eval("=CStr(PV(0.12/12,60,-100,0,1))"));
assertEquals("-1008.99231875519",
eval("=CStr(PV(0.12/12,60,-100,10000))"));
assertEquals("-964.037280348968",
assertEquals("-964.03728034897",
eval("=CStr(PV(0.12/12,60,-100,10000,1))"));

assertEquals("22.2444476849018",
@@ -327,61 +327,94 @@ public class DefaultFunctionsTest extends TestCase
eval("=CStr(Pmt(0.12/12,60,0,10000))"));
assertEquals("-121.232155296057",
eval("=CStr(Pmt(0.12/12,60,0,10000,1))"));
assertEquals("22.2444476849018",
eval("=CStr(Pmt(0.12/12,60,-1000))"));

// FIXME not working for all param combos
// assertEquals("10.0",
// eval("=CStr(IPmt(0.12/12,1,60,-1000))"));
// assertEquals("5.904184782975672",
// eval("=CStr(IPmt(0.12/12,30,60,-1000))"));
// 0
// assertEquals("",
// eval("=CStr(IPmt(0.12/12,1,60,-1000,0,1))"));
// 5.84572750...
// assertEquals("5.845727507896704",
// eval("=CStr(IPmt(0.12/12,30,60,-1000,0,1))"));
// 0
// assertEquals("",
// eval("=CStr(IPmt(0.12/12,1,60,0,10000))"));
// 40.9581521702433
// assertEquals("40.95815217024329",
// eval("=CStr(IPmt(0.12/12,30,60,0,10000))"));
// 0
// assertEquals("",
// eval("=CStr(IPmt(0.12/12,1,60,0,10000,1))"));
// 40.552625911132
// assertEquals("40.55262591113197",
// eval("=CStr(IPmt(0.12/12,30,60,0,10000,1))"));
// assertEquals("10.0",
// eval("=CStr(IPmt(0.12/12,1,60,-1000,10000))"));
// assertEquals("46.862336953218964",
// eval("=CStr(IPmt(0.12/12,30,60,-1000,10000))"));
// 0
// assertEquals("",
// eval("=CStr(IPmt(0.12/12,1,60,-1000,10000,1))"));
// 46.3983534190287
// assertEquals("46.39835341902867",
// eval("=CStr(IPmt(0.12/12,30,60,-1000,10000,1))"));

// FIXME, doesn't work for partial days
// assertEquals("1.3150684931506849",
// eval("=CStr(DDB(2400,300,10*365,1))"));
// assertEquals("40.0",
// eval("=CStr(DDB(2400,300,10*12,1))"));
// assertEquals("480.0",
// eval("=CStr(DDB(2400,300,10,1))"));
// assertEquals("22.122547200000042",
// eval("=CStr(DDB(2400,300,10,10))"));
// assertEquals("245.76",
// eval("=CStr(DDB(2400,300,10,4))"));
// assertEquals("307.20000000000005",
// eval("=CStr(DDB(2400,300,10,3))"));
// assertEquals("480.0",
// eval("=CStr(DDB(2400,300,10,0.1))"));
// 274.768033075174
// assertEquals("",
// eval("=CStr(DDB(2400,300,10,3.5))"));


assertEquals("10",
eval("=CStr(IPmt(0.12/12,1,60,-1000))"));
assertEquals("5.90418478297567",
eval("=CStr(IPmt(0.12/12,30,60,-1000))"));
assertEquals("0",
eval("=CStr(IPmt(0.12/12,1,60,-1000,0,1))"));
assertEquals("5.8457275078967",
eval("=CStr(IPmt(0.12/12,30,60,-1000,0,1))"));
assertEquals("0",
eval("=CStr(IPmt(0.12/12,1,60,0,10000))"));
assertEquals("40.9581521702433",
eval("=CStr(IPmt(0.12/12,30,60,0,10000))"));
assertEquals("0",
eval("=CStr(IPmt(0.12/12,1,60,0,10000,1))"));
assertEquals("40.552625911132",
eval("=CStr(IPmt(0.12/12,30,60,0,10000,1))"));
assertEquals("10",
eval("=CStr(IPmt(0.12/12,1,60,-1000,10000))"));
assertEquals("46.862336953219",
eval("=CStr(IPmt(0.12/12,30,60,-1000,10000))"));
assertEquals("0",
eval("=CStr(IPmt(0.12/12,1,60,-1000,10000,1))"));
assertEquals("46.3983534190287",
eval("=CStr(IPmt(0.12/12,30,60,-1000,10000,1))"));

assertEquals("12.2444476849018",
eval("=CStr(PPmt(0.12/12,1,60,-1000))"));
assertEquals("16.3402629019261",
eval("=CStr(PPmt(0.12/12,30,60,-1000))"));
assertEquals("22.0242056286156",
eval("=CStr(PPmt(0.12/12,1,60,-1000,0,1))"));
assertEquals("16.1784781207189",
eval("=CStr(PPmt(0.12/12,30,60,-1000,0,1))"));
assertEquals("-122.444476849018",
eval("=CStr(PPmt(0.12/12,1,60,0,10000))"));
assertEquals("-163.402629019261",
eval("=CStr(PPmt(0.12/12,30,60,0,10000))"));
assertEquals("-121.232155296057",
eval("=CStr(PPmt(0.12/12,1,60,0,10000,1))"));
assertEquals("-161.784781207189",
eval("=CStr(PPmt(0.12/12,30,60,0,10000,1))"));
assertEquals("-110.200029164116",
eval("=CStr(PPmt(0.12/12,1,60,-1000,10000))"));
assertEquals("-147.062366117335",
eval("=CStr(PPmt(0.12/12,30,60,-1000,10000))"));
assertEquals("-99.2079496674414",
eval("=CStr(PPmt(0.12/12,1,60,-1000,10000,1))"));
assertEquals("-145.60630308647",
eval("=CStr(PPmt(0.12/12,30,60,-1000,10000,1))"));

assertEquals("1.31506849315068",
eval("=CStr(DDB(2400,300,10*365,1))"));
assertEquals("40",
eval("=CStr(DDB(2400,300,10*12,1))"));
assertEquals("480",
eval("=CStr(DDB(2400,300,10,1))"));
assertEquals("22.1225472000002",
eval("=CStr(DDB(2400,300,10,10))"));
assertEquals("245.76",
eval("=CStr(DDB(2400,300,10,4))"));
assertEquals("307.2",
eval("=CStr(DDB(2400,300,10,3))"));
assertEquals("480",
eval("=CStr(DDB(2400,300,10,0.1))"));
assertEquals("274.768033075174",
eval("=CStr(DDB(2400,300,10,3.5))"));

assertEquals("2250",
eval("=CStr(SLN(30000,7500,10))"));
assertEquals("1000",
eval("=CStr(SLN(10000,5000,5))"));
assertEquals("1142.85714285714",
eval("=CStr(SLN(8000,0,7))"));

assertEquals("4090.90909090909",
eval("=CStr(SYD(30000,7500,10,1))"));
assertEquals("409.090909090909",
eval("=CStr(SYD(30000,7500,10,10))"));

assertEquals("-1.63048347266756E-02",
eval("=CStr(Rate(3,200,-610,0,-20,0.1))"));
assertEquals("7.70147248820175E-03",
eval("=CStr(Rate(4*12,-200,8000))"));
assertEquals("",
eval("=CStr(Rate(60,93.22,5000,0.1/12))"));
}

}

Loading…
Cancel
Save