From 164a4dd64bae108abe5ed03b2b4218fa83d17576 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 24 Aug 2025 16:11:19 -0400 Subject: [PATCH 1/2] Implement CPP retirement pension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add CPP retirement pension parameters (maximum, average, eligibility age) - Implement eligibility based on age 60+ and contribution history - Calculate benefit proportional to years of contribution (up to 40 years) - Use average monthly benefit as basis for calculation - Add test coverage for various age and contribution scenarios 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../cpp/retirement/average_monthly.yaml | 15 +++++ .../cpp/retirement/eligibility_age.yaml | 10 ++++ .../cpp/retirement/maximum_monthly.yaml | 15 +++++ .../benefits/cpp/cpp_retirement_pension.yaml | 55 +++++++++++++++++++ .../benefits/cpp/cpp_retirement_eligible.py | 22 ++++++++ .../benefits/cpp/cpp_retirement_pension.py | 33 +++++++++++ .../benefits/cpp/cpp_years_of_contribution.py | 10 ++++ 7 files changed, 160 insertions(+) create mode 100644 policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/average_monthly.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/eligibility_age.yaml create mode 100644 policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/maximum_monthly.yaml create mode 100644 policyengine_canada/tests/benefits/cpp/cpp_retirement_pension.yaml create mode 100644 policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_eligible.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py create mode 100644 policyengine_canada/variables/gov/cra/benefits/cpp/cpp_years_of_contribution.py diff --git a/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/average_monthly.yaml b/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/average_monthly.yaml new file mode 100644 index 000000000..e3b65f44d --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/average_monthly.yaml @@ -0,0 +1,15 @@ +description: Average monthly CPP retirement pension at age 65 +values: + 2020-01-01: 710.41 + 2021-01-01: 702.77 + 2022-01-01: 727.61 + 2023-01-01: 758.32 + 2024-01-01: 816.52 + 2025-01-01: 844.53 +metadata: + unit: currency-CAD + period: month + label: CPP average monthly retirement pension + reference: + - title: CPP retirement pension amount + href: https://www.canada.ca/en/services/benefits/publicpensions/cpp/cpp-benefit/amount.html \ No newline at end of file diff --git a/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/eligibility_age.yaml b/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/eligibility_age.yaml new file mode 100644 index 000000000..4c43dfb7a --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/eligibility_age.yaml @@ -0,0 +1,10 @@ +description: Minimum age for CPP retirement pension eligibility +values: + 2020-01-01: 60 +metadata: + unit: year + period: year + label: CPP retirement pension eligibility age + reference: + - title: CPP retirement pension eligibility + href: https://www.canada.ca/en/services/benefits/publicpensions/cpp/cpp-benefit/eligibility.html \ No newline at end of file diff --git a/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/maximum_monthly.yaml b/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/maximum_monthly.yaml new file mode 100644 index 000000000..564c1f49e --- /dev/null +++ b/policyengine_canada/parameters/gov/cra/benefits/cpp/retirement/maximum_monthly.yaml @@ -0,0 +1,15 @@ +description: Maximum monthly CPP retirement pension at age 65 +values: + 2020-01-01: 1_175.83 + 2021-01-01: 1_203.75 + 2022-01-01: 1_253.59 + 2023-01-01: 1_306.57 + 2024-01-01: 1_364.60 + 2025-01-01: 1_433.00 +metadata: + unit: currency-CAD + period: month + label: CPP maximum monthly retirement pension + reference: + - title: CPP retirement pension amount + href: https://www.canada.ca/en/services/benefits/publicpensions/cpp/cpp-benefit/amount.html \ No newline at end of file diff --git a/policyengine_canada/tests/benefits/cpp/cpp_retirement_pension.yaml b/policyengine_canada/tests/benefits/cpp/cpp_retirement_pension.yaml new file mode 100644 index 000000000..c6ce43d01 --- /dev/null +++ b/policyengine_canada/tests/benefits/cpp/cpp_retirement_pension.yaml @@ -0,0 +1,55 @@ +- name: CPP retirement pension with full contributions + period: 2024 + input: + age: 65 + cpp_years_of_contribution: 40 + output: + # Full contributions (40 years) = average monthly benefit + # $816.52 * 12 = $9,798.24 + cpp_retirement_pension: 9_798.24 + +- name: CPP retirement pension with partial contributions + period: 2024 + input: + age: 65 + cpp_years_of_contribution: 20 + output: + # 20/40 years = 50% of average benefit + # $816.52 * 0.5 * 12 = $4,899.12 + cpp_retirement_pension: 4_899.12 + +- name: No pension below age 60 + period: 2024 + input: + age: 55 + cpp_years_of_contribution: 30 + output: + cpp_retirement_pension: 0 + +- name: No pension without contributions + period: 2024 + input: + age: 65 + cpp_years_of_contribution: 0 + output: + cpp_retirement_pension: 0 + +- name: CPP retirement pension for 2025 + period: 2025 + input: + age: 70 + cpp_years_of_contribution: 45 + output: + # Capped at 40 years = full average benefit + # $844.53 * 12 = $10,134.36 + cpp_retirement_pension: 10_134.36 + +- name: Minimum contribution scenario + period: 2024 + input: + age: 60 + cpp_years_of_contribution: 1 + output: + # 1/40 years = 2.5% of average benefit + # $816.52 * 0.025 * 12 = $244.956 + cpp_retirement_pension: 244.956 \ No newline at end of file diff --git a/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_eligible.py b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_eligible.py new file mode 100644 index 000000000..2bbdc82c0 --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_eligible.py @@ -0,0 +1,22 @@ +from policyengine_canada.model_api import * + + +class cpp_retirement_eligible(Variable): + value_type = bool + entity = Person + label = "Eligible for CPP retirement pension" + definition_period = YEAR + documentation = "Whether person is eligible for CPP retirement pension" + + def formula(person, period, parameters): + p = parameters(period).gov.cra.benefits.cpp.retirement + + # Check age requirement (60+) + age = person("age", period) + meets_age = age >= p.eligibility_age + + # Check contribution requirement (simplified - at least 1 year) + years_contributed = person("cpp_years_of_contribution", period) + has_contributions = years_contributed > 0 + + return meets_age & has_contributions \ No newline at end of file diff --git a/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py new file mode 100644 index 000000000..1801aee8e --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py @@ -0,0 +1,33 @@ +from policyengine_canada.model_api import * + + +class cpp_retirement_pension(Variable): + value_type = float + entity = Person + label = "CPP retirement pension" + definition_period = YEAR + unit = CAD + documentation = "Annual CPP retirement pension amount" + adds = ["cpp_retirement_pension"] + + def formula(person, period, parameters): + p = parameters(period).gov.cra.benefits.cpp.retirement + + # Check eligibility + eligible = person("cpp_retirement_eligible", period) + + # Simplified calculation based on years of contribution + # Use average between 0 and maximum based on contribution years + years_contributed = person("cpp_years_of_contribution", period) + + # Assume full contributions after 40 years + contribution_factor = min_(years_contributed / 40, 1) + + # Calculate monthly amount between 0 and maximum + # For simplicity, use average as midpoint + monthly_amount = contribution_factor * p.average_monthly + + # Convert to annual + annual_amount = monthly_amount * 12 + + return where(eligible, annual_amount, 0) \ No newline at end of file diff --git a/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_years_of_contribution.py b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_years_of_contribution.py new file mode 100644 index 000000000..e5ea52f7d --- /dev/null +++ b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_years_of_contribution.py @@ -0,0 +1,10 @@ +from policyengine_canada.model_api import * + + +class cpp_years_of_contribution(Variable): + value_type = float + entity = Person + label = "Years of CPP contributions" + definition_period = YEAR + unit = "year" + documentation = "Number of years person has contributed to CPP" \ No newline at end of file From be4ae6d97332695cb7c80c98b43f6bb1a8f669f0 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 24 Aug 2025 21:51:06 -0400 Subject: [PATCH 2/2] Fix CPP retirement pension integration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add cpp_retirement_pension to household benefits aggregation - Remove unnecessary self-referential adds declaration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- policyengine_canada/variables/gov/benefits.py | 1 + .../variables/gov/cra/benefits/cpp/cpp_retirement_pension.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_canada/variables/gov/benefits.py b/policyengine_canada/variables/gov/benefits.py index 052eae5ed..9a0fd00fa 100644 --- a/policyengine_canada/variables/gov/benefits.py +++ b/policyengine_canada/variables/gov/benefits.py @@ -11,6 +11,7 @@ class benefits(Variable): "child_benefit", "child_disability_benefit", "canada_workers_benefit", + "cpp_retirement_pension", "dental_benefit", "oas_net", # Ontario programs. diff --git a/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py index 1801aee8e..804814a79 100644 --- a/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py +++ b/policyengine_canada/variables/gov/cra/benefits/cpp/cpp_retirement_pension.py @@ -8,7 +8,6 @@ class cpp_retirement_pension(Variable): definition_period = YEAR unit = CAD documentation = "Annual CPP retirement pension amount" - adds = ["cpp_retirement_pension"] def formula(person, period, parameters): p = parameters(period).gov.cra.benefits.cpp.retirement