|
9 | 9 | StratumConstraint, |
10 | 10 | Target, |
11 | 11 | ) |
| 12 | +from policyengine_us_data.db.create_field_valid_values import FieldValidValues |
12 | 13 | from policyengine_us_data.storage.calibration_targets.soi_metadata import ( |
13 | 14 | RETIREMENT_CONTRIBUTION_TARGETS, |
14 | 15 | ) |
|
25 | 26 | etl_argparser, |
26 | 27 | get_geographic_strata, |
27 | 28 | ) |
| 29 | +from policyengine_us_data.utils.target_variables import ( |
| 30 | + target_variable_components, |
| 31 | +) |
| 32 | + |
| 33 | +BEA_NIPA_WAGES_AND_SALARIES_2024 = 12_387_929_000_000 |
| 34 | +BEA_NIPA_PROPRIETORS_INCOME_2024 = 2_023_080_000_000 |
| 35 | +BEA_NIPA_PERSONAL_INTEREST_INCOME_2024 = 1_926_644_000_000 |
| 36 | +BEA_NIPA_PERSONAL_DIVIDEND_INCOME_2024 = 2_218_700_000_000 |
| 37 | + |
| 38 | +NIPA_PROPRIETORS_INCOME_VARIABLE = ( |
| 39 | + "total_self_employment_income+farm_operations_income+partnership_s_corp_income" |
| 40 | +) |
| 41 | +NIPA_PERSONAL_INTEREST_INCOME_VARIABLE = "interest_income" |
| 42 | +TAXABLE_INTEREST_AND_ORDINARY_DIVIDENDS_VARIABLE = ( |
| 43 | + "taxable_interest_income+dividend_income" |
| 44 | +) |
| 45 | + |
| 46 | +CBO_INCOME_BY_SOURCE_TARGETS = [ |
| 47 | + { |
| 48 | + "variable": "irs_employment_income", |
| 49 | + "parameter": "employment_income", |
| 50 | + "notes": ( |
| 51 | + "CBO detailed AGI-by-source employment income; restricted to " |
| 52 | + "tax filers because this is an AGI tax-return concept" |
| 53 | + ), |
| 54 | + }, |
| 55 | + { |
| 56 | + "variable": "self_employment_income", |
| 57 | + "parameter": "self_employment_income", |
| 58 | + "notes": ( |
| 59 | + "CBO detailed AGI-by-source self-employment income; restricted " |
| 60 | + "to tax filers because this is an AGI tax-return concept" |
| 61 | + ), |
| 62 | + }, |
| 63 | + { |
| 64 | + "variable": "taxable_pension_income", |
| 65 | + "parameter": "taxable_pension_income", |
| 66 | + "notes": ( |
| 67 | + "CBO detailed AGI-by-source taxable pension income; restricted " |
| 68 | + "to tax filers because this is an AGI tax-return concept" |
| 69 | + ), |
| 70 | + }, |
| 71 | + { |
| 72 | + "variable": "taxable_social_security", |
| 73 | + "parameter": "taxable_social_security", |
| 74 | + "notes": ( |
| 75 | + "CBO detailed AGI-by-source taxable Social Security; restricted " |
| 76 | + "to tax filers because this is an AGI tax-return concept" |
| 77 | + ), |
| 78 | + }, |
| 79 | + { |
| 80 | + "variable": "qualified_dividend_income", |
| 81 | + "parameter": "qualified_dividend_income", |
| 82 | + "notes": ( |
| 83 | + "CBO detailed AGI-by-source qualified dividends; restricted to " |
| 84 | + "tax filers because this is an AGI tax-return concept" |
| 85 | + ), |
| 86 | + }, |
| 87 | + { |
| 88 | + "variable": "loss_limited_net_capital_gains", |
| 89 | + "parameter": "net_capital_gain", |
| 90 | + "notes": ( |
| 91 | + "CBO detailed AGI-by-source net capital gains; restricted to " |
| 92 | + "tax filers because this is an AGI tax-return concept" |
| 93 | + ), |
| 94 | + }, |
| 95 | + { |
| 96 | + "variable": TAXABLE_INTEREST_AND_ORDINARY_DIVIDENDS_VARIABLE, |
| 97 | + "parameter": "taxable_interest_and_ordinary_dividends", |
| 98 | + "notes": ( |
| 99 | + "CBO detailed AGI-by-source taxable interest plus ordinary " |
| 100 | + "dividends; restricted to tax filers because this is an AGI " |
| 101 | + "tax-return concept" |
| 102 | + ), |
| 103 | + }, |
| 104 | +] |
| 105 | + |
| 106 | + |
| 107 | +def _register_target_variable(session: Session, variable: str) -> None: |
| 108 | + from policyengine_us.system import system |
| 109 | + |
| 110 | + missing = [ |
| 111 | + component |
| 112 | + for component in target_variable_components(variable) |
| 113 | + if component not in system.variables |
| 114 | + ] |
| 115 | + if missing: |
| 116 | + raise ValueError( |
| 117 | + f"Target variable expression {variable!r} includes unknown " |
| 118 | + f"policyengine-us variables: {missing}" |
| 119 | + ) |
| 120 | + |
| 121 | + existing = session.exec( |
| 122 | + select(FieldValidValues).where( |
| 123 | + FieldValidValues.field_name == "variable", |
| 124 | + FieldValidValues.valid_value == variable, |
| 125 | + ) |
| 126 | + ).first() |
| 127 | + if existing is None: |
| 128 | + session.add( |
| 129 | + FieldValidValues( |
| 130 | + field_name="variable", |
| 131 | + valid_value=variable, |
| 132 | + description="Additive calibration target expression", |
| 133 | + ) |
| 134 | + ) |
| 135 | + session.flush() |
| 136 | + |
28 | 137 |
|
29 | 138 | WIC_NATIONAL_ANNUAL_SUMMARY_SOURCE = ( |
30 | 139 | "https://www.fns.usda.gov/sites/default/files/resource-files/wisummary-4.xlsx" |
@@ -305,7 +414,76 @@ def extract_national_targets(year: int = DEFAULT_YEAR): |
305 | 414 | ] |
306 | 415 | tax_expenditure_targets = [{**target} for target in raw_tax_expenditure_targets] |
307 | 416 |
|
| 417 | + income_by_source = tax_benefit_system.parameters( |
| 418 | + time_period |
| 419 | + ).calibration.gov.cbo.income_by_source |
| 420 | + for target in CBO_INCOME_BY_SOURCE_TARGETS: |
| 421 | + try: |
| 422 | + value = income_by_source._children[target["parameter"]] |
| 423 | + tax_filer_targets.append( |
| 424 | + { |
| 425 | + "variable": target["variable"], |
| 426 | + "value": float(value), |
| 427 | + "source": "CBO Revenue Projections", |
| 428 | + "notes": target["notes"], |
| 429 | + "year": time_period, |
| 430 | + } |
| 431 | + ) |
| 432 | + except (KeyError, AttributeError) as e: |
| 433 | + print( |
| 434 | + "Warning: Could not extract CBO income-by-source " |
| 435 | + f"{target['parameter']} target: {e}" |
| 436 | + ) |
| 437 | + |
308 | 438 | direct_sum_targets = [ |
| 439 | + { |
| 440 | + "variable": "employment_income_before_lsr", |
| 441 | + "value": BEA_NIPA_WAGES_AND_SALARIES_2024, |
| 442 | + "source": "BEA NIPA Table 2.1", |
| 443 | + "notes": ( |
| 444 | + "Gross wages and salaries for all workers, including " |
| 445 | + "nonfilers; FRED/BEA series A034RC1A027NBEA" |
| 446 | + ), |
| 447 | + "year": 2024, |
| 448 | + }, |
| 449 | + { |
| 450 | + "variable": NIPA_PROPRIETORS_INCOME_VARIABLE, |
| 451 | + "value": BEA_NIPA_PROPRIETORS_INCOME_2024, |
| 452 | + "source": "BEA NIPA Table 2.1", |
| 453 | + "notes": ( |
| 454 | + "Proprietors' income with IVA and CCAdj for all persons, " |
| 455 | + "including nonfilers; FRED/BEA series A041RC1A027NBEA. " |
| 456 | + "Mapped to the closest additive PolicyEngine aggregate: " |
| 457 | + "total self-employment, farm operations, and " |
| 458 | + "partnership/S-corp income." |
| 459 | + ), |
| 460 | + "year": 2024, |
| 461 | + }, |
| 462 | + { |
| 463 | + "variable": NIPA_PERSONAL_INTEREST_INCOME_VARIABLE, |
| 464 | + "value": BEA_NIPA_PERSONAL_INTEREST_INCOME_2024, |
| 465 | + "source": "BEA NIPA Table 2.1", |
| 466 | + "notes": ( |
| 467 | + "Personal interest income for all persons, including " |
| 468 | + "nonfilers; FRED/BEA series A064RC1A027NBEA. NIPA also " |
| 469 | + "includes imputed interest, so this is a macro benchmark " |
| 470 | + "rather than a pure tax concept." |
| 471 | + ), |
| 472 | + "year": 2024, |
| 473 | + }, |
| 474 | + { |
| 475 | + "variable": "dividend_income", |
| 476 | + "value": BEA_NIPA_PERSONAL_DIVIDEND_INCOME_2024, |
| 477 | + "source": "BEA NIPA Table 2.1", |
| 478 | + "notes": ( |
| 479 | + "Personal dividend income for all persons, including " |
| 480 | + "nonfilers; FRED/BEA series B703RC1A027NBEA. NIPA " |
| 481 | + "includes dividends received through pension funds and " |
| 482 | + "private trusts, so this is a macro benchmark rather than " |
| 483 | + "a pure tax concept." |
| 484 | + ), |
| 485 | + "year": 2024, |
| 486 | + }, |
309 | 487 | { |
310 | 488 | "variable": "medicaid", |
311 | 489 | "value": 871.7e9, |
@@ -701,6 +879,7 @@ def load_national_targets( |
701 | 879 | # Process direct sum targets |
702 | 880 | for _, target_data in direct_targets_df.iterrows(): |
703 | 881 | target_year = target_data["year"] |
| 882 | + _register_target_variable(session, target_data["variable"]) |
704 | 883 | # Check if target already exists |
705 | 884 | existing_target = session.exec( |
706 | 885 | select(Target).where( |
@@ -767,6 +946,7 @@ def load_national_targets( |
767 | 946 | # Add tax-related targets to filer stratum |
768 | 947 | for _, target_data in tax_filer_df.iterrows(): |
769 | 948 | target_year = target_data["year"] |
| 949 | + _register_target_variable(session, target_data["variable"]) |
770 | 950 | # Check if target already exists |
771 | 951 | existing_target = session.exec( |
772 | 952 | select(Target).where( |
|
0 commit comments