Esempio n. 1
0
    def OnRetirement(self, person):
        self.ympe_fractions.sort(reverse=True)
        working_years = len(self.ympe_fractions)
        dropout_years = world.CPP_GENERAL_DROPOUT_FACTOR * working_years
        cpp_earning_history_length = working_years - dropout_years
        whole_year_index = math.floor(cpp_earning_history_length)
        cpp_average_earnings = (sum(self.ympe_fractions[:whole_year_index]) +
                                self.ympe_fractions[whole_year_index] *
                                (cpp_earning_history_length - whole_year_index)
                                ) / cpp_earning_history_length

        # Calculate the average nominal YMPE for the previous 5 years (excluding current year)
        nominal_ympe_history = [
            utils.Indexed(world.YMPE, person.year - i, 1 + world.PARGE) *
            person.cpi_history[-(i + 1)]
            for i in range(1, world.MPEA_YEARS + 1)
        ]
        indexed_mpea = sum(nominal_ympe_history) / world.MPEA_YEARS

        if person.age == world.CPP_EXPECTED_RETIREMENT_AGE:
            self.benefit_amount = cpp_average_earnings * indexed_mpea * world.CPP_RETIREMENT_BENEFIT_FRACTION
        elif person.age < world.CPP_EXPECTED_RETIREMENT_AGE:
            self.benefit_amount = (
                cpp_average_earnings * indexed_mpea *
                world.CPP_RETIREMENT_BENEFIT_FRACTION *
                (1 - (world.CPP_EXPECTED_RETIREMENT_AGE - person.age) *
                 world.AAF_PRE65))
        elif person.age > world.CPP_EXPECTED_RETIREMENT_AGE:
            self.benefit_amount = (
                cpp_average_earnings * indexed_mpea *
                world.CPP_RETIREMENT_BENEFIT_FRACTION *
                (1 +
                 min(world.AAF_POST65_YEARS_CAP, person.age -
                     world.CPP_EXPECTED_RETIREMENT_AGE) * world.AAF_POST65))
Esempio n. 2
0
 def CalcAmount(self, year_rec):
     if year_rec.is_employed:
         current_ympe = utils.Indexed(world.YMPE, year_rec.year,
                                      1 + world.PARGE) * year_rec.cpi
         earnings = max(
             random.normalvariate(
                 current_ympe * world.EARNINGS_YMPE_FRACTION,
                 world.YMPE_STDDEV * current_ympe), 0)
         return earnings
     else:
         return 0
Esempio n. 3
0
    def CalcPayrollDeductions(self, year_rec):
        """Calculates and stores EI premium and CPP employee controbutions"""
        # CPP employee contribution
        earnings = sum(receipt.amount for receipt in year_rec.incomes
                       if receipt.income_type == incomes.INCOME_TYPE_EARNINGS)
        year_rec.pensionable_earnings = max(
            0,
            min(
                utils.Indexed(world.YMPE, year_rec.year, 1 + world.PARGE) *
                year_rec.cpi, earnings) - world.YBE)
        year_rec.cpp_contribution = year_rec.pensionable_earnings * world.CPP_EMPLOYEE_RATE

        # EI premium
        year_rec.insurable_earnings = min(
            earnings,
            utils.Indexed(world.EI_MAX_INSURABLE_EARNINGS, year_rec.year,
                          1 + world.PARGE) * year_rec.cpi)
        year_rec.ei_premium = year_rec.insurable_earnings * world.EI_PREMIUM_RATE

        return year_rec
Esempio n. 4
0
 def CalcAmount(self, year_rec):
     if year_rec.is_employed:
         earnings_capacity = utils.Indexed(
             world.YMPE, year_rec.year,
             1 + world.PARGE) * year_rec.cpi * world.EARNINGS_YMPE_FRACTION
         earnings = max(
             random.normalvariate(earnings_capacity,
                                  world.YMPE_STDDEV * earnings_capacity), 0)
         return earnings
     else:
         return 0
