diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb..3dfd47ada 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Non-dom status switch. diff --git a/enhanced_frs_brmas.csv.gz b/enhanced_frs_brmas.csv.gz new file mode 100644 index 000000000..423da14eb Binary files /dev/null and b/enhanced_frs_brmas.csv.gz differ diff --git a/policyengine_uk/data/datasets/frs/enhanced_frs.py b/policyengine_uk/data/datasets/frs/enhanced_frs.py index 718d86f57..fe1aebedf 100644 --- a/policyengine_uk/data/datasets/frs/enhanced_frs.py +++ b/policyengine_uk/data/datasets/frs/enhanced_frs.py @@ -153,7 +153,7 @@ class EnhancedFRS(Dataset): data_format = Dataset.TIME_PERIOD_ARRAYS num_years = 7 time_period = 2021 - count_copies = 4 + count_copies = 9 url = "release://policyengine/non-public-microdata/uk-2024-march-efo/enhanced_frs.h5" def generate(self): diff --git a/policyengine_uk/data/gov/enhanced_frs_brmas.csv.gz b/policyengine_uk/data/gov/enhanced_frs_brmas.csv.gz index e1c165c41..423da14eb 100644 Binary files a/policyengine_uk/data/gov/enhanced_frs_brmas.csv.gz and b/policyengine_uk/data/gov/enhanced_frs_brmas.csv.gz differ diff --git a/policyengine_uk/parameters/gov/hmrc/income_tax/reliefs/non_domiciled_status.yaml b/policyengine_uk/parameters/gov/hmrc/income_tax/reliefs/non_domiciled_status.yaml new file mode 100644 index 000000000..2fbaf19de --- /dev/null +++ b/policyengine_uk/parameters/gov/hmrc/income_tax/reliefs/non_domiciled_status.yaml @@ -0,0 +1,6 @@ +description: Whether individuals can claim non-domiciled status for tax purposes. +values: + 2000-01-01: true +metadata: + unit: bool + label: allow non-domiciled tax status diff --git a/policyengine_uk/parameters/gov/simulation/non_domiciled_status/proportion_by_income.yaml b/policyengine_uk/parameters/gov/simulation/non_domiciled_status/proportion_by_income.yaml new file mode 100644 index 000000000..d499b02bc --- /dev/null +++ b/policyengine_uk/parameters/gov/simulation/non_domiciled_status/proportion_by_income.yaml @@ -0,0 +1,43 @@ +description: Proportion of non-domiciled people by income band. +brackets: + - threshold: + 2022-01-01: 0 + amount: + 2022-01-01: 0 # Actually 0.03 but 0 to be safe + - threshold: + 2022-01-01: 100_000 + amount: + 2022-01-01: 0.052 + - threshold: + 2022-01-01: 200_000 + amount: + 2022-01-01: 0.134 + - threshold: + 2022-01-01: 500_000 + amount: + 2022-01-01: 0.211 + - threshold: + 2022-01-01: 1_000_000 + amount: + 2022-01-01: 0.268 + - threshold: + 2022-01-01: 2_000_000 + amount: + 2022-01-01: 0.344 + - threshold: + 2022-01-01: 5_000_000 + amount: + 2022-01-01: 0.414 + - threshold: + 2022-01-01: 10_000_000 + amount: + 2022-01-01: 0.4 + +metadata: + label: proportion of non-domiciled individuals by income band + type: single_amount + threshold_unit: currency-GBP + amount_unit: /1 + reference: + - title: CAGE Policy Briefing no. 36 + href: https://warwick.ac.uk/fac/soc/economics/research/centres/cage/manage/publications/bn36.2022.pdf diff --git a/policyengine_uk/parameters/gov/simulation/non_domiciled_status/total.yaml b/policyengine_uk/parameters/gov/simulation/non_domiciled_status/total.yaml new file mode 100644 index 000000000..1c1aa50e3 --- /dev/null +++ b/policyengine_uk/parameters/gov/simulation/non_domiciled_status/total.yaml @@ -0,0 +1,9 @@ +description: Total number of non-domiciled people. +values: + 2022-01-01: 238_000 +metadata: + unit: person + label: total non-domiciled individuals + reference: + - title: CAGE Policy Briefing no. 36 + href: https://warwick.ac.uk/fac/soc/economics/research/centres/cage/manage/publications/bn36.2022.pdf diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/relief.py b/policyengine_uk/variables/gov/hmrc/income_tax/relief.py index 210872237..456c24400 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/relief.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/relief.py @@ -327,6 +327,10 @@ def formula(person, period, parameters): "taxable_dividend_income", "taxable_miscellaneous_income", ] + if not parameters( + period + ).gov.hmrc.income_tax.reliefs.non_domiciled_status: + COMPONENTS.append("worldwide_income") if parameters( period ).gov.contrib.ubi_center.basic_income.interactions.include_in_taxable_income: diff --git a/policyengine_uk/variables/household/income/worldwide_income.py b/policyengine_uk/variables/household/income/worldwide_income.py new file mode 100644 index 000000000..f851eee9e --- /dev/null +++ b/policyengine_uk/variables/household/income/worldwide_income.py @@ -0,0 +1,57 @@ +from policyengine_uk.model_api import * + + +class is_non_domiciled(Variable): + label = "non-domiciled" + documentation = "Whether the person is non-domiciled for tax purposes." + entity = Person + definition_period = YEAR + value_type = bool + + def formula(person, period, parameters): + if not hasattr(person.simulation, "dataset"): + return 0 + if person.simulation.dataset.name != "enhanced_frs": + return 0 + total_income = person("total_income", period) + non_dom = parameters(period).gov.simulation.non_domiciled_status + weight = person("person_weight", period) + + thresholds = non_dom.proportion_by_income.thresholds + shares_of_non_doms = non_dom.proportion_by_income.amounts + probabilities = np.zeros_like(total_income) + ADJUSTMENT_FACTOR = 1e3 + for i in range(len(thresholds)): + lower = thresholds[i] + upper = thresholds[i + 1] if i + 1 < len(thresholds) else np.inf + in_range = (total_income >= lower) & (total_income < upper) + total_population = weight[in_range].sum() + percent_non_dom = where( + total_population == 0, + 0, + shares_of_non_doms[i] / total_population, + ) + probability = 1 - (1 - percent_non_dom) ** ADJUSTMENT_FACTOR + probabilities[in_range] = probability + + return random(person) < probabilities + + +class worldwide_income(Variable): + label = "worldwide income" + documentation = "Income not taxed due to non-domiciled status." + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + ADJUSTMENT_FACTOR = 2.7 # Gets 3.2bn from abolishing non-dom status + total_income = person("total_income", period) + is_non_dom = person("is_non_domiciled", period) + + return where( + is_non_dom, + total_income * ADJUSTMENT_FACTOR, + 0, + )