Module: Apy::Calculable

Included in:
Interest
Defined in:
lib/apy/calculable.rb

Instance Method Summary collapse

Instance Method Details

#compound(principal, rate:, times:, terms:) ⇒ Object

Simple compound, assuming no additional investment over successive maturity terms

Examples:

Given a “10% APY”, with interest paid monthly (1y maturity date):

compound(1200, rate: 0.1, times: 12, terms: 1) == 1325.66

Given a “0.1923% WPY”, with interest paid weekly (1w maturity date):

compound(1200, rate: 0.1, times: 52, terms: 1) == 1326.07
compound(1200, rate: 0.001923, times: 1, terms: 52) == 1326.07

Given a “0.0274% DPY”, with interest paid daily (1d maturity date):

compound(1200, rate: 0.1, times: 365, terms: 1) == 1326.19
compound(1200, rate: 0.000274, times: 1, terms: 365) == 1326.20

Parameters:

  • principal (Numeric)

    Initial investment

  • rate (Float)

    Expected interest rate for the length of the period

  • times (Integer)

    Times the interest will be paid out per term

  • terms (Integer)

    Number of terms



45
46
47
48
49
# File 'lib/apy/calculable.rb', line 45

def compound(principal, rate:, times:, terms:)
  total_rate = 1 + (rate / times)

  principal * (total_rate**(times * terms))
end

#dca_compound(matrix, times:) ⇒ Object

TODO:

Clean this up, there is most likely an optimized formula for this 🤨

“DCA” compound, assuming a recurring investment continuously added to the principal amount, this new amount additionally compounded for the next period

Examples:

Continuously investing 1200 dollars a year into a contract with a “10% APY”, interest paid out once a month

dca_compound([[1200, 0.1], [1200, 0.1]], times: 12) == 2790.125

Parameters:

  • matrix (Array<Array(Numeric, Numeric)>)

    Matrix whose size is the number of terms; Each array item has the following elements: idx0 additional investment, idx1 the expected rate for the term

  • times (Integer)

    Times the interest will be paid out per term

See Also:



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/apy/calculable.rb', line 59

def dca_compound(matrix, times:)
  result = matrix.each_with_object(
    total: 0,
    interest: 0,
    data: {
      0 => {
        ytd: 0,
        in: 0,
        interest: 0
      }
    }
  ).with_index do |(ary, out), i|
    additional_investment, rate = ary
    prev_ytd = out[:data][i][:ytd]

    to_compound = prev_ytd + additional_investment

    current = compound(to_compound, rate: rate, times: times, terms: 1)
    interest_this_period = current - to_compound

    out[:total] += additional_investment + interest_this_period
    out[:interest] += interest_this_period
    out[:data][i + 1] = {ytd: current, in: additional_investment, interest: interest_this_period}
  end

  result.fetch(:total)
end

#weighted_harmonic_mean(matrix) ⇒ Object

TODO:

Can make this a bit more performant with ‘inject`

Calculate the weighted harmonic mean for a set of values (∑w) / (∑w/i)

Examples:

Providing a set of DCA values

dca = [[1000,156.23],[1000,156.3], [1000,173.15],[1000,188.72], [1000,204.61],[1000,178.23]]
weighted_harmonic_mean(dca)
# => 174.5655590168175

Parameters:

  • matrix (Array<Array<(Numeric, Numeric)>>)

    Matrix whose size is the number of investments, with the following array elements: idx0 invested amount, idx1 purchase price of the asset



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/apy/calculable.rb', line 13

def weighted_harmonic_mean(matrix)
  sum = matrix.sum { |(n, _)| n }.to_f

  total_weight = []
  weighted_values = []

  matrix.each do |(investment, price)|
    weight = investment / sum

    total_weight << weight
    weighted_values << (weight / price)
  end

  total_weight.sum / weighted_values.sum
end