Example #1
0
 def test_returns(self, *args, **kwargs):
     """ Tests Account.returns and Account.returns_history. """
     # Account with $1 balance and 100% non-compounded growth.
     # Should have returns of $1 in its first year:
     account = self.AccountType(self.owner,
                                *args,
                                balance=1,
                                rate=1.0,
                                nper=1,
                                **kwargs)
     # pylint: disable=no-member
     # Pylint is confused by members added by metaclass
     self.assertEqual(account.returns, Money(1))  # $1 return
     self.assertEqual(account.returns_history,
                      {self.initial_year: Money(1)})
Example #2
0
 def test_limit_ordered(self):
     """ Limit contributions with per-node limit in ordered tree. """
     # Limit debt contributions to $100
     # (rather than $1000 max. contribution)
     limits = LimitTuple(max_inflow=Money(100))
     limit_node = TransactionNode(self.debt, limits=limits)
     priority = [self.rrsp, limit_node, self.taxable_account]
     strategy = TransactionTraversal(priority=priority)
     # Contribute $300 to the accounts:
     available = {Decimal(0.5): Money(300)}
     transactions = strategy(available)
     # $100 will go to each account:
     self.assertTransactions(transactions[self.rrsp], Money(100))
     self.assertTransactions(transactions[self.debt], Money(100))
     self.assertTransactions(transactions[self.taxable_account], Money(100))
Example #3
0
 def test_next_disc_growth_trans(self, *args, **kwargs):
     """ Tests next_year with discrete growth and a transaction. """
     # The $2 transaction happens at the start of a compounding
     # period, so behaviour is well-defined. It should grow by a
     # factor of (1 + r/n)^nt, for n = 12 (monthly) and t = 0.5
     account = self.AccountType(self.owner,
                                *args,
                                balance=1,
                                rate=1,
                                nper='M',
                                **kwargs)
     account.add_transaction(Money(2), when='0.5')
     next_val = Money((1 + 1 / 12)**(12) + 2 * (1 + 1 / 12)**(12 * 0.5))
     account.next_year()
     self.assertAlmostEqual(account.balance, next_val, 5)
Example #4
0
 def test_out_empty_all(self):
     """ Test strategy_weighted with mult. RRSPs, empty all accounts. """
     # Try withdrawing more than the accounts have
     val = sum(
         sum(account.max_outflows().values())
         for account in self.accounts
     ) - Money(50)
     available = make_available(Money(val), self.timing)
     results = self.strategy(available, self.accounts)
     # Confirm each account has the expected set of transactions:
     self.assertTransactions(results[self.rrsp], self.rrsp.max_outflows())
     self.assertTransactions(results[self.tfsa], self.tfsa.max_outflows())
     self.assertTransactions(
         results[self.taxable_account],
         self.taxable_account.max_outflows())
Example #5
0
    def test_add_trans_basic_acct(self):
        """ Moves $100 from available to an account. """
        # Receive cash at start of year:
        self.available_acct.add_transaction(value=100, when='start')

        # Move all $100 to account1 right away:
        self.subforecast.add_transaction(value=100,
                                         timing='start',
                                         from_account=self.available_acct,
                                         to_account=self.account2)
        # No more money should be available:
        self.assertEqual(self.available_acct.transactions[Decimal(0)],
                         Money(0))
        # A $100 transaction should be added to account2:
        self.assertEqual(self.account2.transactions[Decimal(0)], Money(100))
Example #6
0
    def test_living_const_contrib(self):
        """ Test living expenses based on constant contribution. """
        # Contribute $100 and live off the rest:
        self.strategy = LivingExpensesStrategy(
            strategy=LivingExpensesStrategy.strategy_const_contribution,
            base_amount=Money(100))
        self.forecast.living_expenses_strategy = self.strategy

        # It _is_ necessary to record inflows from employment
        # for this strategy:
        self.forecast(self.available)

        # Calculate manually and compare results:
        living_expenses = self.total_available - Money(100)
        self.assertEqual(living_expenses, self.forecast.living_expenses)
Example #7
0
 def test_out_all_empty(self):
     """ Test strategy_weighted with outflows to empty all accounts. """
     # Withdraw more than the accounts have:
     val = sum(
         sum(account.max_outflows().values())
         for account in self.accounts
     ) - Money(50)
     available = make_available(Money(val), self.timing)
     results = self.strategy(available, self.accounts)
     # Confirm each account gets the expected total transactions:
     self.assertTransactions(results[self.rrsp], self.rrsp.max_outflows())
     self.assertTransactions(results[self.tfsa], self.tfsa.max_outflows())
     self.assertTransactions(
         results[self.taxable_account],
         self.taxable_account.max_outflows())
