Skip to content

Commit

Permalink
Merge pull request #20 from masterismail/BudgetaryImpactOverall1
Browse files Browse the repository at this point in the history
[1] - added the overall budgetary impact metric
  • Loading branch information
nikhilwoodruff authored Jul 17, 2024
2 parents 791a9ab + c5b6919 commit 2a89618
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 1 deletion.
71 changes: 71 additions & 0 deletions policyengine/economic_impact/budgetary_impact/overall/overall.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from policyengine.economic_impact.base_metric_calculator import BaseMetricCalculator
from policyengine_uk import Microsimulation

class BudgetaryImpact(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):

baseline_total_tax = self.baseline.calculate("household_tax").sum()
reformed_total_tax = self.reformed.calculate("household_tax").sum()

tax_revenue_impact = reformed_total_tax - baseline_total_tax

baseline_total_benefits = self.baseline.calculate("household_benefits").sum()
reformed_total_benefits = self.reformed.calculate("household_benefits").sum()


benefit_spending_impact = reformed_total_benefits - baseline_total_benefits

budgetary_impact = tax_revenue_impact - benefit_spending_impact



return {
"budgetary_impact" : round(budgetary_impact,2)
}

class BenefitSpendingImpact(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):

baseline_total_benefits = self.baseline.calculate("household_benefits").sum()
reformed_total_benefits = self.reformed.calculate("household_benefits").sum()


benefit_spending_impact = reformed_total_benefits - baseline_total_benefits



return {
"baseline_total_benefits": round(baseline_total_benefits,2),
"reformed_total_benefits": round(reformed_total_benefits,2),
"benefit_spending_impact": round(benefit_spending_impact,2)
}

class TaxRevenueImpact(BaseMetricCalculator):
def __init__(self, baseline: Microsimulation, reformed: Microsimulation, default_period: int = 2024) -> None:
super().__init__(baseline, reformed, default_period)
self.baseline = baseline
self.reformed = reformed

def calculate(self):

baseline_total_tax = self.baseline.calculate("household_tax").sum()
reformed_total_tax = self.reformed.calculate("household_tax").sum()

tax_revenue_impact = reformed_total_tax - baseline_total_tax


return {
"baseline_total_tax": round(baseline_total_tax,2),
"reformed_total_tax": round(reformed_total_tax,2),
"tax_revenue_impact": round(tax_revenue_impact,2)
}
9 changes: 9 additions & 0 deletions policyengine/economic_impact/economic_impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@
PensionCredit
)

from .budgetary_impact.overall.overall import (
BudgetaryImpact,
BenefitSpendingImpact,
TaxRevenueImpact
)

from typing import Dict

class EconomicImpact:
Expand Down Expand Up @@ -69,6 +75,9 @@ def __init__(self, reform: dict, country: str) -> None:

# Set up metric calculators
self.metric_calculators: Dict[str, object] = {
"budgetary/overall/budgetary_impact" : BudgetaryImpact(self.baseline, self.reformed),
"budgetary/overall/benefit_spending_impact" : BenefitSpendingImpact(self.baseline, self.reformed),
"budgetary/overall/tax_revenue_impact" : TaxRevenueImpact(self.baseline, self.reformed),
"budgetary/by_program/income_tax" : IncomeTax(self.baseline, self.reformed),
"budgetary/by_program/national_insurance" : NationalInsurance(self.baseline, self.reformed),
"budgetary/by_program/vat" : Vat(self.baseline, self.reformed),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import os
from policyengine import EconomicImpact

def assert_dict_approx_equal(actual, expected, tolerance=1e-4):
def assert_dict_approx_equal(actual, expected, tolerance=1e3):
for key in expected:
assert abs(actual[key] - expected[key]) < tolerance, f"Key {key}: expected {expected[key]}, got {actual[key]}"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# overall
- test_budgetary_impact:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
budgetary_impact: 203274712297.14

- test_benefit_spending_impact:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline_total_benefits: 247160184562.67
reformed_total_benefits: 249032006583.15
benefit_spending_impact: 1871822020.49

- test_tax_revenue_impact:
reform:
gov.hmrc.income_tax.rates.uk[0].rate:
"2024-01-01.2100-12-31": 0.55
country: uk
expected:
baseline_total_tax: 447861864968.89
reformed_total_tax: 653008399286.52
tax_revenue_impact: 205146534317.63
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import pytest
import yaml
import os
from policyengine import EconomicImpact

def assert_dict_approx_equal(actual, expected, tolerance=1e3):
for key in expected:
assert abs(actual[key] - expected[key]) < tolerance, f"Key {key}: expected {expected[key]}, got {actual[key]}"


yaml_file_path = "policyengine/tests/economic_impact/budgetary_impact/overall/overall.yaml"

# Check if the file exists
if not os.path.exists(yaml_file_path):
raise FileNotFoundError(f"The YAML file does not exist at: {yaml_file_path}")

with open(yaml_file_path, 'r') as file:
test_cases = yaml.safe_load(file)

@pytest.mark.parametrize("test_case", test_cases)
def test_economic_impact(test_case):
test_name = list(test_case.keys())[0]
test_data = test_case[test_name]

economic_impact = EconomicImpact(test_data['reform'], test_data['country'])

if 'budgetary' in test_name:
result = economic_impact.calculate("budgetary/overall/budgetary_impact")
elif 'benefit' in test_name:
result = economic_impact.calculate("budgetary/overall/benefit_spending_impact")
elif 'tax' in test_name:
result = economic_impact.calculate("budgetary/overall/tax_revenue_impact")
else:
pytest.fail(f"Unknown test case: {test_name}")

assert_dict_approx_equal(result, test_data['expected'])

if __name__ == "__main__":
pytest.main([__file__])

0 comments on commit 2a89618

Please sign in to comment.