Class: Flt::Solver::TVM

Inherits:
Object
  • Object
show all
Defined in:
lib/solver/tvm.rb

Overview

A Time-Value-of-Money solver

Example:

tvm = TVM.new(Tolerance(3, :decimals), Float.context)
puts tvm.solve(:t=>240, :m0=>10000, :m=>0, :i=>3, :p=>12).inspect # => {:pmt=>-55.45975978539105}

Instance Method Summary collapse

Constructor Details

#initialize(tol, context = Float.context) ⇒ TVM

Returns a new instance of TVM.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/solver/tvm.rb', line 11

def initialize(tol, context=Float.context)
  @context = context
  @var_descriptions = {
    :m=>'money value at time t',
    :t=>'time',
    :m0=>'initial money value',
    :pmt=>'payment per time unit',
    :i=>'percent interest per year',
    :p=>'number of time units per year'
  }
  @vars = @var_descriptions.keys
  vars = @vars
  tvm = self
  @solver = PSolver.new(context, tol) do |m, t, m0, pmt, i, p|
    tvm.equation(m, t, m0, pmt, i, p)
  end
  @solver.default_guesses = 1,2
  @one = @context.Num(1)
end

Instance Method Details

#equation(m, t, m0, pmt, i, p) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/solver/tvm.rb', line 55

def equation(m, t, m0, pmt, i, p)
  i /= 100
  i /= p
  n = -t
  k = @context.exp(lnp1(i)*n) # (i+1)**n
  # Equation: -m*k = m0 + pmt*(1-k)/i
  if i == 0 # TODO: with tolerance?
    m0 + pmt*t + m # k == 1
  else
    m0 + pmt*(@one-k)/i + m*k
  end
end

#lnp1(x) ⇒ Object

ln(x+1)



69
70
71
72
# File 'lib/solver/tvm.rb', line 69

def lnp1(x)
  v = x + 1
  (v == 1) ? x : (x*@context.ln(v) / (v - 1))
end

#parameter_descriptionsObject



31
32
33
# File 'lib/solver/tvm.rb', line 31

def parameter_descriptions
  @var_descriptions
end

#solve(parameters) ⇒ Object

Parameters: (using cash-flow sign convention)

:t time in periods
:p number of periods per year
:i percent yearly interest rate
:pmt payment per period
:m0 initial value
:m value at time :t


42
43
44
45
46
47
48
49
# File 'lib/solver/tvm.rb', line 42

def solve(parameters)
  nil_vars = @vars.select{|var| parameters[var].nil?}
  raise "Too many unknowns" if nil_vars.size>1
  raise "Nothing to solve" if nil_vars.empty?
  var = nil_vars.first
  # determine sensible initial value? => parameters[var] = initial_value
  {var=>@solver.root(var, parameters)}
end

#value(parameters) ⇒ Object



51
52
53
# File 'lib/solver/tvm.rb', line 51

def value(parameters)
  @solver.equation_value(paramters)
end