policyengine-uk 2.40.1__py3-none-any.whl → 2.65.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. policyengine_uk/__init__.py +5 -3
  2. policyengine_uk/data/__init__.py +1 -0
  3. policyengine_uk/data/dataset_schema.py +70 -18
  4. policyengine_uk/data/economic_assumptions.py +36 -10
  5. policyengine_uk/data/filter_dataset.py +52 -0
  6. policyengine_uk/dynamics/labour_supply.py +343 -0
  7. policyengine_uk/dynamics/participation.py +629 -0
  8. policyengine_uk/dynamics/progression.py +384 -0
  9. policyengine_uk/microsimulation.py +105 -0
  10. policyengine_uk/model_api.py +1 -0
  11. policyengine_uk/parameters/gov/boe/base_rate.yaml +34 -0
  12. policyengine_uk/parameters/gov/boe/index.yaml +2 -0
  13. policyengine_uk/parameters/gov/contrib/behavioral_responses/employee_salary_sacrifice_reduction_rate.yaml +14 -0
  14. policyengine_uk/parameters/gov/contrib/behavioral_responses/salary_sacrifice_broad_base_haircut_rate.yaml +22 -0
  15. policyengine_uk/parameters/gov/contrib/cec/state_pension_increase.yaml +1 -1
  16. policyengine_uk/parameters/gov/contrib/ubi_center/carbon_tax.yaml +2 -2
  17. policyengine_uk/parameters/gov/contrib/ubi_center/land_value_tax.yaml +3 -3
  18. policyengine_uk/parameters/gov/dcms/bbc/tv_licence/colour.yaml +5 -5
  19. policyengine_uk/parameters/gov/dfe/education_spending.yaml +1 -1
  20. policyengine_uk/parameters/gov/dft/rail/fare_index.yaml +32 -0
  21. policyengine_uk/parameters/gov/dft/rail/prior_law_fare_index.yaml +32 -0
  22. policyengine_uk/parameters/gov/dft/rail/ridership_index.yaml +30 -0
  23. policyengine_uk/parameters/gov/dft/spending.yaml +2 -2
  24. policyengine_uk/parameters/gov/dwp/ESA/income/earn_disregard.yaml +1 -1
  25. policyengine_uk/parameters/gov/dwp/ESA/income/income_disregard_couple.yaml +1 -1
  26. policyengine_uk/parameters/gov/dwp/ESA/income/income_disregard_lone_parent.yaml +1 -1
  27. policyengine_uk/parameters/gov/dwp/ESA/income/income_disregard_single.yaml +1 -1
  28. policyengine_uk/parameters/gov/dwp/ESA/income/pension_disregard.yaml +1 -1
  29. policyengine_uk/parameters/gov/dwp/IIDB/maximum.yaml +1 -1
  30. policyengine_uk/parameters/gov/dwp/JSA/contrib/amount_over_25.yaml +1 -1
  31. policyengine_uk/parameters/gov/dwp/JSA/contrib/earn_disregard.yaml +1 -1
  32. policyengine_uk/parameters/gov/dwp/JSA/contrib/pension_disregard.yaml +1 -1
  33. policyengine_uk/parameters/gov/dwp/JSA/income/amount_18_24.yaml +7 -7
  34. policyengine_uk/parameters/gov/dwp/JSA/income/amount_over_25.yaml +7 -7
  35. policyengine_uk/parameters/gov/dwp/JSA/income/income_disregard_couple.yaml +1 -1
  36. policyengine_uk/parameters/gov/dwp/JSA/income/income_disregard_lone_parent.yaml +1 -1
  37. policyengine_uk/parameters/gov/dwp/JSA/income/income_disregard_single.yaml +1 -1
  38. policyengine_uk/parameters/gov/dwp/LHA/shared_accommodation_age_threshold.yaml +12 -0
  39. policyengine_uk/parameters/gov/dwp/attendance_allowance/higher.yaml +7 -7
  40. policyengine_uk/parameters/gov/dwp/attendance_allowance/lower.yaml +7 -7
  41. policyengine_uk/parameters/gov/dwp/benefit_cap.yaml +3 -3
  42. policyengine_uk/parameters/gov/dwp/carer_premium/couple.yaml +2 -2
  43. policyengine_uk/parameters/gov/dwp/carer_premium/single.yaml +6 -6
  44. policyengine_uk/parameters/gov/dwp/carers_allowance/rate.yaml +7 -7
  45. policyengine_uk/parameters/gov/dwp/disability_premia/disability_couple.yaml +1 -1
  46. policyengine_uk/parameters/gov/dwp/disability_premia/enhanced_couple.yaml +1 -1
  47. policyengine_uk/parameters/gov/dwp/disability_premia/enhanced_single.yaml +1 -1
  48. policyengine_uk/parameters/gov/dwp/disability_premia/severe_couple.yaml +1 -1
  49. policyengine_uk/parameters/gov/dwp/dla/mobility/higher.yaml +4 -4
  50. policyengine_uk/parameters/gov/dwp/dla/mobility/lower.yaml +8 -8
  51. policyengine_uk/parameters/gov/dwp/dla/self_care/higher.yaml +7 -7
  52. policyengine_uk/parameters/gov/dwp/dla/self_care/lower.yaml +8 -8
  53. policyengine_uk/parameters/gov/dwp/dla/self_care/middle.yaml +7 -7
  54. policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/lone_parent/aged.yaml +1 -1
  55. policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/lone_parent/older.yaml +3 -3
  56. policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/single/aged.yaml +1 -1
  57. policyengine_uk/parameters/gov/dwp/housing_benefit/allowances/single/older.yaml +3 -3
  58. policyengine_uk/parameters/gov/dwp/housing_benefit/means_test/income_disregard/worker.yaml +1 -1
  59. policyengine_uk/parameters/gov/dwp/housing_benefit/non_dep_deduction/amount.yaml +1 -1
  60. policyengine_uk/parameters/gov/dwp/housing_benefit/takeup.yaml +7 -7
  61. policyengine_uk/parameters/gov/dwp/income_support/amounts/amount_16_24.yaml +1 -1
  62. policyengine_uk/parameters/gov/dwp/income_support/amounts/amount_couples_over_18.yaml +1 -1
  63. policyengine_uk/parameters/gov/dwp/income_support/means_test/earn_disregard.yaml +1 -1
  64. policyengine_uk/parameters/gov/dwp/income_support/means_test/income_disregard_couple.yaml +1 -1
  65. policyengine_uk/parameters/gov/dwp/income_support/means_test/income_disregard_lone_parent.yaml +1 -1
  66. policyengine_uk/parameters/gov/dwp/income_support/means_test/income_disregard_single.yaml +1 -1
  67. policyengine_uk/parameters/gov/dwp/income_support/means_test/pension_disregard.yaml +1 -1
  68. policyengine_uk/parameters/gov/dwp/income_support/takeup.yaml +7 -7
  69. policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/carer/addition.yaml +4 -4
  70. policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/minimum_guarantee.yaml +9 -9
  71. policyengine_uk/parameters/gov/dwp/pension_credit/guarantee_credit/severe_disability/addition.yaml +3 -3
  72. policyengine_uk/parameters/gov/dwp/pension_credit/savings_credit/threshold.yaml +5 -5
  73. policyengine_uk/parameters/gov/dwp/pip/daily_living/enhanced.yaml +7 -7
  74. policyengine_uk/parameters/gov/dwp/pip/daily_living/standard.yaml +8 -8
  75. policyengine_uk/parameters/gov/dwp/pip/mobility/enhanced.yaml +4 -4
  76. policyengine_uk/parameters/gov/dwp/pip/mobility/standard.yaml +9 -9
  77. policyengine_uk/parameters/gov/dwp/sda/maximum.yaml +7 -7
  78. policyengine_uk/parameters/gov/dwp/state_pension/basic_state_pension/amount.yaml +11 -11
  79. policyengine_uk/parameters/gov/dwp/state_pension/new_state_pension/amount.yaml +4 -4
  80. policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/limit/child_count.yaml +10 -1
  81. policyengine_uk/parameters/gov/dwp/tax_credits/child_tax_credit/takeup.yaml +7 -7
  82. policyengine_uk/parameters/gov/dwp/tax_credits/working_tax_credit/takeup.yaml +7 -7
  83. policyengine_uk/parameters/gov/dwp/universal_credit/elements/carer/amount.yaml +3 -3
  84. policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/amount.yaml +2 -4
  85. policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/disabled/amount.yaml +2 -4
  86. policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/first/higher_amount.yaml +6 -8
  87. policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/limit/child_count.yaml +6 -1
  88. policyengine_uk/parameters/gov/dwp/universal_credit/elements/child/severely_disabled/amount.yaml +3 -5
  89. policyengine_uk/parameters/gov/dwp/universal_credit/elements/childcare/cap.yaml +2 -6
  90. policyengine_uk/parameters/gov/dwp/universal_credit/elements/disabled/amount.yaml +4 -6
  91. policyengine_uk/parameters/gov/dwp/universal_credit/elements/housing/non_dep_deduction/amount.yaml +4 -1
  92. policyengine_uk/parameters/gov/dwp/universal_credit/rebalancing/active.yaml +9 -0
  93. policyengine_uk/parameters/gov/dwp/universal_credit/rebalancing/new_claimant_health_element.yaml +9 -0
  94. policyengine_uk/parameters/gov/dwp/universal_credit/rebalancing/standard_allowance_uplift.yaml +13 -0
  95. policyengine_uk/parameters/gov/dwp/universal_credit/standard_allowance/amount.yaml +5 -5
  96. policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/maximum_taxable_income.yaml +2 -1
  97. policyengine_uk/parameters/gov/dwp/winter_fuel_payment/eligibility/taxable_income_test/use_maximum_taxable_income.yaml +1 -0
  98. policyengine_uk/parameters/gov/dynamic/obr_labour_supply_assumptions.yaml +9 -0
  99. policyengine_uk/parameters/gov/economic_assumptions/create_economic_assumption_indices.py +1 -1
  100. policyengine_uk/parameters/gov/economic_assumptions/yoy_growth.yaml +522 -153
  101. policyengine_uk/parameters/gov/hmrc/cgt/additional_rate.yaml +5 -0
  102. policyengine_uk/parameters/gov/hmrc/cgt/basic_rate.yaml +5 -0
  103. policyengine_uk/parameters/gov/hmrc/cgt/higher_rate.yaml +4 -0
  104. policyengine_uk/parameters/gov/hmrc/child_benefit/amount/additional.yaml +6 -6
  105. policyengine_uk/parameters/gov/hmrc/child_benefit/amount/eldest.yaml +8 -8
  106. policyengine_uk/parameters/gov/hmrc/child_benefit/takeup/by_age.yaml +1 -1
  107. policyengine_uk/parameters/gov/hmrc/fuel_duty/calculate_fuel_duty_rates.py +464 -0
  108. policyengine_uk/parameters/gov/hmrc/fuel_duty/petrol_and_diesel.yaml +86 -10
  109. policyengine_uk/parameters/gov/hmrc/income_tax/allowances/personal_allowance/amount.yaml +6 -0
  110. policyengine_uk/parameters/gov/hmrc/income_tax/earned_taxable_income_exclusions.yaml +2 -1
  111. policyengine_uk/parameters/gov/hmrc/income_tax/income_tax_additions.yaml +1 -0
  112. policyengine_uk/parameters/gov/hmrc/income_tax/rates/dividends.yaml +12 -0
  113. policyengine_uk/parameters/gov/hmrc/income_tax/rates/property.yaml +46 -0
  114. policyengine_uk/parameters/gov/hmrc/income_tax/rates/savings.yaml +46 -0
  115. policyengine_uk/parameters/gov/hmrc/income_tax/rates/scotland/rates.yaml +2 -2
  116. policyengine_uk/parameters/gov/hmrc/income_tax/rates/uk.yaml +14 -2
  117. policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/rates/employee/additional.yaml +4 -6
  118. policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/rates/employer.yaml +3 -3
  119. policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/thresholds/secondary_threshold.yaml +14 -4
  120. policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml +2 -2
  121. policyengine_uk/parameters/gov/hmrc/national_insurance/salary_sacrifice_pension_cap.yaml +16 -0
  122. policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/index.yaml +12 -0
  123. policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_1/boe_margin.yaml +11 -0
  124. policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_2/additional_rate.yaml +27 -0
  125. policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_2/index.yaml +16 -0
  126. policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/plan_2/upper_threshold.yaml +48 -0
  127. policyengine_uk/parameters/gov/hmrc/student_loans/interest_rates/postgraduate_additional_rate.yaml +11 -0
  128. policyengine_uk/parameters/gov/hmrc/student_loans/postgraduate_repayment_rate.yaml +9 -0
  129. policyengine_uk/parameters/gov/hmrc/student_loans/repayment_rate.yaml +9 -0
  130. policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_1.yaml +25 -0
  131. policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_2.yaml +58 -0
  132. policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_4.yaml +19 -0
  133. policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/plan_5.yaml +16 -0
  134. policyengine_uk/parameters/gov/hmrc/student_loans/thresholds/postgraduate.yaml +21 -0
  135. policyengine_uk/parameters/gov/hmrc/vat/reduced_rate_share.yaml +3 -3
  136. policyengine_uk/parameters/gov/indices/private_rent_index.yaml +9 -9
  137. policyengine_uk/parameters/gov/revenue_scotland/lbtt/non_residential.yaml +2 -2
  138. policyengine_uk/parameters/gov/revenue_scotland/lbtt/rent.yaml +2 -2
  139. policyengine_uk/parameters/gov/revenue_scotland/lbtt/residential/first_time_buyer_rate.yaml +2 -2
  140. policyengine_uk/parameters/gov/revenue_scotland/lbtt/residential/rate.yaml +2 -2
  141. policyengine_uk/parameters/gov/wra/land_transaction_tax/non_residential.yaml +2 -2
  142. policyengine_uk/parameters/gov/wra/land_transaction_tax/rent.yaml +2 -2
  143. policyengine_uk/parameters/gov/wra/land_transaction_tax/residential/higher_rate.yaml +1 -1
  144. policyengine_uk/parameters/gov/wra/land_transaction_tax/residential/primary.yaml +2 -2
  145. policyengine_uk/parameters/household/consumption/carbon/consumption.yaml +8 -6
  146. policyengine_uk/parameters/household/consumption/carbon/intensity.yaml +4 -1
  147. policyengine_uk/parameters/household/consumption/carbon/production.yaml +12 -7
  148. policyengine_uk/parameters/household/consumption/carbon/production_by_source.yaml +76 -41
  149. policyengine_uk/parameters/household/consumption/fuel/prices/petrol.yaml +1 -1
  150. policyengine_uk/parameters/household/poverty/absolute_poverty_threshold_bhc.yaml +1 -1
  151. policyengine_uk/reforms/policyengine/adjust_budgets.py +0 -1
  152. policyengine_uk/scenarios/__init__.py +4 -0
  153. policyengine_uk/scenarios/pip_reform.py +23 -0
  154. policyengine_uk/scenarios/reindex_benefit_cap.py +32 -0
  155. policyengine_uk/scenarios/repeal_two_child_limit.py +10 -0
  156. policyengine_uk/scenarios/uc_reform.py +50 -0
  157. policyengine_uk/simulation.py +619 -0
  158. policyengine_uk/system.py +3 -257
  159. policyengine_uk/tax_benefit_system.py +141 -0
  160. policyengine_uk/tests/behavioral_responses/test_labor_supply_responses.yaml +183 -0
  161. policyengine_uk/tests/microsimulation/reforms_config.yaml +8 -8
  162. policyengine_uk/tests/microsimulation/test_reform_impacts.py +2 -2
  163. policyengine_uk/tests/microsimulation/test_salary_sacrifice_cap_reform.py +401 -0
  164. policyengine_uk/tests/microsimulation/test_validity.py +2 -3
  165. policyengine_uk/tests/microsimulation/update_reform_impacts.py +104 -40
  166. policyengine_uk/tests/policy/baseline/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.yaml +105 -0
  167. policyengine_uk/tests/policy/baseline/finance/benefit/family/child_benefit.yaml +2 -0
  168. policyengine_uk/tests/policy/baseline/finance/benefit/family/income_support.yaml +0 -23
  169. policyengine_uk/tests/policy/baseline/gov/dcms/bbc/tv-licence/tv_licence.yaml +3 -0
  170. policyengine_uk/tests/policy/baseline/gov/dfe/extended_childcare_entitlement/extended_childcare_entitlement.yaml +17 -17
  171. policyengine_uk/tests/policy/baseline/gov/dwp/basic_state_pension.yaml +44 -0
  172. policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/allowances/gift_aid.yaml +71 -0
  173. policyengine_uk/tests/policy/baseline/gov/hmrc/income_tax/allowances/personal_allowance.yaml +161 -0
  174. policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employee.yaml +107 -0
  175. policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employer.yaml +95 -0
  176. policyengine_uk/tests/policy/baseline/gov/hmrc/student_loans/student_loan_interest_rate.yaml +153 -0
  177. policyengine_uk/tests/policy/baseline/gov/hmrc/student_loans/student_loan_repayment.yaml +130 -0
  178. policyengine_uk/tests/policy/baseline/household/wealth/vehicle.yaml +27 -0
  179. policyengine_uk/tests/policy/reforms/nov_2025_budget/income_source_tax_rates.yaml +235 -0
  180. policyengine_uk/tests/policy/reforms/nov_2025_budget/income_tax_freeze.yaml +83 -0
  181. policyengine_uk/tests/policy/reforms/parametric/basic_income/basic_income.yaml +1 -0
  182. policyengine_uk/tests/test_behavioral_responses.py +215 -0
  183. policyengine_uk/tests/test_fiscal_year_parameters.py +131 -0
  184. policyengine_uk/utils/__init__.py +1 -0
  185. policyengine_uk/utils/compare.py +28 -0
  186. policyengine_uk/utils/create_ahc_deflator.py +169 -0
  187. policyengine_uk/utils/create_triple_lock.py +1 -1
  188. policyengine_uk/utils/dependencies.py +259 -0
  189. policyengine_uk/utils/parameters.py +12 -1
  190. policyengine_uk/utils/scenario.py +225 -0
  191. policyengine_uk/utils/solve_private_school_attendance_factor.py +4 -6
  192. policyengine_uk/variables/contrib/policyengine/education_budget_change.py +0 -1
  193. policyengine_uk/variables/contrib/policyengine/employer_ni/baseline_employer_cost.py +5 -1
  194. policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py +23 -23
  195. policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_capital_incidence.py +1 -1
  196. policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_consumer_incidence.py +1 -1
  197. policyengine_uk/variables/contrib/policyengine/other_public_spending_budget_change.py +0 -1
  198. policyengine_uk/variables/gov/dfe/targeted_childcare_entitlement/targeted_childcare_entitlement_eligible.py +0 -1
  199. policyengine_uk/variables/gov/dft/rail_subsidy_spending.py +16 -1
  200. policyengine_uk/variables/gov/dft/rail_usage.py +16 -0
  201. policyengine_uk/variables/gov/dwp/BRMA_LHA_rate.py +7 -2
  202. policyengine_uk/variables/gov/dwp/LHA_category.py +4 -2
  203. policyengine_uk/variables/gov/dwp/additional_state_pension.py +4 -2
  204. policyengine_uk/variables/gov/dwp/basic_state_pension.py +26 -8
  205. policyengine_uk/variables/gov/dwp/is_CTC_eligible.py +1 -1
  206. policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt.py +9 -13
  207. policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_earnings.py +66 -0
  208. policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_health_disability.py +75 -0
  209. policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt_other.py +66 -0
  210. policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py +5 -4
  211. policyengine_uk/variables/gov/gov_tax.py +0 -2
  212. policyengine_uk/variables/gov/hmrc/household_tax.py +0 -1
  213. policyengine_uk/variables/gov/hmrc/income_tax/allowances/gift_aid.py +23 -0
  214. policyengine_uk/variables/gov/hmrc/income_tax/allowances/personal_allowance.py +9 -2
  215. policyengine_uk/variables/gov/hmrc/income_tax/income_tax_pre_charges.py +1 -0
  216. policyengine_uk/variables/gov/hmrc/income_tax/liability/property_income_tax.py +75 -0
  217. policyengine_uk/variables/gov/hmrc/income_tax/liability/savings_income_tax.py +4 -4
  218. policyengine_uk/variables/gov/hmrc/national_insurance/salary_sacrifice_broad_base_haircut.py +43 -0
  219. policyengine_uk/variables/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employee.py +38 -0
  220. policyengine_uk/variables/gov/hmrc/national_insurance/salary_sacrifice_pension_ni_employer.py +27 -0
  221. policyengine_uk/variables/gov/hmrc/pensions/pension_contributions_via_salary_sacrifice_adjusted.py +31 -0
  222. policyengine_uk/variables/gov/hmrc/pensions/salary_sacrifice_returned_to_income.py +41 -0
  223. policyengine_uk/variables/gov/hmrc/student_loans/__init__.py +2 -0
  224. policyengine_uk/variables/gov/hmrc/student_loans/plan_1_interest_rate.py +23 -0
  225. policyengine_uk/variables/gov/hmrc/student_loans/plan_2_interest_rate.py +31 -0
  226. policyengine_uk/variables/gov/hmrc/student_loans/plan_4_interest_rate.py +22 -0
  227. policyengine_uk/variables/gov/hmrc/student_loans/plan_5_interest_rate.py +18 -0
  228. policyengine_uk/variables/gov/hmrc/student_loans/postgraduate_interest_rate.py +23 -0
  229. policyengine_uk/variables/gov/hmrc/student_loans/student_loan_plan.py +27 -0
  230. policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment.py +91 -0
  231. policyengine_uk/variables/gov/hmrc/student_loans/student_loan_repayment_rate.py +31 -0
  232. policyengine_uk/variables/gov/hmrc/would_claim_child_benefit.py +5 -1
  233. policyengine_uk/variables/household/demographic/benunit/benunit_count_adults.py +11 -0
  234. policyengine_uk/variables/household/demographic/is_disabled_for_benefits.py +13 -1
  235. policyengine_uk/variables/household/income/hbai_household_net_income.py +29 -1
  236. policyengine_uk/variables/household/income/hbai_household_net_income_ahc.py +13 -0
  237. policyengine_uk/variables/household/income/household_net_income.py +5 -1
  238. policyengine_uk/variables/household/income/inflation_adjustment.py +24 -0
  239. policyengine_uk/variables/household/post_tax_income.py +12 -0
  240. policyengine_uk/variables/household/wealth/num_vehicles.py +9 -0
  241. policyengine_uk/variables/household/wealth/owns_vehicle.py +17 -0
  242. policyengine_uk/variables/input/consumption/property/council_tax.py +0 -35
  243. policyengine_uk/variables/input/consumption/property/employee_pension_contributions.py +8 -1
  244. policyengine_uk/variables/input/consumption/property/employee_pension_contributions_reported.py +16 -0
  245. policyengine_uk/variables/input/consumption/property/pension_contributions_via_salary_sacrifice.py +16 -0
  246. policyengine_uk/variables/input/employment_income.py +2 -0
  247. policyengine_uk/variables/input/rent.py +0 -40
  248. policyengine_uk/variables/input/savings_interest_income.py +3 -1
  249. {policyengine_uk-2.40.1.dist-info → policyengine_uk-2.65.6.dist-info}/METADATA +17 -8
  250. {policyengine_uk-2.40.1.dist-info → policyengine_uk-2.65.6.dist-info}/RECORD +252 -173
  251. {policyengine_uk-2.40.1.dist-info → policyengine_uk-2.65.6.dist-info}/WHEEL +1 -1
  252. policyengine_uk/repo.py +0 -3
  253. policyengine_uk/tests/policy/baseline/gov/abolitions/abolition_parameters.yaml +0 -250
  254. policyengine_uk/variables/contrib/policyengine/high_income_incident_tax_change.py +0 -22
  255. policyengine_uk-2.40.1.data/data/share/openfisca/openfisca-country-template/CHANGELOG.md +0 -2285
  256. policyengine_uk-2.40.1.data/data/share/openfisca/openfisca-country-template/README.md +0 -37
  257. policyengine_uk-2.40.1.dist-info/licenses/LICENSE +0 -661
  258. {policyengine_uk-2.40.1.data/data/share/openfisca/openfisca-country-template → policyengine_uk-2.65.6.dist-info/licenses}/LICENSE +0 -0
