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 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 test_add_trans_timing_basic(self): """ Add a number of transactions based on a Timing object. """ # Add $50 at when=0.5 and $50 at when=1 timing = Timing(when=1, frequency=2) self.subforecast.add_transaction(value=100, timing=timing, to_account=self.available_dict) # Confirm monies were added at the times noted above: self.assertEqual(self.available_dict, {0.5: 50, 1: 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 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 setUp_decimal(self): initial_year = 2000 person = Person(initial_year, 'Testy McTesterson', 1980, retirement_date=2045) self.timing = Timing({Decimal(0.5): Decimal(1)}) # These accounts have different rates: self.debt_big_high_interest = Debt( person, balance=Decimal(1000), rate=Decimal(1), minimum_payment=Decimal(100), accelerated_payment=Decimal('Infinity'), high_precision=Decimal) self.debt_small_low_interest = Debt( person, balance=Decimal(100), rate=Decimal(0), minimum_payment=Decimal(10), accelerated_payment=Decimal('Infinity'), high_precision=Decimal) self.debt_medium = Debt(person, balance=Decimal(500), rate=Decimal(0.5), minimum_payment=Decimal(50), accelerated_payment=Decimal('Infinity'), high_precision=Decimal) 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, high_precision=Decimal) self.strategy_snowball = DebtPaymentStrategy( DebtPaymentStrategy.strategy_snowball, high_precision=Decimal) self.excess = Decimal(10)
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 setUp(self): initial_year = 2000 person = Person(initial_year, 'Testy McTesterson', 1980, retirement_date=2045) self.timing = Timing({0.5: 1}) # These accounts have different rates: self.debt_big_high_interest = Debt(person, balance=1000, rate=1, minimum_payment=100, accelerated_payment=float('inf')) self.debt_small_low_interest = Debt(person, balance=100, rate=0, minimum_payment=10, accelerated_payment=float('inf')) self.debt_medium = Debt(person, balance=500, rate=0.5, minimum_payment=50, accelerated_payment=float('inf')) 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 = 10
def test_payment(self): """ Tests tax payment carryovers """ # Set up a forecast where we pay $100 in taxes owing in the # middle of year 2, with no other transactions: self.scenario.num_years = 2 self.tax_forecast_dummy.tax_adjustment = -100 trans_time = 0.5 self.tax_forecast_dummy.tax_payment_timing = Timing(trans_time) forecast = Forecast(income_forecast=self.null_forecast, living_expenses_forecast=self.null_forecast, saving_forecast=self.null_forecast, withdrawal_forecast=self.null_forecast, tax_forecast=self.tax_forecast_dummy, scenario=self.scenario) # Now confirm that the refund was in fact received: self.assertEqual(forecast.available[trans_time], -100) # And confirm that there were no other non-zero transactions: self.assertTrue( all(value == 0 for timing, value in forecast.available.items() if timing != trans_time))
def test_refund(self): """ Tests tax refund carryovers """ # Set up a forecast where we receive a $100 refund in the middle # of year 2, with no other transactions: self.scenario.num_years = 2 self.tax_forecast_dummy.tax_adjustment = Money(100) trans_time = Decimal(0.5) self.tax_forecast_dummy.tax_refund_timing = Timing(trans_time) forecast = Forecast( income_forecast=self.null_forecast, living_expenses_forecast=self.null_forecast, saving_forecast=self.null_forecast, withdrawal_forecast=self.null_forecast, tax_forecast=self.tax_forecast_dummy, scenario=self.scenario) # Now confirm that the refund was in fact received: self.assertEqual(forecast.available[trans_time], Money(100)) # And confirm that there were no other non-zero transactions: self.assertTrue(all( value == 0 for timing, value in forecast.available.items() if timing != trans_time))
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(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 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_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 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)