Esempio n. 5
0
 def testIndexed(self):
   self.assertAlmostEqual(utils.Indexed(100, world.BASE_YEAR + 1, 1.10), 110)
   self.assertAlmostEqual(utils.Indexed(100, world.BASE_YEAR, 123), 100)
   self.assertAlmostEqual(utils.Indexed(100, world.BASE_YEAR + 1), 101)
Esempio n. 6
0
 def AnnualUpdate(self, year_rec):
     if not year_rec.is_retired:
         self.ympe_fractions.append(
             year_rec.pensionable_earnings /
             (utils.Indexed(world.YMPE, year_rec.year, 1 + world.PARGE) *
              year_rec.cpi))
Esempio n. 7
0
    def AnnualReview(self, year_rec):
        """End of year calculations for a live person"""
        period = self.Period(year_rec)
        self.period_years[period] += 1
        cpi = year_rec.cpi if self.real_values else 1

        self.accumulators.UpdateConsumption(year_rec.consumption / cpi,
                                            self.year, self.retired, period)
        earnings = sum(receipt.amount for receipt in year_rec.incomes
                       if receipt.income_type == incomes.INCOME_TYPE_EARNINGS)
        cpp = sum(receipt.amount for receipt in year_rec.incomes
                  if receipt.income_type == incomes.INCOME_TYPE_CPP)
        ei_benefits = sum(receipt.amount for receipt in year_rec.incomes
                          if receipt.income_type == incomes.INCOME_TYPE_EI)
        gis = sum(receipt.amount for receipt in year_rec.incomes
                  if receipt.income_type == incomes.INCOME_TYPE_GIS)
        oas = sum(receipt.amount for receipt in year_rec.incomes
                  if receipt.income_type == incomes.INCOME_TYPE_OAS)
        assets = sum(fund.amount for fund in self.funds.values())
        gross_income = sum(receipt.amount
                           for receipt in year_rec.incomes) + sum(
                               receipt.amount
                               for receipt in year_rec.withdrawals)
        rrsp_withdrawals = sum(
            receipt.amount for receipt in year_rec.withdrawals
            if receipt.fund_type in (funds.FUND_TYPE_RRSP,
                                     funds.FUND_TYPE_BRIDGING))
        tfsa_withdrawals = sum(receipt.amount
                               for receipt in year_rec.withdrawals
                               if receipt.fund_type == funds.FUND_TYPE_TFSA)
        nonreg_withdrawals = sum(
            receipt.amount for receipt in year_rec.withdrawals
            if receipt.fund_type == funds.FUND_TYPE_NONREG)
        total_withdrawals = rrsp_withdrawals + tfsa_withdrawals + nonreg_withdrawals
        rrsp_deposits = sum(receipt.amount for receipt in year_rec.deposits
                            if receipt.fund_type in (funds.FUND_TYPE_RRSP,
                                                     funds.FUND_TYPE_BRIDGING))
        tfsa_deposits = sum(receipt.amount for receipt in year_rec.deposits
                            if receipt.fund_type == funds.FUND_TYPE_TFSA)
        nonreg_deposits = sum(receipt.amount for receipt in year_rec.deposits
                              if receipt.fund_type == funds.FUND_TYPE_NONREG)
        savings = rrsp_deposits + tfsa_deposits + nonreg_deposits
        ympe = utils.Indexed(world.YMPE, year_rec.year, 1 + world.PARGE)

        if gross_income < world.LICO_SINGLE_CITY_WP * year_rec.cpi:
            self.gross_income_below_lico_years += 1

        if assets <= 0:
            self.no_assets_years += 1

        if self.age >= world.MINIMUM_RETIREMENT_AGE and not self.retired:
            self.accumulators.earnings_late_working_summary.UpdateOneValue(
                earnings / cpi)
        self.accumulators.lifetime_withdrawals_less_savings.UpdateOneValue(
            (total_withdrawals - savings) / cpi)

        if self.retired:
            self.accumulators.lico_gap_retired.UpdateOneValue(
                max(0, world.LICO_SINGLE_CITY_WP * year_rec.cpi - gross_income)
                / cpi)
            if assets <= 0:
                self.has_been_ruined = True
                self.accumulators.fraction_retirement_years_ruined.UpdateOneValue(
                    1)
            else:
                self.accumulators.fraction_retirement_years_ruined.UpdateOneValue(
                    0)
            self.accumulators.fraction_retirement_years_below_ympe.UpdateOneValue(
                1 if assets < ympe else 0)
            self.accumulators.fraction_retirement_years_below_twice_ympe.UpdateOneValue(
                1 if assets < 2 * ympe else 0)
            if gross_income < world.LICO_SINGLE_CITY_WP * year_rec.cpi:
                self.has_experienced_income_under_lico = True
                self.accumulators.fraction_retirement_years_below_lico.UpdateOneValue(
                    1)
            else:
                self.accumulators.fraction_retirement_years_below_lico.UpdateOneValue(
                    0)
            self.accumulators.retirement_taxes.UpdateOneValue(
                year_rec.taxes_payable / cpi)
            if cpp > 0:
                self.accumulators.positive_cpp_benefits.UpdateOneValue(cpp /
                                                                       cpi)
        else:  # Working period
            self.accumulators.lico_gap_working.UpdateOneValue(
                max(0, world.LICO_SINGLE_CITY_WP * year_rec.cpi - gross_income)
                / cpi)
            self.accumulators.earnings_working.UpdateOneValue(earnings / cpi)
            self.accumulators.working_annual_ei_cpp_deductions.UpdateOneValue(
                (year_rec.cpp_contribution + year_rec.ei_premium) / cpi)
            self.accumulators.working_taxes.UpdateOneValue(
                year_rec.taxes_payable / cpi)
            if earnings > 0:
                self.positive_earnings_years += 1
                self.accumulators.fraction_earnings_saved.UpdateOneValue(
                    savings / earnings)
            if ei_benefits > 0:
                self.ei_years += 1
                self.accumulators.positive_ei_benefits.UpdateOneValue(
                    ei_benefits / cpi)

        if self.age >= world.MAXIMUM_RETIREMENT_AGE:
            if gis > 0:
                self.gis_years += 1
                self.has_received_gis = True
                self.accumulators.fraction_retirement_years_receiving_gis.UpdateOneValue(
                    1)
                self.accumulators.positive_gis_benefits.UpdateOneValue(gis /
                                                                       cpi)
            else:
                self.accumulators.fraction_retirement_years_receiving_gis.UpdateOneValue(
                    0)
            self.accumulators.benefits_gis.UpdateOneValue(gis / cpi)

        if not self.basic_only:
            self.net_government_revenue += (year_rec.taxes_payable +
                                            year_rec.sales_taxes - gis -
                                            oas) / year_rec.cpi

            self.accumulators.period_earnings.UpdateOneValue(
                earnings / cpi, period)
            self.accumulators.period_cpp_benefits.UpdateOneValue(
                cpp / cpi, period)
            self.accumulators.period_oas_benefits.UpdateOneValue(
                oas / cpi, period)
            self.accumulators.period_taxable_gains.UpdateOneValue(
                year_rec.taxable_capital_gains / cpi, period)
            self.accumulators.period_gis_benefits.UpdateOneValue(
                gis / cpi, period)
            self.accumulators.period_social_benefits_repaid.UpdateOneValue(
                year_rec.total_social_benefit_repayment / cpi, period)
            self.accumulators.period_rrsp_withdrawals.UpdateOneValue(
                rrsp_withdrawals / cpi, period)
            self.accumulators.period_tfsa_withdrawals.UpdateOneValue(
                tfsa_withdrawals / cpi, period)
            self.accumulators.period_nonreg_withdrawals.UpdateOneValue(
                nonreg_withdrawals / cpi, period)
            self.accumulators.period_cpp_contributions.UpdateOneValue(
                year_rec.cpp_contribution / cpi, period)
            self.accumulators.period_ei_premiums.UpdateOneValue(
                year_rec.ei_premium / cpi, period)
            self.accumulators.period_taxable_income.UpdateOneValue(
                year_rec.taxable_income / cpi, period)
            self.accumulators.period_income_tax.UpdateOneValue(
                year_rec.taxes_payable / cpi, period)
            self.accumulators.period_sales_tax.UpdateOneValue(
                year_rec.sales_taxes / cpi, period)
            self.accumulators.period_rrsp_savings.UpdateOneValue(
                rrsp_deposits / cpi, period)
            self.accumulators.period_tfsa_savings.UpdateOneValue(
                tfsa_deposits / cpi, period)
            self.accumulators.period_nonreg_savings.UpdateOneValue(
                nonreg_deposits / cpi, period)
            self.accumulators.period_fund_growth.UpdateOneValue(
                sum(rec.growth_amount
                    for rec in year_rec.growth_records) / cpi, period)

            self.accumulators.persons_alive_by_age.UpdateOneValue(1, self.age)
            self.accumulators.gross_earnings_by_age.UpdateOneValue(
                earnings / cpi, self.age)
            self.accumulators.income_tax_by_age.UpdateOneValue(
                year_rec.taxes_payable / cpi, self.age)
            self.accumulators.sales_tax_by_age.UpdateOneValue(
                year_rec.sales_taxes / cpi, self.age)
            self.accumulators.ei_premium_by_age.UpdateOneValue(
                year_rec.ei_premium / cpi, self.age)
            self.accumulators.cpp_contributions_by_age.UpdateOneValue(
                year_rec.cpp_contribution / cpi, self.age)
            self.accumulators.ei_benefits_by_age.UpdateOneValue(
                ei_benefits / cpi, self.age)
            self.accumulators.cpp_benefits_by_age.UpdateOneValue(
                cpp / cpi, self.age)
            self.accumulators.oas_benefits_by_age.UpdateOneValue(
                oas / cpi, self.age)
            self.accumulators.gis_benefits_by_age.UpdateOneValue(
                gis / cpi, self.age)
            self.accumulators.savings_by_age.UpdateOneValue(
                savings / cpi, self.age)
            self.accumulators.rrsp_withdrawals_by_age.UpdateOneValue(
                rrsp_withdrawals / cpi, self.age)
            self.accumulators.tfsa_withdrawals_by_age.UpdateOneValue(
                tfsa_withdrawals / cpi, self.age)
            self.accumulators.nonreg_withdrawals_by_age.UpdateOneValue(
                nonreg_withdrawals / cpi, self.age)
            self.accumulators.rrsp_assets_by_age.UpdateOneValue(
                sum(fund.amount for fund in self.funds.values()
                    if fund.fund_type == funds.FUND_TYPE_RRSP) / cpi, self.age)
            self.accumulators.bridging_assets_by_age.UpdateOneValue(
                sum(fund.amount for fund in self.funds.values()
                    if fund.fund_type == funds.FUND_TYPE_BRIDGING) / cpi,
                self.age)
            self.accumulators.tfsa_assets_by_age.UpdateOneValue(
                sum(fund.amount for fund in self.funds.values()
                    if fund.fund_type == funds.FUND_TYPE_TFSA) / cpi, self.age)
            self.accumulators.nonreg_assets_by_age.UpdateOneValue(
                sum(fund.amount for fund in self.funds.values()
                    if fund.fund_type == funds.FUND_TYPE_NONREG) / cpi,
                self.age)
            if self.retired:
                self.accumulators.cd_withdrawals_by_age.UpdateOneValue(
                    year_rec.cd_drawdown_amount / cpi, self.age)
                self.accumulators.ced_withdrawals_by_age.UpdateOneValue(
                    year_rec.ced_drawdown_amount / cpi, self.age)
                self.accumulators.cd_requested_by_age.UpdateOneValue(
                    year_rec.cd_drawdown_request / cpi, self.age)
                self.accumulators.ced_requested_by_age.UpdateOneValue(
                    year_rec.ced_drawdown_request / cpi, self.age)

                self.accumulators.rrsp_ced_assets_by_age.UpdateOneValue(
                    self.funds["ced_rrsp"].amount / cpi, self.age)
                self.accumulators.tfsa_ced_assets_by_age.UpdateOneValue(
                    self.funds["ced_tfsa"].amount / cpi, self.age)
                self.accumulators.nonreg_ced_assets_by_age.UpdateOneValue(
                    self.funds["ced_nonreg"].amount / cpi, self.age)
                self.accumulators.rrsp_cd_assets_by_age.UpdateOneValue(
                    self.funds["cd_rrsp"].amount / cpi, self.age)
                self.accumulators.tfsa_cd_assets_by_age.UpdateOneValue(
                    self.funds["cd_tfsa"].amount / cpi, self.age)
                self.accumulators.nonreg_cd_assets_by_age.UpdateOneValue(
                    self.funds["cd_nonreg"].amount / cpi, self.age)

                self.accumulators.ced_ruined_by_age.UpdateOneValue(
                    1 if sum(fund.amount for name, fund in self.funds.items()
                             if name.startswith("ced")) == 0 else 0, self.age)
                self.accumulators.cd_ruined_by_age.UpdateOneValue(
                    1 if sum(fund.amount for name, fund in self.funds.items()
                             if name.startswith("cd")) == 0 else 0, self.age)

        self.age += 1
        self.year += 1