@@ -3,8 +3,23 @@ from policyengine_uk.model_api import *
3
3
 
4
4
  class rail_subsidy_spending(Variable):
5
5
  label = "rail subsidy spending"
6
- documentation = "Total spending on rail subsidies for this household."
6
+ documentation = (
7
+ "Total spending on rail subsidies for this household. "
8
+ "Computed as rail_usage × fare_index, allowing reforms to "
9
+ "modify fare prices independently of usage quantity."
10
+ )
7
11
  entity = Household
8
12
  definition_period = YEAR
9
13
  value_type = float
10
14
  unit = GBP
15
+
16
+ def formula(household, period, parameters):
17
+ # Get rail usage (quantity at base year prices)
18
+ # This should be provided by policyengine-uk-data
19
+ rail_usage = household("rail_usage", period)
20
+
21
+ # Get the fare index for the current period
22
+ fare_index = parameters(period).gov.dft.rail.fare_index
23
+
24
+ # Spending = quantity × price
25
+ return rail_usage * fare_index
@@ -0,0 +1,16 @@
1
+ from policyengine_uk.model_api import *
2
+
3
+
4
+ class rail_usage(Variable):
5
+ label = "rail usage"
6
+ documentation = (
7
+ "Rail usage quantity in base year (2020) price terms. "
8
+ "Should be provided by policyengine-uk-data, derived from "
9
+ "rail_subsidy_spending / fare_index at survey year. "
10
+ "Uprated by rail ridership growth trends (~1.9% annually)."
11
+ )
12
+ entity = Household
13
+ definition_period = YEAR
14
+ value_type = float
15
+ unit = GBP
16
+ uprating = "gov.dft.rail.ridership_index"
@@ -51,13 +51,18 @@ class BRMA_LHA_rate(Variable):
51
51
  ["brma", "lha_category"]
