Exemple #1
0
    def test_extend_inflation_adjusted(self):
        """ Tests extend_inflation_adjusted(). """
        inf = {
            1998: Decimal(0.25),
            1999: Decimal(0.5),
            2000: Decimal(0.75),
            2001: Decimal(1),
            2002: Decimal(2)}
        vals = {1999: 2, 2001: 4, 2003: 8}

        def inflation_adjust(target_year, base_year):
            """ Inflation from base_year to target_year. """
            return inf[target_year] / inf[base_year]

        # Test each year from 1997 to 2004:
        with self.assertRaises(KeyError):
            extend_inflation_adjusted(vals, inflation_adjust, 1997)
        self.assertEqual(extend_inflation_adjusted(
            vals, inflation_adjust, 1998), 1)
        self.assertEqual(extend_inflation_adjusted(
            vals, inflation_adjust, 1999), 2)
        self.assertEqual(extend_inflation_adjusted(
            vals, inflation_adjust, 2000), 3)
        self.assertEqual(extend_inflation_adjusted(
            vals, inflation_adjust, 2001), 4)
        self.assertEqual(extend_inflation_adjusted(
            vals, inflation_adjust, 2002), 8)
        self.assertEqual(extend_inflation_adjusted(
            vals, inflation_adjust, 2003), 8)
        with self.assertRaises(KeyError):
            extend_inflation_adjusted(vals, inflation_adjust, 2004)
Exemple #2
0
    def _spousal_tax_credit(self, person, year):
        """ Determines the spousal tax credit amount claimable.

        This method assigns the credit to the higher-earning
        partner. Multiple people can be passed and the credit
        will be determined for each individually.

        Where both partners have the same income, the credit is
        assigned to one partner in an implementation-dependent
        way (e.g. based on a hash).

        Args:
            person (Person): One member of a couple (or a single
                person, in which case the credit will be $0).
            year (int): The year in which the spousal tax credit is
                claimable.

        Returns:
            Money: The amount of the credit claimable by the person
                in `year`.
        """
        # Unmarried folks don't get the credit:
        if person.spouse is None:
            return 0  # Money value

        # Determine the maximum claimable amount:
        max_spousal_amount = extend_inflation_adjusted(
            self.constants.TAX_SPOUSAL_AMOUNT[self.jurisdiction],
            self.inflation_adjust, year)

        # We need to know the spouse's net income to assess the credit:
        # TODO: Pass in deductions for both spouses as args?
        # This would help to avoid calling self.deductions many times.
        spouse = person.spouse
        spouse_net_income = (spouse.taxable_income -
                             self.deduction(spouse, year))

        # Figure out whether to assign the credit to this person or
        # their spouse based on who has more income:

        # If this is the lower-earner, use their spouse instead:
        person_net_income = (person.taxable_income -
                             self.deduction(person, year))
        if person_net_income < spouse_net_income:
            return 0  # Money value
        # If their incomes are the same, use memory location to
        # decide in a deterministic way:
        if person_net_income == spouse_net_income:
            if id(person) < id(spouse):
                return 0  # Money value

        # The credit is determined by reducing the spousal amount
        # by the spouse's (net) income, but in any event it's not
        # negative.
        credit = max(max_spousal_amount - spouse_net_income, 0)  # Money value

        return credit
Exemple #3
0
 def accum(self, year, bracket=None):
     """ The accumulated tax payable for a given tax bracket. """
     # If we don't have this accum, we'll need to generate it.
     # add_brackets() does this for us.
     if year not in self._accum:
         # Get the inflation-adjusted tax brackets for this year:
         brackets = extend_inflation_adjusted(self._tax_brackets,
                                              self.inflation_adjust, year)
         self.add_brackets(brackets, year)
     if bracket is None:
         return self._accum[year]
     return self._accum[year][bracket]
