Skip to content
Open
3 changes: 2 additions & 1 deletion .rvmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
rvm_gemset_create_on_use_flag=1
rvm gemset use monetico
rvm gemset use monetico
rvm use 1.9.3@monetico --create
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ source 'http://rubygems.org'

# Specify your gem's dependencies in monetico.gemspec
gemspec

group :development, :test do
end
36 changes: 36 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
# encoding: utf-8

#!/usr/bin/env rake
require "bundler/gem_tasks"

require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
require 'rake'

require 'rspec/core'
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |spec|
spec.pattern = FileList['spec/**/*_spec.rb']
end

RSpec::Core::RakeTask.new(:rcov) do |spec|
spec.pattern = 'spec/**/*_spec.rb'
spec.rcov = true
end

task :default => :spec

require 'rake/rdoctask'
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""

rdoc.rdoc_dir = 'rdoc'
rdoc.title = "simple_metar_parser #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
35 changes: 35 additions & 0 deletions lib/excel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Excel
def pmt(rate, nper, pv, fv=0, type=0)
((-pv * pvif(rate, nper) - fv ) / ((1.0 + rate * type) * fvifa(rate, nper)))
end

def ipmt(rate, per, nper, pv, fv=0, type=0)
p = pmt(rate, nper, pv, fv, 0);
ip = -(pv * pow1p(rate, per - 1) * rate + p * pow1pm1(rate, per - 1))
(type == 0) ? ip : ip / (1 + rate)
end

def ppmt(rate, per, nper, pv, fv=0, type=0)
p = pmt(rate, nper, pv, fv, type)
ip = ipmt(rate, per, nper, pv, fv, type)
p - ip
end

protected

def pow1pm1(x, y)
(x <= -1) ? ((1 + x) ** y) - 1 : Math.exp(y * Math.log(1.0 + x)) - 1
end

def pow1p(x, y)
(x.abs > 0.5) ? ((1 + x) ** y) : Math.exp(y * Math.log(1.0 + x))
end

def pvif(rate, nper)
pow1p(rate, nper)
end

def fvifa(rate, nper)
(rate == 0) ? nper : pow1pm1(rate, nper) / rate
end
end
23 changes: 11 additions & 12 deletions lib/monetico.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
require "bigdecimal"
require "excel"
require "monetico/money_array"
require "monetico/calculable"
require "monetico/loan"
require "monetico/version"

class Float
def big; BigDecimal(self.to_s); end
include Monetico::Calculable
end

def round_to(x)
(self * 10**x).ceil.to_f / 10**x
end
class BigDecimal
include Monetico::Calculable
end

def round_down(x)
if self >= 0
(self * 10**x).floor.to_f / 10**x
else
-((-self * 10**x).floor.to_f / 10**x)
end
end
class Fixnum
# usable for #big
include Monetico::Calculable
end

module Monetico
# Your code goes here...
end
25 changes: 25 additions & 0 deletions lib/monetico/calculable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Monetico
module Calculable
DEF_PREC = 2

def big
BigDecimal(self.to_s);
end

alias_method :to_big, :big

def round_to(x = DEF_PREC)
(self * 10**x).ceil.to_f / 10**x
end

alias_method :round_up, :round_to

def round_down(x = DEF_PREC)
if self >= 0
(self * 10**x).floor.to_f / 10**x
else
-((-self * 10**x).floor.to_f / 10**x)
end
end
end
end
198 changes: 178 additions & 20 deletions lib/monetico/loan.rb
Original file line number Diff line number Diff line change
@@ -1,57 +1,215 @@
module Monetico
class Loan
include Excel

CADENCE = {
monthly: 12,
weekly: 52,
}

def initialize(amount, interest_rate, no_installments, cadence=:monthly, kind=:desc)
# methods
# <ex. capital_real> - rounded
# <ex. capital_real_real> - float, used in internal calculations
# <ex. calculate_capital> - calculate for the first time, float

def initialize(amount, interest_rate, no_installments, cadence = :monthly, kind = :desc)
@amount = amount.big
@interest_rate = interest_rate.big / CADENCE[cadence]
@interest_rate = interest_rate.big / CADENCE[cadence]
@no_installments = no_installments
@kind = kind
end

def capital
private

# Capital - amount / number of installments
def capital
round capital_real
end

