Skip to content

Commit

Permalink
Merge pull request #882 from PolicyEngine/non-dom
Browse files Browse the repository at this point in the history
Add INITIAL non-dom status
  • Loading branch information
nikhilwoodruff authored Jun 28, 2024
2 parents ea0f53c + 449b727 commit bac3687
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 1 deletion.
4 changes: 4 additions & 0 deletions changelog_entry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- bump: minor
changes:
added:
- Non-dom status switch.
Binary file added enhanced_frs_brmas.csv.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion policyengine_uk/data/datasets/frs/enhanced_frs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Binary file modified policyengine_uk/data/gov/enhanced_frs_brmas.csv.gz
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions policyengine_uk/variables/gov/hmrc/income_tax/relief.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
57 changes: 57 additions & 0 deletions policyengine_uk/variables/household/income/worldwide_income.py
Original file line number Diff line number Diff line change
@@ -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,
)

0 comments on commit bac3687

Please sign in to comment.