Example #8
0
    def setUp(self):
        initial_year = 2000
        person = Person(initial_year,
                        'Testy McTesterson',
                        1980,
                        retirement_date=2045)

        self.timing = Timing({Decimal(0.5): 1})

        # These accounts have different rates:
        self.debt_big_high_interest = Debt(
            person,
            balance=Money(1000),
            rate=1,
            minimum_payment=Money(100),
            accelerated_payment=Money('Infinity'))
        self.debt_small_low_interest = Debt(
            person,
            balance=Money(100),
            rate=0,
            minimum_payment=Money(10),
            accelerated_payment=Money('Infinity'))
        self.debt_medium = Debt(person,
                                balance=Money(500),
                                rate=0.5,
                                minimum_payment=Money(50),
                                accelerated_payment=Money('Infinity'))

        self.debts = {
            self.debt_big_high_interest, self.debt_medium,
            self.debt_small_low_interest
        }

        self.max_payments = {
            debt: self.max_payment({debt})
            for debt in self.debts
        }
        self.min_payments = {
            debt: self.min_payment({debt})
            for debt in self.debts
        }

        self.strategy_avalanche = DebtPaymentStrategy(
            DebtPaymentStrategy.strategy_avalanche)
        self.strategy_snowball = DebtPaymentStrategy(
            DebtPaymentStrategy.strategy_snowball)

        self.excess = Money(10)
Example #9
0
    def test_next(self, *args, **kwargs):
        """ Test TFSA.next_year. """
        super().test_next(*args, **kwargs)

        # Set up variables for testing.
        accruals = self.get_accruals()
        rand = Random()
        owner = Person(min(accruals),
                       self.owner.name,
                       1950,
                       retirement_date=2015,
                       gross_income=self.owner.gross_income,
                       raise_rate={
                           year: 0
                           for year in range(min(accruals),
                                             max(accruals) + 2)
                       },
                       tax_treatment=self.owner.tax_treatment)
        account = self.AccountType(owner,
                                   *args,
                                   inflation_adjust=self.inflation_adjust,
                                   rate=0,
                                   balance=0,
                                   **kwargs)

        # For each year, confirm that the balance and contribution room
        # are updated appropriately
        transactions = Money(0)
        for year in accruals:
            # Add a transaction (either an inflow or outflow)
            transaction = rand.randint(-account.balance.amount,
                                       account.contribution_room.amount)
            account.add_transaction(transaction)
            # Confirm that contribution room is the same as accruals,
            # less any net transactions
            accrual = sum(
                [accruals[i] for i in range(min(accruals), year + 1)])
            target = Money(accrual) - transactions
            self.assertEqual(account.contribution_room, target)
            # Confirm that balance is equal to the sum of transactions
            # over the previous years (note that this is a no-growth
            # scenario, since rate=0)
            self.assertEqual(account.balance, transactions)
            # Advance the account to next year and repeat tests
            account.next_year()
            # Update the running total of transactions, to be referenced
            # in the next round of tests.
            transactions += Money(transaction)
Example #10
0
 def test_max_inflows_neg(self, *args, **kwargs):
     """ Test max_inflows with negative balance """
     # This method should always return Money('Infinity')
     account = self.AccountType(self.owner, *args, balance=-100, **kwargs)
     result = account.max_inflows(self.timing)
     for value in result.values():
         self.assertEqual(value, Money('Infinity'))
Example #11
0
 def test_insufficient_income(self):
     """ Test a lower net income than the contribution rate. """
     # Try to contribute more than net income:
     method = LivingExpensesStrategy.strategy_const_contribution
     strategy = LivingExpensesStrategy(method, Money(2001))
     # Living expenses are $0 in this case, not -$1:
     self.assertEqual(strategy(people=self.people), 0)
Example #12
0
    def test_add_trans_shortfall(self):
        """ Transaction that must cause a negative balance. """
        # Want to withdraw $100 when this amount will not be available
        # at any point in time:
        self.available_dict[Decimal(0)] = Money(50)
        self.available_dict[Decimal(1)] = Money(49)

        # Try to move $100 in cash to account2 at mid-year:
        self.subforecast.add_transaction(value=100,
                                         timing=0.5,
                                         from_account=self.available_dict,
                                         to_account=self.account2)
        # Transaction should occur immediately:
        self.assertEqual(self.available_dict[Decimal(0.5)], Money(-100))
        # A $100 transaction should be added to account2:
        self.assertEqual(self.account2.transactions[Decimal(0.5)], Money(100))