Esempio n. 8
0
    def MeddleWithCash(self, year_rec):
        """This performs all operations on subject's cash pile"""
        cash = 0

        # Get money from incomes. GIS is excluded and done after withdrawals
        for income in self.incomes[:-1]:
            amount, taxable, year_rec = income.GiveMeMoney(year_rec)
            cash += amount

        # Update RRSP room
        earnings = sum(receipt.amount for receipt in year_rec.incomes
                       if receipt.income_type == incomes.INCOME_TYPE_EARNINGS)
        self.rrsp_room += min(
            earnings * world.RRSP_ACCRUAL_FRACTION,
            utils.Indexed(world.RRSP_LIMIT, year_rec.year, 1 + world.PARGE) *
            year_rec.cpi)
        year_rec.rrsp_room = self.rrsp_room

        # Do withdrawals
        if self.retired:
            # Bridging
            if "bridging" in self.funds and self.age < world.CPP_EXPECTED_RETIREMENT_AGE:
                bridging_withdrawal_amount = self.bridging_withdrawal_table[
                    self.age] * self.funds["bridging"].amount
                withdrawn, gains, year_rec = self.funds["bridging"].Withdraw(
                    bridging_withdrawal_amount, year_rec)
                cash += withdrawn
                self.total_retirement_withdrawals += withdrawn / year_rec.cpi

            # CD drawdown strategy
            proportions = (self.strategy.drawdown_preferred_rrsp_fraction,
                           self.strategy.drawdown_preferred_tfsa_fraction, 1)
            fund_chain = [
                self.funds["cd_rrsp"], self.funds["cd_tfsa"],
                self.funds["cd_nonreg"]
            ]
            year_rec.cd_drawdown_request = self.cd_drawdown_amount * year_rec.cpi
            withdrawn, gains, year_rec = funds.ChainedWithdraw(
                year_rec.cd_drawdown_request, fund_chain, proportions,
                year_rec)
            cash += withdrawn
            year_rec.cd_drawdown_amount = withdrawn
            self.total_retirement_withdrawals += withdrawn / year_rec.cpi

            # CED drawdown_strategy
            fund_chain = [
                self.funds["ced_rrsp"], self.funds["ced_tfsa"],
                self.funds["ced_nonreg"]
            ]
            year_rec.ced_drawdown_request = sum(
                f.amount for f in fund_chain) * world.CED_PROPORTION[self.age]
            withdrawn, gains, year_rec = funds.ChainedWithdraw(
                year_rec.ced_drawdown_request, fund_chain, proportions,
                year_rec)
            cash += withdrawn
            year_rec.ced_drawdown_amount = withdrawn
            self.total_retirement_withdrawals += withdrawn / year_rec.cpi
        else:
            target_cash = utils.Indexed(
                world.YMPE, year_rec.year, 1 + world.PARGE
            ) * year_rec.cpi * self.strategy.savings_threshold * world.EARNINGS_YMPE_FRACTION
            if cash < target_cash:
                # Attempt to withdraw difference from savings
                amount_to_withdraw = target_cash - cash
                proportions = (
                    self.strategy.working_period_drawdown_tfsa_fraction,
                    self.strategy.working_period_drawdown_nonreg_fraction, 1)
                fund_chain = [
                    self.funds["wp_tfsa"], self.funds["wp_nonreg"],
                    self.funds["wp_rrsp"]
                ]
                withdrawn, gains, year_rec = funds.ChainedWithdraw(
                    amount_to_withdraw, fund_chain, proportions, year_rec)
                cash += withdrawn
            else:
                # Save
                earnings_to_save = max(earnings - target_cash,
                                       0) * self.strategy.savings_rate
                proportions = (self.strategy.savings_rrsp_fraction,
                               self.strategy.savings_tfsa_fraction, 1)
                fund_chain = [
                    self.funds["wp_rrsp"], self.funds["wp_tfsa"],
                    self.funds["wp_nonreg"]
                ]
                deposited, year_rec = funds.ChainedDeposit(
                    earnings_to_save, fund_chain, proportions, year_rec)
                cash -= deposited
                if deposited > 0:
                    self.positive_savings_years += 1

        # Update funds
        for fund in self.funds.values():
            fund.Update(year_rec)

        # update the Person's view of RRSP and TFSA room
        self.tfsa_room = year_rec.tfsa_room
        self.rrsp_room = year_rec.rrsp_room

        # Calculate EI premium and CPP contributions
        year_rec = self.CalcPayrollDeductions(year_rec)

        # Now we try to get money from GIS because year_rec is populated with the needed values.
        income = self.incomes[-1]  # GIS is last in this list
        amount, taxable, year_rec = income.GiveMeMoney(year_rec)
        cash += amount

        # Pay income taxes
        year_rec.taxes_payable = self.CalcIncomeTax(year_rec)
        cash -= year_rec.taxes_payable

        # Update incomes
        for income in self.incomes:
            income.AnnualUpdate(year_rec)

        # Pay sales tax
        non_hst_consumption = min(cash, world.SALES_TAX_EXEMPTION)
        hst_consumption = cash - non_hst_consumption
        year_rec.consumption = hst_consumption / (
            1 + world.HST_RATE) + non_hst_consumption
        year_rec.sales_taxes = hst_consumption * world.HST_RATE

        return year_rec
