I'm having this issue as well - one example works and the next doesn't
Here's my code
define('FINANCIAL_MAX_ITERATIONS', 128); define('FINANCIAL_PRECISION', 1.0e-08);
function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
$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;
}
$rate = abs(round($rate*100,5));
return $rate;
} // function RATE()
And some examples RATE(60,-1338.88,274775,0,0,0.03200); // works - returns 3.40321
RATE(60,-2415.44,448925,0,0,0.04150); // doesn't work - returns 3.16288 when it should be 4.32
Any ideas? I've tried 4-5 pieces of code that all look similar and this one seems to give me the best results, even though they aren't consistent. I feel the code works, because how could it give me one correct result and the rest aren't??
p.s: I'm a newb at posting here, I've been reading this site for about 5 years though - hope I was thorough enough ;)
Similar question, with good info, but still doesn't solve my problem
Secant method of iteration into PHP
Another good question, but still doesn't do what I need...
Calculating interest rate in PHP
The newest version, found here: https://phpexcel.codeplex.com/SourceControl/latest#trunk/Classes/PHPExcel/Calculation/Financial.php of the RATE() function is not working for me.
I created a new PHP page, cut out that specific function and tested it but with a big difference in output to Excel.
<?php
define('FINANCIAL_MAX_ITERATIONS', 128);
define('FINANCIAL_PRECISION', 1.0e-08);
function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
//$nper = (int) PHPExcel_Calculation_Functions::flattenSingleValue($nper);
//$pmt = PHPExcel_Calculation_Functions::flattenSingleValue($pmt);
//$pv = PHPExcel_Calculation_Functions::flattenSingleValue($pv);
//$fv = (is_null($fv)) ? 0.0 : PHPExcel_Calculation_Functions::flattenSingleValue($fv);
//$type = (is_null($type)) ? 0 : (int) PHPExcel_Calculation_Functions::flattenSingleValue($type);
//$guess = (is_null($guess)) ? 0.1 : PHPExcel_Calculation_Functions::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()
$nper = 180;
$pmt = 6729.045954705334;
$pv = -400000;
$test = RATE($nper, $pmt, $pv);
?>
The function call above yields: 0.008774735218308
Excel 2013:
=RATE(180;6729,04595470533;-400000)
Yields: 0,0158263127885
Any ideas?
I set some static values and checked if the value was within. In that case i use one $guess otherwise i use a different $guess. In Excel it works regardless of $guess and passed in values.
The last time I posted this question I got spanked by you guys because it was not clear what I was asking…I blew it, sorry. So let me try again.
I am trying to mimic the Excel Rate function in PHP. I did obtain some code that does function correctly for other Excel functions but is erratic with the Rate function.
There are two steps involved:
1. Calculate the payment
PMT($rate/12, $nper*12, $pv, $fv, $type)
$rate = .08 // interest rate
$nper = 30 // loan term in years
$pv = (100000 + 4000) // loan amt + loan fees
$fv = 0
$type = 0
PMT = -$763.12 // my PHP result and Excel result
2. Calculate the rate
RATE($nper, $pmt, $pv, $fv, $type, $guess)
$nper = 360
$pmt = -763.12 // must remain negative in formula
$pv = 100000 // loan amt only (excludes loan fees)
$fv = 0
$type = 0
$guess = 0.09 // excel default is 0.10 but it doesn’t work so I lowered
RATE = 0.00701507 // Excel result (correct)
RATE = 0.0048541 // my PHP result (incorrect)
Okay, here’s the catch. When the interest rate is changed from 0.08 to 0.07 the result is a lower PMT of $691.91 which then makes both RATE’s almost identical.
RATE = 0.0061609831 // Excel
RATE = 0.0061609269 // my PHP
My question is this: Have any of you used the Excel RATE function in PHP? Were you successful then please share your formula with me. Or did you encounter this problem and find a way to fix it then please tell me what you did. Thank you.
Check out this solution:
<?php
define('FINANCIAL_MAX_ITERATIONS', 128);
define('FINANCIAL_PRECISION', 1.0e-08);
function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
$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;
$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 (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;
}
$nper = 60;
$pmt = 93.22;
$pv = -5000;
$fv = 0;
$type = 0;
$guess = 0.09;
var_dump(RATE($nper, $pmt, $pv, $fv, $guess));
?>
Do you know that in PHP (and some other languages) floor((0.1+0.7)*10) equals 7 and not 8?
I'm pretty sure it is all because of this "nuance": http://php.net/manual/en/language.types.float.php
Instead of using float numbers in your calculations, consider using BCMath functions, which guarantee high precision as all numbers are represented as strings: http://php.net/manual/en/book.bc.php
Related: PHP - Floating Number Precision
the code is below, I am trying to call the "RATE" function.
RATE(120, -271.09, 20000) is how it is used in excel, with this code I think it should work the same.
What am I doing wrong? how can I get it to work
My code is here: http://pastebin.com/AbKqc8g1
The code posted at your URL is defining a class structure for whatever your application is. Therefore you would need to create an instance of the class before you call any functions within that class. For example, the following should help get you started:
$financial = new Financial;
$rate = $financial->RATE(120,-271.09,20000);
print_r($rate);
Quite how you are trying to use this within your application is unclear but using the above principle you should start to see some output.
You have to make an instance of this class and then run the RATE method.
<?php
require("class.Financial.php");
$fin = new Financial();
$result = $fin->RATE(120, -271.09, 20000);
echo $result;
?>
# Scott M
here is the function:
/**
* RATE
*
**/
function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1)
{
$rate = $guess;
$i = 0;
$x0 = 0;
$x1 = $rate;
if (abs($rate) < FINANCIAL_ACCURACY) {
$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
while ((abs($y0 - $y1) > FINANCIAL_ACCURACY) && ($i < FINANCIAL_MAX_ITERATIONS))
{
$rate = ($y1 * $x0 - $y0 * $x1) / ($y1 - $y0);
$x0 = $x1;
$x1 = $rate;
if (abs($rate) < FINANCIAL_ACCURACY) {
$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;
}
I'm working on converting a mortgage calculator in PHP, but I don't necessarily need a PHP solution. I'm looking for the logic needed to replicate the Excel RATE function. I've found a solution which uses bisection, and if worse comes to worse, I use that.
I know someone out there in the interwebs world has knowledge of such a function, so I'd love to have an easy answer instead of creating a solution from scratch.
References:
http://office.microsoft.com/en-us/excel-help/rate-HP005209232.aspx
http://en.wikipedia.org/wiki/Newton%27s_method
Thanks
Implementation of the MS Excel RATE() function using the secant method (a finite difference approximation of Newton's method) taken from PHPExcel:
define('FINANCIAL_MAX_ITERATIONS', 128);
define('FINANCIAL_PRECISION', 1.0e-08);
function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
$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 (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()
I tried to use the code above, but the results simply aren´t the same as Excel (or Google Spreadsheet).
I dont know if you need to implement this function yet, but in any case, I looked at how this algorithm was built and even though I was not able to access the excel source code (or the google worksheet) I found that this is not a simple calculation. About this math, more can be read here:
https://brownmath.com/bsci/loan.htm#Eq8
The function, in PHP, may be something like this:
function rate($nprest, $vlrparc, $vp, $guess = 0.25) {
$maxit = 100;
$precision = 14;
$guess = round($guess,$precision);
for ($i=0 ; $i<$maxit ; $i++) {
$divdnd = $vlrparc - ( $vlrparc * (pow(1 + $guess , -$nprest)) ) - ($vp * $guess);
$divisor = $nprest * $vlrparc * pow(1 + $guess , (-$nprest - 1)) - $vp;
$newguess = $guess - ( $divdnd / $divisor );
$newguess = round($newguess, $precision);
if ($newguess == $guess) {
return $newguess;
} else {
$guess = $newguess;
}
}
return null;
}
For Laravel use the same function but you remove define
define('FINANCIAL_MAX_ITERATIONS', 128);
define('FINANCIAL_PRECISION', 1.0e-08);
and financial_max_iterations = 20; -> same excel
The code is:
function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) {
$financial_max_iterations = 20;
$financial_precision = 0.00000008;
$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 (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;
}
it worked for me
TL;DR: Here's a SQL Server version. It doesn't work for some values, and the PHP code above will probably fail for the same values.
LONG ANSWER: I needed a RATE function for SQL Server. Using the PHPExcel answer above, and using https://charlottecredittechnology.blogspot.com/2013/05/sql-2008-excel-like-rate-function-part.html I wrote a SQL Server scalar function:
ALTER function [dbo].[Rate](
#nper integer, #pmt float, #pv float, #fv float, #type bit = 0, #guess float = 0.1
) returns numeric(38,10) as
/*
Calculate the effective interest rate of a sequence of regular payments.
*/
begin
declare #returns numeric(38,10) = 0;
if #type is null set #type = 0;
declare #i integer;
declare #rate float = #guess;
declare #FINANCIAL_MAX_ITERATIONS integer = 100;
declare #FINANCIAL_PRECISION float = 0.0000001;
declare #y float, #y0 float, #y1 float, #f float, #x0 float, #x1 float;
set #rate = #guess;
if Abs(#rate) < #FINANCIAL_PRECISION
begin
set #f = 0;
set #y = #pv * (1+#nper*#rate) + #pmt * (1+#rate*#type) * #nper + #fv;
end
else
begin
set #f = Exp(#nper * Log(1+#rate));
set #y = #pv * #f + #pmt * (1/#rate + #type) * (#f-1) + #fv;
end;
set #y0 = #pv + #pmt * #nper + #fv;
set #y1 = #pv * #f + #pmt * (1/#rate + #type) * (#f-1) + #fv;
-- Newton secant method.
set #i = 0;
set #x0 = 0;
set #x1 = #rate;
while Abs(#y0-#y1) > #FINANCIAL_PRECISION and #i < #FINANCIAL_MAX_ITERATIONS
begin
set #rate = (#y1 * #x0 - #y0 * #x1) / (#y1-#y0);
set #x0 = #x1;
set #x1 = #rate;
if Abs(#rate) < #FINANCIAL_PRECISION
begin
set #y = #pv * (1+#nper*#rate) + #pmt * (1+#rate*#type) * #nper + #fv;
end
else
begin
set #f = Exp(#nper * Log(1+#rate));
set #y = #pv * #f + #pmt * (1/#rate + #type) * (#f-1) + #fv;
end;
set #y0 = #y1;
set #y1 = #y;
set #i = #i + 1;
end;
return Convert(numeric(38,10), #rate);
end;
Unfortunately it does not always work. Here's the results of some tests I put together and checked using Excel:
-- (1) OK
select dbo.RATE(4*12, -200, 8000, 0, default, default) * 12 -- SQL formula
0.0924 (9.24%) -- SQL result
=RATE(4*12, -200, 8000, 0) * 12 -- Excel formula
9.24% -- Excel result
-- (2) OK
select dbo.RATE(12, -1000, 12000, 0, default, default) * 12 -- SQL formula
0 (0%) -- SQL result
=RATE(12, -1000, 12000, 0) * 12 -- Excel formula
0% -- Excel result
-- (3) OK
select dbo.RATE(30, -400, 4000, 0, 1, default) -- SQL formula
0.10496 (10.496%) -- SQL result
=RATE(30, -400, 4000, 0, 1) -- Excel formula
10.4964% -- Excel result
-- (4) OK
select dbo.RATE(120, 28.1, -2400, 0, default, default) -- SQL formula
0.0059905810 (0.599%) -- SQL result
=RATE(120, 28.1, -2400, 0) -- Excel formula
0.5991% -- Excel result
-- (5) OK
select dbo.RATE(10, -1000, 10000, -10000, default, default) -- SQL formula
0.1 (10%) -- SQL result
=RATE(10, -1000, 10000, -10000) -- Excel formula
10% -- Excel result
-- (6) WRONG ANSWER (unless you set #guess to 0.01)
select dbo.RATE(475, -1022.93, 272779.21, 0, default, default) -- SQL formula
0 -- SQL result
=RATE(475, -1022.93, 272779.21, 0, 0) -- Excel formula
0.2716% -- Excel result
-- (7) ERROR
select dbo.RATE(252, -29002.85, 2500000, 0, default, default) -- SQL formula
invalid floating point operation -- SQL result
=RATE(252, -29002.85, 2500000, 0) -- Excel formula
1.0833% -- Excel result
-- (8) OK
select dbo.RATE(24, -46.14, 1000, 0, default, default) -- SQL formula
0.0083244385 (0.83244%) -- SQL result
=RATE(24, -46.14, 1000, 0) -- Excel formula
0.8324% -- Excel result
Tests (7) and (8) were taken from RATE Function from EXCEL in Swift providing different results and look for the answer using the Newton-Raphson method.