52
52
  ).weekly_rent.quantile(percentile)
53
53
 
54
+ # Convert MultiIndex Series to DataFrame for merge
55
+ lha_rates_df = lha_rates.reset_index()
56
+ lha_rates_df.columns = ["brma", "lha_category", "weekly_rent"]
57
+
54
58
  lha_lookup_table = pd.DataFrame(
55
59
  {
56
60
  "brma": brma,
57
61
  "lha_category": category,
58
62
  }
59
63
  )
60
- lha_lookup_table["weekly_rent"] = lha_lookup_table.apply(
61
- lambda x: lha_rates.loc[x.brma, x.lha_category], axis=1
64
+ # Use merge instead of row-by-row apply for vectorised lookup
65
+ lha_lookup_table = lha_lookup_table.merge(
66
+ lha_rates_df, on=["brma", "lha_category"], how="left"
62
67
  )
63
68
  return lha_lookup_table.weekly_rent.values * 52
@@ -36,11 +36,13 @@ class LHA_category(Variable):
36
36
  household.max(person("age", period))
37
37
  )
38
38
  has_children = benunit.any(person("is_child", period))
39
- # Households with only one adult, if under 35, can only claim shared if without children:
39
+ # Households with only one adult, if under age threshold, can only
40
+ # claim shared if without children:
40
41
  # https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/28
