def testChainedWithdrawForcedWithdrawPreferZero(self): year_rec = utils.YearRecord() year_rec.age = 94 year_rec.growth_rate=0 rrsp = funds.RRSP() rrsp.amount = 50 rrsp.Update(year_rec) tfsa = funds.TFSA() tfsa.amount = 50 nonreg = funds.NonRegistered() nonreg.amount = 30 nonreg.unrealized_gains = 15 fund_chain = (rrsp, tfsa, nonreg) proportions = (0, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(60, fund_chain, proportions, utils.YearRecord()) self.assertEqual(withdrawn, 60) self.assertEqual(gains, 12.5) self.assertSequenceEqual( year_rec.withdrawals, [funds.WithdrawReceipt(10, 0, funds.FUND_TYPE_RRSP), funds.WithdrawReceipt(25, 0, funds.FUND_TYPE_TFSA), funds.WithdrawReceipt(25, 12.5, funds.FUND_TYPE_NONREG)]) self.assertEqual(rrsp.amount, 40) self.assertEqual(tfsa.amount, 25) self.assertEqual(nonreg.amount, 5)
def testChainedWithdrawForcedWithdrawProportionalDeposit(self): year_rec = utils.YearRecord() rrsp = funds.RRSP() rrsp.amount = 100 rrsp.forced_withdraw = 80 tfsa = funds.TFSA() tfsa.amount = 50 year_rec.tfsa_room = 50 nonreg = funds.NonRegistered() nonreg.amount = 50 nonreg.unrealized_gains = 25 fund_chain = (rrsp, tfsa, nonreg) proportions = (0.1, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(60, fund_chain, proportions, year_rec) self.assertEqual(withdrawn, 60) self.assertEqual(gains, 0) self.assertSequenceEqual( year_rec.withdrawals, [funds.WithdrawReceipt(80, 0, funds.FUND_TYPE_RRSP)]) self.assertSequenceEqual( year_rec.deposits, [funds.DepositReceipt(10, funds.FUND_TYPE_TFSA), funds.DepositReceipt(10, funds.FUND_TYPE_NONREG)]) self.assertEqual(rrsp.amount, 20) self.assertEqual(tfsa.amount, 60) self.assertEqual(nonreg.amount, 60)
def testChainedWithdrawRealScenario(self): year_rec, tfsa, rrsp, _, nonreg = _SetUpChain( rrsp_amount=248968, tfsa_amount=304691, nonreg_amount=39) fund_chain = (rrsp, tfsa, nonreg) proportions = (0.35, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(16603, fund_chain, proportions, year_rec) self.assertAlmostEqual(withdrawn, 16603)
def OnRetirement(self, year_rec): """This deals with events happening at the point of retirement.""" # Update all incomes for income in self.incomes: income.OnRetirement(self) # Create RRSP bridging fund if needed if self.age < world.CPP_EXPECTED_RETIREMENT_AGE: requested = ( world.CPP_EXPECTED_RETIREMENT_AGE - self.age ) * world.OAS_BENEFIT * self.strategy.oas_bridging_fraction self.funds["wp_rrsp"], self.funds["bridging"] = funds.SplitFund( self.funds["wp_rrsp"], funds.RRSPBridging(), requested) if self.funds["bridging"].amount < requested: top_up_amount = min(self.rrsp_room, requested - self.funds["bridging"].amount) fund_chain = [self.funds["wp_nonreg"], self.funds["wp_tfsa"]] withdrawn, _, year_rec = funds.ChainedWithdraw( top_up_amount, fund_chain, (1, 1), year_rec) self.funds["bridging"].amount += withdrawn year_rec.deposits.append( funds.DepositReceipt(withdrawn, funds.FUND_TYPE_RRSP)) self.rrsp_room -= withdrawn self.bridging_annual_withdrawal = self.funds["bridging"].amount / ( world.CPP_EXPECTED_RETIREMENT_AGE - self.age) # Split each fund into a CED and a CD fund self.funds["cd_rrsp"], self.funds["ced_rrsp"] = funds.SplitFund( self.funds["wp_rrsp"], funds.RRSP(), self.strategy.drawdown_ced_fraction * self.funds["wp_rrsp"].amount) del self.funds["wp_rrsp"] self.funds["cd_tfsa"], self.funds["ced_tfsa"] = funds.SplitFund( self.funds["wp_tfsa"], funds.TFSA(), self.strategy.drawdown_ced_fraction * self.funds["wp_tfsa"].amount) del self.funds["wp_tfsa"] self.funds["cd_nonreg"], self.funds["ced_nonreg"] = funds.SplitFund( self.funds["wp_nonreg"], funds.NonRegistered(), self.strategy.drawdown_ced_fraction * self.funds["wp_nonreg"].amount) del self.funds["wp_nonreg"] self.cd_drawdown_amount = sum(fund.amount for fund in ( self.funds["cd_rrsp"], self.funds["cd_tfsa"], self.funds["cd_nonreg"])) * self.strategy.initial_cd_fraction self.assets_at_retirement = sum( fund.amount for fund in self.funds.values()) / year_rec.cpi if not self.basic_only: self.accumulators.fraction_persons_involuntarily_retired.UpdateOneValue( 1 if self.age < self.strategy.planned_retirement_age else 0)
def testChainedWithdrawTwoZeroFunds(self): year_rec, tfsa, rrsp, _, nonreg = _SetUpChain( rrsp_amount=0, tfsa_amount=0, nonreg_amount=80) fund_chain = (rrsp, tfsa, nonreg) proportions = (1/3, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(60, fund_chain, proportions, year_rec) self.assertAlmostEqual(withdrawn, 60) self.assertAlmostEqual(rrsp.amount, 0) self.assertAlmostEqual(tfsa.amount, 0) self.assertAlmostEqual(nonreg.amount, 20)
def testChainedWithdrawPartialInsufficientFundsOneAdjustment(self): year_rec, tfsa, rrsp, _, nonreg = _SetUpChain( rrsp_amount=30, tfsa_amount=16, nonreg_amount=40) fund_chain = (rrsp, tfsa, nonreg) proportions = (1/3, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(60, fund_chain, proportions, year_rec) self.assertAlmostEqual(withdrawn, 60) self.assertAlmostEqual(rrsp.amount, 8) self.assertAlmostEqual(tfsa.amount, 0) self.assertAlmostEqual(nonreg.amount, 18)
def testChainedWithdrawInsufficientFunds(self): year_rec, tfsa, rrsp, _, nonreg = _SetUpChain( rrsp_amount=20, tfsa_amount=50, nonreg_amount=20) fund_chain = (rrsp, tfsa, nonreg) proportions = (0.1, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(160, fund_chain, proportions, year_rec) self.assertAlmostEqual(withdrawn, 90) self.assertAlmostEqual(rrsp.amount, 0) self.assertAlmostEqual(tfsa.amount, 0) self.assertAlmostEqual(nonreg.amount, 0)
def testChainedWithdrawSufficientFunds(self): year_rec, tfsa, rrsp, _, nonreg = _SetUpChain( rrsp_amount=20, tfsa_amount=50, nonreg_amount=30, nonreg_gains=15) fund_chain = (rrsp, tfsa, nonreg) proportions = (0.1, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(60, fund_chain, proportions, year_rec) self.assertAlmostEqual(withdrawn, 60) self.assertAlmostEqual(gains, 13.5) self.assertAlmostEqual(rrsp.amount, 14) self.assertAlmostEqual(tfsa.amount, 23) self.assertAlmostEqual(nonreg.amount, 3) self.assertAlmostEqual(nonreg.unrealized_gains, 1.5)
def testChainedWithdrawForcedWithdrawWantZero(self): rrsp = funds.RRSP() rrsp.amount = 100 rrsp.forced_withdraw = 20 nonreg = funds.NonRegistered() fund_chain = (rrsp, nonreg) proportions = (0, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw(0, fund_chain, proportions, utils.YearRecord()) self.assertEqual(withdrawn, 0) self.assertEqual(gains, 0) self.assertSequenceEqual( year_rec.withdrawals, [funds.WithdrawReceipt(20, 0, funds.FUND_TYPE_RRSP)]) self.assertSequenceEqual( year_rec.deposits, [funds.DepositReceipt(20, funds.FUND_TYPE_NONREG)]) self.assertEqual(rrsp.amount, 80) self.assertEqual(nonreg.amount, 20)
def testChainedWithdrawForcedWithdrawOverflow(self): rrsp = funds.RRSP() rrsp.amount = 100 rrsp.forced_withdraw = 80 tfsa = funds.TFSA() tfsa.amount = 50 tfsa.room = 0 fund_chain = (rrsp, tfsa) proportions = (0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw( 60, fund_chain, proportions, utils.YearRecord()) self.assertEqual(withdrawn, 80) self.assertEqual(gains, 0) self.assertSequenceEqual( year_rec.withdrawals, [funds.WithdrawReceipt(80, 0, funds.FUND_TYPE_RRSP)]) self.assertSequenceEqual( year_rec.deposits, [funds.DepositReceipt(0, funds.FUND_TYPE_TFSA)]) self.assertEqual(rrsp.amount, 20) self.assertEqual(tfsa.amount, 50)
def testChainedWithdrawPartialInsufficientFunds(self): rrsp = funds.RRSP() rrsp.amount = 20 tfsa = funds.TFSA() tfsa.amount = 20 nonreg = funds.NonRegistered() nonreg.amount = 40 nonreg.unrealized_gains = 20 fund_chain = (rrsp, tfsa, nonreg) proportions = (0.1, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw( 60, fund_chain, proportions, utils.YearRecord()) self.assertEqual(withdrawn, 60) self.assertEqual(gains, 17) self.assertSequenceEqual(year_rec.withdrawals, [ funds.WithdrawReceipt(6, 0, funds.FUND_TYPE_RRSP), funds.WithdrawReceipt(20, 0, funds.FUND_TYPE_TFSA), funds.WithdrawReceipt(34, 17, funds.FUND_TYPE_NONREG) ]) self.assertEqual(rrsp.amount, 14) self.assertEqual(tfsa.amount, 0) self.assertEqual(nonreg.amount, 6)
def testChainedWithdrawInsufficientFunds(self): # TODO check if this is correct behaviour, or if we need more complicated # logic for insufficient funds at the end of the chain rrsp = funds.RRSP() rrsp.amount = 20 tfsa = funds.TFSA() tfsa.amount = 50 nonreg = funds.NonRegistered() nonreg.amount = 20 nonreg.unrealized_gains = 10 fund_chain = (rrsp, tfsa, nonreg) proportions = (0.1, 0.5, 1) withdrawn, gains, year_rec = funds.ChainedWithdraw( 60, fund_chain, proportions, utils.YearRecord()) self.assertEqual(withdrawn, 53) self.assertEqual(gains, 10) self.assertSequenceEqual(year_rec.withdrawals, [ funds.WithdrawReceipt(6, 0, funds.FUND_TYPE_RRSP), funds.WithdrawReceipt(27, 0, funds.FUND_TYPE_TFSA), funds.WithdrawReceipt(20, 10, funds.FUND_TYPE_NONREG) ]) self.assertEqual(rrsp.amount, 14) self.assertEqual(tfsa.amount, 23) self.assertEqual(nonreg.amount, 0)
def MeddleWithCash(self, year_rec): """This performs all operations on subject's cash pile""" cash = 0 # Get money from incomes. GIS is excluded and done after withdrawals for income in self.incomes[:-1]: amount, taxable, year_rec = income.GiveMeMoney(year_rec) cash += amount # Update RRSP room earnings = sum(receipt.amount for receipt in year_rec.incomes if receipt.income_type == incomes.INCOME_TYPE_EARNINGS) self.rrsp_room += min( earnings * world.RRSP_ACCRUAL_FRACTION, utils.Indexed(world.RRSP_LIMIT, year_rec.year, 1 + world.PARGE) * year_rec.cpi) year_rec.rrsp_room = self.rrsp_room # Do withdrawals if self.retired: # Bridging if "bridging" in self.funds and self.age < world.CPP_EXPECTED_RETIREMENT_AGE: bridging_withdrawal_amount = self.bridging_withdrawal_table[ self.age] * self.funds["bridging"].amount withdrawn, gains, year_rec = self.funds["bridging"].Withdraw( bridging_withdrawal_amount, year_rec) cash += withdrawn self.total_retirement_withdrawals += withdrawn / year_rec.cpi # CD drawdown strategy proportions = (self.strategy.drawdown_preferred_rrsp_fraction, self.strategy.drawdown_preferred_tfsa_fraction, 1) fund_chain = [ self.funds["cd_rrsp"], self.funds["cd_tfsa"], self.funds["cd_nonreg"] ] year_rec.cd_drawdown_request = self.cd_drawdown_amount * year_rec.cpi withdrawn, gains, year_rec = funds.ChainedWithdraw( year_rec.cd_drawdown_request, fund_chain, proportions, year_rec) cash += withdrawn year_rec.cd_drawdown_amount = withdrawn self.total_retirement_withdrawals += withdrawn / year_rec.cpi # CED drawdown_strategy fund_chain = [ self.funds["ced_rrsp"], self.funds["ced_tfsa"], self.funds["ced_nonreg"] ] year_rec.ced_drawdown_request = sum( f.amount for f in fund_chain) * world.CED_PROPORTION[self.age] withdrawn, gains, year_rec = funds.ChainedWithdraw( year_rec.ced_drawdown_request, fund_chain, proportions, year_rec) cash += withdrawn year_rec.ced_drawdown_amount = withdrawn self.total_retirement_withdrawals += withdrawn / year_rec.cpi else: target_cash = utils.Indexed( world.YMPE, year_rec.year, 1 + world.PARGE ) * year_rec.cpi * self.strategy.savings_threshold * world.EARNINGS_YMPE_FRACTION if cash < target_cash: # Attempt to withdraw difference from savings amount_to_withdraw = target_cash - cash proportions = ( self.strategy.working_period_drawdown_tfsa_fraction, self.strategy.working_period_drawdown_nonreg_fraction, 1) fund_chain = [ self.funds["wp_tfsa"], self.funds["wp_nonreg"], self.funds["wp_rrsp"] ] withdrawn, gains, year_rec = funds.ChainedWithdraw( amount_to_withdraw, fund_chain, proportions, year_rec) cash += withdrawn else: # Save earnings_to_save = max(earnings - target_cash, 0) * self.strategy.savings_rate proportions = (self.strategy.savings_rrsp_fraction, self.strategy.savings_tfsa_fraction, 1) fund_chain = [ self.funds["wp_rrsp"], self.funds["wp_tfsa"], self.funds["wp_nonreg"] ] deposited, year_rec = funds.ChainedDeposit( earnings_to_save, fund_chain, proportions, year_rec) cash -= deposited if deposited > 0: self.positive_savings_years += 1 # Update funds for fund in self.funds.values(): fund.Update(year_rec) # update the Person's view of RRSP and TFSA room self.tfsa_room = year_rec.tfsa_room self.rrsp_room = year_rec.rrsp_room # Calculate EI premium and CPP contributions year_rec = self.CalcPayrollDeductions(year_rec) # Now we try to get money from GIS because year_rec is populated with the needed values. income = self.incomes[-1] # GIS is last in this list amount, taxable, year_rec = income.GiveMeMoney(year_rec) cash += amount # Pay income taxes year_rec.taxes_payable = self.CalcIncomeTax(year_rec) cash -= year_rec.taxes_payable # Update incomes for income in self.incomes: income.AnnualUpdate(year_rec) # Pay sales tax non_hst_consumption = min(cash, world.SALES_TAX_EXEMPTION) hst_consumption = cash - non_hst_consumption year_rec.consumption = hst_consumption / ( 1 + world.HST_RATE) + non_hst_consumption year_rec.sales_taxes = hst_consumption * world.HST_RATE return year_rec