Exemple #4
0
    def __init__(self, owner, balance=None, rate=None,
                 nper=None, inputs=None, initial_year=None,
                 default_timing=None,
                 contribution_room=None, contributor=None,
                 inflation_adjust=None, **kwargs):
        """ Initializes a TFSA object.

        Args:
            inflation_adjust: A method with the following form:
                `inflation_adjust(val, this_year, target_year)`.

                Returns a Decimal object which is the inflation-
                adjustment factor from base_year to target_year.

                Optional. If not provided, all values are assumed to be
                in real terms, so no inflation adjustment is performed.
        """
        # This method does have a lot of arguments, but they're mostly
        # inherited from a superclass. We're stuck with them here.
        # pylint: disable=too-many-arguments

        super().__init__(
            owner, balance=balance, rate=rate,
            nper=nper, inputs=inputs, initial_year=initial_year,
            default_timing=default_timing,
            contribution_room=contribution_room, contributor=contributor,
            inflation_adjust=inflation_adjust,
            **kwargs)

        # This is our baseline for estimating contribution room
        # (By law, inflation-adjustments are relative to 2009, the
        # first year that TFSAs were available, and rounded to the
        # nearest $500)
        self._base_accrual_year = min(self.constants.TFSA_ANNUAL_ACCRUAL.keys())
        self._base_accrual = round(extend_inflation_adjusted(
            self.constants.TFSA_ANNUAL_ACCRUAL,
            self.inflation_adjust,
            self._base_accrual_year
        ) / self.constants.TFSA_ACCRUAL_ROUNDING_FACTOR) * \
            self.constants.TFSA_ACCRUAL_ROUNDING_FACTOR

        # If contribution_room is not provided, infer it based on age.
        if self.contribution_room is None:
            self.contribution_room = self._infer_initial_contribution_rm()
Exemple #5
0
    def __init__(self, *args, rrif_conversion_year=None, **kwargs):
        """ Initializes an RRSP object.

        This class also implements RRIFs (which RRSPs are converted into
        either at a user-defined time or by operation of law). A new
        object is not created when the RRSP converts to an RRIF; rather,
        the object's behaviour changes to limit inflows, require
        minimum withdrawals, and reduce withholding taxes.

        See documentation for `RegisteredAccount` for information on
        args not listed below.

        Args:
            rrif_conversion_year (int): The year in which the `RRSP`
                object's behaviour switches from RRSP rules to RRIF
                rules.
        """
        super().__init__(*args, **kwargs)

        # Although `person` might provide a retirement_age, the RRSP
        # won't necessarily be turned into an RRIF at the retirement
        # date (depending on withdrawal strategy).
        # TODO: Allow RRIF_conversion_year to be passed as an argument?
        # We could use the below convert-at-71 logic if None is passed.
        # TODO: Automatically trigger RRIF conversion after outflow?
        # (Perhaps control this behaviour with an arg?)

        self._rrif_conversion_year = None
        self.rrif_conversion_year = rrif_conversion_year

        # Determine the max contribution room accrual in initial_year:
        self._initial_accrual = extend_inflation_adjusted(
            constants.RRSP_ACCRUAL_MAX, self.inflation_adjust,
            self.initial_year)

        # If no contribution room was provided, set it to $0.
        if self.contribution_room is None:
            self.contribution_room = Money(0)
Exemple #6
0
    def _pension_income_credit(self, person, year):
        """ Determines the pension income credit claimable by `person`.

        Args:
            person (Person): The person who will claim the pension
                income credit (if any)
            year (int): The year in which the pension income credit is
                claimable.

        Returns:
            Money: The amount of the credit claimable by person in year.
        """
        pension_income = abs(
            sum(account.outflows() for account in person.accounts
                if isinstance(account, RRSP)))
        # NOTE: Other qualified pension income sources can be
        # added here
        # Each jurisdiction has a maximum claimable amount for the
        # pension credit, so determine that (inflation-adjusted
        # amount) here:
        deduction_max = extend_inflation_adjusted(
            self.constants.TAX_PENSION_CREDIT[self.jurisdiction],
            self.inflation_adjust, year)
        return min(pension_income, deduction_max)
Exemple #7
0
    def next_contribution_room(self):
        """ Determines the amount of contribution room for next year.

        Args:
            income (Money): The amount of taxable income for this year
                used to calculate RRSP contribution room.
            year (int): The year in which the income is received.

        Returns:
            The contribution room for the RRSP for the year *after*
            `year`.
        """
        year = self.this_year

        if self.contributor.age(year + 1) > constants.RRSP_RRIF_CONVERSION_AGE:
            # If past the mandatory RRIF conversion age, no
            # contributions are allowed.
            return Money(0)
        else:
            # TODO: Add pension adjustment?

            # Contribution room is determined based on the contributor's
            # gross income for the previous year.
            income = self.contributor.gross_income_history[self.this_year]

            # First, determine how much more contribution room will
            # accrue due to this year's income:
            accrual = income * constants.RRSP_ACCRUAL_RATE
            # Second, compare to the (inflation-adjusted) max accrual
            # for next year:
            max_accrual = extend_inflation_adjusted(constants.RRSP_ACCRUAL_MAX,
                                                    self.inflation_adjust,
                                                    year + 1)
            # Don't forget to add in any rollovers:
            rollover = self.contribution_room - self.inflows()
            return min(accrual, Money(max_accrual)) + rollover
Exemple #8
0
 def personal_deduction(self, year):
     """ Personal deduction for `year`. """
     return extend_inflation_adjusted(self._personal_deduction,
                                      self.inflation_adjust, year)