I'm trying to calculate the interest on a loan given the initial loan amount, the number of repayments and the amount of repayments. I can't seem to get a close enough figure using a basic formula, so I've been trying to use the Newton Raphson method, which can be seen in use here: http://www.efunda.com/formulae/finance/loan_calculator.cfm (this is the exact functionality I am looking to implement)
I've tried using the RATE() function from PHPExcel, however I'm not getting a correct output for my inputs, i.e. the interest rate comes back as 0.1% or similar (when in fact it's more like 5.75%)
This is the relevant code for PHPExcel's
/** FINANCIAL_MAX_ITERATIONS */
define('FINANCIAL_MAX_ITERATIONS', 128);
/** FINANCIAL_PRECISION */
define('FINANCIAL_PRECISION', 1.0e-08);
/**
* Convert an array to a single scalar value by extracting the first element
*
* @param mixed $value Array or scalar value
* @return mixed
*/
function flattenSingleValue($value = '') {
while (is_array($value)) {
$value = array_pop($value);
}
return $value;
}
/**
* RATE
*
* Returns the interest rate per period of an annuity.
* RATE is calculated by iteration and can have zero or more solutions.
* If the successive results of RATE do not converge to within 0.0000001 after 20 iterations,
* RATE returns the #NUM! error value.
*
* Excel Function:
* RATE(nper,pmt,pv[,fv[,type[,guess]]])
*
* @access public
* @category Financial Functions
* @param float nper The total number of payment periods in an annuity.
* @param float pmt The payment made each period and cannot change over the life
* of the annuity.
* Typically, pmt includes principal and interest but no other
* fees or taxes.
* @param float pv The present value - the total amount that a series of future
* payments is worth now.
* @param float fv The future value, or a cash balance you want to attain after
* the last payment is made. If fv is omitted, it is assumed
* to be 0 (the future value of a loan, for example, is 0).
* @param integer type A number 0 or 1 and indicates when payments are due:
* 0 or omitted At the end of the period.
* 1 At the beginning of the period.
* @param float guess Your guess for what the rate will be.
* If you omit guess, it is assumed to be 10 percent.
* @return float
**/
function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
$nper = (int) flattenSingleValue($nper);
$pmt = flattenSingleValue($pmt);
$pv = flattenSingleValue($pv);
$fv = (is_null($fv)) ? 0.0 : flattenSingleValue($fv);
$type = (is_null($type)) ? 0 : (int) flattenSingleValue($type);
$guess = (is_null($guess)) ? 0.1 : flattenSingleValue($guess);
$rate = $guess;
if (abs($rate) < FINANCIAL_PRECISION) {
$y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
} else {
$f = exp($nper * log(1 + $rate));
$y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
}
$y0 = $pv + $pmt * $nper + $fv;
$y1 = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
// find root by secant method
$i = $x0 = 0.0;
$x1 = $rate;
while ((abs($y0 - $y1) > FINANCIAL_PRECISION) && ($i < FINANCIAL_MAX_ITERATIONS)) {
$rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
$x0 = $x1;
$x1 = $rate;
if (($nper * abs($pmt)) > ($pv - $fv))
$x1 = abs($x1);
if (abs($rate) < FINANCIAL_PRECISION) {
$y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv;
} else {
$f = exp($nper * log(1 + $rate));
$y = $pv * $f + $pmt * (1 / $rate + $type) * ($f - 1) + $fv;
}
$y0 = $y1;
$y1 = $y;
++$i;
}
return $rate;
} // function RATE()
My input to the function is:
RATE(60, 1100, 50000); // Outputs 0.00420298759161
RATE(60, -1100, 50000); // Outputs 0.00959560344752
RATE(60, 1100, 66000); // Outputs -1.05036370955
Where 60 is the number of months in 5 years, 1100 or -1100 is the amount repaid each month, and 50,000 is the total borrowed.
I'm not a mathematician, the above function doesn't make much sense to me, but my reading says this is the best way to calculate the rate. Hopefully I'm just making a silly mistake...