diff --git a/tmval/growth.py b/tmval/growth.py index 81517eb..5b836c1 100644 --- a/tmval/growth.py +++ b/tmval/growth.py @@ -1,4 +1,5 @@ import datetime as dt +import numpy as np from typing import Callable @@ -53,12 +54,32 @@ def effective_rate( ) return effective_rate + def discount_interval( + self, + t1, + t2 + ): + + discount_rate = (self.val(t=t2) - self.val(t=t1)) / self.val(t=t2) + return discount_rate + + def effective_discount( + self, + n + ): + t1 = n - 1 + t2 = n + effective_discount = self.discount_interval( + t1=t1, + t2=t2 + ) + return effective_discount + def get_accumulation(self): - k = self.k amt_func = self.func def acc_func(t): - return amt_func(t, k) / k + return amt_func(k=1, t=t) accumulation = Accumulation(f=acc_func) return accumulation @@ -225,3 +246,113 @@ def __init__( def acc_func(self, t): return (1 + self.interest_rate) ** t + + +def compound_solver(pv=None, fv=None, i=None, t=None): + + args = [pv, fv, i, t] + if args.count(None) > 1: + raise Exception("Only one argument can be missing.") + + if pv is None: + res = fv / ((1 + i) ** t) + elif fv is None: + res = pv * ((1 + i) ** t) + elif i is None: + res = ((fv / pv) ** (1 / t)) - 1 + else: + res = np.log(fv / pv) / np.log(1 + i) + + return res + + +class TieredBal: + """ + Tiered investment account + """ + def __init__( + self, + tiers: list, + rates: list + ): + self.tiers = tiers + self.rates = rates + + def __call__( + self, + k: float, + t: float + ): + # determine jump balances and rates + jump_balances = [i for i in self.tiers if i > k] + if len(jump_balances) == 0: + jump_rates = [] + else: + jump_rates = self.rates[:len(self.rates) - 1] + jump_rates = jump_rates[-len(jump_balances):] + + # determine jump times + jump_times = [] + pv = k + t_base = 0 + for fv, i in zip(jump_balances, jump_rates): + jump_increment = compound_solver(pv=pv, fv=fv, i=i) + jump_times.append(t_base + jump_increment) + t_base = t_base + jump_increment + pv = fv + + # find applicable tiers + jump_times.insert(0, 0) + jump_rates = self.rates[-len(jump_times):] + jump_tiers = self.tiers[-len(jump_times):] + + # construct growth function and calculate balance + index = len([i for i in jump_times if i <= t]) - 1 + lower_t = jump_times[index] + base_amt = max(jump_tiers[index], k) + rate = jump_rates[index] + time = t - lower_t + + bal = base_amt * ((1 + rate) ** time) + + return bal + + +class TieredTime: + """ + Tiered time investment account. Interest varies with time. + """ + + def __init__( + self, + tiers: list, + rates: list + ): + self.tiers = tiers + self.rates = rates + + def __call__( + self, + k: float, + t: float + ): + # find the cumulative tiers that apply at time t + jump_times = np.cumsum(self.tiers) + jump_times = [i for i in jump_times if i < t] + + rates = self.rates[:len(jump_times)] + times = self.tiers[:len(jump_times)] + times.pop(0) + times.append(t - max(jump_times)) + + # for each tier that applies, calculate the cumulative balance + bal = k + for rate, time in zip(rates, times): + bal = bal * ((1 + rate) ** time) + + return bal + + +def k_solver(f: Callable, fv=None, t=None): + res = fv / f(t) + return res \ No newline at end of file