Example #13
0
    def test_add_trans_delay_acct(self):
        """ Transaction that should be shifted to later time. """
        # Receive cash mid-year:
        self.available_acct.add_transaction(value=100, when=0.5)

        # Try to move $100 in cash to account1 at the start of the year
        # (i.e. before cash is actually on-hand):
        self.subforecast.add_transaction(value=100,
                                         timing='start',
                                         from_account=self.available_acct,
                                         to_account=self.account2)
        # Transactions should be delayed until mid-year:
        self.assertEqual(self.available_acct.transactions[Decimal(0.5)],
                         Money(0))
        # A $100 transaction should be added to account2:
        self.assertEqual(self.account2.transactions[Decimal(0.5)], Money(100))
Example #14
0
    def test_gross_withdrawals(self):
        """ Test total withdrawn from accounts. """
        # Set up forecast:
        self.forecast(self.available)

        # For default `available`, should withdraw $20,000.
        self.assertEqual(self.forecast.gross_withdrawals, Money(20000))
Example #15
0
 def test_init_cont_room_default(self, *args, **kwargs):
     """ Init TFSA with default contribution_room. """
     # TFSAs began in 2009. Confirm that we're correctly determining
     # future contribution room based on an inflation-adjusted 2009
     # amount (a schedule of such accruals is returned by
     # get_accruals).
     accruals = self.get_accruals()
     # Use a person who's at least old enough to qualify for all
     # available TFSA accruals.
     owner = Person(self.initial_year, "test", 1950, retirement_date=2015)
     # For each starting year, confirm that available contribution
     # room is the sum of past accruals.
     for year in accruals:
         account = self.AccountType(owner,
                                    *args,
                                    inflation_adjust=self.inflation_adjust,
                                    initial_year=year,
                                    **kwargs)
         target = Money(
             sum([accruals[i] for i in range(min(accruals), year + 1)]))
         self.assertEqual(account.contribution_room, target)
         # We're starting each TFSA in a different year, which can
         # confuse the linked-account recordkeeping (later-year
         # accounts will default to using the contribution room
         # of the first account). Destroy the link to simplify this.
         account.max_inflow_link.unregister()
Example #16
0
 def test_init_federal(self):
     """ Test TaxCanada.__init__ for federal jurisdiction. """
     # There's some type-conversion going on, so test the Decimal-
     # valued `amount` of the Tax's tax bracket's keys against the
     # Decimal key object of the Constants tax brackets.
     tax = TaxCanada(self.inflation_adjustments, self.province)
     for year in constants.TAX_BRACKETS['Federal']:
         self.assertEqual(
             tax.federal_tax.tax_brackets(year),
             {
                 Money(bracket): value
                 for bracket, value in
                 constants.TAX_BRACKETS['Federal'][year].items()})
         self.assertEqual(
             tax.federal_tax.personal_deduction(year),
             constants.TAX_PERSONAL_DEDUCTION['Federal'][year])
         self.assertEqual(
             tax.federal_tax.credit_rate(year),
             constants.TAX_CREDIT_RATE['Federal'][year])
     self.assertTrue(callable(tax.federal_tax.inflation_adjust))
     # Test that the default timings for CRA refunds/payments have
     # been set:
     self.assertEqual(
         set(tax.payment_timing), {constants.TAX_PAYMENT_TIMING})
     self.assertEqual(
         set(tax.refund_timing), {constants.TAX_REFUND_TIMING})
Example #17
0
    def test_tax_withheld(self):
        """ Test tax withheld from accounts. """
        # Set up forecast:
        self.withholding_forecast(self.available)

        # Total withholdings are $10000 (half of $20,000 withdrawn)
        self.assertEqual(self.withholding_forecast.tax_withheld, Money(-10000))
Example #18
0
 def test_min_inflows_pos(self, *args, **kwargs):
     """ Test min_inflow with positive balance """
     # This method should always return $0
     account = self.AccountType(self.owner, *args, balance=100, **kwargs)
     result = account.min_inflows(self.timing)
     for value in result.values():
         self.assertAlmostEqual(value, Money(0), places=4)
