def setUp(self): """ Builds stock variables to test with. """ self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {0: 0.5}}) # A person who is paid $1000 gross ($500 withheld): timing = Timing(frequency='BW') self.person1 = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=1000, tax_treatment=tax, payment_timing=timing) # A person who is paid $500 gross ($250 withheld): self.person2 = Person(initial_year=self.initial_year, name="Test 2", birth_date="1 January 1982", retirement_date="31 December 2047", gross_income=500, tax_treatment=tax, payment_timing=timing) # An account owned by person1 with $100 to withdraw self.account1 = Account(owner=self.person1, balance=100, rate=0) # An account owned by person2 with $200 to withdraw self.account2 = Account(owner=self.person2, balance=100, rate=0) # An account that belongs in some sense to both people # with $50 to withdraw self.account_joint = Account(owner=self.person1, balance=100, rate=0) self.person2.accounts.add(self.account_joint) self.forecast = TaxForecast(initial_year=self.initial_year, people={self.person1, self.person2}, tax_treatment=tax)
def setUp(self): """ Builds stock variables to test with. """ self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {0: 0.5}}) # A person who is paid $200 gross ($100 net) every 2 weeks: timing = Timing(frequency='BW') self.person1 = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=5200, tax_treatment=tax, payment_timing=timing) # A person who is paid $100 gross ($50 net) every 2 weeks: self.person2 = Person(initial_year=self.initial_year, name="Test 2", birth_date="1 January 1982", retirement_date="31 December 2047", gross_income=2600, tax_treatment=tax, payment_timing=timing) # Track inflows from employment: self.available = {0.5 + i / 26: 150 for i in range(26)} self.total_available = sum(self.available.values()) # Contribute 50% of net income (i.e. $3900): self.strategy = LivingExpensesStrategy( strategy=LivingExpensesStrategy.strategy_gross_percent, rate=0.5) self.forecast = LivingExpensesForecast( initial_year=self.initial_year, people={self.person1, self.person2}, living_expenses_strategy=self.strategy)
def setUp_decimal(self): """ Set up stock variables with Decimal inputs. """ self.initial_year = 2000 # Live on $24000/yr while working and $12000/yr in retirement: self.working = LivingExpensesStrategy( LivingExpensesStrategy.strategy_const_living_expenses, base_amount=Decimal(24000)) self.retirement = LivingExpensesStrategy( LivingExpensesStrategy.strategy_const_living_expenses, base_amount=Decimal(12000)) self.strategy = LivingExpensesStrategySchedule(self.working, self.retirement) # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {Decimal(0): Decimal(0.5)}}) # Set up a person with $50000 gross income, $2000 net income: self.person1 = Person( initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2001", # next year gross_income=Decimal(50000), tax_treatment=tax, payment_timing=Timing(frequency="BW")) self.people = {self.person1}
def test_next(self): """ Test next_year to confirm that properties are advanced. """ initial_year = 2017 gross_income = 100 tax = Tax({initial_year: { 0: 0, 200: 0.5, 1000: 0.75 }}, inflation_adjust={ 2017: 1, 2018: 1, 2019: 1, 2020: 1 }, personal_deduction={2017: 0}) person = Person(initial_year, 'Name', 2000, raise_rate=2.0, retirement_date=self.retirement_date, gross_income=gross_income, tax_treatment=tax) self.assertEqual(person.gross_income, gross_income) self.assertEqual(person.net_income, Money(100)) self.assertEqual(person.this_year, initial_year) person.next_year() # 200% raise - gross income is now $300 self.assertEqual(person.gross_income, Money(300)) self.assertEqual(person.net_income, Money(250)) self.assertEqual(person.this_year, initial_year + 1)
def setUp_decimal(self): """ Builds stock variables with Decimal inputs. """ # pylint: disable=invalid-name # Pylint doesn't like `setUp_decimal`, but it's not our naming # convention, so don't complain to us! # pylint: enable=invalid-name self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {Decimal(0): Decimal(0.5)}}) # A person who is paid $200 gross ($100 net) every 2 weeks: timing = Timing(frequency='BW', high_precision=Decimal) self.person1 = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=Decimal(5200), tax_treatment=tax, payment_timing=timing, high_precision=Decimal) # A person who is paid $100 gross ($50 net) every 2 weeks: self.person2 = Person(initial_year=self.initial_year, name="Test 2", birth_date="1 January 1982", retirement_date="31 December 2047", gross_income=Decimal(2600), tax_treatment=tax, payment_timing=timing, high_precision=Decimal) self.forecast = IncomeForecast(initial_year=self.initial_year, people={self.person1, self.person2}, high_precision=Decimal)
def setUp(self): """ Builds a default Person for testing of complex objects. """ # Build inputs to Person: self.settings = Settings() # default settings object self.initial_year = self.settings.initial_year # convenience self.scenario = Scenario( inflation=self.settings.inflation, stock_return=self.settings.stock_return, bond_return=self.settings.bond_return, other_return=self.settings.other_return, management_fees=self.settings.management_fees, initial_year=self.settings.initial_year, num_years=self.settings.num_years) self.tax_treatment = Tax( tax_brackets=self.settings.tax_brackets, personal_deduction=self.settings.tax_personal_deduction, credit_rate=self.settings.tax_credit_rate, inflation_adjust=self.scenario.inflation_adjust) # Build a Person object to test against later: self.person = Person( initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2040", gross_income=10000, raise_rate=0, spouse=None, tax_treatment=self.tax_treatment)
def setUp(self): """ Builds stock variables to test with. """ self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={ self.initial_year: {0: 0.5}}) # Accounts need an owner: timing = Timing(frequency='BW') self.person = Person( initial_year=self.initial_year, name="Test", birth_date="1 January 1980", retirement_date="31 December 1999", # last year gross_income=5200, tax_treatment=tax, payment_timing=timing) # We want at least two accounts which are withdrawn from # in different orders depending on the strategy. self.account = Account( owner=self.person, balance=60000) # $60,000 <- BIGGER! self.rrsp = canada.accounts.RRSP( owner=self.person, contribution_room=1000, balance=6000) # $6,000 # Assume there are $2000 in inflows and $22,000 in outflows, # for a net need of $20,000: self.available = { 0.25: 1000, 0.5: -11000, 0.75: 1000, 1: -11000 } # Now we can set up the big-ticket items: self.strategy = TransactionStrategy( strategy=TransactionStrategy.strategy_ordered, weights={"RRSP": 1, "Account": 2}) self.forecast = WithdrawalForecast( initial_year=self.initial_year, people={self.person}, accounts={self.account, self.rrsp}, transaction_strategy=self.strategy) # Set up another forecast for testing withholding behaviour: self.withholding_account = WithholdingAccount( owner=self.person, balance=100000) self.withholding_strategy = TransactionStrategy( strategy=TransactionStrategy.strategy_ordered, weights={"WithholdingAccount": 1}) self.withholding_forecast = WithdrawalForecast( initial_year=self.initial_year, people={self.person}, accounts={self.withholding_account}, transaction_strategy=self.withholding_strategy)
def setUp_decimal(self): """ Set up stock variables based on Decimal inputs. """ # Set up inflation of various rates covering 1999-2002: self.year_half = 1999 # -50% inflation (values halved) in this year self.year_1 = 2000 # baseline year; no inflation self.year_2 = 2001 # 100% inflation (values doubled) in this year self.year_10 = 2002 # Values multiplied by 10 in this year self.inflation_adjustment = { self.year_half: Decimal(0.5), self.year_1: Decimal(1), self.year_2: Decimal(2), self.year_10: Decimal(10) } # We need to provide a callable object that returns inflation # adjustments between years. Build that here: def variable_inflation(year, base_year=self.year_1): """ Returns inflation-adjustment factor between two years. """ return (self.inflation_adjustment[year] / self.inflation_adjustment[base_year]) self.variable_inflation = variable_inflation # Build all the objects we need to build an instance of # `LivingExpensesStrategy`: self.initial_year = self.year_1 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {Decimal(0): Decimal(0.5)}}) # Set up people with $4000 gross income, $2000 net income: biweekly_timing = Timing(frequency="BW") self.person1 = Person( initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2001", # next year gross_income=Decimal(1000), tax_treatment=tax, payment_timing=biweekly_timing) self.person2 = Person( initial_year=self.initial_year, name="Test 2", birth_date="1 January 1975", retirement_date="31 December 2001", # next year gross_income=Decimal(3000), tax_treatment=tax, payment_timing=biweekly_timing) self.people = {self.person1, self.person2} # Give person1 a $1000 account and person2 a $9,000 account: self.account1 = Account(owner=self.person1, balance=Decimal(1000), rate=0) self.account2 = Account(owner=self.person2, balance=Decimal(9000), rate=0)
def test_tax_deduction(self): """ Test Person.tax_deduction. """ initial_year = 2017 gross_income = 300 tax = Tax({initial_year: {0: 0, 200: 0.5, 1000: 0.75}}, {2017: 1, 2018: 1, 2019: 1, 2020: 1}, {2017: 0}) person = Person( self.initial_year, 'Name', 2000, retirement_date=self.retirement_date, gross_income=gross_income, tax_treatment=tax) self.assertEqual(person.tax_deduction, 0)
def test_init_type_conv_str(self): """ Tests Tax.__init__ with args requiring type-conversion. """ # Use an all-str dict and confirm that the output is correctly # typed tax_brackets = { str(year): { str(bracket.amount): str(self.tax_brackets[year][bracket]) for bracket in self.tax_brackets[year] } for year in self.tax_brackets } inflation_adjustments = { str(year): str(self.inflation_adjustments[year]) for year in self.inflation_adjustments } tax = Tax(tax_brackets, inflation_adjust=inflation_adjustments) for year in self.tax_brackets: self.assertEqual(tax.tax_brackets(year), self.tax_brackets[year]) self.assertTrue( type_check(tax.tax_brackets(year), {Money: Decimal})) self.assertTrue(callable(tax.inflation_adjust))
def setUp_decimal(self): """ Builds stock variables to test with. """ # pylint: disable=invalid-name # Pylint doesn't like `setUp_decimal`, but it's not our naming # convention, so don't complain to us! # pylint: enable=invalid-name self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {Decimal(0): Decimal(0.5)}}) # A person who is paid $1000 gross ($500 withheld): timing = Timing(frequency='BW', high_precision=Decimal) self.person1 = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=Decimal(1000), tax_treatment=tax, payment_timing=timing, high_precision=Decimal) # A person who is paid $500 gross ($250 withheld): self.person2 = Person(initial_year=self.initial_year, name="Test 2", birth_date="1 January 1982", retirement_date="31 December 2047", gross_income=Decimal(500), tax_treatment=tax, payment_timing=timing, high_precision=Decimal) # An account owned by person1 with $100 to withdraw self.account1 = Account(owner=self.person1, balance=Decimal(100), rate=Decimal(0), high_precision=Decimal) # An account owned by person2 with $200 to withdraw self.account2 = Account(owner=self.person2, balance=Decimal(100), rate=Decimal(0), high_precision=Decimal) # An account that belongs in some sense to both people # with $50 to withdraw self.account_joint = Account(owner=self.person1, balance=Decimal(100), rate=Decimal(0), high_precision=Decimal) self.person2.accounts.add(self.account_joint) self.forecast = TaxForecast(initial_year=self.initial_year, people={self.person1, self.person2}, tax_treatment=tax, high_precision=Decimal)
def setUp_decimal(self): """ Builds stock variables based on Decimal inputs. """ # pylint: disable=invalid-name # Pylint doesn't like `setUp_decimal`, but it's not our naming # convention, so don't complain to us! # pylint: enable=invalid-name self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: { Decimal(0): Decimal(0.5) }}, high_precision=Decimal) # Accounts need an owner: timing = Timing(frequency='BW', high_precision=Decimal) self.person = Person(initial_year=self.initial_year, name="Test", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=Decimal(5200), tax_treatment=tax, payment_timing=timing, high_precision=Decimal) # We want at least two accounts which are contributed to # in different orders depending on the strategy. self.account = Account(owner=self.person, high_precision=Decimal) self.rrsp = canada.accounts.RRSP(owner=self.person, contribution_room=Decimal(1000), high_precision=Decimal) # Track money available for use by the forecast: self.available = defaultdict(lambda: Decimal(0)) for i in range(26): # biweekly inflows from employment self.available[Decimal(0.5 + i) / 26] = Decimal(150) for i in range(12): # monthly living expenses and reductions: self.available[Decimal(i) / 12] -= Decimal(75) # The result: $3000 available self.total_available = sum(self.available.values()) # Now we can set up the big-ticket items: # Use an ordered strategy by default: self.strategy = TransactionTraversal([self.account, self.rrsp], high_precision=Decimal) self.forecast = SavingForecast( initial_year=self.initial_year, retirement_accounts={self.account, self.rrsp}, debt_accounts=set(), transaction_strategy=self.strategy, high_precision=Decimal)
def test_init_basic(self): """ Test Tax.__init__ with only mandatory arguments. """ tax = Tax(self.tax_brackets, inflation_adjust=self.inflation_adjustments) for year in self.tax_brackets: self.assertEqual(tax.tax_brackets(year), self.tax_brackets[year]) self.assertEqual(tax.accum(year), self.accum[year]) self.assertTrue(callable(tax.inflation_adjust)) self.assertEqual(tax.personal_deduction(self.initial_year), 0) self.assertEqual(tax.credit_rate(self.initial_year), 1)
def test_init_optional(self): """ Test Tax.__init__ with all arguments, including optional. """ tax = Tax(self.tax_brackets, inflation_adjust=self.inflation_adjustments, personal_deduction=self.personal_deduction, credit_rate=self.credit_rate) for year in self.tax_brackets: self.assertEqual(tax.tax_brackets(year), self.tax_brackets[year]) self.assertEqual(tax.accum(year), self.accum[year]) self.assertEqual(tax.personal_deduction(year), self.personal_deduction[year]) self.assertEqual(tax.credit_rate(year), self.credit_rate[year]) self.assertTrue(callable(tax.inflation_adjust))
def setUp_decimal(self): """ Builds stock variables to test with. """ # pylint: disable=invalid-name # Pylint doesn't like `setUp_decimal`, but it's not our naming # convention, so don't complain to us! # pylint: enable=invalid-name self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {Decimal(0): Decimal(0.5)}}) # A person who is paid $200 gross ($100 net) every 2 weeks: timing = Timing(frequency='BW', high_precision=Decimal) self.person1 = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=Decimal(5200), tax_treatment=tax, payment_timing=timing) # A person who is paid $100 gross ($50 net) every 2 weeks: self.person2 = Person(initial_year=self.initial_year, name="Test 2", birth_date="1 January 1982", retirement_date="31 December 2047", gross_income=Decimal(2600), tax_treatment=tax, payment_timing=timing) # Track inflows from employment: self.available = { Decimal(0.5 + i) / 26: Decimal(150) for i in range(26) } self.total_available = sum(self.available.values()) # Contribute 50% of net income (i.e. $3900): self.strategy = LivingExpensesStrategy( strategy=LivingExpensesStrategy.strategy_gross_percent, rate=Decimal(0.5)) self.forecast = LivingExpensesForecast( initial_year=self.initial_year, people={self.person1, self.person2}, living_expenses_strategy=self.strategy)
def setUp_decimal(self): """ Sets up default vaules using Decimal inputs """ self.initial_year = 2020 self.name = "Testy McTesterson" self.birth_date = datetime(2000, 2, 1) # 1 February 2000 self.retirement_date = datetime(2065, 6, 26) # 26 June 2065 self.gross_income = Decimal(100000) # $100000 self.raise_rate = Decimal(1) # 100% self.tax_treatment = Tax( {self.initial_year: { Decimal(0): Decimal('0.1'), Decimal(1000): Decimal('0.2'), Decimal(100000): Decimal('0.3')} }, inflation_adjust={ year: Decimal(1 + (year - self.initial_year) / 16) for year in range(self.initial_year, self.initial_year + 100) }, personal_deduction={self.initial_year: Decimal(100)}, credit_rate={self.initial_year: Decimal('0.15')}) self.spouse = Person( initial_year=self.initial_year, name="Spouse", birth_date=1998, retirement_date=2063, gross_income=Decimal(50000), raise_rate=self.raise_rate, spouse=None, tax_treatment=self.tax_treatment) self.owner = Person( initial_year=self.initial_year, name=self.name, birth_date=self.birth_date, retirement_date=self.retirement_date, gross_income=self.gross_income, raise_rate=self.raise_rate, spouse=self.spouse, tax_treatment=self.tax_treatment)
def setUp(self): """ Sets up default vaules for testing """ self.initial_year = 2020 self.name = "Testy McTesterson" self.birth_date = datetime(2000, 2, 1) # 1 February 2000 self.retirement_date = datetime(2065, 6, 26) # 26 June 2065 self.gross_income = 100000 # $100000 self.raise_rate = 1 # 100% self.tax_treatment = Tax( {self.initial_year: { 0: 0.1, 1000: 0.2, 100000: 0.3} }, inflation_adjust={ year: 1 + (year - self.initial_year / 16) for year in range(self.initial_year, self.initial_year + 100) }, personal_deduction={self.initial_year: 100}, credit_rate={self.initial_year: 0.15}) self.spouse = Person( initial_year=self.initial_year, name="Spouse", birth_date=1998, retirement_date=2063, gross_income=50000, raise_rate=self.raise_rate, spouse=None, tax_treatment=self.tax_treatment) self.owner = Person( initial_year=self.initial_year, name=self.name, birth_date=self.birth_date, retirement_date=self.retirement_date, gross_income=self.gross_income, raise_rate=self.raise_rate, spouse=self.spouse, tax_treatment=self.tax_treatment)
def test_tax_withheld(self): """ Test Person.tax_withheld. """ initial_year = 2017 gross_income = 300 tax = Tax({initial_year: { 0: 0, 200: 0.5, 1000: 0.75 }}, inflation_adjust={ 2017: 1, 2018: 1, 2019: 1, 2020: 1 }, personal_deduction={2017: 0}) person = Person(self.initial_year, 'Name', 2000, retirement_date=self.retirement_date, gross_income=gross_income, tax_treatment=tax) self.assertEqual(person.tax_withheld, Money(50))
def setUp(self): """ Builds stock variables to test with. """ self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {0: 0.5}}) # Accounts need an owner: timing = Timing(frequency='BW') self.person = Person(initial_year=self.initial_year, name="Test", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=5200, tax_treatment=tax, payment_timing=timing) # We want at least two accounts which are contributed to # in different orders depending on the strategy. self.account = Account(owner=self.person) self.rrsp = canada.accounts.RRSP(owner=self.person, contribution_room=1000) # Track money available for use by the forecast: self.available = defaultdict(lambda: 0) for i in range(26): # biweekly inflows from employment self.available[(0.5 + i) / 26] = 150 for i in range(12): # monthly living expenses and reductions: self.available[i / 12] -= 75 # The result: $3000 available self.total_available = sum(self.available.values()) # Now we can set up the big-ticket items: # Use an ordered strategy by default: self.strategy = TransactionTraversal([self.account, self.rrsp]) self.forecast = SavingForecast( initial_year=self.initial_year, retirement_accounts={self.account, self.rrsp}, debt_accounts=set(), transaction_strategy=self.strategy)
def test_decimal(self): """ Test compatibility with Decimal inputs. """ # Convert values to Decimal-based self.setUp_decimal() # The test itself is based on test_next initial_year = 2017 gross_income = 100 tax = Tax( {initial_year: { Decimal(0): Decimal(0), Decimal(200): Decimal(0.5), Decimal(1000): Decimal(0.75)}}, inflation_adjust={ 2017: Decimal(1), 2018: Decimal(1), 2019: Decimal(1), 2020: Decimal(1)}, personal_deduction={2017: Decimal(0)}) person = Person( initial_year, 'Name', 2000, raise_rate=Decimal(2.0), retirement_date=self.retirement_date, gross_income=gross_income, tax_treatment=tax) # In the first year: $100 gross income, $100 taxable income, # $100 net income. self.assertEqual(person.gross_income, gross_income) self.assertEqual(person.net_income, Decimal(100)) self.assertEqual(person.this_year, initial_year) self.assertEqual(person.taxable_income, Decimal(100)) person.next_year() # 200% raise # In the second year: $300 gross income, $300 taxable income, # $250 net income (as income over $200 is taxed at 50%) self.assertEqual(person.gross_income, Decimal(300)) self.assertEqual(person.net_income, Decimal(250)) self.assertEqual(person.this_year, initial_year + 1) self.assertEqual(person.taxable_income, Decimal(300))
def setUp(self): """ Builds stock variables to test with. """ self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={self.initial_year: {Money(0): Decimal(0.5)}}) # A person who is paid $200 gross ($100 net) every 2 weeks: timing = Timing(frequency='BW') self.person1 = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=Money(5200), tax_treatment=tax, payment_timing=timing) # A person who is paid $100 gross ($50 net) every 2 weeks: self.person2 = Person(initial_year=self.initial_year, name="Test 2", birth_date="1 January 1982", retirement_date="31 December 2047", gross_income=Money(2600), tax_treatment=tax, payment_timing=timing) self.forecast = IncomeForecast(initial_year=self.initial_year, people={self.person1, self.person2})
def setUp(self): """ Builds stock variables to test with. """ self.initial_year = 2000 # We will occasionally need to swap out subforecasts when # we want them to have no effect (e.g. no withdrawals because # we're not yet retired). Use null_forecast for that: self.null_forecast = DummyForecast(self.initial_year) # Paid $100 at the start of each month self.income_forecast_dummy = DummyForecast( self.initial_year, {when / 12: 100 for when in range(12)}) self.income_forecast_dummy.people = None # Spend $70 on living expenses at the start of each month self.living_expenses_forecast_dummy = DummyForecast( self.initial_year, {when / 12: -70 for when in range(12)}) # Contribute the balance ($30/mo, $360/yr): self.saving_forecast_dummy = DummyForecast( self.initial_year, {when + 1 / 12: -30 for when in range(12)}) # Withdraw $300 at the start and middle of the year: self.withdrawal_forecast_dummy = DummyForecast(self.initial_year, { 0: 300, 0.5: 300 }) # Refund for $100 next year: self.tax_forecast_dummy = DummyForecast(self.initial_year) self.tax_forecast_dummy.tax_adjustment = 100 self.tax_forecast_dummy.tax_refund_timing = Timing('start') # Also build a real ContributionForecast so that we can # test cash flows into accounts according to the overall # Forecast: # Simple tax rate: 50% on all income: tax = Tax(tax_brackets={self.initial_year: {0: 0.5}}) # One person, to own the account: timing = Timing(frequency='BW') self.person = Person(initial_year=self.initial_year, name="Test", birth_date="1 January 1980", retirement_date="31 December 2045", gross_income=5200, tax_treatment=tax, payment_timing=timing) # An account for savings to go to: self.account = Account(owner=self.person) # A strategy is required, but since there's only # one account the result will always be the same: self.strategy = TransactionTraversal(priority=[self.account]) self.saving_forecast = SavingForecast( initial_year=self.initial_year, retirement_accounts={self.account}, debt_accounts=set(), transaction_strategy=self.strategy) # Now assign `people`, `accounts`, and `debts` attrs to # appropriate subforecasts so that Forecast can retrieve # them: self.income_forecast_dummy.people = {self.person} self.saving_forecast_dummy.debt_accounts = set() self.withdrawal_forecast_dummy.accounts = {self.account} # Also add these to the null forecast, since it could be # substituted for any of the above dummy forecasts: self.null_forecast.people = self.income_forecast_dummy.people self.null_forecast.accounts = self.withdrawal_forecast_dummy.accounts self.null_forecast.debt_accounts = ( self.saving_forecast_dummy.debt_accounts) # Forecast depends on SubForecasts having certain properties, # so add those here: self.income_forecast_dummy.net_income = (sum( self.income_forecast_dummy.transactions.values())) self.living_expenses_forecast_dummy.living_expenses = (sum( self.living_expenses_forecast_dummy.transactions.values())) self.withdrawal_forecast_dummy.gross_withdrawals = (sum( self.withdrawal_forecast_dummy.transactions.values())) self.tax_forecast_dummy.tax_owing = 600 # Add the same properties to the null forecast, since it # could be substituted for any of the above: self.null_forecast.net_income = self.income_forecast_dummy.net_income self.null_forecast.living_expenses = ( self.living_expenses_forecast_dummy.living_expenses) self.null_forecast.gross_withdrawals = ( self.withdrawal_forecast_dummy.gross_withdrawals) self.null_forecast.tax_owing = self.tax_forecast_dummy.tax_owing # Finally, we need a Scenario to build a Forecast. # This is the simplest possible: 1 year, no growth. self.scenario = Scenario(self.initial_year, num_years=1)
def setUp_decimal(self): """ Builds default strategies/persons/etc. with Decimal inputs. """ # pylint: disable=invalid-name # This name is based on `setUp`, which doesn't follow Pylint's rules # pylint: enable=invalid-name # Use a default settings object: # (This is conditional so that subclasses can assign their own # settings object before calling super().setUp()) if not hasattr(self, 'settings'): self.settings = Settings() # To simplify tests, modify Settings so that forecasts are # just 2 years with easy-to-predict contributions ($1000/yr) self.settings.num_years = 2 self.settings.living_expenses_strategy = ( LivingExpensesStrategy.strategy_const_contribution) self.settings.living_expenses_base_amount = Decimal(1000) # Allow subclasses to use subclasses of Forecaster by assigning # to forecaster_type if not hasattr(self, 'forecaster_type'): self.forecaster_type = Forecaster # Build default `SubForecast` inputs based on `settings`: self.initial_year = self.settings.initial_year self.scenario = Scenario( inflation=Decimal(self.settings.inflation), stock_return=Decimal(self.settings.stock_return), bond_return=Decimal(self.settings.bond_return), other_return=Decimal(self.settings.other_return), management_fees=Decimal(self.settings.management_fees), initial_year=self.settings.initial_year, num_years=self.settings.num_years) self.living_expenses_strategy = LivingExpensesStrategy( strategy=self.settings.living_expenses_strategy, base_amount=Decimal(self.settings.living_expenses_base_amount), rate=Decimal(self.settings.living_expenses_rate), inflation_adjust=self.scenario.inflation_adjust) self.saving_strategy = TransactionStrategy( strategy=self.settings.saving_strategy, weights={ year: Decimal(val) for (year, val) in self.settings.saving_weights.items() }) self.withdrawal_strategy = TransactionStrategy( strategy=self.settings.withdrawal_strategy, weights={ year: Decimal(val) for (year, val) in self.settings.withdrawal_weights.items() }) self.allocation_strategy = AllocationStrategy( strategy=self.settings.allocation_strategy, min_equity=Decimal(self.settings.allocation_min_equity), max_equity=Decimal(self.settings.allocation_max_equity), target=Decimal(self.settings.allocation_target), standard_retirement_age=( self.settings.allocation_std_retirement_age), risk_transition_period=self.settings.allocation_risk_trans_period, adjust_for_retirement_plan=( self.settings.allocation_adjust_retirement)) self.debt_payment_strategy = DebtPaymentStrategy( strategy=self.settings.debt_payment_strategy, high_precision=Decimal) self.tax_treatment = Tax( tax_brackets={ year: { Decimal(lower): Decimal(upper) } for (year, vals) in self.settings.tax_brackets.items() for (lower, upper) in vals.items() }, personal_deduction={ year: Decimal(val) for (year, val) in self.settings.tax_personal_deduction.items() }, credit_rate={ year: Decimal(val) for (year, val) in self.settings.tax_credit_rate.items() }, inflation_adjust=self.scenario.inflation_adjust, high_precision=Decimal) # Now build some Ledger objects to test against: # A person making $10,000/yr self.person = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2040", gross_income=Decimal(10000), raise_rate=Decimal(0), spouse=None, tax_treatment=self.tax_treatment, high_precision=Decimal) # An account with $1000 in it (and no interest) self.account = Account(owner=self.person, balance=Decimal(1000), high_precision=Decimal) # A debt with a $100 balance (and no interest) self.debt = Debt(owner=self.person, balance=Decimal(100), high_precision=Decimal) # Init a Forecaster object here for convenience: self.forecaster = self.forecaster_type(settings=self.settings, high_precision=Decimal)
def setUp(self): self.initial_year = 2000 # Build 100 years of inflation adjustments with steadily growing # adjustment factors. First, pick a nice number (ideally a power # of 2 to avoid float precision issues); inflation_adjustment # will grow by adding the inverse (1/n) of this number annually growth_factor = 32 year_range = range(self.initial_year, self.initial_year + 100) self.inflation_adjustments = { year: 1 + (year - self.initial_year) / growth_factor for year in year_range } # For convenience, store the year where inflation has doubled # the nominal value of money self.double_year = self.initial_year + growth_factor # Build some brackets with nice round numbers: self.tax_brackets = {self.initial_year: {0: 0.1, 100: 0.2, 10000: 0.3}} # For convenience, build a sorted, type-converted array of # each of the tax bracket thresholds: self.brackets = sorted({ key: self.tax_brackets[self.initial_year][key] for key in self.tax_brackets[self.initial_year].keys() }) # For convenience in testing, build an accum dict that # corresponds to the tax brackets above. self.accum = {self.initial_year: {0: 0, 100: 10, 10000: 1990}} self.personal_deduction = {self.initial_year: 100} self.credit_rate = {self.initial_year: 0.1} self.tax = Tax(self.tax_brackets, inflation_adjust=self.inflation_adjustments, personal_deduction=self.personal_deduction, credit_rate=self.credit_rate) # Set up a simple person with no account-derived taxable income self.person = Person(self.initial_year, "Tester", self.initial_year - 25, retirement_date=self.initial_year + 40, gross_income=0) # Set up two people, spouses, on which to do more complex tests self.person1 = Person(self.initial_year, "Tester 1", self.initial_year - 20, retirement_date=self.initial_year + 45, gross_income=100000) self.person2 = Person(self.initial_year, "Tester 2", self.initial_year - 22, retirement_date=self.initial_year + 43, gross_income=50000) # Give the first person two accounts, one taxable and one # tax-deferred. Withdraw the entirety from the taxable account, # so that we don't need to worry about tax on unrealized growth: self.taxable_account1 = TaxableAccount(owner=self.person1, acb=0, balance=50000, rate=0.05, nper=1) self.taxable_account1.add_transaction(-50000, when='start') self.rrsp = RRSP(owner=self.person1, inflation_adjust=self.inflation_adjustments, contribution_room=0, balance=10000, rate=0.05, nper=1) # Employment income is fully taxable, and only half of capital # gains (the income from the taxable account) is taxable: self.person1_taxable_income = (self.person1.gross_income + self.taxable_account1.balance / 2) # Give the second person two accounts, one taxable and one # non-taxable. Withdraw the entirety from the taxable account, # so that we don't need to worry about tax on unrealized growth, # and withdraw a bit from the non-taxable account (which should # have no effect on taxable income): self.taxable_account2 = TaxableAccount(owner=self.person2, acb=0, balance=20000, rate=0.05, nper=1) self.taxable_account2.add_transaction(-20000, when='start') self.tfsa = TFSA(owner=self.person2, balance=50000, rate=0.05, nper=1) self.tfsa.add_transaction(-20000, when='start') # Employment income is fully taxable, and only half of capital # gains (the income from the taxable account) is taxable: self.person2_taxable_income = (self.person2.gross_income + self.taxable_account2.balance / 2)
def setUp_decimal(self): """ Builds stock variables to test with. """ # pylint: disable=invalid-name # Pylint doesn't like `setUp_decimal`, but it's not our naming # convention, so don't complain to us! # pylint: enable=invalid-name self.initial_year = 2000 # Simple tax treatment: 50% tax rate across the board. tax = Tax(tax_brackets={ self.initial_year: {Decimal(0): Decimal(0.5)}}, high_precision=Decimal) # Accounts need an owner: timing = Timing(frequency='BW',high_precision=Decimal) self.person = Person( initial_year=self.initial_year, name="Test", birth_date="1 January 1980", retirement_date="31 December 1999", # last year gross_income=Decimal(5200), tax_treatment=tax, payment_timing=timing, high_precision=Decimal) # We want at least two accounts which are withdrawn from # in different orders depending on the strategy. self.account = Account( owner=self.person, balance=Decimal(60000), # $60,000 <- BIGGER! high_precision=Decimal) self.rrsp = canada.accounts.RRSP( owner=self.person, contribution_room=Decimal(1000), balance=Decimal(6000), # $6,000 high_precision=Decimal) # Assume there are $2000 in inflows and $22,000 in outflows, # for a net need of $20,000: self.available = { Decimal(0.25): Decimal(1000), Decimal(0.5): Decimal(-11000), Decimal(0.75): Decimal(1000), Decimal(1): Decimal(-11000) } # Now we can set up the big-ticket items: self.strategy = TransactionStrategy( strategy=TransactionStrategy.strategy_ordered, weights={"RRSP": Decimal(1), "Account": Decimal(2)}) self.forecast = WithdrawalForecast( initial_year=self.initial_year, people={self.person}, accounts={self.account, self.rrsp}, transaction_strategy=self.strategy, high_precision=Decimal) # Set up another forecast for testing withholding behaviour: self.withholding_account = WithholdingAccount( owner=self.person, balance=Decimal(100000), high_precision=Decimal) self.withholding_strategy = TransactionStrategy( strategy=TransactionStrategy.strategy_ordered, weights={"WithholdingAccount": Decimal(1)}, high_precision=Decimal) self.withholding_forecast = WithdrawalForecast( initial_year=self.initial_year, people={self.person}, accounts={self.withholding_account}, transaction_strategy=self.withholding_strategy, high_precision=Decimal)
def setUp(self): """ Builds default strategies, persons, etc. """ # Use a default settings object: # (This is conditional so that subclasses can assign their own # settings object before calling super().setUp()) if not hasattr(self, 'settings'): self.settings = Settings() # To simplify tests, modify Settings so that forecasts are # just 2 years with easy-to-predict contributions ($1000/yr) self.settings.num_years = 2 self.settings.living_expenses_strategy = ( LivingExpensesStrategy.strategy_const_contribution) self.settings.living_expenses_base_amount = 1000 # Allow subclasses to use subclasses of Forecaster by assigning # to forecaster_type if not hasattr(self, 'forecaster_type'): self.forecaster_type = Forecaster # Build default `SubForecast` inputs based on `settings`: self.initial_year = self.settings.initial_year self.scenario = Scenario(inflation=self.settings.inflation, stock_return=self.settings.stock_return, bond_return=self.settings.bond_return, other_return=self.settings.other_return, management_fees=self.settings.management_fees, initial_year=self.settings.initial_year, num_years=self.settings.num_years) self.living_expenses_strategy = LivingExpensesStrategy( strategy=self.settings.living_expenses_strategy, base_amount=self.settings.living_expenses_base_amount, rate=self.settings.living_expenses_rate, inflation_adjust=self.scenario.inflation_adjust) self.saving_strategy = TransactionStrategy( strategy=self.settings.saving_strategy, weights=self.settings.saving_weights) self.withdrawal_strategy = TransactionStrategy( strategy=self.settings.withdrawal_strategy, weights=self.settings.withdrawal_weights) self.allocation_strategy = AllocationStrategy( strategy=self.settings.allocation_strategy, min_equity=self.settings.allocation_min_equity, max_equity=self.settings.allocation_max_equity, target=self.settings.allocation_target, standard_retirement_age=( self.settings.allocation_std_retirement_age), risk_transition_period=self.settings.allocation_risk_trans_period, adjust_for_retirement_plan=( self.settings.allocation_adjust_retirement)) self.debt_payment_strategy = DebtPaymentStrategy( strategy=self.settings.debt_payment_strategy) self.tax_treatment = Tax( tax_brackets=self.settings.tax_brackets, personal_deduction=self.settings.tax_personal_deduction, credit_rate=self.settings.tax_credit_rate, inflation_adjust=self.scenario.inflation_adjust) # Now build some Ledger objects to test against: # A person making $10,000/yr self.person = Person(initial_year=self.initial_year, name="Test 1", birth_date="1 January 1980", retirement_date="31 December 2040", gross_income=10000, raise_rate=0, spouse=None, tax_treatment=self.tax_treatment) # An account with $1000 in it (and no interest) self.account = Account(owner=self.person, balance=1000) # A debt with a $100 balance (and no interest) self.debt = Debt(owner=self.person, balance=100) # Init a Forecaster object here for convenience: self.forecaster = self.forecaster_type(settings=self.settings)