Esempio n. 9
0
    def CalcIncomeTax(self, year_rec):
        """Calculates the amount of income tax to be paid"""
        # Calculate Total Income
        income_sum = sum(receipt.amount for receipt in year_rec.incomes)
        rrsp_withdrawal_sum = sum(
            receipt.amount for receipt in year_rec.withdrawals
            if receipt.fund_type in (funds.FUND_TYPE_RRSP,
                                     funds.FUND_TYPE_BRIDGING))
        capital_gains = (sum(receipt.gains
                             for receipt in year_rec.withdrawals) +
                         sum(receipt.gross_gain
                             for receipt in year_rec.tax_receipts))
        if capital_gains > 0:
            taxable_capital_gains = capital_gains * world.CG_INCLUSION_RATE
        else:
            self.capital_loss_carry_forward += -capital_gains
            taxable_capital_gains = 0
        year_rec.taxable_capital_gains = taxable_capital_gains

        cpp_death_benefit = world.CPP_DEATH_BENEFIT if year_rec.is_dead else 0

        total_income = income_sum + rrsp_withdrawal_sum + taxable_capital_gains + cpp_death_benefit

        # Calculate Net Income before adjustments
        rrsp_contribution_sum = sum(
            receipt.amount for receipt in year_rec.deposits
            if receipt.fund_type == funds.FUND_TYPE_RRSP)
        net_income_before_adjustments = max(
            total_income - rrsp_contribution_sum, 0)

        # Employment Insurance Social Benefits Repayment
        ei_benefits = sum(receipt.amount for receipt in year_rec.incomes
                          if receipt.income_type == incomes.INCOME_TYPE_EI)
        ei_base_amount = utils.Indexed(
            world.EI_MAX_INSURABLE_EARNINGS, year_rec.year,
            1 + world.PARGE) * world.EI_REPAYMENT_BASE_FRACTION * year_rec.cpi
        ei_benefit_repayment = min(
            max(0, net_income_before_adjustments - ei_base_amount),
            ei_benefits) * world.EI_REPAYMENT_REDUCTION_RATE

        # Old Age Security and Net Federal Supplements Repayment
        oas_plus_gis = sum(receipt.amount for receipt in year_rec.incomes
                           if receipt.income_type in (incomes.INCOME_TYPE_OAS,
                                                      incomes.INCOME_TYPE_GIS))

        prospective_social_benefit_repayment = max(
            0,
            max(0, net_income_before_adjustments - ei_benefit_repayment) -
            world.SBR_BASE_AMOUNT * year_rec.cpi) * world.SBR_REDUCTION_RATE
        oas_and_gis_repayment = min(oas_plus_gis,
                                    prospective_social_benefit_repayment)

        # Total Social Benefit Repayment
        total_social_benefit_repayment = ei_benefit_repayment + oas_and_gis_repayment
        year_rec.total_social_benefit_repayment = total_social_benefit_repayment

        # Other Payments Deduction
        gis_income = sum(receipt.amount for receipt in year_rec.incomes
                         if receipt.income_type == incomes.INCOME_TYPE_GIS)
        oas_income = sum(receipt.amount for receipt in year_rec.incomes
                         if receipt.income_type == incomes.INCOME_TYPE_OAS)
        try:
            oas_benefit_repaid = oas_and_gis_repayment * oas_income / (
                gis_income + oas_income)
        except ZeroDivisionError:
            oas_benefit_repaid = 0
        net_federal_supplements_deduction = gis_income - (
            total_social_benefit_repayment -
            (ei_benefit_repayment + oas_benefit_repaid))

        # Net Income
        net_income = net_income_before_adjustments - total_social_benefit_repayment

        # Taxable Income
        applied_capital_loss_amount = min(
            taxable_capital_gains,
            self.capital_loss_carry_forward * world.CG_INCLUSION_RATE)
        taxable_income = max(
            0, net_income -
            (net_federal_supplements_deduction + applied_capital_loss_amount))
        year_rec.taxable_income = taxable_income

        # Age amount
        age_amount_reduction = max(
            0, net_income - world.AGE_AMOUNT_EXEMPTION *
            year_rec.cpi) * world.AGE_AMOUNT_REDUCTION_RATE
        age_amount = max(
            0, world.AGE_AMOUNT_MAXIMUM * year_rec.cpi - age_amount_reduction)

        # Federal non-refundable tax credits
        if year_rec.is_dead:
            federal_non_refundable_credits = 0
        else:
            federal_non_refundable_credits = (
                world.BASIC_PERSONAL_AMOUNT * year_rec.cpi + age_amount +
                year_rec.cpp_contribution +
                year_rec.ei_premium) * world.NON_REFUNDABLE_CREDIT_RATE

        # Federal tax on taxable income
        # Tax schedule is in real terms, so we need to convert to/from nominal values
        net_federal_tax = max(
            0, world.FEDERAL_TAX_SCHEDULE[taxable_income / year_rec.cpi] *
            year_rec.cpi - federal_non_refundable_credits)

        # Tax payable
        tax_payable = net_federal_tax + total_social_benefit_repayment + net_federal_tax * world.PROVINCIAL_TAX_FRACTION

        return tax_payable