Example #19
0
    def test_init_basic(self, *args, **kwargs):
        """ Tests Account.__init__ """
        # Basic test: All correct values, check for equality and type
        owner = self.owner
        balance = Money(0)
        rate = 1.0
        nper = 1  # This is the easiest case to test
        initial_year = self.initial_year
        account = self.AccountType(owner,
                                   *args,
                                   balance=balance,
                                   rate=rate,
                                   nper=nper,
                                   **kwargs)
        # Test primary attributes
        # pylint: disable=no-member
        # Pylint is confused by members added by metaclass
        self.assertEqual(account.balance_history, {initial_year: balance})
        self.assertEqual(account.rate_history, {initial_year: rate})
        self.assertEqual(account.transactions_history, {initial_year: {}})
        self.assertEqual(account.balance, balance)
        self.assertEqual(account.rate, rate)
        self.assertEqual(account.nper, 1)
        self.assertEqual(account.initial_year, initial_year)
        self.assertEqual(account.this_year, initial_year)

        # Check types
        # pylint: disable=no-member
        # Pylint is confused by members added by metaclass
        self.assertTrue(type_check(account.balance_history, {int: Money}))
        self.assertIsInstance(account.balance, Money)
        self.assertTrue(type_check(account.rate_history, {int: Decimal}))
        self.assertIsInstance(account.rate, Decimal)
        self.assertIsInstance(account.nper, int)
        self.assertIsInstance(account.initial_year, int)
Example #20
0
 def test_min_inflows_large_balance(self):
     """ Test `min_inflows` with balance greater than min. payment. """
     self.debt.minimum_payment = 100
     self.debt.balance = Money(-1000)
     result = self.debt.min_inflows(self.timing)
     # Inflows should be capped at minimum payment:
     self.assertEqual(sum(result.values()), self.debt.minimum_payment)
Example #21
0
 def test_account_trans_ordered(self):
     """ Test account transactions under ordered strategy. """
     # Set up forecast:
     self.forecast.transaction_strategy = TransactionStrategy(
         strategy=TransactionStrategy.strategy_ordered,
         weights={
             "RRSP": 1,
             "Account": 2
         })
     self.forecast(self.available)
     # We are withdrawing $20,000. We'll withdraw the whole balance
     # of `rrsp` ($6000), with the rest from `account`:
     self.assertTransactions(self.forecast.account_transactions[self.rrsp],
                             Money(-6000))
     self.assertTransactions(
         self.forecast.account_transactions[self.account], Money(-14000))
Example #22
0
 def test_account_trans_weighted(self):
     """ Test account transactions under weighted strategy. """
     # Set up forecast:
     self.forecast.transaction_strategy = TransactionStrategy(
         strategy=TransactionStrategy.strategy_weighted,
         weights={
             "RRSP": 3000,
             "Account": 17000
         })
     self.forecast(self.available)
     # We are withdrawing $20,000. We'll withdraw $3000 from
     # `rrsp`, with the rest from `account`:
     self.assertTransactions(self.forecast.account_transactions[self.rrsp],
                             Money(-3000))
     self.assertTransactions(
         self.forecast.account_transactions[self.account], Money(-17000))
Example #23
0
 def test_cont_inf_adjust_basic(self, *args, **kwargs):
     """ Test inflation-adjust for `contribution_room` accrual max. """
     # Start with the last year for which we know the nominal accrual
     # max already. The next year's accrual max will need to be
     # estimated via inflation-adjustment:
     self.set_initial_year(max(constants.RRSP_ACCRUAL_MAX))
     # Inflation-adjust the (known) accrual max for the previous year
     # to get the max for this year.
     max_accrual = (
         constants.RRSP_ACCRUAL_MAX[self.initial_year] *
         self.inflation_adjust(self.initial_year + 1, self.initial_year))
     # Let's have income that's between the initial year's max
     # accrual and the next year's max accrual:
     income = Money(
         (max_accrual + constants.RRSP_ACCRUAL_MAX[self.initial_year]) /
         2) / constants.RRSP_ACCRUAL_RATE
     self.owner.gross_income = income
     account = self.AccountType(
         self.owner,
         *args,
         rate=0,
         inflation_adjust=self.inflation_adjust,
         contribution_room=self.initial_contribution_room,
         **kwargs)
     account.next_year()
     # New contribution room should be simply determined by the
     # accrual rate set in Constants plus rollover.
     self.assertEqual(
         account.contribution_room, self.initial_contribution_room +
         constants.RRSP_ACCRUAL_RATE * income)
