Cálculo VPL e TIR

Cálculo VPL e TIR (método Bisseção)

by Alexandre Gomiero de Oliveira

HTML

<form id="calculos">
<div class="fundo cor_azul">
Montante em cada período:
<input id="mont" type="text" size="52" placeholder="ex: -30000.02, 10000.5, 5000, ..." value="" />
<ul style="font-size: 11pt">
    <li>Valores separados por vírgulas: <code>-3000,350.20, ...</code></li>
    <li>não pode haver inversão de sinal após o primeiro valor</li>
</ul>
</div><br />
<div class="fundo  cor_azul">
Taxa de desconto: <input id="desc" type="text" size="12" placeholder="ex: 0.05 para 5%" value="" /><br />
</div><br />
<input id="calc" type="button" name="name" value="calcular" onclick="calcula();">
<input id="reset" type="reset" name="name" value="limpar" />
<br /><br />
<div class="fundo cor_verde">
<table>
<tr>
    <td>VPL ($):</td><td><input id="result_vpl" type="text" class="alinhamento" size="8" align="right" readonly="true" /></td>
</tr><tr>
    <td>TIR (%):</td><td><input id="result_tir" type="text" class="alinhamento" size="8" form="calculos" readonly="true" /></td>
</tr>
</table>
</div>
</form>

CSS

body {
    font-family: sans-serif;
}
.fundo {
    border-radius: 5px;
    padding: 15px;
    width: 400px;
}
.cor_azul {
    background: rgb(230, 230, 250);
}
.cor_verde {
    background: rgb(220, 235, 220);
}
.alinhamento {
    text-align: right;
}
code {
  background-color: lightgray;font-size: 10pt;
}

JavaScript

"use strict";

/*
 * Calcula o Valor Presente Líquido para
 * um período constante sem inversão de sinal
 *
 * @taxa => taxa de desconto
 * @montantes => vetor com os valores com os recebimentos ou pagamentos
 * -100,30,30,10,10,10,10
 */
function vpl(taxa, montantes)
{
    var ret = montantes[0];
    
    for (var i=1; i<montantes.length; i++)
        ret += montantes[i] / Math.pow( (1.0 + taxa), i);
    return ret;
}

/*
 * Calcula a Taxa Interna de Retorno
 *
 * @montantes => vetor com os valores
 */
function tir(montantes)
{	
    var ret = -1000000000.0;
    var juros_inicial = -1.0;
    var juros_medio = 0.0;
    var juros_final = 1.0;
    var vpl_inicial = 0.0;
    var vpl_final = 0.0;
    var vf = 0.0;
    var erro = 1e-5;
    
		for (var i=0; i<100; i++) {
    	vpl_inicial = vpl(juros_inicial, montantes);
      vpl_final = vpl(juros_final, montantes);
      if (sinal(vpl_inicial) != sinal(vpl_final))
      	break;
      juros_inicial -= 1.0;
      juros_final += 1.0;
    };
    var count = 0;
    for (;;) {
      // Busca por Bisseção
      var juros_medio = (juros_inicial + juros_final) / 2.0;        
      var vpl_medio = vpl(juros_medio, montantes)
        
      if (Math.abs(vpl_medio) <= erro) {
          // Resultado foi encontrado
          return juros_medio*100.0;
      };
      if (sinal(vpl_inicial) == sinal(vpl_medio)) {
      		juros_inicial = juros_medio;
          vpl_inicial = vpl(juros_medio, montantes);          
      } else {
      		juros_final = juros_medio;
          vpl_final = vpl(juros_medio, montantes);          
      };
      if (++count > 10000)
      	throw "looping inválido";
    };
    return ret;
};

function sinal(x) {
	return x < 0.0 ? -1 : 1;
}

function calcula()
{
    var taxa_campo = document.getElementById('desc');
    var montantes_campo = document.getElementById('mont');
    var result_vpl_campo = document.getElementById('result_vpl');
    var result_tir_campo = document.getElementById('result_tir');
    var taxa...