如何在 PHP 中实现 MS Excel 的“IRR()”公式?
我尝试了本页中提到的算法,但结果不准确,而且速度非常慢。
经过一段时间的研究,我最终得到了下面复制的函数。
它是基于这个问题。
function IRR($investment, $flow, $precision = 0.001) {
$min = 0;
$max = 1;
$net_present_value = 1;
while(abs($net_present_value - $investment) > $precision) {
$net_present_value = 0;
$guess = ($min + $max) / 2;
foreach ($flow as $period => $cashflow) {
$net_present_value += $cashflow / (1 + $guess) ** ($period + 1);
}
if ($net_present_value - $investment > 0) {
$min = $guess;
} else {
$max = $guess;
}
}
return $guess * 100;
}
这是根据托马斯的答案修改的。它通过一开始的检查来停止无限循环,以确保现金流大于投资。我还提高了精度并最多运行 20 次。
private function IRR($investment, $flow, $precision = 0.000001) {
if (array_sum($flow) < $investment):
return 0;
endif;
$maxIterations = 20;
$i =0;
if (is_array($flow)):
$min = 0;
$max = 1;
$net_present_value = 1;
while ((abs($net_present_value - $investment) > $precision)&& ($i < $maxIterations)) {
$net_present_value = 0;
$guess = ($min + $max) / 2;
foreach ($flow as $period => $cashflow) {
$net_present_value += $cashflow / (1 + $guess) ** ($period + 1);
}
if ($net_present_value - $investment > 0) {
$min = $guess;
} else {
$max = $guess;
}
$i++;
}
return $guess * 100;
else:
return 0;
endif;
}
function IRR($values, $guess = 0.1) {
$maxIterations = 100;
$tolerance = 0.00001;
$count = count($values);
$positive = false;
$negative = false;
for ($i = 0; $i < $count; $i++) {
if ($values[$i] > 0) {
$positive = true;
} else {
$negative = true;
}
}
if (!$positive || !$negative) {
return null;
}
$guess = ($guess == 0) ? 0.1 : $guess;
for ($i = 0; $i < $maxIterations; $i++) {
$npv = 0;
$dnpv = 0;
for ($j = 0; $j < $count; $j++) {
$npv += $values[$j] / pow(1 + $guess, $j);
$dnpv -= $j * $values[$j] / pow(1 + $guess, $j + 1);
}
$newGuess = $guess - $npv / $dnpv;
if (abs($newGuess - $guess) < $tolerance) {
return $newGuess;
}
$guess = $newGuess;
}
enter code here
return null;
}
对于需要使用
IRR
但没有实现的人,我们可以从这个链接中的PhpSpreadsheet
使用它
这是我使用此函数的 Laravel 代码
use PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\Variable\Periodic;
$data = array(-1000000, -1000000, -1000000, 4000000);
$irr_result = Periodic::rate($data);