42
+ p = parameters(period).gov.dwp.LHA
41
43
  can_only_claim_shared = (
42
44
  (num_adults_in_hh == 1)
43
- & (eldest_adult_age_in_hh < 35)
45
+ & (eldest_adult_age_in_hh < p.shared_accommodation_age_threshold)
44
46
  & ~has_children
45
47
  )
46
48
  return select(
@@ -12,8 +12,10 @@ class additional_state_pension(Variable):
12
12
  simulation = person.simulation
13
13
  if simulation.dataset is None:
14
14
  return 0
15
-
16
- data_year = simulation.dataset.time_period
15
+ try:
16
+ data_year = min(simulation.dataset.years)
17
+ except:
18
+ data_year = period.start.year
17
19
  reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR
18
20
  type = person("state_pension_type", data_year)
19
21
  maximum_basic_sp = parameters(
@@ -10,20 +10,38 @@ class basic_state_pension(Variable):
10
10
 
11
11
  def formula(person, period, parameters):
12
12
  simulation = person.simulation
13
- if simulation.dataset is None:
14
- return 0
13
+ has_dataset = simulation.dataset is not None
14
+
15
+ # Determine the data year: from dataset if available, otherwise current
16
+ if has_dataset:
17
+ try:
18
+ data_year = min(simulation.dataset.years)
19
+ except:
20
+ data_year = period.start.year
21
+ else:
22
+ data_year = period.start.year
15
23
 
16
- data_year = simulation.dataset.time_period
17
24
  reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR
18
- type = person("state_pension_type", period)
25
+ pension_type = person("state_pension_type", period)
19
26
  maximum_basic_sp = parameters(
20
27
  data_year
21
28
  ).gov.dwp.state_pension.basic_state_pension.amount
29
+
30
+ # For BASIC pension type, cap at the maximum; otherwise return 0
22
31
  amount_in_data_year = where(
23
- type == type.possible_values.BASIC,
32
+ pension_type == pension_type.possible_values.BASIC,
24
33
  min_(reported, maximum_basic_sp),
25
34
  0,
26
35
  )
27
- triple_lock = parameters.gov.economic_assumptions.indices.triple_lock
28
- uprating_since_data_year = triple_lock(period) / triple_lock(data_year)
29
- return amount_in_data_year * uprating_since_data_year * WEEKS_IN_YEAR
36
+
37
+ # Apply triple lock uprating only when using dataset
38
+ # (i.e., when data year differs from simulation period)
39
+ if has_dataset:
40
+ triple_lock = (
41
+ parameters.gov.economic_assumptions.indices.triple_lock
42
+ )
43
+ uprating = triple_lock(period) / triple_lock(data_year)
44
+ else:
45
+ uprating = 1
46
+
47
+ return amount_in_data_year * uprating * WEEKS_IN_YEAR
@@ -11,7 +11,7 @@ class is_CTC_eligible(Variable):
11
11
  def formula(benunit, period, parameters):
12
12
  already_claiming = (
13
13
  add(benunit, period, ["child_tax_credit_reported"]) > 0
14
- )
14
+ ) & (~add(benunit, period, ["would_claim_uc"]) > 0)
15
15
  return (
16
16
  benunit.any(benunit.members("is_child_for_CTC", period))
17
17
  & already_claiming
@@ -4,20 +4,16 @@ from policyengine_uk.model_api import *
4
4
  class is_benefit_cap_exempt(Variable):
5
5
  value_type = bool
6
6
  entity = BenUnit
7
- label = "Whether exempt from the benefits cap"
7
+ label = (
8
+ "Whether exempt from the benefits cap because of health or disability"
9
+ )
8
10
  definition_period = YEAR
11
+ reference = "https://www.gov.uk/benefit-cap/when-youre-not-affected"
9
12
 
10
13
  def formula(benunit, period, parameters):
11
- QUAL_PERSONAL_BENEFITS = [
12
- "carers_allowance",
13
- "dla",
14
- "esa_contrib",
15
- ]
16
- QUAL_BENUNIT_BENEFITS = ["working_tax_credit", "esa_income"]
17
- qualifying_benunit_benefits = add(
18
- benunit, period, QUAL_BENUNIT_BENEFITS
14
+ exempt_health = benunit(
15
+ "is_benefit_cap_exempt_health_disability", period
19
16
  )
20
- qualifying_personal_benefits = add(
21
- benunit, period, QUAL_PERSONAL_BENEFITS
22
- )
23
- return (qualifying_personal_benefits + qualifying_benunit_benefits) > 0
17
+ exempt_other = benunit("is_benefit_cap_exempt_other", period)
18
+ exempt_earnings = benunit("is_benefit_cap_exempt_earnings", period)
19
+ return exempt_health | exempt_earnings | exempt_other
@@ -0,0 +1,66 @@
1
+ from policyengine_uk.model_api import *
2
+
3
+
4
+ class is_benefit_cap_exempt_earnings(Variable):
5
+ value_type = bool
6
+ entity = BenUnit
7
+ label = "Whether exempt from the benefits cap for non-health/disability reasons"
8
+ definition_period = YEAR
9
+ reference = "https://www.gov.uk/benefit-cap/when-youre-not-affected"
10
+
11
+ def formula(benunit, period, parameters):
12
+ # Check if anyone in benefit unit is over state pension age
13
+ person = benunit.members
14
+ over_pension_age = person("is_SP_age", period)
15
+ has_pensioner = benunit.any(over_pension_age)
16
+
17
+ # UC-specific exemptions
18
+ # Limited capability for work and work-related activity
19
+ has_lcwra = benunit.any(
20
+ person("uc_limited_capability_for_WRA", period)
21
+ )
22
+
23
+ # Carer element in UC indicates caring for someone with disability
24
+ gets_uc_carer_element = benunit("uc_carer_element", period) > 0
25
+
26
+ # Earnings exemption for UC (£846/month = £10,152/year)
27
+ # Note: Only check earned income, not UC amount itself to avoid circular dependency
28
+ uc_earned = benunit.sum(
29
+ benunit.members("employment_income", period)
30
+ + benunit.members("self_employment_income", period)
31
+ - benunit.members("income_tax", period)
32
+ - benunit.members("national_insurance", period)
33
+ )
34
+ earnings_threshold = 10_152
35
+ meets_earnings_test = uc_earned >= earnings_threshold
36
+
37
+ # Disability and carer benefits that exempt from cap
38
+ QUAL_PERSONAL_BENEFITS = [
39
+ "attendance_allowance",
40
+ "carers_allowance",
41
+ "dla", # Disability Living Allowance (includes components)
42
+ "pip_dl", # PIP daily living component
43
+ "pip_m", # PIP mobility component
44
+ "iidb", # Industrial injuries disability benefit
45
+ ]
46
+
47
+ # ESA and Working Tax Credit
48
+ QUAL_BENUNIT_BENEFITS = [
49
+ "esa_income", # Income-based ESA
50
+ "working_tax_credit", # If getting WTC, likely working enough
51
+ ]
52
+
53
+ qualifying_personal_benefits = add(
54
+ benunit, period, QUAL_PERSONAL_BENEFITS
55
+ )
56
+ qualifying_benunit_benefits = add(
57
+ benunit, period, QUAL_BENUNIT_BENEFITS
58
+ )
59
+
60
+ # Check for Armed Forces Compensation Scheme payments
61
+ afcs = benunit("afcs", period) > 0
62
+
63
+ # ESA contribution-based with support component
64
+ esa_support_component = benunit("esa_contrib", period) > 0
65
+
66
+ return meets_earnings_test
@@ -0,0 +1,75 @@
1
+ from policyengine_uk.model_api import *
2
+
3
+
4
+ class is_benefit_cap_exempt_health_disability(Variable):
5
+ value_type = bool
6
+ entity = BenUnit
7
+ label = (
8
+ "Whether exempt from the benefits cap because of health or disability"
9
+ )
10
+ definition_period = YEAR
11
+ reference = "https://www.gov.uk/benefit-cap/when-youre-not-affected"
12
+
13
+ def formula(benunit, period, parameters):
14
+ # Check if anyone in benefit unit is over state pension age
15
+ person = benunit.members
16
+ over_pension_age = person("is_SP_age", period)
17
+ has_pensioner = benunit.any(over_pension_age)
18
+
19
+ # UC-specific exemptions
20
+ # Limited capability for work and work-related activity
21
+ has_lcwra = benunit.any(
22
+ person("uc_limited_capability_for_WRA", period)
23
+ )
24
+
25
+ # Carer element in UC indicates caring for someone with disability
26
+ gets_uc_carer_element = benunit("uc_carer_element", period) > 0
27
+
28
+ # Earnings exemption for UC (£846/month = £10,152/year)
29
+ # Note: Only check earned income, not UC amount itself to avoid circular dependency
30
+ uc_earned = benunit.sum(
31
+ benunit.members("employment_income", period)
32
+ + benunit.members("self_employment_income", period)
33
+ - benunit.members("income_tax", period)
34
+ - benunit.members("national_insurance", period)
35
+ )
36
+ earnings_threshold = 10_152
37
+ meets_earnings_test = uc_earned >= earnings_threshold
38
+
39
+ # Disability and carer benefits that exempt from cap
40
+ QUAL_PERSONAL_BENEFITS = [
41
+ "attendance_allowance",
42
+ "carers_allowance",
43
+ "dla", # Disability Living Allowance (includes components)
44
+ "pip_dl", # PIP daily living component
45
+ "pip_m", # PIP mobility component
46
+ "iidb", # Industrial injuries disability benefit
47
+ ]
48
+
49
+ # ESA and Working Tax Credit
50
+ QUAL_BENUNIT_BENEFITS = [
51
+ "esa_income", # Income-based ESA
52
+ "working_tax_credit", # If getting WTC, likely working enough
53
+ ]
54
+
55
+ qualifying_personal_benefits = add(
56
+ benunit, period, QUAL_PERSONAL_BENEFITS
57
+ )
58
+ qualifying_benunit_benefits = add(
59
+ benunit, period, QUAL_BENUNIT_BENEFITS
60
+ )
61
+
62
+ # Check for Armed Forces Compensation Scheme payments
63
+ afcs = benunit("afcs", period) > 0
64
+
65
+ # ESA contribution-based with support component
66
+ esa_support_component = benunit("esa_contrib", period) > 0
67
+
68
+ return (
69
+ has_lcwra
70
+ | gets_uc_carer_element
71
+ | (qualifying_personal_benefits > 0)
72
+ | (qualifying_benunit_benefits > 0)
73
+ | afcs
74
+ | esa_support_component
75
+ )
@@ -0,0 +1,66 @@
1
+ from policyengine_uk.model_api import *
2
+
3
+
4
+ class is_benefit_cap_exempt_other(Variable):
5
+ value_type = bool
6
+ entity = BenUnit
7
+ label = "Whether exempt from the benefits cap for non-health/disability reasons"
8
+ definition_period = YEAR
9
+ reference = "https://www.gov.uk/benefit-cap/when-youre-not-affected"
10
+
11
+ def formula(benunit, period, parameters):
12
+ # Check if anyone in benefit unit is over state pension age
13
+ person = benunit.members
14
+ over_pension_age = person("is_SP_age", period)
15
+ has_pensioner = benunit.any(over_pension_age)
16
+
17
+ # UC-specific exemptions
18
+ # Limited capability for work and work-related activity
19
+ has_lcwra = benunit.any(
20
+ person("uc_limited_capability_for_WRA", period)
21
+ )
22
+
23
+ # Carer element in UC indicates caring for someone with disability
24
+ gets_uc_carer_element = benunit("uc_carer_element", period) > 0
25
+
26
+ # Earnings exemption for UC (£846/month = £10,152/year)
27
+ # Note: Only check earned income, not UC amount itself to avoid circular dependency
28
+ uc_earned = benunit.sum(
29
+ benunit.members("employment_income", period)
30
+ + benunit.members("self_employment_income", period)
31
+ - benunit.members("income_tax", period)
32
+ - benunit.members("national_insurance", period)
33
+ )
34
+ earnings_threshold = 10_152
35
+ meets_earnings_test = uc_earned >= earnings_threshold
36
+
37
+ # Disability and carer benefits that exempt from cap
38
+ QUAL_PERSONAL_BENEFITS = [
39
+ "attendance_allowance",
40
+ "carers_allowance",
41
+ "dla", # Disability Living Allowance (includes components)
42
+ "pip_dl", # PIP daily living component
43
+ "pip_m", # PIP mobility component
44
+ "iidb", # Industrial injuries disability benefit
45
+ ]
46
+
47
+ # ESA and Working Tax Credit
48
+ QUAL_BENUNIT_BENEFITS = [
49
+ "esa_income", # Income-based ESA
50
+ "working_tax_credit", # If getting WTC, likely working enough
51
+ ]
52
+
53
+ qualifying_personal_benefits = add(
54
+ benunit, period, QUAL_PERSONAL_BENEFITS
55
+ )
56
+ qualifying_benunit_benefits = add(
57
+ benunit, period, QUAL_BENUNIT_BENEFITS
58
+ )
59
+
60
+ # Check for Armed Forces Compensation Scheme payments
61
+ afcs = benunit("afcs", period) > 0
62
+
63
+ # ESA contribution-based with support component
64
+ esa_support_component = benunit("esa_contrib", period) > 0
65
+
66
+ return has_pensioner | afcs | esa_support_component
@@ -45,11 +45,12 @@ class winter_fuel_allowance(Variable):
45
45
  )
46
46
 
47
47
  meets_mtb_requirement = (
48
- on_mtb | ~wfp.eligibility.require_benefits | meets_income_passport
48
+ on_mtb
49
+ | (not wfp.eligibility.require_benefits)
50
+ | meets_income_passport
49
51
  )
50
- meets_spa_requirement = (
51
- household.any(is_SP_age)
52
- | ~wfp.eligibility.state_pension_age_requirement
52
+ meets_spa_requirement = household.any(is_SP_age) | (
53
+ not wfp.eligibility.state_pension_age_requirement
53
54
  )
54
55
  meets_higher_age_requirement = household.any(
55
56
  age >= wfp.eligibility.higher_age_requirement
@@ -26,12 +26,10 @@ class gov_tax(Variable):
26
26
  "national_insurance",
27
27
  "LVT",
28
28
  "carbon_tax",
29
- "vat_change",
30
29
  "capital_gains_tax",
31
30
  "private_school_vat",
32
31
  "corporate_incident_tax_revenue_change",
33
32
  "consumer_incident_tax_revenue_change",
34
- "high_income_incident_tax_change",
35
33
  "ni_employer",
36
34
  "student_loan_repayments",
37
35
  "vat",
@@ -29,7 +29,6 @@ class household_tax(Variable):
29
29
  "private_school_vat",
30
30
  "corporate_incident_tax_revenue_change",
31
31
  "consumer_incident_tax_revenue_change",
32
- "high_income_incident_tax_change",
33
32
  "employer_ni_response_capital_incidence",
34
33
  "employer_ni_response_consumer_incidence",
35
34
  "student_loan_repayments",
@@ -7,3 +7,26 @@ class gift_aid(Variable):
7
7
  label = "Expenditure under Gift Aid"
8
8
  definition_period = YEAR
9
9
  unit = GBP
10
+ reference = dict(
11
+ title="Income Tax Act 2007, Part 8 Chapter 2 (Gift Aid)",
12
+ href="https://www.legislation.gov.uk/ukpga/2007/3/part/8/chapter/2",
13
+ )
14
+
15
+
16
+ class gift_aid_grossed_up(Variable):
17
+ value_type = float
18
+ entity = Person
19
+ label = "Gift Aid grossed up by basic rate"
20
+ definition_period = YEAR
21
+ unit = GBP
22
+ reference = dict(
23
+ title="Income Tax Act 2007, s. 58 (2)",
24
+ href="https://www.legislation.gov.uk/ukpga/2007/3/section/58",
25
+ )
26
+
27
+ def formula(person, period, parameters):
28
+ gift_aid = person("gift_aid", period)
29
+ basic_rate = parameters(period).gov.hmrc.income_tax.rates.uk.rates[0]
30
+ # Grossed up amount = gift_aid / (1 - basic_rate)
31
+ # With basic_rate = 0.2: gift_aid / 0.8 = gift_aid * 1.25
32
+ return gift_aid / (1 - basic_rate)
@@ -7,13 +7,20 @@ class personal_allowance(Variable):
7
7
  label = "Personal Allowance for the year"
8
8
  unit = GBP
9
9
  definition_period = YEAR
10
- reference = "Income Tax Act 2007 s. 35"
10
+ reference = dict(
11
+ title="Income Tax Act 2007 s. 35, s. 58",
12
+ href="https://www.legislation.gov.uk/ukpga/2007/3/section/35",
13
+ )
11
14
 
12
15
  def formula(person, period, parameters):
13
16
  params = parameters(period)
14
17
  PA = params.gov.hmrc.income_tax.allowances.personal_allowance
15
18
  personal_allowance = PA.amount
16
19
  ANI = person("adjusted_net_income", period)
17
- excess = max_(0, ANI - PA.maximum_ANI)
20
+ # Per ITA 2007 s.58, deduct grossed-up Gift Aid from ANI
21
+ # when calculating Personal Allowance taper
22
+ gift_aid_grossed_up = person("gift_aid_grossed_up", period)
23
+ ANI_for_taper = ANI - gift_aid_grossed_up
24
+ excess = max_(0, ANI_for_taper - PA.maximum_ANI)
18
25
  reduction = excess * PA.reduction_rate
19
26
  return max_(0, personal_allowance - reduction)
@@ -16,4 +16,5 @@ class income_tax_pre_charges(Variable):
16
16
  "earned_income_tax",
17
17
  "savings_income_tax",
18
18
  "dividend_income_tax",
19
+ "property_income_tax",
19
20
  ]
@@ -0,0 +1,75 @@
1
+ from policyengine_uk.model_api import *
2
+
3
+
4
+ class property_income_tax(Variable):
5
+ value_type = float
6
+ entity = Person
7
+ label = "Income tax on property income"
8
+ definition_period = YEAR
9
+ reference = [
10
+ dict(
11
+ title="Income Tax (Trading and Other Income) Act 2005, s. 268",
12
+ href="https://www.legislation.gov.uk/ukpga/2005/5/section/268",
13
+ ),
14
+ dict(
15
+ title="OBR Economic and Fiscal Outlook November 2025",
16
+ href="https://obr.uk/efo/economic-and-fiscal-outlook-november-2025/",
17
+ ),
18
+ ]
19
+ unit = GBP
20
+
21
+ def formula(person, period, parameters):
22
+ """
23
+ Calculate income tax on property income.
24
+
25
+ For Scottish taxpayers, property income is taxed using the normal
26
+ Scottish income tax rates (the new property-specific rates do not
27
+ apply to Scotland).
28
+
29
+ For rUK taxpayers, property income is taxed using property-specific
30
+ rates. From April 2027, these are 2pp higher than standard UK rates
31
+ (22%, 42%, 47% vs 20%, 40%, 45%).
32
+
33
+ Property income is stacked on top of other earned income for
34
+ determining which tax band it falls into.
35
+ """
36
+ rates = parameters(period).gov.hmrc.income_tax.rates
37
+ taxable_property = person("taxable_property_income", period)
38
+ earned_taxable = person("earned_taxable_income", period)
39
+ other_earned_taxable = max_(0, earned_taxable - taxable_property)
40
+ is_scottish = person("pays_scottish_income_tax", period)
41
+
42
+ # Scottish taxpayers: use Scottish rates via calc method
43
+ # Tax on (other earned + property) minus tax on (other earned)
44
+ scottish_tax = rates.scotland.rates.calc(
45
+ other_earned_taxable + taxable_property
46
+ ) - rates.scotland.rates.calc(other_earned_taxable)
47
+
48
+ # rUK taxpayers: use property-specific rates
49
+ property_rates = rates.property
50
+ thresholds = rates.uk.thresholds
51
+ basic_upper = thresholds[1] # Higher rate threshold
52
+ higher_upper = thresholds[2] # Additional rate threshold
53
+
54
+ # Property income in basic rate band
55
+ basic_rate_space = max_(0, basic_upper - other_earned_taxable)
56
+ property_in_basic = min_(taxable_property, basic_rate_space)
57
+
58
+ # Property income in higher rate band
59
+ higher_start = max_(other_earned_taxable, basic_upper)
60
+ higher_rate_space = max_(0, higher_upper - higher_start)
61
+ remaining_after_basic = max_(0, taxable_property - property_in_basic)
62
+ property_in_higher = min_(remaining_after_basic, higher_rate_space)
63
+
64
+ # Property income in additional rate band
65
+ property_in_additional = max_(
66
+ 0, taxable_property - property_in_basic - property_in_higher
67
+ )
68
+
69
+ ruk_tax = (
70
+ property_rates.basic * property_in_basic
71
+ + property_rates.higher * property_in_higher
72
+ + property_rates.additional * property_in_additional
73
+ )
74
+
75
+ return where(is_scottish, scottish_tax, ruk_tax)
@@ -13,12 +13,12 @@ class savings_income_tax(Variable):
13
13
  unit = GBP
14
14
 
15
15
  def formula(person, period, parameters):
16
- rates = parameters(period).gov.hmrc.income_tax.rates.uk.rates
16
+ savings_rates = parameters(period).gov.hmrc.income_tax.rates.savings
17
17
  basic_rate_amount = person("basic_rate_savings_income", period)
18
18
  higher_rate_amount = person("higher_rate_savings_income", period)
19
19
  add_rate_amount = person("add_rate_savings_income", period)
20
20
  return (
21
- rates[0] * basic_rate_amount
22
- + rates[1] * higher_rate_amount
23
- + rates[2] * add_rate_amount
21
+ savings_rates.basic * basic_rate_amount
22
+ + savings_rates.higher * higher_rate_amount
23
+ + savings_rates.additional * add_rate_amount
24
24
  )
@@ -0,0 +1,43 @@
1
+ from policyengine_uk.model_api import *
2
+
3
+
4
+ class salary_sacrifice_broad_base_haircut(Variable):
5
+ label = "Salary sacrifice broad-base employment income haircut"
6
+ documentation = (
7
+ "Reduction in employment income for ALL workers due to employers spreading "
8
+ "the increased NI costs from the salary sacrifice cap across all employees. "
9
+ "This is a negative value that reduces employment_income. "
10
+ "\n\n"
11
+ "When the salary sacrifice cap is active, employers face increased NI costs "
12
+ "on excess contributions. They spread these costs across ALL employees (not "
13
+ "just salary sacrificers), as they cannot target only affected workers without "
14
+ "those workers negotiating to recoup the loss. "
15
+ "\n\n"
16
+ "See https://policyengine.org/uk/research/uk-salary-sacrifice-cap for methodology."
17
+ )
18
+ entity = Person
19
+ definition_period = YEAR
20
+ value_type = float
21
+ unit = GBP
22
+ reference = "https://policyengine.org/uk/research/uk-salary-sacrifice-cap"
23
+
24
+ def formula(person, period, parameters):
25
+ cap = parameters(
26
+ period
27
+ ).gov.hmrc.national_insurance.salary_sacrifice_pension_cap
28
+
29
+ # If cap is infinite, no haircut applies
30
+ if np.isinf(cap):
31
+ return 0
32
+
33
+ # Get the broad-base haircut rate (applies to ALL workers)
34
+ haircut_rate = parameters(
35
+ period
36
+ ).gov.contrib.behavioral_responses.salary_sacrifice_broad_base_haircut_rate
37
+
38
+ # Apply haircut to employment income before any salary sacrifice adjustments
39
+ # Use employment_income_before_lsr to avoid circular dependency
40
+ employment_income = person("employment_income_before_lsr", period)
41
+
42
+ # Return negative value (this reduces employment income for everyone)
43
+ return -employment_income * haircut_rate