1

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...

4

1 回答 1

3

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:

enter image description here

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.
于 2013-09-17T02:44:53.227 回答