def testRRSPForcedWithdrawActive(self): fund = funds.RRSP() fund.amount = 10000 year_rec = utils.YearRecord() year_rec.age = 70 fund.Update(year_rec) self.assertEqual(fund.forced_withdraw, 528)
def testRRSPRoomLimit(self): fund = funds.RRSP() year_rec = utils.YearRecord() year_rec.incomes.append(incomes.IncomeReceipt(140000, incomes.INCOME_TYPE_EARNINGS)) self.assertEqual(fund.room, world.RRSP_INITIAL_LIMIT) fund.Update(year_rec) self.assertEqual(fund.room, world.RRSP_INITIAL_LIMIT+world.RRSP_LIMIT)
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 testChainedTransactionDifferentProportion(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 = 100 nonreg = funds.NonRegistered() fund_chain = (rrsp, tfsa, nonreg) withdrawal_proportions = (0.5, 0.5, 1) deposit_proportions = (0.5, 0.8, 1) withdrawn, gains, year_rec = funds.ChainedTransaction( 60, fund_chain, withdrawal_proportions, deposit_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(16, funds.FUND_TYPE_TFSA), funds.DepositReceipt(4, funds.FUND_TYPE_NONREG)]) self.assertEqual(rrsp.amount, 20) self.assertEqual(tfsa.amount, 66) self.assertEqual(nonreg.amount, 4)
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 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"]] # Not using ChainedWithdraw because it can't express strict overflow withdrawn, _, year_rec = funds.ChainedTransaction( top_up_amount, fund_chain, (1, 1), (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_withdrawal_table = world.GenerateCEDDrawdownTable( self.age, world.CPP_EXPECTED_RETIREMENT_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 / year_rec.cpi 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 __init__(self, strategy, gender=FEMALE, basic_only=False, real_values=True): self.year = world.BASE_YEAR self.age = world.START_AGE self.gender = gender self.strategy = strategy self.cpi = 1 # Ignoring factor of 100 and StatsCan rounding rules here. self.cpi_history = [] self.basic_only = basic_only self.real_values = real_values self.employed_last_year = True self.retired = False # CAUTION: GIS must be the last income in the list. self.incomes = [ incomes.Earnings(), incomes.EI(), incomes.CPP(), incomes.OAS(), incomes.GIS() ] self.funds = { "wp_tfsa": funds.TFSA(), "wp_rrsp": funds.RRSP(), "wp_nonreg": funds.NonRegistered() } self.involuntary_retirement_random = random.random() self.tfsa_room = world.TFSA_INITIAL_CONTRIBUTION_LIMIT self.rrsp_room = world.RRSP_INITIAL_LIMIT self.capital_loss_carry_forward = 0 self.accumulators = utils.AccumulatorBundle() self.has_been_ruined = False self.has_received_gis = False self.has_experienced_income_under_lico = False # The following hold real dollar amounts self.assets_at_retirement = 0 self.total_retirement_withdrawals = 0 self.total_lifetime_withdrawals = 0 self.total_working_savings = 0 self.positive_earnings_years = 0 self.positive_savings_years = 0 self.ei_years = 0 self.gis_years = 0 self.gross_income_below_lico_years = 0 self.no_assets_years = 0 self.period_years = { EMPLOYED: 0, UNEMPLOYED: 0, RETIRED: 0, INVOLUNTARILY_RETIRED: 0 }
def testRRSPDeposit(self): fund = funds.RRSP() year_rec = utils.YearRecord() year_rec.rrsp_room = 20 deposited, year_rec = fund.Deposit(15, year_rec) self.assertEqual(deposited, 15) self.assertEqual(fund.amount, 15) self.assertIn(funds.DepositReceipt(15, funds.FUND_TYPE_RRSP), year_rec.deposits) self.assertEqual(year_rec.rrsp_room, 5)
def testRRSPUpdate(self): fund = funds.RRSP() fund.amount = 20 year_rec = utils.YearRecord() year_rec.growth_rate = 0.2 year_rec.inflation = 1 year_rec.incomes.append(incomes.IncomeReceipt(10000, incomes.INCOME_TYPE_EARNINGS)) fund.Update(year_rec) self.assertEqual(fund.amount, 48) self.assertEqual(fund.unrealized_gains, 0)
def testRRSPWithdraw(self): fund = funds.RRSP() fund.amount = 20 year_rec = utils.YearRecord() year_rec.rrsp_room = 0 withdrawn, gains, year_rec = fund.Withdraw(15, year_rec) self.assertEqual(withdrawn, 15) self.assertEqual(fund.amount, 5) self.assertIn(funds.WithdrawReceipt(15, 0, funds.FUND_TYPE_RRSP), year_rec.withdrawals) self.assertEqual(year_rec.rrsp_room, 0)
def testChainedDepositInsufficientRoom(self): year_rec = utils.YearRecord() tfsa = funds.TFSA() year_rec.tfsa_room = 30 rrsp = funds.RRSP() year_rec.rrsp_room = 50 fund_chain = (tfsa, rrsp) proportions = (1, 1) deposited, year_rec = funds.ChainedDeposit(100, fund_chain, proportions, year_rec) self.assertEqual(deposited, 80) self.assertSequenceEqual(year_rec.deposits, [funds.DepositReceipt(30, funds.FUND_TYPE_TFSA), funds.DepositReceipt(50, funds.FUND_TYPE_RRSP)]) self.assertEqual(tfsa.amount, 30) self.assertEqual(rrsp.amount, 50)
def _SetUpChain(tfsa_amount=0, tfsa_room=0, rrsp_amount=0, rrsp_room=0, bridging_amount=0, nonreg_amount=0, nonreg_gains=0): year_rec = utils.YearRecord() tfsa = funds.TFSA() tfsa.amount = tfsa_amount year_rec.tfsa_room = tfsa_room rrsp = funds.RRSP() rrsp.amount = rrsp_amount year_rec.rrsp_room = rrsp_room bridging = funds.RRSPBridging() bridging.amount = bridging_amount nonreg = funds.NonRegistered() nonreg.amount = nonreg_amount nonreg.unrealized_gains = nonreg_gains return year_rec, tfsa, rrsp, bridging, nonreg
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 testChainedDepositProportions(self): year_rec = utils.YearRecord() tfsa = funds.TFSA() year_rec.tfsa_room = 30 rrsp = funds.RRSP() year_rec.rrsp_room = 30 nonreg = funds.NonRegistered() fund_chain = (tfsa, rrsp, nonreg) proportions = (0.2, 0.5, 1) deposited, year_rec = funds.ChainedDeposit(100, fund_chain, proportions, year_rec) self.assertEqual(deposited, 100) self.assertSequenceEqual(year_rec.deposits, [funds.DepositReceipt(20, funds.FUND_TYPE_TFSA), funds.DepositReceipt(30, funds.FUND_TYPE_RRSP), funds.DepositReceipt(50, funds.FUND_TYPE_NONREG)]) self.assertEqual(tfsa.amount, 20) self.assertEqual(rrsp.amount, 30) self.assertEqual(nonreg.amount, 50)
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)