public :capital

def capital_real
@capital = calculate_capital if @capital.nil?
@capital
end

def calculate_capital
@amount / @no_installments
end

# Total amount of interests
def total_interests
round total_interests_real
end

public :total_interests

def total_interests_real
@total_interests = calculate_total_interests if @total_interests.nil?
@total_interests
end

def calculate_total_interests
if const?
par = (1 + @interest_rate) ** @no_installments
payback_amount = @amount * @interest_rate * par / (par - 1)

payback_amount * @no_installments - @amount
payback_amount = @amount * @interest_rate * par / (par - 1)
payback_amount * @no_installments - @amount
else
0.5.big * @interest_rate * @no_installments * (@amount + capital)
0.5.big * @interest_rate * @no_installments * (@amount + capital_real)
end
end

# Interests for period/installment
def interests(idx)
(@amount - (idx - 1) * capital) * @interest_rate
round interests_real(idx)
end

public :interests

def interests_real(idx)
@interests = Array.new if @interests.nil?
@interests[idx] = calculate_interests(idx) if @interests[idx].nil?
@interests[idx]
end

# little refactoring
alias_method :interests_for_period, :interests

def calculate_interests(idx)
if const?
ipmt(@interest_rate, idx, @no_installments, @amount).abs
else
(@amount - (idx - 1) * capital_real) * @interest_rate
end
end

# Monthly payment
def monthly_payment
round monthly_payment_real
end

public :monthly_payment

def monthly_payment_real
@monthly_payment = calculate_monthly_payment if @monthly_payment_float.nil?
@monthly_payment
end

def calculate_monthly_payment
pmt(@interest_rate, @no_installments, @amount).abs
end

# Capital for period
def capital_for_period(idx)
round capital_for_period_real(idx)
end

public :capital_for_period

def capital_for_period_real(idx)
# only for desc
return capital_real if not const?

# only for const
@capitals = Array.new if @capitals.nil?
@capitals[idx] = calculate_capital_for_period(idx) if @capitals[idx].nil?
@capitals[idx]
end

def calculate_capital_for_period(idx)
ppmt(@interest_rate, idx, @no_installments, @amount).abs
end

# Amount for period
def amounts(idx)
capital_for_period(idx) + interests(idx)
end

public :amounts

def const?
@kind == :const
end

def payback(range)
from = range.begin
to = range.end
public 'const?'

# round money value
def round(v)
MoneyArray.money_round_value(v)
end

# Loan debug
def to_s
s = "Loan\n"
table = payback_all
table.each_with_index do |t, i|
s += "#{t[:no]}: #{t[:amount].to_f};\t#{t[:balance].to_f};\t#{t[:capital].to_f};\t#{t[:interests].to_f}\n"
end
return s
end

public :to_s

# Paybacks items
# all payback items for all
def calculate_payback
from = 1
to = @no_installments
range = (from..to)

current_amount = 0.0

if const?
par = (1 + @interest_rate) ** @no_installments

range.map do |n|
{ no: n, interests: interests(n), amount: @amount * @interest_rate * par / (par - 1) }
res = range.map do |n|
current_amount += monthly_payment_real
{ no: n, interests: interests(n), amount: monthly_payment_real, capital: capital_for_period(n), balance: @amount + total_interests_real - current_amount }
end
else
range.map do |n|
{ no: n, interests: interests(n), amount: capital + interests(n) }
res = range.map do |n|
amount = capital_real + interests(n)
current_amount += amount
{ no: n, interests: interests(n), amount: amount, capital: capital_real, balance: @amount + total_interests_real - current_amount }
end
end
end
@paybacks = res
@paybacks_round = round_paybacks(res)
end

def const?
@kind == :const
def payback_real(range)
calculate_payback if @paybacks.nil?
@paybacks[range]
end

def payback(range)
calculate_payback if @paybacks.nil?
@paybacks_round[range]
end
private :const?

def payback_all
calculate_payback if @paybacks.nil?
@paybacks_round
end

public :payback_all

public :payback


def round_paybacks(res)
[:interests, :amount, :capital, :balance].each do |k|
tmp = MoneyArray.factory(res.collect { |r| r[k] })
tmp = tmp.money_round

res.each_with_index do |r, i|
r[k] = tmp[i]
end

end
return res
end


end
end
end
Loading