コード例 #1
0
    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)
コード例 #2
0
 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)
コード例 #3
0
    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}
コード例 #4
0
ファイル: test_person.py プロジェクト: dxcv/forecaster
 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)
コード例 #5
0
ファイル: test_income.py プロジェクト: ChrisCScott/forecaster
    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)
コード例 #6
0
    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)
コード例 #7
0
    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)
コード例 #8
0
    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)
コード例 #9
0
ファイル: test_person.py プロジェクト: ChrisCScott/forecaster
 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)
コード例 #10
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))
コード例 #11
0
    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)
コード例 #12
0
    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)
コード例 #13
0
ファイル: test_tax.py プロジェクト: ChrisCScott/forecaster
 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)
コード例 #14
0
ファイル: test_tax.py プロジェクト: ChrisCScott/forecaster
 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))
コード例 #15
0
 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)
コード例 #16
0
ファイル: test_person.py プロジェクト: ChrisCScott/forecaster
 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)
コード例 #17
0
ファイル: test_person.py プロジェクト: ChrisCScott/forecaster
 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)
コード例 #18
0
ファイル: test_person.py プロジェクト: dxcv/forecaster
 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))
コード例 #19
0
    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)
コード例 #20
0
ファイル: test_person.py プロジェクト: ChrisCScott/forecaster
 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))
コード例 #21
0
ファイル: test_income.py プロジェクト: dxcv/forecaster
 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})
コード例 #22
0
    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)
コード例 #23
0
    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)
コード例 #24
0
ファイル: test_tax.py プロジェクト: ChrisCScott/forecaster
    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)
コード例 #25
0
    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)
コード例 #26
0
    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)