问题
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...
回答1:
You can use "Binary Search" instead of "Newton Raphson method".
function rate($month, $payment, $amount)
{
// make an initial guess
$error = 0.0000001; $high = 1.00; $low = 0.00;
$rate = (2.0 * ($month * $payment - $amount)) / ($amount * $month);
while(true) {
// check for error margin
$calc = pow(1 + $rate, $month);
$calc = ($rate * $calc) / ($calc - 1.0);
$calc -= $payment / $amount;
if ($calc > $error) {
// guess too high, lower the guess
$high = $rate;
$rate = ($high + $low) / 2;
} elseif ($calc < -$error) {
// guess too low, higher the guess
$low = $rate;
$rate = ($high + $low) / 2;
} else {
// acceptable guess
break;
}
}
return $rate * 12;
}
var_dump(rate(60, 1000, 20000));
// Return 0.56138305664063, which means 56.1383%
The "Binary Search" and "Newton Raphson method" are basically a guess method. It make an initial guess and improve their guess over time until it meet the acceptable guess. "Newton Raphson method" usually is faster than "binary search" because it has a better "improving guess" strategy.
The concept is simple:
We want to know r
which is the interest rate. We know, N, C, and P. We don't know what is r
, but let just guess any number between 0.00 to 1.00. (In this case, we assume that the interest rate cannot be over 100%).
- Step 1: Make a random guess
- Step 2: Plug the rate into formula,
- Step 3: Check if the guess is too low, higher the guess, goto Step 2
- Step 4: Check if the guess is too high, lower the guess, goto Step 2
- Step 5: We got acceptable rate.
- Step 6:
r
is monthly rate. Multiple by 12 to get annually rate.
来源:https://stackoverflow.com/questions/18840190/calculating-interest-rate-in-php