Example #24
0
    def test_living_const_living(self):
        """ Test living expenses based on constant living expenses. """
        # Live off of $1200/yr:
        self.strategy = LivingExpensesStrategy(
            strategy=LivingExpensesStrategy.strategy_const_living_expenses,
            base_amount=Money(1200))
        self.forecast.living_expenses_strategy = self.strategy

        # It's not necessary to record inflows from employment,
        # but since this is usually how it'll be called we do
        # so here:
        self.forecast(self.available)

        # Calculate manually and compare results:
        living_expenses = Money(1200)
        self.assertEqual(living_expenses, self.forecast.living_expenses)
Example #25
0
    def test_init_type_conversion(self, *args, **kwargs):
        """ Test type conversion of __init__ inputs. """
        super().test_init_type_conversion(*args, **kwargs)

        # Try type conversion for inflation_adjustments
        inflation_adjustments = {
            '2000': '1',
            2001.0: 1.25,
            Decimal(2002): 1.5,
            2003: Decimal('1.75'),
            2017.0: Decimal(2.0)
        }

        account = self.AccountType(self.owner,
                                   *args,
                                   contribution_room=500,
                                   inflation_adjust=inflation_adjustments,
                                   **kwargs)
        self.assertEqual(account.contributor, self.owner)
        self.assertEqual(account.contribution_room, Money('500'))
        self.assertEqual(account.inflation_adjust(2000), Decimal(1))
        self.assertEqual(account.inflation_adjust(2001), Decimal(1.25))
        self.assertEqual(account.inflation_adjust(2002), Decimal(1.5))
        self.assertEqual(account.inflation_adjust(2003), Decimal(1.75))
        self.assertEqual(account.inflation_adjust(2017), Decimal(2))
Example #26
0
 def test_call_money(self):
     """ Test TaxCanada.__call__ on Money input """
     taxable_income = Money(100000)
     self.assertEqual(
         self.tax(taxable_income, self.initial_year),
         self.tax.federal_tax(taxable_income, self.initial_year) +
         self.tax.provincial_tax(taxable_income, self.initial_year))
Example #27
0
 def test_capital_gain_real_start(self, *args, **kwargs):
     """ Test capital gains realized at start of year. """
     # Init account with $50 acb.
     # Balance is $100, of which $50 is capital gains.
     account = self.AccountType(
         self.owner, *args,
         acb=50, balance=100, rate=1, **kwargs)
     # Add a $100 start-of-year transaction. This should increase
     # ACB (to $150), but doesn't change capital gains:
     account.add_transaction(100, 'start')
     # Withdraw the entire starting balance.
     account.add_transaction(-200, 'start')
     # Regardless of the transaction, capital gain is only $50:
     self.assertEqual(account.capital_gain, Money(50))
     # ACB is unchanged, since it's the start of year figure:
     self.assertEqual(account.acb, Money(50))
Example #28
0
 def test_capital_gain_real_end(self, *args, **kwargs):
     """ Test capital gains realized at end of year. """
     # Init account with $50 acb.
     # Balance is $100, of which $50 is capital gains.
     account = self.AccountType(
         self.owner, *args,
         acb=50, balance=100, rate=1, **kwargs)
     # Add a $100 start-of-year transaction. This should increase
     # ACB to $150; the end of year balance will be $400, with $250
     # in capital gains:
     account.add_transaction(100, 'start')
     # Withdraw the entire ending balance.
     account.add_transaction(-400, 'end')
     self.assertEqual(account.capital_gain, Money(250))
     # ACB is unchanged, since it's the start of year figure:
     self.assertEqual(account.acb, Money(50))
Example #29
0
 def test_max_inflows_no_accel(self):
     """ Test `max_inflows` with zero `accelerated_payment`. """
     self.debt.minimum_payment = 100
     self.debt.balance = Money(-200)
     self.debt.accelerated_payment = 0
     result = self.debt.max_inflows(self.timing)
     # Total inflows should be limited to minimum_payment:
     self.assertEqual(sum(result.values()), self.debt.minimum_payment)
Example #30
0
 def test_taxable_income_zero(self, *args, **kwargs):
     """ Test TaxableAccount.taxable_income with no sales. """
     # Balance is $100, of which $50 is capital gains.
     account = self.AccountType(
         self.owner, *args,
         acb=50, balance=100, rate=1, **kwargs)
     # No capital gains are realized yet, so capital_gains=$0
     self.assertEqual(account.taxable_income, Money(0))