Source code for cashflows.rate

"""
Interest rate transformations
===============================================================================


"""


import numpy
from cashflows.gtimeseries import _timeid2index
from cashflows.gtimeseries import *




[docs]def iconv(nrate=None, erate=None, prate=None, pyr=1): """The function `iconv` computes the conversion among periodic, nominal and effective interest rates. Only an interest rate (periodic, nominal or effective) must be specified and the other two are computed. The periodic rate is the rate used in each compounding period. The effective rate is the equivalent rate that produces the same interest earnings that a periodic rate when there is P compounding periods in a year. The nominal rate is defined as the annual rate computed as P times the periodic rate. Args: nrate (float, list, TimeSeries): nominal interest rate per year. erate (float, list, TimeSeries): effective interest rate per year. prate (float, list, TimeSeries): periodic rate pyr (int, list): number of compounding periods per year Returns: A tuple: * (**nrate**, **prate**): when **erate** is specified. * (**erate**, **prate**): when **nrate** is specified. * (**nrate**, **erate**): when **prate** is specified. Effective rate to nominal rate compounded monthly and monthly peridic rate. >>> iconv(erate=10, pyr=12) # doctest: +ELLIPSIS (9.56..., 0.79...) >>> iconv(prate=1, pyr=12) # doctest: +ELLIPSIS (12, 12.68...) >>> iconv(nrate=10, pyr=12) # doctest: +ELLIPSIS (10.47..., 0.83...) `iconv` accepts Python vectors. >>> iconv(erate=10, pyr=[3, 6, 12]) # doctest: +ELLIPSIS ([9.68..., 9.60..., 9.56...], [3.22..., 1.60..., 0.79...]) >>> iconv(prate=1, pyr=[3, 6, 12]) # doctest: +ELLIPSIS ([3, 6, 12], [3.03..., 6.15..., 12.68...]) >>> iconv(nrate=10, pyr=[3, 6, 12]) # doctest: +ELLIPSIS ([10.33..., 10.42..., 10.47...], [3.33..., 1.66..., 0.83...]) >>> iconv(erate=[10, 12, 14], pyr=12) # doctest: +ELLIPSIS ([9.56..., 11.38..., 13.17...], [0.79..., 0.94..., 1.09...]) >>> iconv(prate=[1, 2, 3], pyr=12) # doctest: +ELLIPSIS ([12, 24, 36], [12.68..., 26.82..., 42.57...]) >>> iconv(nrate=[10, 12, 14], pyr=12) # doctest: +ELLIPSIS ([10.47..., 12.68..., 14.93...], [0.83..., 1.0, 1.16...]) When a rate and the number of compounding periods (`pyr`) are vectors, they must have the same length. Computations are executed using the first rate with the first compounding and so on. >>> iconv(erate=[10, 12, 14], pyr=[3, 6, 12]) # doctest: +ELLIPSIS ([9.68..., 11.44..., 13.17...], [3.22..., 1.90..., 1.09...]) >>> iconv(nrate=[10, 12, 14], pyr=[3, 6, 12]) # doctest: +ELLIPSIS ([10.33..., 12.61..., 14.93...], [3.33..., 2.0, 1.16...]) >>> iconv(prate=[1, 2, 3], pyr=[3, 6, 12]) # doctest: +ELLIPSIS ([3, 12, 36], [3.03..., 12.61..., 42.57...]) `iconv` accepts TimeSeries objects >>> erate, prate = iconv(nrate = interest_rate(const_value=12, nper=12, pyr=2)) >>> prate # doctest: +NORMALIZE_WHITESPACE Time Series: Start = (0, 0) End = (5, 1) pyr = 2 Data = (0, 0)-(5, 1) [12] 6.00 >>> erate # doctest: +NORMALIZE_WHITESPACE Time Series: Start = (0, 0) End = (5, 1) pyr = 2 Data = (0, 0)-(5, 1) [12] 12.36 >>> erate, prate = iconv(nrate = interest_rate(const_value=12, nper=12, pyr=4)) >>> prate # doctest: +NORMALIZE_WHITESPACE Qtr0 Qtr1 Qtr2 Qtr3 0 3.00 3.00 3.00 3.00 1 3.00 3.00 3.00 3.00 2 3.00 3.00 3.00 3.00 >>> erate # doctest: +NORMALIZE_WHITESPACE Qtr0 Qtr1 Qtr2 Qtr3 0 12.55 12.55 12.55 12.55 1 12.55 12.55 12.55 12.55 2 12.55 12.55 12.55 12.55 >>> nrate, prate = iconv(erate = erate) >>> nrate # doctest: +NORMALIZE_WHITESPACE Qtr0 Qtr1 Qtr2 Qtr3 0 12.00 12.00 12.00 12.00 1 12.00 12.00 12.00 12.00 2 12.00 12.00 12.00 12.00 >>> prate # doctest: +NORMALIZE_WHITESPACE Qtr0 Qtr1 Qtr2 Qtr3 0 3.00 3.00 3.00 3.00 1 3.00 3.00 3.00 3.00 2 3.00 3.00 3.00 3.00 >>> nrate, erate = iconv(prate = prate) >>> nrate # doctest: +NORMALIZE_WHITESPACE Qtr0 Qtr1 Qtr2 Qtr3 0 12.00 12.00 12.00 12.00 1 12.00 12.00 12.00 12.00 2 12.00 12.00 12.00 12.00 >>> erate # doctest: +NORMALIZE_WHITESPACE Qtr0 Qtr1 Qtr2 Qtr3 0 12.55 12.55 12.55 12.55 1 12.55 12.55 12.55 12.55 2 12.55 12.55 12.55 12.55 """ numnone = 0 if nrate is None: numnone += 1 if erate is None: numnone += 1 if prate is None: numnone += 1 if numnone != 2: raise ValueError('Two of the rates must be set to `None`') if isinstance(nrate, list) and isinstance(pyr, list) and len(nrate) != len(pyr): raise ValueError('List must have the same length') if isinstance(erate, list) and isinstance(pyr, list) and len(erate) != len(pyr): raise ValueError('List must have the same length') if isinstance(prate, list) and isinstance(pyr, list) and len(prate) != len(pyr): raise ValueError('List must have the same length') maxlen = 1 if isinstance(nrate, list): maxlen = max(maxlen, len(nrate)) if isinstance(erate, list): maxlen = max(maxlen, len(erate)) if isinstance(prate, list): maxlen = max(maxlen, len(prate)) if isinstance(pyr, list): maxlen = max(maxlen, len(pyr)) if isinstance(pyr, (int, float)): pyr = [pyr] * maxlen pyr = numpy.array(pyr) if nrate is not None: if isinstance(nrate, TimeSeries): erate = nrate.copy() prate = nrate.copy() for index in range(len(nrate.data)): prate[index] = nrate[index] / nrate.pyr for index in range(len(nrate.data)): erate[index] = 100 * (numpy.power(1 + prate[index]/100, nrate.pyr) - 1) return (erate, prate) else: if isinstance(nrate, (int, float)): nrate = [nrate] * maxlen nrate = numpy.array(nrate) prate = nrate / pyr erate = 100 * (numpy.power(1 + prate/100, pyr) - 1) prate = prate.tolist() erate = erate.tolist() if maxlen == 1: prate = prate[0] erate = erate[0] return (erate, prate) if erate is not None: if isinstance(erate, TimeSeries): nrate = erate.copy() prate = erate.copy() for index in range(len(erate.data)): prate[index] = 100 * (numpy.power(1 + erate[index]/100, 1. / erate.pyr) - 1) for index in range(len(erate.data)): nrate[index] = erate.pyr * prate[index] return (nrate, prate) else: if isinstance(erate, (int, float)): erate = [erate] * maxlen erate = numpy.array(erate) prate = 100 * (numpy.power(1 + erate / 100, 1 / pyr) - 1) nrate = pyr * prate prate = prate.tolist() nrate = nrate.tolist() if maxlen == 1: prate = prate[0] nrate = nrate[0] return (nrate, prate) if isinstance(prate, TimeSeries): erate = prate.copy() nrate = prate.copy() for index in range(len(prate.data)): nrate[index] = prate[index] * prate.pyr for index in range(len(prate.data)): erate[index] = 100 * (numpy.power(1 + prate[index]/100, prate.pyr) - 1) return (nrate, erate) else: if isinstance(prate, (int, float)): prate = [prate] * maxlen prate = numpy.array(prate) erate = 100 * (numpy.power(1 + prate / 100, pyr) - 1) nrate = pyr * prate erate = erate.tolist() nrate = nrate.tolist() if maxlen == 1: erate = erate[0] nrate = nrate[0] return (nrate, erate)
[docs]def to_discount_factor(nrate=None, erate=None, prate=None, base_date=0): """Returns a list of discount factors calculated as 1 / (1 + r)^(t - t0). Args: nrate (TimeSeries): Nominal interest rate per year. nrate (TimeSeries): Effective interest rate per year. prate (TimeSeries): Periodic interest rate. base_date (int, tuple): basis time. Returns: List of float values Only one of the interest rates must be supplied for the computation. >>> nrate = interest_rate(const_value=4,nper=10, pyr=4) >>> erate, prate = iconv(nrate=nrate) >>> to_discount_factor(nrate=nrate, base_date=2) # doctest: +ELLIPSIS [1.0201, 1.01, 1.0, 0.990..., 0.980..., 0.970..., 0.960..., 0.951..., 0.942..., 0.932...] >>> to_discount_factor(erate=erate, base_date=2) # doctest: +ELLIPSIS [1.0201, 1.01, 1.0, 0.990..., 0.980..., 0.970..., 0.960..., 0.951..., 0.942..., 0.932...] >>> to_discount_factor(prate=prate, base_date=2) # doctest: +ELLIPSIS [1.0201, 1.01, 1.0, 0.990..., 0.980..., 0.970..., 0.960..., 0.951..., 0.942..., 0.932...] """ numnone = 0 if nrate is None: numnone += 1 if erate is None: numnone += 1 if prate is None: numnone += 1 if numnone != 2: raise ValueError('Two of the rates must be set to `None`') if nrate is not None: prate = nrate.copy() prate.data = [x/nrate.pyr for x in nrate.data] # periodic rate if erate is not None: prate = erate.copy() prate.data = [100 * (numpy.power(1 + x/100, 1. / erate.pyr) - 1) for x in erate.data] # periodic rate factor = [x/100 for x in prate.data] for index, _ in enumerate(factor): if index == 0: factor[0] = 1 / (1 + factor[0]) else: factor[index] = factor[index-1] / (1 + factor[index]) if isinstance(base_date, tuple): base_date = _timeid2index(base_date, basis=prate.start, pyr=prate.pyr) div = factor[base_date] for index, _ in enumerate(factor): factor[index] = factor[index] / div return factor
[docs]def to_compound_factor(nrate=None, erate=None, prate=None, base_date=0): """Returns a list of compounding factors calculated as (1 + r)^(t - t0). Args: nrate (TimeSeries): Nominal interest rate per year. nrate (TimeSeries): Effective interest rate per year. prate (TimeSeries): Periodic interest rate. base_date (int, tuple): basis time. Returns: Compound factor (list) **Examples** >>> nrate = interest_rate(const_value=4,nper=10, pyr=4) >>> erate, prate = iconv(nrate=nrate) >>> to_compound_factor(prate=prate, base_date=2) # doctest: +ELLIPSIS [0.980..., 0.990..., 1.0, 1.01, 1.0201, 1.030..., 1.040..., 1.051..., 1.061..., 1.072...] >>> to_compound_factor(nrate=nrate, base_date=2) # doctest: +ELLIPSIS [0.980..., 0.990..., 1.0, 1.01, 1.0201, 1.030..., 1.040..., 1.051..., 1.061..., 1.072...] >>> to_compound_factor(erate=erate, base_date=2) # doctest: +ELLIPSIS [0.980..., 0.990..., 1.0, 1.01, 1.0201, 1.030..., 1.040..., 1.051..., 1.061..., 1.072...] """ factor = to_discount_factor(nrate=nrate, erate=erate, prate=prate, base_date=base_date) for time, _ in enumerate(factor): factor[time] = 1 / factor[time] return factor
[docs]def equivalent_rate(nrate=None, erate=None, prate=None): """Returns the equivalent interest rate over a time period. Args: nrate (TimeSeries): Nominal interest rate per year. nrate (TimeSeries): Effective interest rate per year. prate (TimeSeries): Periodic interest rate. Returns: float value. Only one of the interest rate must be supplied for the computation. >>> equivalent_rate(prate=interest_rate([10]*5)) # doctest: +ELLIPSIS 10.0... """ numnone = 0 if nrate is None: numnone += 1 if erate is None: numnone += 1 if prate is None: numnone += 1 if numnone != 2: raise ValueError('Two of the rates must be set to `None`') if nrate is not None: value = nrate.tolist() factor = 1 for element in value[1:]: factor *= (1 + element / 100 / nrate.pyr) return 100 * nrate.pyr * (factor**(1/(len(value) - 1)) - 1) if prate is not None: value = prate.tolist() factor = 1 for element in value[1:]: factor *= (1 + element / 100) return 100 * (factor**(1/(len(value) - 1)) - 1) if erate is not None: value = erate.tolist() factor = 1 for element in value[1:]: factor *= (1 + (numpy.power(1 + element/100, 1. / erate.pyr) - 1)) return 100 * (factor**(1/(len(value) - 1)) - 1)
if __name__ == "__main__": import doctest doctest.testmod()