def __init__(self, r, fee=0., fee_freq='Q', start=None, end=None, lookback={}, strict=False, dist_amt=None, dist_pct=None, dist_freq=None, v0=float(1e6), include_start=True, freq='M', name=None, in_format='num'): self.gross = returns.prep(r=r, freq=freq, name=name, in_format=in_format) # fee_freq: if str -> frequency; if int/float -> periods/yr # Get `fee` to a per-period float if isinstance(fee_freq, (int, float)): self.fee_freq = fee_freq self.fee = fee / self.fee_freq elif isinstance(fee_freq, str): self.fee_freq = utils.convertfreq(fee_freq) self.fee = fee / self.fee_freq # Logic for interaction of `start`, `end`, and `lookback` # TODO: how should lookback be passed? Consider params to # `constrain_horizon` if any((start, end)) and not lookback: self.gross = self.gross[start:end] elif lookback: # TODO: cleanup self.gross = utils.constrain_horizon(self.gross, **lookback) elif all((any((start, end)), lookback)): raise ValueError('if `lookback` is specified, both `start` and' ' `end` should be None') self.index = self.gross.index self.columns = self.gross.columns masktypes = {12. : 'is_month_end', 4. : 'is_quarter_end', 1. : 'is_quarter_end'} mask = getattr(self.index, masktypes[self.fee_freq]) self.feesched = np.where(mask, self.fee, 0.) # Net of fees (not yet of distributions) self.net = (1. + self.gross.values) \ * (1. - self.feesched.reshape(-1,1)) - 1. self.net = DataFrame(self.net, index=self.index, columns=self.columns) self.dist_amt = dist_amt self.dist_pct = dist_pct self.dist_freq = dist_freq self.v0 = v0 self.include_start = include_start
def amortize(rate, nper, pv, freq='M'): """Construct an amortization schedule for a fixed-rate loan. Rate -> annualized input Example ======= # a 6.75% $200,000 loan, 30-year tenor, payments due monthly # view the 5 final months print(amortize(rate=.0675, nper=30, pv=200000).round(2).tail()) beg_bal prin interest end_bal 356 6377.95 -1261.32 -35.88 5116.63 357 5116.63 -1268.42 -28.78 3848.22 358 3848.22 -1275.55 -21.65 2572.67 359 2572.67 -1282.72 -14.47 1289.94 360 1289.94 -1289.94 -7.26 -0.00 """ freq = utils.convertfreq(freq) rate = rate / freq nper = nper * freq periods = np.arange(1, nper + 1, dtype=int) principal = np.ppmt(rate, periods, nper, pv) interest = np.ipmt(rate, periods, nper, pv) pmt = np.pmt(rate, nper, pv) def balance(pv, rate, nper, pmt): dfac = (1 + rate)**nper return pv * dfac - pmt * (dfac - 1) / rate res = DataFrame( { 'beg_bal': balance(pv, rate, periods - 1, -pmt), 'prin': principal, 'interest': interest, 'end_bal': balance(pv, rate, periods, -pmt) }, index=periods)['beg_bal', 'prin', 'interest', 'end_bal'] return res