diff --git a/changelog.d/1981.added.md b/changelog.d/1981.added.md new file mode 100644 index 0000000000..7ca46c6819 --- /dev/null +++ b/changelog.d/1981.added.md @@ -0,0 +1 @@ +- Added the Lifetime ISA holdings that microcosm datasets carry (`lifetime_isa_balance`, `has_lifetime_isa`, `household_lifetime_isa_balance`) and the Lifetime ISA withdrawal-charge parameters. Universal Credit, Housing Benefit, Income Support, income-based JSA, income-related ESA and Pension Credit now count a Lifetime ISA at its surrender value (75% of the balance under 60, the whole balance from 60) as capital of the holder's own benefit unit when the holder is its claimant or partner, and not before the Lifetime ISA existed (6 April 2017). Datasets without the columns are unaffected. diff --git a/docs/book/assumptions/growthfactors.md b/docs/book/assumptions/growthfactors.md index 1b093f7b61..4d88c837b9 100644 --- a/docs/book/assumptions/growthfactors.md +++ b/docs/book/assumptions/growthfactors.md @@ -60,7 +60,7 @@ We derive these rates from OBR GDP growth and ONS population projections. |-------------|------|------|------|------|------|------|------|------| | Growth rate | 9.2% | 5.0% | 3.8% | 2.8% | 2.8% | 3.1% | 3.3% | 3.3% | -We use per capita GDP for these variables: `capital_gains`, `capital_gains_badr`, `capital_gains_before_response`, `capital_gains_carried_interest`, `capital_gains_residential_property`, `corporate_wealth`, `dividend_income`, `gross_financial_wealth`, `lump_sum_income`, `main_residence_value`, `maintenance_income`, `miscellaneous_income`, `mortgage_capital_repayment`, `net_financial_wealth`, `non_residential_property_value`, `other_investment_income`, `other_residential_property_value`, `owned_land`, `pension_income`, `private_pension_wealth`, `private_transfer_income`, `property_income`, `savings`, `savings_interest_income`, `sublet_income` +We use per capita GDP for these variables: `capital_gains`, `capital_gains_badr`, `capital_gains_before_response`, `capital_gains_carried_interest`, `capital_gains_residential_property`, `corporate_wealth`, `dividend_income`, `gross_financial_wealth`, `household_lifetime_isa_balance`, `lifetime_isa_balance`, `lump_sum_income`, `main_residence_value`, `maintenance_income`, `miscellaneous_income`, `mortgage_capital_repayment`, `net_financial_wealth`, `non_residential_property_value`, `other_investment_income`, `other_residential_property_value`, `owned_land`, `pension_income`, `private_pension_wealth`, `private_transfer_income`, `property_income`, `savings`, `savings_interest_income`, `sublet_income` ## Council tax diff --git a/policyengine_uk/data/uprating_indices.yaml b/policyengine_uk/data/uprating_indices.yaml index 4fbc8bc819..c7175e2fcc 100644 --- a/policyengine_uk/data/uprating_indices.yaml +++ b/policyengine_uk/data/uprating_indices.yaml @@ -86,6 +86,8 @@ gov.economic_assumptions.yoy_growth.obr.per_capita.gdp: - corporate_wealth - dividend_income - gross_financial_wealth +- household_lifetime_isa_balance +- lifetime_isa_balance - lump_sum_income - main_residence_value - maintenance_income diff --git a/policyengine_uk/parameters/gov/dwp/ESA/income/capital/person_sources.yaml b/policyengine_uk/parameters/gov/dwp/ESA/income/capital/person_sources.yaml new file mode 100644 index 0000000000..ca96d72a24 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/ESA/income/capital/person_sources.yaml @@ -0,0 +1,26 @@ +description: > + Person-level model variables counted as income-related Employment and + Support Allowance capital of the person's own benefit unit. They are summed + over the benefit unit's claimant and partner (`is_uc_claimant`), so they + need none of the household proxy that the household `sources` use, and a + dependant's holding is left out: regulation 83 adds only the partner's + capital to the claimant's. `lifetime_isa_countable_capital` is a Lifetime + ISA at its surrender value: regulation 113 values capital at its current + market or surrender value, and DMG 52665 counts a Lifetime ISA at 75% of its + value under 60 and in full from 60. Lifetime ISAs exist from 6 April 2017, + and the list is empty before then. +metadata: + label: Income-related ESA person-level capital sources + period: year + unit: list + reference: + - title: The Employment and Support Allowance Regulations 2008 reg. 83 + href: https://www.legislation.gov.uk/uksi/2008/794/regulation/83 + - title: The Employment and Support Allowance Regulations 2008 reg. 113 + href: https://www.legislation.gov.uk/uksi/2008/794/regulation/113 + - title: Decision Makers' Guide, Chapter 52 (Capital), para. 52665 + href: https://assets.publishing.service.gov.uk/media/69d8f30ad3e08b8871b665ac/dmg-Ch-52.pdf +values: + 2008-10-27: [] + 2017-04-06: + - lifetime_isa_countable_capital diff --git a/policyengine_uk/parameters/gov/dwp/JSA/income/capital/person_sources.yaml b/policyengine_uk/parameters/gov/dwp/JSA/income/capital/person_sources.yaml new file mode 100644 index 0000000000..4df9c7ea98 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/JSA/income/capital/person_sources.yaml @@ -0,0 +1,27 @@ +description: > + Person-level model variables counted as income-based Jobseeker's Allowance + capital of the person's own benefit unit. They are summed over the benefit + unit's claimant and partner (`is_uc_claimant`), so they need none of the + household proxy that the household `sources` use, and a dependant's holding + is left out: regulation 109 kept a child's or young person's capital out of + the claimant's, and since children moved to child tax credit they are + outside the assessment altogether. `lifetime_isa_countable_capital` is a + Lifetime ISA at its surrender value: regulation 111 values capital at its + current market or surrender value, and DMG 29665 counts a Lifetime ISA at + 75% of its value under 60 and in full from 60. Lifetime ISAs exist from 6 + April 2017, and the list is empty before then. +metadata: + label: Income-based JSA person-level capital sources + period: year + unit: list + reference: + - title: The Jobseeker's Allowance Regulations 1996 reg. 109 + href: https://www.legislation.gov.uk/uksi/1996/207/regulation/109 + - title: The Jobseeker's Allowance Regulations 1996 reg. 111 + href: https://www.legislation.gov.uk/uksi/1996/207/regulation/111 + - title: Decision Makers' Guide, Chapter 29 (Capital), para. 29665 + href: https://assets.publishing.service.gov.uk/media/6894a69d3080e72710b2e237/dmg-ch-29.pdf +values: + 1996-10-07: [] + 2017-04-06: + - lifetime_isa_countable_capital diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/capital/person_sources.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/capital/person_sources.yaml new file mode 100644 index 0000000000..b268261563 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/capital/person_sources.yaml @@ -0,0 +1,29 @@ +description: > + Person-level model variables counted as Housing Benefit capital of the + person's own benefit unit. They are summed over the benefit unit's claimant + and partner (`is_uc_claimant`), so they need none of the household proxy + that the household `sources` use, and a dependant's holding is left out: + regulation 45 says the capital of a child or young person in the claimant's + family is not the claimant's. `lifetime_isa_countable_capital` is a + Lifetime ISA at its surrender value: regulation 47 (regulation 45 of the + pension-age regulations) values capital at its current market or surrender + value, in the words of the DWP benefits' regulations, for which DWP guidance + counts a Lifetime ISA at 75% of its value under 60 and in full from 60. + Lifetime ISAs exist from 6 April 2017, and the list is empty before then. +metadata: + label: Housing Benefit person-level capital sources + period: year + unit: list + reference: + - title: The Housing Benefit Regulations 2006 reg. 45 + href: https://www.legislation.gov.uk/uksi/2006/213/regulation/45 + - title: The Housing Benefit Regulations 2006 reg. 47 + href: https://www.legislation.gov.uk/uksi/2006/213/regulation/47 + - title: The Housing Benefit (Persons who have attained the qualifying age for state pension credit) Regulations 2006 reg. 45 + href: https://www.legislation.gov.uk/uksi/2006/214/regulation/45 + - title: Decision Makers' Guide, Chapter 29 (Capital), para. 29665 (Income Support and JSA, whose valuation rule has the same words) + href: https://assets.publishing.service.gov.uk/media/6894a69d3080e72710b2e237/dmg-ch-29.pdf +values: + 2006-04-10: [] + 2017-04-06: + - lifetime_isa_countable_capital diff --git a/policyengine_uk/parameters/gov/dwp/income_support/means_test/capital/person_sources.yaml b/policyengine_uk/parameters/gov/dwp/income_support/means_test/capital/person_sources.yaml new file mode 100644 index 0000000000..d18f864b9b --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/income_support/means_test/capital/person_sources.yaml @@ -0,0 +1,27 @@ +description: > + Person-level model variables counted as Income Support capital of the + person's own benefit unit. They are summed over the benefit unit's claimant + and partner (`is_uc_claimant`), so they need none of the household proxy + that the household `sources` use, and a dependant's holding is left out: + regulation 47 kept a child's or young person's capital out of the + claimant's, and since children moved to child tax credit they are outside + the assessment altogether. `lifetime_isa_countable_capital` is a Lifetime + ISA at its surrender value: regulation 49 values capital at its current + market or surrender value, and DMG 29665 counts a Lifetime ISA at 75% of its + value under 60 and in full from 60. Lifetime ISAs exist from 6 April 2017, + and the list is empty before then. +metadata: + label: Income Support person-level capital sources + period: year + unit: list + reference: + - title: The Income Support (General) Regulations 1987 reg. 47 + href: https://www.legislation.gov.uk/uksi/1987/1967/regulation/47 + - title: The Income Support (General) Regulations 1987 reg. 49 + href: https://www.legislation.gov.uk/uksi/1987/1967/regulation/49 + - title: Decision Makers' Guide, Chapter 29 (Capital), para. 29665 + href: https://assets.publishing.service.gov.uk/media/6894a69d3080e72710b2e237/dmg-ch-29.pdf +values: + 2010-01-01: [] + 2017-04-06: + - lifetime_isa_countable_capital diff --git a/policyengine_uk/parameters/gov/dwp/pension_credit/income/capital/person_sources.yaml b/policyengine_uk/parameters/gov/dwp/pension_credit/income/capital/person_sources.yaml new file mode 100644 index 0000000000..56fd084bd9 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/pension_credit/income/capital/person_sources.yaml @@ -0,0 +1,27 @@ +description: > + Person-level model variables counted as Pension Credit capital of the + person's own benefit unit. They are summed over the benefit unit's claimant + and partner (`is_uc_claimant`), so they need none of the household proxy + that the household `sources` use, and a dependant's holding is left out: + section 5 of the State Pension Credit Act 2002 adds only the partner's + capital to the claimant's. `lifetime_isa_countable_capital` is a Lifetime + ISA at its surrender value: regulation 19 values capital at its current + market or surrender value, and DMG 84751 counts a Lifetime ISA at 75% of its + value under 60 and in full from 60, so a claimant's own Lifetime ISA counts + in full. Lifetime ISAs exist from 6 April 2017, and the list is empty before + then. +metadata: + label: Pension Credit person-level capital sources + period: year + unit: list + reference: + - title: State Pension Credit Act 2002 s. 5 + href: https://www.legislation.gov.uk/ukpga/2002/16/section/5 + - title: The State Pension Credit Regulations 2002 reg. 19 + href: https://www.legislation.gov.uk/uksi/2002/1792/regulation/19 + - title: Decision Makers' Guide, Chapter 84 (Deemed weekly income from capital), para. 84751 + href: https://assets.publishing.service.gov.uk/media/69d8f133daa863aa5db665a5/dmg-Ch-84.pdf +values: + 2003-10-06: [] + 2017-04-06: + - lifetime_isa_countable_capital diff --git a/policyengine_uk/parameters/gov/dwp/universal_credit/means_test/capital/person_sources.yaml b/policyengine_uk/parameters/gov/dwp/universal_credit/means_test/capital/person_sources.yaml new file mode 100644 index 0000000000..36baf598bb --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/universal_credit/means_test/capital/person_sources.yaml @@ -0,0 +1,26 @@ +description: > + Person-level model variables counted as Universal Credit capital of the + person's own benefit unit. They are summed over the benefit unit's claimant + and partner (`is_uc_claimant`), so they need none of the household proxy + that the household `sources` use, and a dependant's holding is left out: + section 5 of the Welfare Reform Act 2012 tests the claimant's, or the joint + claimants' combined, capital. `lifetime_isa_countable_capital` is a + Lifetime ISA at its surrender value: regulation 49(1) values capital at its + current market or surrender value, and ADM H1659 counts a Lifetime ISA at + 75% of its value under 60 and in full from 60. Lifetime ISAs exist from 6 + April 2017, and the list is empty before then. +metadata: + label: Universal Credit person-level capital sources + period: year + unit: list + reference: + - title: Welfare Reform Act 2012 s. 5 + href: https://www.legislation.gov.uk/ukpga/2012/5/section/5 + - title: The Universal Credit Regulations 2013 reg. 49(1) + href: https://www.legislation.gov.uk/uksi/2013/376/regulation/49/1 + - title: Advice for Decision Making, Chapter H1 (Capital), para. H1659 + href: https://assets.publishing.service.gov.uk/media/693ace15c72b0f8ccf33d609/adm-ch-H1.pdf +values: + 2013-04-29: [] + 2017-04-06: + - lifetime_isa_countable_capital diff --git a/policyengine_uk/parameters/gov/hmrc/isa/README.md b/policyengine_uk/parameters/gov/hmrc/isa/README.md new file mode 100644 index 0000000000..84784a6b52 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/isa/README.md @@ -0,0 +1 @@ +# Individual Savings Accounts diff --git a/policyengine_uk/parameters/gov/hmrc/isa/lifetime/README.md b/policyengine_uk/parameters/gov/hmrc/isa/lifetime/README.md new file mode 100644 index 0000000000..f0f1144b7e --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/isa/lifetime/README.md @@ -0,0 +1 @@ +# Lifetime ISA diff --git a/policyengine_uk/parameters/gov/hmrc/isa/lifetime/charge_free_age.yaml b/policyengine_uk/parameters/gov/hmrc/isa/lifetime/charge_free_age.yaml new file mode 100644 index 0000000000..1b93c5363a --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/isa/lifetime/charge_free_age.yaml @@ -0,0 +1,11 @@ +description: > + Age from which withdrawals from a Lifetime ISA carry no withdrawal charge. +values: + 2017-04-06: 60 +metadata: + label: Lifetime ISA charge-free age + period: year + unit: year + reference: + - title: The Individual Savings Account Regulations 1998, Schedule paragraph 4(1) + href: https://www.legislation.gov.uk/uksi/1998/1870/schedule/paragraph/4 diff --git a/policyengine_uk/parameters/gov/hmrc/isa/lifetime/withdrawal_charge.yaml b/policyengine_uk/parameters/gov/hmrc/isa/lifetime/withdrawal_charge.yaml new file mode 100644 index 0000000000..90c07d0ca5 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/isa/lifetime/withdrawal_charge.yaml @@ -0,0 +1,19 @@ +description: > + Share of the amount withdrawn from a Lifetime ISA that is charged when the + withdrawal is not exempt, that is, when it is made before the charge-free age + and not towards a first home or with a terminal illness. +values: + 2017-04-06: 0.25 + 2020-03-06: 0.2 + 2021-04-06: 0.25 +metadata: + label: Lifetime ISA withdrawal charge + period: year + unit: /1 + reference: + - title: The Individual Savings Account Regulations 1998, Schedule paragraph 5 + href: https://www.legislation.gov.uk/uksi/1998/1870/schedule/paragraph/5 + - title: The Individual Savings Account Regulations 1998, Schedule paragraph 5A (20% from 6 March 2020 to 5 April 2021) + href: https://www.legislation.gov.uk/uksi/1998/1870/schedule/paragraph/5A + - title: The Individual Savings Account (Amendment No. 3) (Coronavirus) Regulations 2020 + href: https://www.legislation.gov.uk/uksi/2020/506/made diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/lifetime_isa_capital.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/lifetime_isa_capital.yaml new file mode 100644 index 0000000000..259462514e --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/lifetime_isa_capital.yaml @@ -0,0 +1,446 @@ +- name: Holders and the household total come from the person balances + period: 2025 + absolute_error_margin: 0 + input: + people: + person_1: + age: 30 + lifetime_isa_balance: 4_000 + person_2: + age: 28 + lifetime_isa_balance: 6_000 + person_3: + age: 55 + benunits: + benunit_1: + members: [person_1, person_2] + benunit_2: + members: person_3 + households: + household: + members: [person_1, person_2, person_3] + output: + has_lifetime_isa: [true, true, false] + household_lifetime_isa_balance: 10_000 + +- name: Under 60 the Lifetime ISA counts after the 25% withdrawal charge + period: 2025 + absolute_error_margin: 0 + input: + people: + person: + age: 59 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + output: + lifetime_isa_countable_capital: 7_500 + +- name: From 60 the Lifetime ISA counts in full + period: 2025 + absolute_error_margin: 0 + input: + people: + person: + age: 60 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + output: + lifetime_isa_countable_capital: 10_000 + +- name: In 2020-21 the withdrawal charge was 20% + period: 2020 + absolute_error_margin: 0 + input: + people: + person: + age: 30 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + output: + lifetime_isa_countable_capital: 8_000 + +- name: In 2021-22 the withdrawal charge is 25% again + period: 2021 + absolute_error_margin: 0 + input: + people: + person: + age: 30 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + output: + lifetime_isa_countable_capital: 7_500 + +- name: Universal Credit counts the Lifetime ISA at its surrender value + period: 2025 + absolute_error_margin: 0 + input: + people: + person: + age: 30 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + output: + uc_assessable_capital: 7_500 + +- name: A Universal Credit claimant aged 60 counts the whole Lifetime ISA + period: 2025 + absolute_error_margin: 0 + input: + people: + person: + age: 60 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + output: + uc_assessable_capital: 10_000 + +- name: Savings and a Lifetime ISA together reach the Universal Credit capital limit + period: 2025 + absolute_error_margin: 0 + input: + people: + person: + age: 30 + lifetime_isa_balance: 8_000 + benunits: + benunit: + members: person + households: + household: + members: person + savings: 10_000 + output: + uc_assessable_capital: 16_000 + is_uc_eligible: true + +- name: Savings and a Lifetime ISA together exceed the Universal Credit capital limit + period: 2025 + absolute_error_margin: 0 + input: + people: + person: + age: 30 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + savings: 10_000 + output: + uc_assessable_capital: 17_500 + is_uc_eligible: false + +- name: An adult child's Lifetime ISA counts in the child's Universal Credit benunit only + period: 2025 + absolute_error_margin: 0 + input: + people: + parent_1: + age: 50 + parent_2: + age: 48 + adult_child: + age: 25 + lifetime_isa_balance: 10_000 + benunits: + parents: + members: [parent_1, parent_2] + child: + members: adult_child + households: + household: + members: [parent_1, parent_2, adult_child] + savings: 9_000 + output: + # Household savings are shared by adults (6,000 and 3,000); the + # Lifetime ISA adds 7,500 to the child's benunit alone. + uc_assessable_capital: [6_000, 10_500] + +- name: Reported Universal Credit capital overrides the holder's Lifetime ISA + period: 2025 + absolute_error_margin: 0 + input: + people: + parent_1: + age: 50 + parent_2: + age: 48 + adult_child: + age: 25 + lifetime_isa_balance: 10_000 + benunits: + parents: + members: [parent_1, parent_2] + child: + members: adult_child + uc_reported_capital: 2_000 + households: + household: + members: [parent_1, parent_2, adult_child] + savings: 9_000 + output: + uc_assessable_capital: [7_000, 2_000] + +- name: An adult child's Lifetime ISA counts in the child's Housing Benefit benunit only + period: 2025 + absolute_error_margin: 0 + input: + people: + parent_1: + age: 50 + parent_2: + age: 48 + adult_child: + age: 25 + lifetime_isa_balance: 10_000 + benunits: + parents: + members: [parent_1, parent_2] + child: + members: adult_child + households: + household: + members: [parent_1, parent_2, adult_child] + savings: 9_000 + output: + housing_benefit_assessable_capital: [6_000, 10_500] + +- name: A parent's Income Support claim does not count an adult child's Lifetime ISA + period: 2025 + absolute_error_margin: 0 + input: + people: + parent: + age: 45 + income_support_reported: true + child: + age: 4 + adult_child: + age: 22 + lifetime_isa_balance: 10_000 + benunits: + parent_and_child: + members: [parent, child] + would_claim_IS: true + adult_child: + members: adult_child + households: + household: + members: [parent, child, adult_child] + savings: 2_000 + output: + # The claiming benunit takes the whole household stock (2,000); the + # Lifetime ISA stays with its holder. + income_support_assessable_capital: [2_000, 7_500] + +- name: A parent's income-based JSA claim does not count an adult child's Lifetime ISA + period: 2025 + absolute_error_margin: 0 + input: + people: + parent: + age: 45 + jsa_income_reported: 3_000 + adult_child: + age: 22 + lifetime_isa_balance: 10_000 + benunits: + parent: + members: parent + adult_child: + members: adult_child + households: + household: + members: [parent, adult_child] + savings: 2_000 + output: + jsa_income_assessable_capital: [2_000, 7_500] + +- name: A parent's income-related ESA claim does not count an adult child's Lifetime ISA + period: 2025 + absolute_error_margin: 0 + input: + people: + parent: + age: 45 + esa_income_reported: 3_000 + adult_child: + age: 22 + lifetime_isa_balance: 10_000 + benunits: + parent: + members: parent + adult_child: + members: adult_child + households: + household: + members: [parent, adult_child] + savings: 2_000 + output: + esa_income_assessable_capital: [2_000, 7_500] + +- name: Grandparents' Pension Credit does not count a grandchild's Lifetime ISA + period: 2025 + absolute_error_margin: 0 + input: + people: + grandparent_1: + age: 70 + grandparent_2: + age: 68 + grandchild: + age: 25 + lifetime_isa_balance: 10_000 + benunits: + grandparents: + members: [grandparent_1, grandparent_2] + grandchild: + members: grandchild + households: + household: + members: [grandparent_1, grandparent_2, grandchild] + savings: 4_000 + output: + pension_credit_assessable_capital: [4_000, 0] + +- name: Pension Credit counts a younger partner's Lifetime ISA after the charge + period: 2025 + absolute_error_margin: 0 + input: + people: + claimant: + age: 70 + partner: + age: 45 + lifetime_isa_balance: 10_000 + benunits: + couple: + members: [claimant, partner] + households: + household: + members: [claimant, partner] + output: + pension_credit_assessable_capital: 7_500 + +- name: A dependent young person's Lifetime ISA does not count against the parent's claim + period: 2025 + absolute_error_margin: 0 + input: + people: + parent: + age: 45 + is_parent: true + young_person: + # A qualifying young person: 18 and in non-advanced education. + age: 18 + current_education: POST_SECONDARY + lifetime_isa_balance: 10_000 + benunits: + family: + members: [parent, young_person] + households: + household: + members: [parent, young_person] + output: + is_uc_claimant: [true, false] + uc_assessable_capital: 0 + housing_benefit_assessable_capital: 0 + income_support_assessable_capital: 0 + jsa_income_assessable_capital: 0 + esa_income_assessable_capital: 0 + +- name: A holder the dataset records as a dependant does not count + period: 2025 + absolute_error_margin: 0 + input: + people: + parent: + age: 50 + dependant: + age: 19 + is_uc_claimant: false + lifetime_isa_balance: 10_000 + benunits: + family: + members: [parent, dependant] + households: + household: + members: [parent, dependant] + output: + uc_assessable_capital: 0 + housing_benefit_assessable_capital: 0 + +- name: A pensioner's Pension Credit does not count a dependent young person's Lifetime ISA + period: 2025 + absolute_error_margin: 0 + input: + people: + pensioner: + age: 70 + is_parent: true + young_person: + age: 18 + current_education: POST_SECONDARY + lifetime_isa_balance: 10_000 + benunits: + family: + members: [pensioner, young_person] + households: + household: + members: [pensioner, young_person] + output: + pension_credit_assessable_capital: 0 + +- name: Before the Lifetime ISA existed the means tests count none + period: 2016 + absolute_error_margin: 0 + input: + people: + person: + age: 30 + lifetime_isa_balance: 10_000 + benunits: + benunit: + members: person + households: + household: + members: person + output: + uc_assessable_capital: 0 + housing_benefit_assessable_capital: 0 + income_support_assessable_capital: 0 + jsa_income_assessable_capital: 0 + esa_income_assessable_capital: 0 diff --git a/policyengine_uk/tests/test_lifetime_isa_capital.py b/policyengine_uk/tests/test_lifetime_isa_capital.py new file mode 100644 index 0000000000..c06f16af6d --- /dev/null +++ b/policyengine_uk/tests/test_lifetime_isa_capital.py @@ -0,0 +1,86 @@ +import pandas as pd +import pytest + +from policyengine_uk import Simulation +from policyengine_uk.data.dataset_schema import UKSingleYearDataset +from policyengine_uk.data.economic_assumptions import extend_single_year_dataset +from policyengine_uk.system import system + +LIFETIME_ISA_HOLDER = { + "people": { + "person": { + "age": {"2025": 30}, + "lifetime_isa_balance": {"2025": 10_000}, + } + }, + "benunits": {"benunit": {"members": ["person"]}}, + "households": {"household": {"members": ["person"], "savings": {"2025": 1_000}}}, +} + + +def test_lifetime_isa_balances_uprate_with_the_other_financial_stocks(): + # Datasets store the person balances and their household sum; both must + # move with savings, or the stored household total drifts from its parts. + dataset = UKSingleYearDataset( + person=pd.DataFrame( + { + "person_id": [1, 2], + "person_benunit_id": [1, 1], + "person_household_id": [1, 1], + "age": [30, 28], + "lifetime_isa_balance": [4_000.0, 6_000.0], + } + ), + benunit=pd.DataFrame({"benunit_id": [1]}), + household=pd.DataFrame( + { + "household_id": [1], + "region": ["LONDON"], + "tenure_type": ["RENT_PRIVATELY"], + "council_tax": [1_500.0], + "rent": [12_000.0], + "household_weight": [1.0], + "savings": [1_000.0], + "household_lifetime_isa_balance": [10_000.0], + } + ), + fiscal_year=2025, + ) + + extended = extend_single_year_dataset( + dataset, + tax_benefit_system_parameters=system.parameters, + end_year=2027, + ) + + for year in (2026, 2027): + growth = extended[year].household["savings"].iloc[0] / 1_000.0 + assert growth != pytest.approx(1.0) + person = extended[year].person["lifetime_isa_balance"] + household = extended[year].household["household_lifetime_isa_balance"] + assert list(person) == pytest.approx([4_000.0 * growth, 6_000.0 * growth]) + assert household.iloc[0] == pytest.approx(person.sum()) + + +def test_emptying_a_person_sources_list_disregards_the_lifetime_isa_there_only(): + # The Treasury Committee's proposal to disregard Lifetime ISAs in Universal + # Credit is a change to one list; the other means tests keep counting them. + reform = { + "gov.dwp.universal_credit.means_test.capital.person_sources": { + "2025-01-01.2100-12-31": [] + } + } + baseline = Simulation(situation=LIFETIME_ISA_HOLDER) + reformed = Simulation(situation=LIFETIME_ISA_HOLDER, reform=reform) + + assert baseline.calculate("uc_assessable_capital", 2025)[0] == 8_500 + assert reformed.calculate("uc_assessable_capital", 2025)[0] == 1_000 + assert reformed.calculate("housing_benefit_assessable_capital", 2025)[0] == 8_500 + + +def test_lifetime_isa_capital_is_not_prorated_monthly(): + simulation = Simulation(situation=LIFETIME_ISA_HOLDER) + + assert simulation.calculate("lifetime_isa_balance", "2025-06")[0] == 10_000 + assert simulation.calculate("lifetime_isa_countable_capital", "2025-06")[0] == 7_500 + assert simulation.calculate("uc_assessable_capital", "2025-06")[0] == 8_500 diff --git a/policyengine_uk/variables/gov/dwp/esa_income_assessable_capital.py b/policyengine_uk/variables/gov/dwp/esa_income_assessable_capital.py index 473bcdfebb..89cd48f57d 100644 --- a/policyengine_uk/variables/gov/dwp/esa_income_assessable_capital.py +++ b/policyengine_uk/variables/gov/dwp/esa_income_assessable_capital.py @@ -11,7 +11,10 @@ class esa_income_assessable_capital(Variable): "household level, the model allocates full household capital to any " "benunit with a reported income-related ESA award and only falls back to " "an adult-share proxy when nobody in the household is on that reported " - "claim path." + "claim path. " + "Person-level sources, such as a Lifetime ISA, count only for the " + "holder's own benunit, and only when the holder is its claimant or " + "partner (is_uc_claimant): a dependant's capital is not the claimant's." ) definition_period = YEAR unit = GBP @@ -38,4 +41,12 @@ def formula(benunit, period, parameters): fallback_divisor = max_(1, household_adults) claiming_proxy = where(claiming_esa_income, household_capital, 0) fallback_proxy = household_capital * benunit_adults / fallback_divisor - return where(household_reporting_claimants > 0, claiming_proxy, fallback_proxy) + claimant_or_partner = person("is_uc_claimant", period) + person_capital = sum( + benunit.sum(person(source, period) * claimant_or_partner) + for source in ESA.capital.person_sources + ) + household_share = where( + household_reporting_claimants > 0, claiming_proxy, fallback_proxy + ) + return household_share + person_capital diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_assessable_capital.py b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_assessable_capital.py index c978c5f699..ea49fbf511 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_assessable_capital.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/housing_benefit_assessable_capital.py @@ -6,8 +6,12 @@ class housing_benefit_assessable_capital(Variable): entity = BenUnit label = "Housing Benefit assessable capital" documentation = ( - "Housing Benefit capital counted from the configured capital sources, " - "allocated across benunits using a household adult-share proxy." + "Housing Benefit capital counted from the configured capital sources. " + "Household sources are allocated across benunits using a household " + "adult-share proxy; person-level sources, such as a Lifetime ISA, " + "count only for the holder's own benunit, and only when the holder is " + "its claimant or partner (is_uc_claimant): a dependant's capital is " + "not the claimant's." ) definition_period = YEAR unit = GBP @@ -19,6 +23,11 @@ def formula(benunit, period, parameters): any_over_SP_age = benunit.any(person("is_SP_age", period)) p = parameters(period).gov.dwp.housing_benefit.means_test.capital household_capital = sum(household(source, period) for source in p.sources) + claimant_or_partner = person("is_uc_claimant", period) + person_capital = sum( + benunit.sum(person(source, period) * claimant_or_partner) + for source in p.person_sources + ) benunit_adults = add(benunit, period, ["is_adult"]) household_adults = benunit.max( person.household.sum(person.household.members("is_adult", period)) @@ -30,4 +39,8 @@ def formula(benunit, period, parameters): 0, ) guarantee_credit = any_over_SP_age & (benunit("guarantee_credit", period) > 0) - return where(guarantee_credit, 0, max_(0, household_capital_proxy)) + return where( + guarantee_credit, + 0, + max_(0, household_capital_proxy + person_capital), + ) diff --git a/policyengine_uk/variables/gov/dwp/income_support_assessable_capital.py b/policyengine_uk/variables/gov/dwp/income_support_assessable_capital.py index e497c670f8..07905e8c0e 100644 --- a/policyengine_uk/variables/gov/dwp/income_support_assessable_capital.py +++ b/policyengine_uk/variables/gov/dwp/income_support_assessable_capital.py @@ -10,7 +10,10 @@ class income_support_assessable_capital(Variable): "capital test. Because the dataset only stores these stocks at household " "level, the model allocates full household capital to any benunit on the " "IS claim path and only falls back to an adult-share proxy when nobody in " - "the household is on that path." + "the household is on that path. " + "Person-level sources, such as a Lifetime ISA, count only for the " + "holder's own benunit, and only when the holder is its claimant or " + "partner (is_uc_claimant): a dependant's capital is not the claimant's." ) definition_period = YEAR unit = GBP @@ -42,4 +45,12 @@ def formula(benunit, period, parameters): fallback_divisor = max_(1, household_adults) claiming_proxy = where(would_claim_is, household_capital, 0) fallback_proxy = household_capital * benunit_adults / fallback_divisor - return where(household_claiming_adults > 0, claiming_proxy, fallback_proxy) + claimant_or_partner = person("is_uc_claimant", period) + person_capital = sum( + benunit.sum(person(source, period) * claimant_or_partner) + for source in IS.means_test.capital.person_sources + ) + household_share = where( + household_claiming_adults > 0, claiming_proxy, fallback_proxy + ) + return household_share + person_capital diff --git a/policyengine_uk/variables/gov/dwp/jsa_income_assessable_capital.py b/policyengine_uk/variables/gov/dwp/jsa_income_assessable_capital.py index 2483cc9945..3fa403e268 100644 --- a/policyengine_uk/variables/gov/dwp/jsa_income_assessable_capital.py +++ b/policyengine_uk/variables/gov/dwp/jsa_income_assessable_capital.py @@ -11,7 +11,10 @@ class jsa_income_assessable_capital(Variable): "household level, the model allocates full household capital to any " "benunit with a reported income-based JSA award and only falls back to " "an adult-share proxy when nobody in the household is on that reported " - "claim path." + "claim path. " + "Person-level sources, such as a Lifetime ISA, count only for the " + "holder's own benunit, and only when the holder is its claimant or " + "partner (is_uc_claimant): a dependant's capital is not the claimant's." ) definition_period = YEAR unit = GBP @@ -38,4 +41,12 @@ def formula(benunit, period, parameters): fallback_divisor = max_(1, household_adults) claiming_proxy = where(claiming_jsa_income, household_capital, 0) fallback_proxy = household_capital * benunit_adults / fallback_divisor - return where(household_reporting_claimants > 0, claiming_proxy, fallback_proxy) + claimant_or_partner = person("is_uc_claimant", period) + person_capital = sum( + benunit.sum(person(source, period) * claimant_or_partner) + for source in JSA.capital.person_sources + ) + household_share = where( + household_reporting_claimants > 0, claiming_proxy, fallback_proxy + ) + return household_share + person_capital diff --git a/policyengine_uk/variables/gov/dwp/lifetime_isa_countable_capital.py b/policyengine_uk/variables/gov/dwp/lifetime_isa_countable_capital.py new file mode 100644 index 0000000000..6daf2817c3 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/lifetime_isa_countable_capital.py @@ -0,0 +1,30 @@ +from policyengine_uk.model_api import * + + +class lifetime_isa_countable_capital(Variable): + label = "Lifetime ISA value counted as means-tested capital" + documentation = ( + "Surrender value of the person's Lifetime ISAs: the balance less the " + "withdrawal charge below the charge-free age, and the whole balance " + "from that age. Every means test values capital at its current market " + "or surrender value, and DWP guidance counts a Lifetime ISA at 75% of " + "its value under 60 and in full from 60. Not modelled: the " + "charge-free withdrawals towards a first home or with a terminal " + "illness." + ) + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = STOCK + reference = [ + "https://www.legislation.gov.uk/uksi/2013/376/regulation/49", + "https://assets.publishing.service.gov.uk/media/693ace15c72b0f8ccf33d609/adm-ch-H1.pdf#page=56", + "https://www.legislation.gov.uk/uksi/1998/1870/schedule", + ] + + def formula(person, period, parameters): + p = parameters(period).gov.hmrc.isa.lifetime + balance = person("lifetime_isa_balance", period) + charge_free = person("age", period) >= p.charge_free_age + return where(charge_free, balance, balance * (1 - p.withdrawal_charge)) diff --git a/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_assessable_capital.py b/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_assessable_capital.py index 9f4c1fde97..6b3dfeed1c 100644 --- a/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_assessable_capital.py +++ b/policyengine_uk/variables/gov/dwp/pension_credit/pension_credit_assessable_capital.py @@ -9,7 +9,10 @@ class pension_credit_assessable_capital(Variable): "Pension Credit capital counted from the configured capital sources, " "split only across pension-age adults in the household so pensioner " "couples pool capital together without dilution by unrelated working-" - "age adults." + "age adults. Person-level sources, such as a Lifetime ISA, count " + "only for the holder's own benunit, and only when the holder is its " + "claimant or partner (is_uc_claimant): a dependant's capital is not " + "the claimant's." ) definition_period = YEAR unit = GBP @@ -20,6 +23,11 @@ def formula(benunit, period, parameters): person = benunit.members p = parameters(period).gov.dwp.pension_credit.income.capital household_capital = sum(household(source, period) for source in p.sources) + claimant_or_partner = person("is_uc_claimant", period) + person_capital = sum( + benunit.sum(person(source, period) * claimant_or_partner) + for source in p.person_sources + ) any_pension_age = benunit.any(person("is_SP_age", period)) benunit_pension_age_adults = benunit.sum(person("is_SP_age", period)) household_pension_age_adults = benunit.max( @@ -29,4 +37,8 @@ def formula(benunit, period, parameters): household_capital_proxy = ( household_capital * benunit_pension_age_adults / adult_divisor ) - return where(any_pension_age, max_(0, household_capital_proxy), 0) + return where( + any_pension_age, + max_(0, household_capital_proxy + person_capital), + 0, + ) diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/uc_assessable_capital.py b/policyengine_uk/variables/gov/dwp/universal_credit/uc_assessable_capital.py index eef9c8f729..ebe2537056 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/uc_assessable_capital.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/uc_assessable_capital.py @@ -7,7 +7,11 @@ class uc_assessable_capital(Variable): label = "Universal Credit assessable capital" documentation = ( "Universal Credit capital counted from the configured capital sources, " - "with benunit-reported overrides when available." + "with benunit-reported overrides when available. Household sources are " + "allocated by the household proxy; person-level sources, such as a " + "Lifetime ISA, count only for the holder's own benunit, and only when " + "the holder is its claimant or partner (is_uc_claimant): a dependant's " + "capital is not the claimant's." ) definition_period = YEAR unit = GBP @@ -19,6 +23,11 @@ def formula(benunit, period, parameters): household_capital = sum( household(source, period) for source in p.capital.sources ) + claimant_or_partner = benunit.members("is_uc_claimant", period) + person_capital = sum( + benunit.sum(benunit.members(source, period) * claimant_or_partner) + for source in p.capital.person_sources + ) benunit_adults = add(benunit, period, ["is_adult"]) household_reported_capital = household("household_uc_reported_capital", period) household_unreported_adults = household( @@ -38,6 +47,6 @@ def formula(benunit, period, parameters): assessed_capital = where( use_reported_capital, reported_capital, - household_capital_proxy, + household_capital_proxy + person_capital, ) return max_(0, assessed_capital) diff --git a/policyengine_uk/variables/household/wealth/has_lifetime_isa.py b/policyengine_uk/variables/household/wealth/has_lifetime_isa.py new file mode 100644 index 0000000000..858b94f38f --- /dev/null +++ b/policyengine_uk/variables/household/wealth/has_lifetime_isa.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class has_lifetime_isa(Variable): + label = "holds a Lifetime ISA" + documentation = ( + "Whether the person holds a Lifetime ISA with a positive balance. A " + "dataset that stores this column keeps the stored value: it is " + "recomputed only when not stored, so a reform or situation that edits " + "lifetime_isa_balance leaves it unchanged." + ) + entity = Person + definition_period = YEAR + value_type = bool + + def formula(person, period): + return person("lifetime_isa_balance", period) > 0 diff --git a/policyengine_uk/variables/household/wealth/household_lifetime_isa_balance.py b/policyengine_uk/variables/household/wealth/household_lifetime_isa_balance.py new file mode 100644 index 0000000000..2ec53080cb --- /dev/null +++ b/policyengine_uk/variables/household/wealth/household_lifetime_isa_balance.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * + + +class household_lifetime_isa_balance(Variable): + label = "household Lifetime ISA balance" + documentation = ( + "Lifetime ISA balances summed over the household's members. Like " + "lifetime_isa_balance, it is a component of gross_financial_wealth and " + "must never be added to it. A dataset that stores this column keeps " + "the stored value: it is recomputed only when not stored, so a reform " + "or situation that edits lifetime_isa_balance leaves it unchanged. No " + "means test reads it." + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = STOCK + adds = ["lifetime_isa_balance"] diff --git a/policyengine_uk/variables/input/lifetime_isa_balance.py b/policyengine_uk/variables/input/lifetime_isa_balance.py new file mode 100644 index 0000000000..c18525a546 --- /dev/null +++ b/policyengine_uk/variables/input/lifetime_isa_balance.py @@ -0,0 +1,23 @@ +from policyengine_uk.model_api import * + + +class lifetime_isa_balance(Variable): + label = "Lifetime ISA balance" + documentation = ( + "Current value of the person's Lifetime ISAs, cash and stocks and " + "shares together. The Wealth and Assets Survey counts every ISA, " + "Lifetime ISAs included, inside household gross financial wealth, so " + "this is already a component of gross_financial_wealth and " + "net_financial_wealth and must never be added to them. It is not part " + "of savings (savings accounts) or corporate_wealth (shares, including " + "stocks and shares ISAs), and total_wealth does not include it. Means " + "tests count it through lifetime_isa_countable_capital, at the value " + "left after the withdrawal charge." + ) + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + uprating = "gov.economic_assumptions.indices.obr.per_capita.gdp" + quantity_type = STOCK + default_value = 0