def test_repayment_schedule(self): loan, _ = create_loan_with_funding( opening_balance=7500., start_datetime=self.datetime, duration_days=360, repayment_frequency_days=30, repayment_amount=690.00, interest_daily=0.0005, ) db.session.commit() ua = UserAccount(loan.user.id) rep_schedule = ua.repayment_schedule_for_date(self.datetime.date()) final_date = max(date for date in rep_schedule) final_amount = rep_schedule[final_date] del rep_schedule[final_date] expected = { self.datetime.date() + timedelta(d): 690. for d in range(30, 359, 30) } self.assertEqual(rep_schedule, expected) cf = [ CashFlow(amount=a, datetime=k, type=1) for k, a in expected.items() ] remnant = ua.balance_from_cashflows( ua.cashflows + cf, ua.interest_rates_from_loans(ua.loans), final_date) self.assertEqual(final_amount, round(remnant, 2))
def request_funding(): """ Request funding - Check that approval in question is still the active one. Balance is 0? (flexibiltiy? Returns: """ data = request.get_json() amount = data['amount'] approval_id = data['approval_reference'] dt = time_now() user_account = UserAccount(g.user.id) funding, error = endpoints.request_funding(user_account, approval_id=approval_id, amount=amount, dt=dt) if error: raise BadRequest(error) else: schedule = { k.isoformat(): v for k, v in user_account.repayment_schedule_for_loan( funding).items() } return json.dumps({ 'funding_reference': funding.id, 'repayment_account': BANK_ACCOUNT, 'repayment_schedule': schedule })
def get_schedule(): user_account = UserAccount(g.user.id) as_of = get_date(time_now()) balance = user_account.balance(as_of) schedule = user_account.repayment_schedule_for_date(as_of) return json.dumps({ 'balance': balance, 'schedule': {k.isoformat(): v for k, v in schedule.items()} })
def test_cashflows_by_period_mixtypes(self): day = timedelta(days=1) offsets = [-5, 0, 1, 10, 10, 15] amounts = [-1000, 200, 300, 250, 150, 50] datetimes = [ self.datetime + offsets[0] * day, self.datetime + offsets[1] * day, self.dt + offsets[2] * day, self.datetime + offsets[3] * day, self.dt + offsets[4] * day, self.datetime + offsets[5] * day, ] cashflows = [ CashFlow(amount=am, datetime=dt) for am, dt in zip(amounts, datetimes) ] before, during, after = UserAccount.cashflows_by_period( self.dt, self.dt + day * 10, cashflows) self.assertListEqual(before, cashflows[:2]) self.assertListEqual(during, cashflows[2:5]) self.assertListEqual(after, cashflows[5:]) self.assertListEqual([c.amount for c in during], [300, 250, 150])
def test_balance_interpolate_edge_rate(self): rates = [ Rate(self.base_date, 0.003), Rate(self.base_date + timedelta(1), 0.004) ] output = UserAccount.balance_interpolate(100, self.base_date, self.base_date + timedelta(1), rates) self.assertEqual(output, 100 * 1.003)
def test_balance_from_cashflows(self): expected = [(-5, 0), (0, 1000), (1, 1000 * 1.004), (15, 1000 * (1.004**15)), (20, 1000 * (1.004**20) - 200), (21, (1000 * (1.004**20) - 200) * 1.004), (30, (1000 * (1.004**20) - 200) * (1.004**10) - 550)] for day, exp in expected: output = UserAccount.balance_from_cashflows( self.cashflows, self.rates, self.base_date + day * self.day) self.assertEqual(output, exp)
def test_cashflows_by_period_one_day(self): cashflows = [ CashFlow(datetime=self.datetime, amount=1), CashFlow(datetime=self.datetime, amount=2) ] before, during, after = UserAccount.cashflows_by_period( self.dt - timedelta(1), self.dt, cashflows) self.assertListEqual(before, []) self.assertListEqual(during, cashflows) self.assertListEqual(after, [])
def test_balance_from_cashflows2(self): day30 = (1000 * (1.004**20) - 200) * (1.004**10) - 550 expected = [(31, day30 * 1.004), (40, day30 * (1.004**10) + 50), (50, day30 * (1.004**20) + 50 * (1.004**10)), (60, day30 * (1.004**20) + 50 * (1.004**10)), (100, day30 * (1.004**20) + 50 * (1.004**10)), (101, (day30 * (1.004**20) + 50 * (1.004**10)) * 1.001)] for day, exp in expected: output = UserAccount.balance_from_cashflows( self.cashflows, self.rates, self.base_date + day * self.day) self.assertAlmostEqual(output, exp, places=12)
def test_simple_loan_schedule(self): user = UserFactory() loan = LoanFactory( user=user, opening_balance=7500., duration_days=360, repayment_frequency_days=30, repayment_amount=690.00, interest_daily=0.0005, start_datetime=self.datetime, ) db.session.commit() ua = UserAccount(user.id) final_payment = 664.79 expected_schedule = { self.dt + timedelta(i * 30): 690. for i in range(1, 12) } expected_schedule[self.dt + timedelta(12 * 30)] = final_payment self.assertDictEqual(expected_schedule, ua.repayment_schedule_for_loan(loan))
def test_balance_interpolate_multiple_rates(self): rates = [ Rate(self.base_date, 0.003), Rate(self.base_date + timedelta(10), 0.004), Rate(self.base_date + timedelta(15), 0.007), Rate(self.base_date + timedelta(20), 0.001) ] output = UserAccount.balance_interpolate( 200, self.base_date + timedelta(4), self.base_date + timedelta(24), rates) self.assertEqual( output, 200 * (1 + 0.003)**6 * (1 + 0.004)**5 * (1 + 0.007)**5 * (1 + 0.001)**4)
def test_cashflows_by_period_dates(self): day = timedelta(days=1) offsets = [-5, 0, 1, 10, 10, 15] amounts = [-1000, 200, 300, 250, 150, 50] cashflows = [ CashFlow(amount=am, datetime=self.dt + day * off) for am, off in zip(amounts, offsets) ] before, during, after = UserAccount.cashflows_by_period( self.dt, self.dt + day * 10, cashflows) self.assertListEqual(before, cashflows[:2]) self.assertListEqual(during, cashflows[2:5]) self.assertListEqual(after, cashflows[5:]) self.assertListEqual([c.amount for c in during], [300, 250, 150])
def test_interest_rates_from_loans(self): base_dt = datetime(2015, 7, 15, 12, 0, 0) day = timedelta(days=1) loans = [ Loan(start_datetime=shifted(days, hours, base_dt), interest_daily=interest) for days, hours, interest in [(0, 0, 0.001), ( 5, 1, 0.002), (5, 2, 0.004), (10, 1, 0.003)] ] rates = UserAccount.interest_rates_from_loans(loans) self.assertListEqual([r.date for r in rates], [ base_dt.date(), base_dt.date() + 5 * day, base_dt.date() + 10 * day ]) self.assertListEqual([r.rate for r in rates], [0.001, 0.004, 0.003])
def test_balance_interpolate_same_day(self): rates = [Rate(self.base_date - timedelta(50), 0.003)] output = UserAccount.balance_interpolate(100, self.base_date, self.base_date, rates) self.assertEqual(output, 100.)
def test_balance_interpolate_partial_rate(self): rates = [Rate(self.base_date + timedelta(50), 0.003)] output = UserAccount.balance_interpolate( 100, self.base_date, self.base_date + timedelta(60), rates) self.assertEqual(output, 100 * (1 + 0.003)**10)
def test_balance_interpolate_future_rate(self): rates = [Rate(self.base_date + timedelta(70), 0.003)] output = UserAccount.balance_interpolate( 100, self.base_date, self.base_date + timedelta(60), rates) self.assertEqual(output, 100)
def test_balance_interpolate_no_rates(self): output = UserAccount.balance_interpolate( 100, self.base_date, self.base_date + timedelta(60), []) self.assertEqual(output, 100)
def test_no_cashflows(self): output = UserAccount.balance_from_cashflows([], self.rates, self.base_date) self.assertEqual(output, 0)
def balance(as_of): return UserAccount.balance_from_cashflows( cashflows, [Rate(self.dt, interest_daily)], as_of)
def test_before_cashflows(self): output = UserAccount.balance_from_cashflows( self.cashflows, self.rates, self.base_date - 5 * self.day) self.assertEqual(output, 0)
def test_cashflows_by_period_empty(self): before, during, after = UserAccount.cashflows_by_period( self.dt, self.dt + timedelta(50), []) self.assertListEqual(before, []) self.assertListEqual(during, []) self.assertListEqual(after, [])