From cdcb980308b574a5a2f052a52e83abce8499af50 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 27 Sep 2026 16:05:02 -0400 Subject: [PATCH] Fix Class 2 NICs against s.11 SSCBA 1992 for 2015-16 to 2023-24 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 2023-24 weekly rate £3.45 (SI 2023/236 reg 3(a)); model year 2023 charged £3.15. - From 2022-23 Class 2 is payable only on profits that exceed the lower profits threshold (£11,908, SI 2022/1329; £12,570, SI 2023/236 reg 3(b)). Profits from the small profits threshold up to it are treated as paid (s.11(5A)-(5B)); the model charged 52 weeks on them. - Small profits threshold: £6,725 in 2023-24 and 2024-25 (not CPI uprated), £5,965 in 2015-16 and 2016-17 (not 3,965). CPI projections now run from the latest statutory value. - Compulsory Class 2 ends 6 April 2024 (NICRRA 2023 s.3), not 1 January. - Every value cites its amending instrument. - Tests: YAML cases per year and threshold; a statute-table differential, range and monotonicity property test over model years 2015-2030; a 2023-24 regulation 100 pin using the default £3.45 rate. - NI notebook: describe the rule and regenerate saved charts; the rates chart now selects percentage parameters by unit. Fixes #1887 Co-Authored-By: Claude Opus 5.5 --- changelog.d/1887.fixed.md | 1 + docs/book/policy/model-baseline.md | 2 +- .../gov/hmrc/national-insurance.ipynb | 558 ++++++++---------- .../national_insurance/class_2/flat_rate.yaml | 66 ++- .../class_2/lower_profits_threshold.yaml | 21 + .../lower_profits_threshold_applies.yaml | 21 + .../class_2/small_profits_threshold.yaml | 81 ++- .../class_2/ni_class_2.yaml | 125 +++- policyengine_uk/tests/test_ni_class_2.py | 202 +++++++ .../tests/test_ni_class_4_properties.py | 11 +- .../national_insurance/class_2/ni_class_2.py | 14 +- 11 files changed, 747 insertions(+), 355 deletions(-) create mode 100644 changelog.d/1887.fixed.md create mode 100644 policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold.yaml create mode 100644 policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold_applies.yaml create mode 100644 policyengine_uk/tests/test_ni_class_2.py diff --git a/changelog.d/1887.fixed.md b/changelog.d/1887.fixed.md new file mode 100644 index 0000000000..7126853599 --- /dev/null +++ b/changelog.d/1887.fixed.md @@ -0,0 +1 @@ +- Fix Class 2 National Insurance for 2015-16 to 2023-24 against s.11 SSCBA 1992: charge £3.45 a week in 2023-24; from 2022-23 charge only profits above the Class 2 lower profits threshold (£11,908, then £12,570), with profits from the small profits threshold up to it treated as paid; keep the small profits threshold at £6,725 in 2023-24 and 2024-25 and correct it to £5,965 in 2015-16 and 2016-17; date the abolition 6 April 2024. Every value now cites its amending instrument. diff --git a/docs/book/policy/model-baseline.md b/docs/book/policy/model-baseline.md index 53ab48307f..a036429c0b 100644 --- a/docs/book/policy/model-baseline.md +++ b/docs/book/policy/model-baseline.md @@ -22,7 +22,7 @@ Annual benefit uprating applied 4.1% increases to state pensions and 1.7% to wor ### Autumn Statement 2023 -The government announced further National Insurance cuts, reducing the [employee main rate from 10% to 8%](https://github.com/PolicyEngine/policyengine-uk/blob/master/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/rates/employee/main.yaml#L22-L27) and the [self-employed Class 4 rate from 9% to 6%](https://github.com/PolicyEngine/policyengine-uk/blob/master/policyengine_uk/parameters/gov/hmrc/national_insurance/class_4/rates/main.yaml#L16-L21) in fiscal year 2024-25. [Class 2 National Insurance contributions were abolished](https://github.com/PolicyEngine/policyengine-uk/blob/master/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml#L14-L19) for self-employed people, with the flat rate set to £0 in fiscal year 2024-25. +The government announced further National Insurance cuts, reducing the [employee main rate from 10% to 8%](https://github.com/PolicyEngine/policyengine-uk/blob/master/policyengine_uk/parameters/gov/hmrc/national_insurance/class_1/rates/employee/main.yaml#L22-L27) and the [self-employed Class 4 rate from 9% to 6%](https://github.com/PolicyEngine/policyengine-uk/blob/master/policyengine_uk/parameters/gov/hmrc/national_insurance/class_4/rates/main.yaml#L16-L21) in fiscal year 2024-25. [Class 2 National Insurance contributions were abolished](https://github.com/PolicyEngine/policyengine-uk/blob/master/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml#L51-L56) for self-employed people, with the flat rate set to £0 in fiscal year 2024-25. ### Capital gains tax changes (Autumn Budget 2024) diff --git a/docs/book/programs/gov/hmrc/national-insurance.ipynb b/docs/book/programs/gov/hmrc/national-insurance.ipynb index 8c475d72b8..5dfa6f8d2c 100644 --- a/docs/book/programs/gov/hmrc/national-insurance.ipynb +++ b/docs/book/programs/gov/hmrc/national-insurance.ipynb @@ -20,9 +20,9 @@ "NI has four main classes:\n", "\n", "- **Class 1**: Paid by employees and their employers. This is based on the employee's earnings and is deducted directly from wages.\n", - "- **Class 2**: Paid by self-employed people with profits above a certain threshold.\n", + "- **Class 2**: A flat weekly contribution paid by self-employed people with profits above a certain threshold. It stopped being compulsory on 6 April 2024.\n", "- **Class 3**: Voluntary contributions, often made by people who want to fill gaps in their National Insurance record.\n", - "- **Class 4**: Paid by self-employed people with profits above a certain threshold, in addition to Class 2 NICs." + "- **Class 4**: Paid by self-employed people with profits above a certain threshold (in addition to Class 2 NICs before April 2024)." ] }, { @@ -56,78 +56,8 @@ "execution_count": 1, "metadata": {}, "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
ClassMethodology & BasisThresholds & LimitsRate ApplicationReference
0Class 1Based on employment income. \\n- Monthly and an...Primary Threshold, Upper Earnings LimitMain and Additional ratesSocial Security Contributions and Benefits Act...
1Class 2Based on self-employment income. \\n- Weekly fl...Small Profits ThresholdFlat rateSocial Security Contributions and Benefits Act...
2Class 3Voluntary contributions for those filling gaps...None (voluntary)Flat rateSocial Security Contributions and Benefits Act...
3Class 4Based on self-employment profits. \\n- Annual m...Lower Profits Limit, Upper Profits LimitMain and Additional ratesSocial Security Contributions and Benefits Act...
\n", - "
" - ], - "text/plain": [ - " Class ... Reference\n", - "0 Class 1 ... Social Security Contributions and Benefits Act...\n", - "1 Class 2 ... Social Security Contributions and Benefits Act...\n", - "2 Class 3 ... Social Security Contributions and Benefits Act...\n", - "3 Class 4 ... Social Security Contributions and Benefits Act...\n", - "\n", - "[4 rows x 5 columns]" - ] + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
ClassMethodology & BasisThresholds & LimitsRate ApplicationReference
0Class 1Based on employment income. \\n- Monthly and an...Primary Threshold, Upper Earnings LimitMain and Additional ratesSocial Security Contributions and Benefits Act...
1Class 2Based on self-employment income. \\n- Weekly fl...Small Profits Threshold; lower profits thresho...Flat rateSocial Security Contributions and Benefits Act...
2Class 3Voluntary contributions for those filling gaps...None (voluntary)Flat rateSocial Security Contributions and Benefits Act...
3Class 4Based on self-employment profits. \\n- Annual m...Lower Profits Limit, Upper Profits LimitMain and Additional ratesSocial Security Contributions and Benefits Act...
\n
", + "text/plain": " Class Methodology & Basis \\\n0 Class 1 Based on employment income. \\n- Monthly and an... \n1 Class 2 Based on self-employment income. \\n- Weekly fl... \n2 Class 3 Voluntary contributions for those filling gaps... \n3 Class 4 Based on self-employment profits. \\n- Annual m... \n\n Thresholds & Limits \\\n0 Primary Threshold, Upper Earnings Limit \n1 Small Profits Threshold; lower profits thresho... \n2 None (voluntary) \n3 Lower Profits Limit, Upper Profits Limit \n\n Rate Application \\\n0 Main and Additional rates \n1 Flat rate \n2 Flat rate \n3 Main and Additional rates \n\n Reference \n0 Social Security Contributions and Benefits Act... \n1 Social Security Contributions and Benefits Act... \n2 Social Security Contributions and Benefits Act... \n3 Social Security Contributions and Benefits Act... " } } ], @@ -146,7 +76,7 @@ " ],\n", " \"Thresholds & Limits\": [\n", " \"Primary Threshold, Upper Earnings Limit\",\n", - " \"Small Profits Threshold\",\n", + " \"Small Profits Threshold; lower profits threshold (2022-23 and 2023-24)\",\n", " \"None (voluntary)\",\n", " \"Lower Profits Limit, Upper Profits Limit\",\n", " ],\n", @@ -171,7 +101,7 @@ { "cell_type": "markdown", "metadata": {}, - "source": "## How PolicyEngine computes National Insurance liability\n\nFor each person, PolicyEngine computes NI contributions class-by-class:\n\n- **Class 1 employee** (`ni_class_1_employee`): the sum of `ni_class_1_employee_primary` (main rate on earnings between the Primary Threshold and the Upper Earnings Limit) and `ni_class_1_employee_additional` (additional rate on earnings above the UEL).\n- **Class 1 employer** (`ni_class_1_employer`): paid on earnings above the Secondary Threshold; surfaced through the `ni_employer` variable.\n- **Class 2** (`ni_class_2`): a flat weekly rate paid by the self-employed with profits above the Small Profits Threshold.\n- **Class 3** (`ni_class_3`): a voluntary flat weekly rate; the variable is exposed but defaults to zero unless explicitly set.\n- **Class 4** (`ni_class_4`): main rate between the Lower and Upper Profits Limits plus additional rate above the UPL. Where the person also pays primary Class 1 contributions (or, before 6 April 2024, Class 2 contributions), liability is capped at the annual maximum in regulation 100 of the Social Security (Contributions) Regulations 2001 (`ni_class_4_maximum`).\n\nThe individual total `national_insurance` sums the four employee and self-employed components directly — `ni_class_1_employee + ni_class_2 + ni_class_3 + ni_class_4` — so voluntary Class 3 contributions are included in the household-side liability. The convenience aggregates `ni_employee` (Class 1 employee only) and `ni_self_employed` (Class 2 + Class 4) are also exposed for reporting. The Class 1 *employer* charge (`ni_class_1_employer`, surfaced as `ni_employer`) is an employer-side cost rather than part of the individual's `national_insurance`, so it is tracked separately and feeds the government revenue aggregates on its own.\n\nParameters live in `policyengine_uk/parameters/gov/hmrc/national_insurance/` and the per-class formulas in `policyengine_uk/variables/gov/hmrc/national_insurance/`." + "source": "## How PolicyEngine computes National Insurance liability\n\nFor each person, PolicyEngine computes NI contributions class-by-class:\n\n- **Class 1 employee** (`ni_class_1_employee`): the sum of `ni_class_1_employee_primary` (main rate on earnings between the Primary Threshold and the Upper Earnings Limit) and `ni_class_1_employee_additional` (additional rate on earnings above the UEL).\n- **Class 1 employer** (`ni_class_1_employer`): paid on earnings above the Secondary Threshold; surfaced through the `ni_employer` variable.\n- **Class 2** (`ni_class_2`): 52 weeks of a flat weekly rate for the self-employed. Before 2022-23 it is payable on profits at or above the Small Profits Threshold. In 2022-23 and 2023-24 it is payable only on profits above the Class 2 lower profits threshold; profits from the Small Profits Threshold up to it are treated as paid and cost nothing. Compulsory Class 2 ended on 6 April 2024; voluntary Class 2 is not modelled.\n- **Class 3** (`ni_class_3`): a voluntary flat weekly rate; the variable is exposed but defaults to zero unless explicitly set.\n- **Class 4** (`ni_class_4`): main rate between the Lower and Upper Profits Limits plus additional rate above the UPL. Where the person also pays primary Class 1 contributions (or, before 6 April 2024, Class 2 contributions), liability is capped at the annual maximum in regulation 100 of the Social Security (Contributions) Regulations 2001 (`ni_class_4_maximum`).\n\nThe individual total `national_insurance` sums the four employee and self-employed components directly — `ni_class_1_employee + ni_class_2 + ni_class_3 + ni_class_4` — so voluntary Class 3 contributions are included in the household-side liability. The convenience aggregates `ni_employee` (Class 1 employee only) and `ni_self_employed` (Class 2 + Class 4) are also exposed for reporting. The Class 1 *employer* charge (`ni_class_1_employer`, surfaced as `ni_employer`) is an employer-side cost rather than part of the individual's `national_insurance`, so it is tracked separately and feeds the government revenue aggregates on its own.\n\nParameters live in `policyengine_uk/parameters/gov/hmrc/national_insurance/` and the per-class formulas in `policyengine_uk/variables/gov/hmrc/national_insurance/`." }, { "cell_type": "markdown", @@ -200,8 +130,8 @@ }, "data": [ { - "hovertemplate": "Label=NI Lower Earnings Limit
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Lower Earnings Limit", + "hovertemplate": "Label=NI lower earnings limit
Instant=%{x}
Value=%{y}", + "legendgroup": "NI lower earnings limit", "line": { "color": "#636efa", "dash": "solid", @@ -211,7 +141,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Lower Earnings Limit", + "name": "NI lower earnings limit", "orientation": "v", "showlegend": true, "type": "scatter", @@ -233,26 +163,26 @@ ], "xaxis": "x", "y": [ - 120, - 120, - 123, - 123, - 123, - 123, - 123, - 123, - 123, - 123, - 123, - 123, - 123, - 123 + 120.0, + 120.0, + 123.0, + 123.0, + 123.0, + 123.0, + 129.0, + 131.57979643487917, + 134.2112670927332, + 136.8951949163344, + 139.63314579122738, + 142.42590266018465, + 145.27424846597842, + 148.17974909415344 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Primary Threshold
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Primary Threshold", + "hovertemplate": "Label=NI primary threshold
Instant=%{x}
Value=%{y}", + "legendgroup": "NI primary threshold", "line": { "color": "#EF553B", "dash": "solid", @@ -262,7 +192,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Primary Threshold", + "name": "NI primary threshold", "orientation": "v", "showlegend": true, "type": "scatter", @@ -284,26 +214,26 @@ ], "xaxis": "x", "y": [ - 183, - 184, - 190, + 183.0, + 184.0, + 190.0, 241.73, 241.73, 241.73, 241.73, 241.73, - 241.73, - 241.73, - 241.73, - 241.73, - 241.73, - 241.73 + 246.56436986040532, + 251.49511067607608, + 256.5250991919456, + 261.65577378048056, + 266.8885728141475, + 272.2263730378798 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Upper Earnings Limit
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Upper Earnings Limit", + "hovertemplate": "Label=NI upper earnings limit
Instant=%{x}
Value=%{y}", + "legendgroup": "NI upper earnings limit", "line": { "color": "#00cc96", "dash": "solid", @@ -313,7 +243,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Upper Earnings Limit", + "name": "NI upper earnings limit", "orientation": "v", "showlegend": true, "type": "scatter", @@ -335,10 +265,7 @@ ], "xaxis": "x", "y": [ - 962, - 966.73, - 966.73, - 966.73, + 962.0, 966.73, 966.73, 966.73, @@ -346,15 +273,18 @@ 966.73, 966.73, 966.73, - 966.73, - 966.73, - 966.73 + 986.0636796225112, + 1005.7827673184257, + 1025.8987678063527, + 1046.4174334455963, + 1067.3445165954613, + 1088.6915219745565 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Secondary Threshold
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Secondary Threshold", + "hovertemplate": "Label=NI secondary threshold
Instant=%{x}
Value=%{y}", + "legendgroup": "NI secondary threshold", "line": { "color": "#ab63fa", "dash": "solid", @@ -364,7 +294,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Secondary Threshold", + "name": "NI secondary threshold", "orientation": "v", "showlegend": true, "type": "scatter", @@ -386,26 +316,26 @@ ], "xaxis": "x", "y": [ - 169, - 170, - 175, - 175, - 175, - 175, - 175, - 175, - 175, - 175, - 175, - 175, - 175, - 175 + 169.0, + 170.0, + 175.0, + 175.0, + 175.0, + 96.0, + 96.0, + 96.0, + 96.0, + 96.0, + 96.0, + 97.92006459426726, + 99.8783474633293, + 101.87592517830709 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Class 2 Small Profits Threshold
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Class 2 Small Profits Threshold", + "hovertemplate": "Label=NI class 2 small profits threshold
Instant=%{x}
Value=%{y}", + "legendgroup": "NI class 2 small profits threshold", "line": { "color": "#FFA15A", "dash": "solid", @@ -415,7 +345,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Class 2 Small Profits Threshold", + "name": "NI class 2 small profits threshold", "orientation": "v", "showlegend": true, "type": "scatter", @@ -437,26 +367,26 @@ ], "xaxis": "x", "y": [ - 6475, - 6515, - 6725, - 6725, - 6725, - 6725, - 6725, - 6725, - 6725, - 6725, - 6725, - 6725, - 6725, - 6725 + 6475.0, + 6515.0, + 6725.0, + 6725.0, + 6725.0, + 6845.0, + 7105.0, + 7247.088788138113, + 7392.0236642935615, + 7539.84775101206, + 7690.647293385044, + 7844.465413958232, + 8001.345235277337, + 8161.373002433799 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Lower Profits Limit
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Lower Profits Limit", + "hovertemplate": "Label=NI lower profits limit
Instant=%{x}
Value=%{y}", + "legendgroup": "NI lower profits limit", "line": { "color": "#19d3f3", "dash": "solid", @@ -466,7 +396,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Lower Profits Limit", + "name": "NI lower profits limit", "orientation": "v", "showlegend": true, "type": "scatter", @@ -488,26 +418,26 @@ ], "xaxis": "x", "y": [ - 9500, - 9568, - 11908, - 11908, - 11908, - 11908, - 12570, - 12570, - 12570, - 12570, - 12570, - 12570, - 12570, - 12570 + 9500.0, + 9568.0, + 11908.0, + 11908.0, + 12570.0, + 12570.0, + 12570.0, + 12821.380164235901, + 13077.795560896562, + 13339.32248138235, + 13606.113508494018, + 13878.244933631944, + 14155.793048196501, + 14438.910434988438 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Upper Profits Limit
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Upper Profits Limit", + "hovertemplate": "Label=NI upper profits limit
Instant=%{x}
Value=%{y}", + "legendgroup": "NI upper profits limit", "line": { "color": "#FF6692", "dash": "solid", @@ -517,7 +447,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Upper Profits Limit", + "name": "NI upper profits limit", "orientation": "v", "showlegend": true, "type": "scatter", @@ -539,20 +469,20 @@ ], "xaxis": "x", "y": [ - 50000, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270, - 50270 + 50000.0, + 50270.0, + 50270.0, + 50270.0, + 50270.0, + 50270.0, + 50270.0, + 51275.32067272385, + 52300.77826939301, + 53346.67789491573, + 54413.62975910853, + 55501.9389668797, + 56611.910623137475, + 57744.1549376984 ], "yaxis": "y" } @@ -565,10 +495,10 @@ "height": 600, "images": [ { - "sizex": 0.2, - "sizey": 0.2, + "sizex": 0.15, + "sizey": 0.15, "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", - "x": 1, + "x": 1.1, "xanchor": "right", "xref": "paper", "y": -0.15, @@ -670,7 +600,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -706,7 +636,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -730,7 +660,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -766,7 +696,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -781,7 +711,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -817,7 +747,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -844,7 +774,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -880,7 +810,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -895,7 +825,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -931,7 +861,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -1076,7 +1006,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -1112,7 +1042,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -1203,7 +1133,7 @@ ], "sequential": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -1239,13 +1169,13 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], "sequentialminus": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -1281,7 +1211,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ] @@ -1417,8 +1347,8 @@ "xaxis": { "anchor": "y", "domain": [ - 0, - 1 + 0.0, + 1.0 ], "title": { "text": "Instant" @@ -1427,8 +1357,8 @@ "yaxis": { "anchor": "x", "domain": [ - 0, - 1 + 0.0, + 1.0 ], "title": { "text": "Value" @@ -1463,7 +1393,7 @@ " for param in ni.get_descendants()\n", " if param not in threshold_parameters\n", " and isinstance(param, Parameter)\n", - " and param.metadata.get(\"unit\") != \"bool\"\n", + " and param.metadata.get(\"unit\") == \"/1\"\n", "]\n", "\n", "instants = [f\"{2020 + i}-04-06\" for i in range(0, 14)]\n", @@ -1548,8 +1478,8 @@ }, "data": [ { - "hovertemplate": "Label=NI Class 4 additional rate
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Class 4 additional rate", + "hovertemplate": "Label=NI class 1 additional rate
Instant=%{x}
Value=%{y}", + "legendgroup": "NI class 1 additional rate", "line": { "color": "#636efa", "dash": "solid", @@ -1559,7 +1489,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Class 4 additional rate", + "name": "NI class 1 additional rate", "orientation": "v", "showlegend": true, "type": "scatter", @@ -1583,24 +1513,24 @@ "y": [ 0.02, 0.02, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035, - 0.035 + 0.0325, + 0.0325, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Class 4 main rate
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Class 4 main rate", + "hovertemplate": "Label=NI class 1 main rate
Instant=%{x}
Value=%{y}", + "legendgroup": "NI class 1 main rate", "line": { "color": "#EF553B", "dash": "solid", @@ -1610,7 +1540,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Class 4 main rate", + "name": "NI class 1 main rate", "orientation": "v", "showlegend": true, "type": "scatter", @@ -1632,26 +1562,26 @@ ], "xaxis": "x", "y": [ - 0.09, - 0.09, - 0.1025, - 0.09, - 0.09, - 0.09, - 0.09, - 0.09, - 0.09, - 0.09, - 0.09, - 0.09, - 0.09, - 0.09 + 0.12, + 0.12, + 0.1325, + 0.12, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08, + 0.08 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Class 1 additional rate
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Class 1 additional rate", + "hovertemplate": "Label=NI employer rate
Instant=%{x}
Value=%{y}", + "legendgroup": "NI employer rate", "line": { "color": "#00cc96", "dash": "solid", @@ -1661,7 +1591,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Class 1 additional rate", + "name": "NI employer rate", "orientation": "v", "showlegend": true, "type": "scatter", @@ -1683,26 +1613,26 @@ ], "xaxis": "x", "y": [ - 0.02, - 0.02, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325, - 0.0325 + 0.138, + 0.138, + 0.138, + 0.138, + 0.138, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15, + 0.15 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Class 1 main rate
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Class 1 main rate", + "hovertemplate": "Label=NI class 4 additional rate
Instant=%{x}
Value=%{y}", + "legendgroup": "NI class 4 additional rate", "line": { "color": "#ab63fa", "dash": "solid", @@ -1712,7 +1642,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Class 1 main rate", + "name": "NI class 4 additional rate", "orientation": "v", "showlegend": true, "type": "scatter", @@ -1734,26 +1664,26 @@ ], "xaxis": "x", "y": [ - 0.12, - 0.12, - 0.1325, - 0.12, - 0.12, - 0.12, - 0.12, - 0.12, - 0.12, - 0.12, - 0.12, - 0.12, - 0.12, - 0.12 + 0.02, + 0.02, + 0.035, + 0.035, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02, + 0.02 ], "yaxis": "y" }, { - "hovertemplate": "Label=NI Employer rate
Instant=%{x}
Value=%{y}", - "legendgroup": "NI Employer rate", + "hovertemplate": "Label=NI class 4 main rate
Instant=%{x}
Value=%{y}", + "legendgroup": "NI class 4 main rate", "line": { "color": "#FFA15A", "dash": "solid", @@ -1763,7 +1693,7 @@ "symbol": "circle" }, "mode": "lines", - "name": "NI Employer rate", + "name": "NI class 4 main rate", "orientation": "v", "showlegend": true, "type": "scatter", @@ -1785,20 +1715,20 @@ ], "xaxis": "x", "y": [ - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138, - 0.138 + 0.09, + 0.09, + 0.1025, + 0.09, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06, + 0.06 ], "yaxis": "y" } @@ -1811,10 +1741,10 @@ "height": 600, "images": [ { - "sizex": 0.2, - "sizey": 0.2, + "sizex": 0.15, + "sizey": 0.15, "source": "https://raw.githubusercontent.com/PolicyEngine/policyengine-app/master/src/images/logos/policyengine/blue.png", - "x": 1, + "x": 1.1, "xanchor": "right", "xref": "paper", "y": -0.15, @@ -1916,7 +1846,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -1952,7 +1882,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -1976,7 +1906,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -2012,7 +1942,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -2027,7 +1957,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -2063,7 +1993,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -2090,7 +2020,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -2126,7 +2056,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -2141,7 +2071,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -2177,7 +2107,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -2322,7 +2252,7 @@ }, "colorscale": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -2358,7 +2288,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], @@ -2449,7 +2379,7 @@ ], "sequential": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -2485,13 +2415,13 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ], "sequentialminus": [ [ - 0, + 0.0, "#0d0887" ], [ @@ -2527,7 +2457,7 @@ "#fdca26" ], [ - 1, + 1.0, "#f0f921" ] ] @@ -2663,8 +2593,8 @@ "xaxis": { "anchor": "y", "domain": [ - 0, - 1 + 0.0, + 1.0 ], "title": { "text": "Instant" @@ -2673,8 +2603,8 @@ "yaxis": { "anchor": "x", "domain": [ - 0, - 1 + 0.0, + 1.0 ], "title": { "text": "Value" @@ -2689,7 +2619,7 @@ ], "source": [ "fig = px.line(\n", - " df[(df[\"Threshold\"] == False) & (df[\"Value\"] < 1)], # Don't plot the flat rate,\n", + " df[df[\"Threshold\"] == False],\n", " x=\"Instant\",\n", " y=\"Value\",\n", " color=\"Label\",\n", diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml index ff37748484..8f0acf62ac 100644 --- a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/flat_rate.yaml @@ -1,25 +1,65 @@ -description: Flat rate National Insurance contribution for self-employed earners. +description: Weekly Class 2 National Insurance contribution that self-employed earners are liable to pay under section 11(2) of the Social Security Contributions and Benefits Act 1992. Compulsory Class 2 ended on 6 April 2024; voluntary Class 2 is not modelled. values: - 2015-04-01: 2.8 - 2017-04-01: 2.85 - 2018-04-01: 2.95 - 2019-04-01: 3 - 2020-04-01: 3.05 + 2015-04-06: + value: 2.8 + metadata: + reference: + - title: National Insurance Contributions Act 2015 Sch 1 para 3 (substituting s 11(2) of the Social Security Contributions and Benefits Act 1992) + href: https://www.legislation.gov.uk/ukpga/2015/5/schedule/1/paragraph/3/enacted + - title: Social Security Contributions and Benefits Act 1992 s 11(2) (as at 6 April 2016, unchanged for 2016-17) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2016-04-06 + 2017-04-06: + value: 2.85 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2017 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2017/415/regulation/3/made + 2018-04-06: + value: 2.95 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2018 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2018/337/regulation/3/made + 2019-04-06: + value: 3 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2019 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2019/262/regulation/3/made + 2020-04-06: + value: 3.05 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2020 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2020/299/regulation/3/made + - title: Social Security Contributions and Benefits Act 1992 s 11(2) (as at 6 April 2021, unchanged for 2021-22) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2021-04-06 2022-04-06: value: 3.15 metadata: reference: - - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2022(3)(a) - href: https://www.legislation.gov.uk/uksi/2022/232/regulation/3/made#regulation-3-a - 2024-01-01: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2022 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2022/232/regulation/3/made + 2023-04-06: + value: 3.45 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2023 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2023/236/regulation/3/made + - title: Social Security Contributions and Benefits Act 1992 s 11(2) (as at 6 April 2023) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2023-04-06 + 2024-04-06: value: 0 metadata: reference: - - title: "Autumn Statement 2023: National Insurance Factsheet" - href: https://www.gov.uk/government/publications/autumn-statement-2023-national-insurance-factsheet/autumn-statement-2023-national-insurance-factsheet + - title: National Insurance Contributions (Reduction in Rates) Act 2023 s 3(1)(a) and (3) (omitting s 11(2) of the Social Security Contributions and Benefits Act 1992 from 6 April 2024) + href: https://www.legislation.gov.uk/ukpga/2023/57/section/3/enacted metadata: unit: currency-GBP + period: week label: NI class 2 flat rate reference: - - https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/882271/Table-a4.pdf - - https://www.gov.uk/government/publications/rates-and-allowances-national-insurance-contributions/rates-and-allowances-national-insurance-contributions + - title: Social Security Contributions and Benefits Act 1992 s 11 + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11 + - title: "GOV.UK | Rates and allowances: National Insurance contributions" + href: https://www.gov.uk/government/publications/rates-and-allowances-national-insurance-contributions/rates-and-allowances-national-insurance-contributions diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold.yaml b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold.yaml new file mode 100644 index 0000000000..b0354aa651 --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold.yaml @@ -0,0 +1,21 @@ +description: Lower profits threshold for Class 2 National Insurance contributions under section 11(4)(a) of the Social Security Contributions and Benefits Act 1992. In 2022-23 and 2023-24 self-employed earners were liable for Class 2 only on profits above it. Section 11(4)(a) was omitted from 6 April 2024, when compulsory Class 2 ended; the 2023-24 value applies to reforms that restore a Class 2 rate. +values: + 2022-04-06: + value: 11_908 + metadata: + reference: + - title: The Social Security (Class 2 National Insurance Contributions Increase of Threshold) Regulations 2022 reg 2(3) + href: https://www.legislation.gov.uk/uksi/2022/1329/regulation/2/made + 2023-04-06: + value: 12_570 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2023 reg 3(b) + href: https://www.legislation.gov.uk/uksi/2023/236/regulation/3/made +metadata: + unit: currency-GBP + period: year + label: NI class 2 lower profits threshold + reference: + - title: Social Security Contributions and Benefits Act 1992 s 11(2), (4) and (5A) (as at 6 April 2023) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2023-04-06 diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold_applies.yaml b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold_applies.yaml new file mode 100644 index 0000000000..70c16fd04a --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/lower_profits_threshold_applies.yaml @@ -0,0 +1,21 @@ +description: Class 2 National Insurance liability starts above the lower profits threshold rather than at the small profits threshold. From 2022-23 profits from the small profits threshold up to the lower profits threshold are treated as paid. +values: + 2015-04-06: + value: false + metadata: + reference: + - title: Social Security Contributions and Benefits Act 1992 s 11(2) (as at 6 April 2021) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2021-04-06 + 2022-04-06: + value: true + metadata: + reference: + - title: The Social Security (Class 2 National Insurance Contributions Increase of Threshold) Regulations 2022 regs 1 and 2(2) + href: https://www.legislation.gov.uk/uksi/2022/1329/regulation/2/made +metadata: + unit: bool + period: year + label: Class 2 liability starts above the lower profits threshold + reference: + - title: Social Security Contributions and Benefits Act 1992 s 11(2), (5A) and (5B) (as at 6 April 2023) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2023-04-06 diff --git a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/small_profits_threshold.yaml b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/small_profits_threshold.yaml index 25af299979..e41baf8e24 100644 --- a/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/small_profits_threshold.yaml +++ b/policyengine_uk/parameters/gov/hmrc/national_insurance/class_2/small_profits_threshold.yaml @@ -1,35 +1,80 @@ -description: Small profits National Insurance threshold for self-employed earners. +description: Small profits threshold for Class 2 National Insurance contributions under section 11(4) of the Social Security Contributions and Benefits Act 1992. Before 2022-23 self-employed earners with profits at or above it were liable for Class 2; in 2022-23 and 2023-24 profits from this threshold up to the lower profits threshold were treated as paid. values: - 2015-04-06: 3_965 - 2017-04-06: 6_025 - 2018-04-06: 6_205 - 2019-04-06: 6_365 - 2020-04-06: 6_475 - 2021-04-06: 6_515 + 2015-04-06: + value: 5_965 + metadata: + reference: + - title: National Insurance Contributions Act 2015 Sch 1 para 3 (substituting s 11(4) of the Social Security Contributions and Benefits Act 1992) + href: https://www.legislation.gov.uk/ukpga/2015/5/schedule/1/paragraph/3/enacted + - title: Social Security Contributions and Benefits Act 1992 s 11(4) (as at 6 April 2016, unchanged for 2016-17) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2016-04-06 + 2017-04-06: + value: 6_025 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2017 reg 3(b) + href: https://www.legislation.gov.uk/uksi/2017/415/regulation/3/made + 2018-04-06: + value: 6_205 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2018 reg 3(b) + href: https://www.legislation.gov.uk/uksi/2018/337/regulation/3/made + 2019-04-06: + value: 6_365 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2019 reg 3(b) + href: https://www.legislation.gov.uk/uksi/2019/262/regulation/3/made + 2020-04-06: + value: 6_475 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2020 reg 3(b) + href: https://www.legislation.gov.uk/uksi/2020/299/regulation/3/made + 2021-04-06: + value: 6_515 + metadata: + reference: + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2021 reg 3 + href: https://www.legislation.gov.uk/uksi/2021/157/regulation/3/made 2022-04-06: value: 6_725 metadata: reference: - - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2022(3)(b) - href: https://www.legislation.gov.uk/uksi/2022/232/regulation/3/made#regulation-3-b + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments and National Insurance Funds Payments) Regulations 2022 reg 3(b) + href: https://www.legislation.gov.uk/uksi/2022/232/regulation/3/made + 2023-04-06: + value: 6_725 + metadata: + reference: + - title: Social Security Contributions and Benefits Act 1992 s 11(4)(b) (as at 6 April 2023) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2023-04-06 + 2024-04-06: + value: 6_725 + metadata: + reference: + - title: Social Security Contributions and Benefits Act 1992 s 11(4)(b) (as at 6 April 2024) + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11/2024-04-06 2025-04-06: value: 6_845 metadata: reference: - - title: "GOV.UK | Rates and allowances: National Insurance contributions" - href: https://www.gov.uk/government/publications/rates-and-allowances-national-insurance-contributions/rates-and-allowances-national-insurance-contributions + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments, National Insurance Funds Payments and Extension of Veteran's Relief) Regulations 2025 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2025/288/regulation/3/made 2026-04-06: value: 7_105 metadata: reference: - - title: "GOV.UK | Rates and allowances: National Insurance contributions" - href: https://www.gov.uk/government/publications/rates-and-allowances-national-insurance-contributions/rates-and-allowances-national-insurance-contributions + - title: The Social Security (Contributions) (Rates, Limits and Thresholds Amendments, National Insurance Funds Payments and Extension of Veteran's Relief) Regulations 2026 reg 3(a) + href: https://www.legislation.gov.uk/uksi/2026/231/regulation/3/made metadata: unit: currency-GBP + period: year label: NI class 2 small profits threshold - uprating: - parameter: gov.economic_assumptions.indices.obr.consumer_price_index - start_instant: 2022-04-06 + uprating: gov.economic_assumptions.indices.obr.consumer_price_index reference: - - https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/882271/Table-a4.pdf - - https://www.gov.uk/government/publications/rates-and-allowances-national-insurance-contributions/rates-and-allowances-national-insurance-contributions + - title: Social Security Contributions and Benefits Act 1992 s 11 + href: https://www.legislation.gov.uk/ukpga/1992/4/section/11 + - title: "GOV.UK | Rates and allowances: National Insurance contributions" + href: https://www.gov.uk/government/publications/rates-and-allowances-national-insurance-contributions/rates-and-allowances-national-insurance-contributions diff --git a/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_2/ni_class_2.yaml b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_2/ni_class_2.yaml index 3a458e7faa..472cb2ef28 100644 --- a/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_2/ni_class_2.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/hmrc/national_insurance/class_2/ni_class_2.yaml @@ -1,13 +1,134 @@ -- name: NI Class 2 - under LPL. +# Expected values come from s.11 SSCBA 1992 as in force for each tax year +# (legislation.gov.uk point-in-time text) and the SIs that set its figures. + +# 2023-24: £3.45 a week (SI 2023/236 reg 3(a)) on profits that exceed the +# £12,570 lower profits threshold (reg 3(b)). Profits from the £6,725 small +# profits threshold up to it are treated as paid (s.11(5A)-(5B)). +- name: NI Class 2 - 2023-24, below the small profits threshold. period: 2023 input: self_employment_income: 5_000 output: ni_class_2: 0 -- name: NI Class 2 - over LPL. +- name: NI Class 2 - 2023-24, at the small profits threshold, treated as paid. + period: 2023 + input: + self_employment_income: 6_725 + output: + ni_class_2: 0 + +- name: NI Class 2 - 2023-24, between the thresholds, treated as paid. + period: 2023 + input: + self_employment_income: 10_000 + output: + ni_class_2: 0 + +- name: NI Class 2 - 2023-24, at the lower profits threshold, not liable. + period: 2023 + input: + self_employment_income: 12_570 + output: + ni_class_2: 0 + +- name: NI Class 2 - 2023-24, just above the lower profits threshold. + period: 2023 + input: + self_employment_income: 12_571 + output: + ni_class_2: 3.45 * 52 + +- name: NI Class 2 - 2023-24, above the lower profits threshold. period: 2023 input: self_employment_income: 15_000 + output: + ni_class_2: 3.45 * 52 + +- name: NI Class 2 - 2023-24, above the upper profits limit. + period: 2023 + input: + self_employment_income: 60_000 + output: + ni_class_2: 179.4 + +- name: NI Class 2 - 2023-24, over State Pension age. + period: 2023 + input: + age: 70 + self_employment_income: 60_000 + output: + ni_class_2: 0 + +# 2022-23: £3.15 a week (SI 2022/232 reg 3(a)) on profits that exceed the +# £11,908 lower profits threshold, with effect from 6 April 2022 +# (SI 2022/1329 regs 1 and 2). +- name: NI Class 2 - 2022-23, between the thresholds, treated as paid. + period: 2022 + input: + self_employment_income: 8_000 + output: + ni_class_2: 0 + +- name: NI Class 2 - 2022-23, at the lower profits threshold, not liable. + period: 2022 + input: + self_employment_income: 11_908 + output: + ni_class_2: 0 + +- name: NI Class 2 - 2022-23, above the lower profits threshold. + period: 2022 + input: + self_employment_income: 11_909 output: ni_class_2: 3.15 * 52 + +# 2021-22: £3.05 a week (unchanged from SI 2020/299) on profits of, or +# exceeding, the £6,515 small profits threshold (SI 2021/157 reg 3). +- name: NI Class 2 - 2021-22, just below the small profits threshold. + period: 2021 + input: + self_employment_income: 6_514 + output: + ni_class_2: 0 + +- name: NI Class 2 - 2021-22, at the small profits threshold. + period: 2021 + input: + self_employment_income: 6_515 + output: + ni_class_2: 3.05 * 52 + +# 2020-21: £3.05 a week from £6,475 (SI 2020/299 reg 3). +- name: NI Class 2 - 2020-21, at the small profits threshold. + period: 2020 + input: + self_employment_income: 6_475 + output: + ni_class_2: 3.05 * 52 + +# 2015-16: £2.80 a week from £5,965 (NICA 2015 Sch 1 para 3). +- name: NI Class 2 - 2015-16, just below the small profits threshold. + period: 2015 + input: + self_employment_income: 5_964 + output: + ni_class_2: 0 + +- name: NI Class 2 - 2015-16, at the small profits threshold. + period: 2015 + input: + self_employment_income: 5_965 + output: + ni_class_2: 2.8 * 52 + +# 2024-25: s.11(2) omitted from 6 April 2024 (NICA (Reduction in Rates) Act +# 2023 s.3); no compulsory Class 2. +- name: NI Class 2 - 2024-25, abolished. + period: 2024 + input: + self_employment_income: 60_000 + output: + ni_class_2: 0 diff --git a/policyengine_uk/tests/test_ni_class_2.py b/policyengine_uk/tests/test_ni_class_2.py new file mode 100644 index 0000000000..4e7b68ef60 --- /dev/null +++ b/policyengine_uk/tests/test_ni_class_2.py @@ -0,0 +1,202 @@ +"""Class 2 National Insurance against s.11 SSCBA 1992, tax year by tax year. + +STATUTE holds each tax year's figures as enacted, read from legislation.gov.uk +(the amending instrument's made text and the point-in-time text of s.11), +independently of the parameter files. + +Invariants, for every model year and profits >= 0: + +1. Differential: ni_class_2 is 52 weeks at the statutory weekly rate when + s.11(2) makes the earner liable, and 0 otherwise. Before 2022-23 the + earner is liable on profits of, or exceeding, the small profits threshold. + In 2022-23 and 2023-24 only profits that exceed the lower profits + threshold are liable; profits from the small profits threshold up to it + are treated as paid (s.11(5A)-(5B)), which costs nothing. From 2024-25 + s.11(2) is omitted and no one is liable. +2. ni_class_2 is either 0 or 52 x the weekly rate. +3. ni_class_2 is non-decreasing in profits. + +The model counts 52 weeks of self-employment in a year. Profits are stored +as float32, so the reference is evaluated on the float32 value. +""" + +from fractions import Fraction + +import numpy as np +import pytest +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_uk import Simulation +from policyengine_uk.system import system + +WEEKS = 52 + +# Tax year starting in April of the key: (weekly rate under s.11(2), small +# profits threshold, lower profits threshold or None where s.11(2) keys on the +# small profits threshold). A rate of 0 means s.11(2) is omitted. +STATUTE = { + 2015: ("2.80", 5_965, None), # NICA 2015 Sch 1 para 3 + 2016: ("2.80", 5_965, None), # unchanged (s.11 as at 6 April 2016) + 2017: ("2.85", 6_025, None), # SI 2017/415 reg 3 + 2018: ("2.95", 6_205, None), # SI 2018/337 reg 3 + 2019: ("3.00", 6_365, None), # SI 2019/262 reg 3 + 2020: ("3.05", 6_475, None), # SI 2020/299 reg 3 + 2021: ("3.05", 6_515, None), # SI 2021/157 reg 3; rate unchanged + 2022: ("3.15", 6_725, 11_908), # SI 2022/232 reg 3; SI 2022/1329 reg 2 + 2023: ("3.45", 6_725, 12_570), # SI 2023/236 reg 3; SPT unchanged + 2024: ("0", 6_725, None), # s.11(2) omitted: NICRRA 2023 (c. 57) s.3 + 2025: ("0", 6_845, None), # SI 2025/288 reg 3(a) + 2026: ("0", 7_105, None), # SI 2026/231 reg 3(a) +} +MODEL_YEARS = list(range(2015, 2031)) +PROPERTY_SETTINGS = settings( + max_examples=10, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) + + +def statute(year): + # Class 2 stays abolished after the last tabulated year. + return STATUTE[min(year, max(STATUTE))] + + +def statutory_class_2(year, profits): + rate, small_profits_threshold, lower_profits_threshold = statute(year) + profits = Fraction(float(np.float32(profits))) + if lower_profits_threshold is None: + liable = profits >= small_profits_threshold + else: + liable = profits > lower_profits_threshold + return WEEKS * Fraction(rate) if liable else Fraction(0) + + +# Every threshold s.11 has used, with points either side of each. +THRESHOLD_PROFITS = sorted( + {0.0} + | { + float(threshold) + offset + for _, spt, lpt in STATUTE.values() + for threshold in filter(None, (spt, lpt)) + for offset in (-1, -0.01, 0, 0.01, 1) + } +) + + +def simulate(profits_list): + # One person per profit level, with the same profits in every model year, + # so a single simulation covers every year. + people = { + f"p{i}": { + "age": {year: 40 for year in MODEL_YEARS}, + "self_employment_income": {year: profits for year in MODEL_YEARS}, + } + for i, profits in enumerate(profits_list) + } + return Simulation( + situation={ + "people": people, + "benunits": {f"b{i}": {"members": [f"p{i}"]} for i in range(len(people))}, + "households": {f"h{i}": {"members": [f"p{i}"]} for i in range(len(people))}, + } + ) + + +def assert_invariants(profits_list): + sim = simulate(profits_list) + order = np.argsort(np.asarray(profits_list, dtype=np.float32), kind="stable") + for year in MODEL_YEARS: + class_2 = sim.calculate("ni_class_2", year) + full_year = WEEKS * float(statute(year)[0]) + for i, profits in enumerate(profits_list): + expected = float(statutory_class_2(year, profits)) + assert class_2[i] == pytest.approx(expected, abs=0.01), (year, profits) + assert class_2[i] == 0 or class_2[i] == pytest.approx(full_year, abs=0.01) + assert np.all(np.diff(class_2[order]) >= 0), (year, profits_list, class_2) + + +@pytest.mark.parametrize("year", sorted(STATUTE)) +def test_class_2_parameters_match_statute(year): + class_2 = system.parameters(f"{year}-01-01").gov.hmrc.national_insurance.class_2 + rate, small_profits_threshold, lower_profits_threshold = STATUTE[year] + + assert class_2.flat_rate == pytest.approx(float(rate)) + assert class_2.small_profits_threshold == small_profits_threshold + if Fraction(rate) > 0: + assert class_2.lower_profits_threshold_applies == ( + lower_profits_threshold is not None + ) + if lower_profits_threshold is not None: + assert class_2.lower_profits_threshold == lower_profits_threshold + + +def test_ni_class_2_matches_statute_at_every_threshold(): + rng = np.random.default_rng(1_992) + spread = list(rng.uniform(0, 200_000, 200).round(2)) + assert_invariants(THRESHOLD_PROFITS + spread + [60_000.0, 1_000_000.0]) + + +@PROPERTY_SETTINGS +@given( + st.lists( + st.one_of( + st.floats(0, 200_000, allow_nan=False, allow_infinity=False), + st.integers(0, 200_000).map(float), + st.sampled_from(THRESHOLD_PROFITS), + ), + min_size=1, + max_size=24, + ) +) +def test_ni_class_2_properties(profits_list): + assert_invariants(profits_list) + + +def test_2023_annual_maximum_uses_the_2023_24_class_2_rate(): + # Regulation 100 of SI 2001/1004 for 2023-24, with Class 2 at the + # default £3.45 a week. The Class 4 limits and rates are pinned to their + # 2023-24 statutory values (s.15(3) and (3ZA) SSCBA 1992) so the test + # exercises Class 2 alone. Profits £60,000; primary Class 1 £3,000. + # Step 3: 9% x (50,270 - 12,570) + 53 x 3.45 = 3,393 + 182.85 + # Step 4: 3,575.85 - 179.40 (Class 2) - 3,000 (Class 1) = 396.45 + # Case 2: 396.45 + 2% x (37,700 - 396.45 / 9%) + 2% x 9,730 + year = 2023 + all_years = "2000-01-01.2100-12-31" + class_4 = "gov.hmrc.national_insurance.class_4" + sim = Simulation( + situation={ + "people": { + "person": { + "age": {year: 40}, + "self_employment_income": {year: 60_000}, + "ni_class_1_employee_primary": { + f"{year}-{month:02d}": 250 for month in range(1, 13) + }, + } + }, + "benunits": {"benunit": {"members": ["person"]}}, + "households": {"household": {"members": ["person"]}}, + }, + reform={ + f"{class_4}.thresholds.lower_profits_limit": {all_years: 12_570}, + f"{class_4}.thresholds.upper_profits_limit": {all_years: 50_270}, + f"{class_4}.rates.main": {all_years: 0.09}, + f"{class_4}.rates.additional": {all_years: 0.02}, + }, + ) + + step_4 = Fraction("0.09") * 37_700 + 53 * Fraction("3.45") - Fraction("179.40") + step_4 -= 3_000 + step_5 = step_4 / Fraction("0.09") + maximum = step_4 + Fraction("0.02") * (37_700 - step_5) + Fraction("0.02") * 9_730 + + assert sim.calculate("ni_class_2", year)[0] == pytest.approx(179.40, abs=0.01) + assert float(maximum) == pytest.approx(1_256.95, abs=0.005) + assert sim.calculate("ni_class_4_maximum", year)[0] == pytest.approx( + float(maximum), abs=0.01 + ) + assert sim.calculate("ni_class_4", year)[0] == pytest.approx( + float(maximum), abs=0.01 + ) diff --git a/policyengine_uk/tests/test_ni_class_4_properties.py b/policyengine_uk/tests/test_ni_class_4_properties.py index 9f0e3fa512..0019b2246c 100644 --- a/policyengine_uk/tests/test_ni_class_4_properties.py +++ b/policyengine_uk/tests/test_ni_class_4_properties.py @@ -12,15 +12,18 @@ implementation of s.15(3) SSCBA 1992 capped by the literal regulation 100 steps (with the Case 1 comparison done exactly). The one exception is a Case 1 tie that holds in real numbers but not in binary (before April - 2024: 53 x weekly Class 2 = 2 x (Class 1 + Class 2), UPL below the small - profits threshold); the model then follows the real-valued tie. + 2024: 53 x weekly Class 2 = 2 x (Class 1 + Class 2), UPL below the profits + at which Class 2 becomes payable); the model then follows the real-valued + tie. 3. The maximum only ever reduces liability: 0 <= ni_class_4 <= the pre-maximum amount. 4. Monotonicity: with employment income held fixed, ni_class_4 is non-decreasing in self-employment profits wherever Class 2 does not change. Before April 2024 regulation 100 subtracts Class 2, so crossing - the small profits threshold can lower Class 4; Class 2 + Class 4 is then - still non-decreasing when the main rate is at least the additional rate. + the profits at which Class 2 becomes payable (the small profits threshold, + or from 2022-23 the Class 2 lower profits threshold) can lower Class 4; + Class 2 + Class 4 is then still non-decreasing when the main rate is at + least the additional rate. Class 2 counts in regulation 100 only before 6 April 2024 (SI 2024/377). diff --git a/policyengine_uk/variables/gov/hmrc/national_insurance/class_2/ni_class_2.py b/policyengine_uk/variables/gov/hmrc/national_insurance/class_2/ni_class_2.py index a36358bb4d..0b3ef93fae 100644 --- a/policyengine_uk/variables/gov/hmrc/national_insurance/class_2/ni_class_2.py +++ b/policyengine_uk/variables/gov/hmrc/national_insurance/class_2/ni_class_2.py @@ -6,7 +6,6 @@ class ni_class_2(Variable): entity = Person label = "NI Class 2 contributions" definition_period = YEAR - reference = "Social Security and Benefits Act 1992 s. 11" unit = GBP defined_for = "ni_liable" reference = "https://www.legislation.gov.uk/ukpga/1992/4/section/11" @@ -14,5 +13,14 @@ class ni_class_2(Variable): def formula(person, period, parameters): class_2 = parameters(period).gov.hmrc.national_insurance.class_2 profits = person("self_employment_income", period) - over_threshold = profits >= class_2.small_profits_threshold - return over_threshold * class_2.flat_rate * WEEKS_IN_YEAR + # Section 11(2) SSCBA 1992. From 2022-23 only profits that exceed the + # lower profits threshold are liable; profits from the small profits + # threshold up to it are treated as paid (s.11(5A)-(5B)) and cost + # nothing. Before then liability started at the small profits + # threshold. The flat rate is 0 once s.11(2) is omitted in 2024-25. + liable = where( + class_2.lower_profits_threshold_applies, + profits > class_2.lower_profits_threshold, + profits >= class_2.small_profits_threshold, + ) + return liable * class_2.flat_rate * WEEKS_IN_YEAR