Spaces:
Running
Running
| # -*- coding: utf-8 -*- | |
| """ | |
| Consolidated tax calculation engine for the Social Security Tax Torpedo app. | |
| Merges the best of Python_version.py (OOP structure, all 4 filing statuses) | |
| and Tax_calc_breakdown.ipynb (evolved functions, auto-computed base_amount_cap). | |
| All functions are importable and self-contained -- no top-level execution. | |
| """ | |
| from __future__ import annotations | |
| from dataclasses import dataclass, field | |
| from typing import List, Tuple, Dict, Optional | |
| import numpy as np | |
| import pandas as pd | |
| Bracket = Tuple[float, float, float] # (lower_inclusive, upper_exclusive, rate) | |
| # --------------------------------------------------------------------------- | |
| # Data classes | |
| # --------------------------------------------------------------------------- | |
| class SSBThresholds: | |
| """ | |
| IRS provisional-income thresholds for Social Security taxation. | |
| These differ by filing status. | |
| Typical values: | |
| - Single / HOH: t1=25,000 t2=34,000 | |
| - MFJ: t1=32,000 t2=44,000 | |
| - MFS (lived with spouse): effectively 0 / 0 (special rule) | |
| """ | |
| t1: float | |
| t2: float | |
| class TaxConfig: | |
| """ | |
| Complete tax configuration for one filing status. | |
| ssb_base_amount_cap is auto-computed as 0.5 * (t2 - t1). | |
| """ | |
| name: str | |
| brackets: List[Bracket] | |
| standard_deduction: float | |
| ssb_thresholds: SSBThresholds | |
| ssb_base_amount_cap: float = field(init=False) | |
| def __post_init__(self): | |
| object.__setattr__( | |
| self, | |
| "ssb_base_amount_cap", | |
| 0.5 * (self.ssb_thresholds.t2 - self.ssb_thresholds.t1), | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # 2016 tax bracket data (all 4 filing statuses) | |
| # Rates: 10%, 15%, 25%, 27.75%, 33%, 35%, 39.6% | |
| # Note: 2016 also had a personal exemption of $4,050 per person, | |
| # which is NOT modeled here. Only the standard deduction is used. | |
| # --------------------------------------------------------------------------- | |
| TAX_BRACKETS_SGL = [ | |
| (0, 9275, 0.10), | |
| (9275, 37650, 0.15), | |
| (37650, 91150, 0.25), | |
| (91150, 190150, 0.2775), | |
| (190150, 413350, 0.33), | |
| (413350, 415050, 0.35), | |
| (415050, float("inf"), 0.396), | |
| ] | |
| TAX_BRACKETS_MFJ = [ | |
| (0, 18550, 0.10), | |
| (18550, 75300, 0.15), | |
| (75300, 151900, 0.25), | |
| (151900, 231450, 0.2775), | |
| (231450, 413350, 0.33), | |
| (413350, 466950, 0.35), | |
| (466950, float("inf"), 0.396), | |
| ] | |
| TAX_BRACKETS_HOH = [ | |
| (0, 13250, 0.10), | |
| (13250, 50400, 0.15), | |
| (50400, 130150, 0.25), | |
| (130150, 210800, 0.2775), | |
| (210800, 413350, 0.33), | |
| (413350, 441000, 0.35), | |
| (441000, float("inf"), 0.396), | |
| ] | |
| TAX_BRACKETS_MFS = [ | |
| (0, 9275, 0.10), | |
| (9275, 37650, 0.15), | |
| (37650, 75950, 0.25), | |
| (75950, 115725, 0.2775), | |
| (115725, 206675, 0.33), | |
| (206675, 233475, 0.35), | |
| (233475, float("inf"), 0.396), | |
| ] | |
| # Standard deductions (2016) + personal exemptions folded in | |
| # Personal exemption: $4,050 per person (1 for SGL/HOH/MFS, 2 for MFJ) | |
| STD_DEDUCTION_SGL = 6300 + 4050 # $10,350 | |
| STD_DEDUCTION_MFJ = 12600 + 4050 + 4050 # $20,700 | |
| STD_DEDUCTION_HOH = 9300 + 4050 # $13,350 | |
| STD_DEDUCTION_MFS = 6300 + 4050 # $10,350 | |
| # Filing-status configurations | |
| CFG_SGL = TaxConfig( | |
| name="Single", | |
| brackets=TAX_BRACKETS_SGL, | |
| standard_deduction=STD_DEDUCTION_SGL, | |
| ssb_thresholds=SSBThresholds(t1=25000, t2=34000), | |
| ) | |
| CFG_MFJ = TaxConfig( | |
| name="Married Filing Jointly", | |
| brackets=TAX_BRACKETS_MFJ, | |
| standard_deduction=STD_DEDUCTION_MFJ, | |
| ssb_thresholds=SSBThresholds(t1=32000, t2=44000), | |
| ) | |
| CFG_HOH = TaxConfig( | |
| name="Head of Household", | |
| brackets=TAX_BRACKETS_HOH, | |
| standard_deduction=STD_DEDUCTION_HOH, | |
| ssb_thresholds=SSBThresholds(t1=25000, t2=34000), | |
| ) | |
| CFG_MFS = TaxConfig( | |
| name="Married Filing Separately", | |
| brackets=TAX_BRACKETS_MFS, | |
| standard_deduction=STD_DEDUCTION_MFS, | |
| ssb_thresholds=SSBThresholds(t1=25000, t2=34000), | |
| ) | |
| CONFIGS: Dict[str, TaxConfig] = { | |
| "SGL": CFG_SGL, | |
| "MFJ": CFG_MFJ, | |
| "HOH": CFG_HOH, | |
| "MFS": CFG_MFS, | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Core tax calculation functions | |
| # --------------------------------------------------------------------------- | |
| def ssb_tax(other_income: float, ssb: float, cfg: TaxConfig) -> float: | |
| """ | |
| Taxable Social Security benefits (IRS worksheet-style). | |
| PI = other_income + 0.5 * ssb | |
| Tier 1 (PI <= t1): taxable = 0 | |
| Tier 2 (t1 < PI <= t2): taxable = min(0.5*(PI-t1), 0.5*ssb) | |
| Tier 3 (PI > t2): taxable = min(0.85*ssb, base_amount + 0.85*(PI-t2)) | |
| """ | |
| t1, t2 = cfg.ssb_thresholds.t1, cfg.ssb_thresholds.t2 | |
| pi = other_income + 0.5 * ssb | |
| if pi <= t1: | |
| return 0.0 | |
| if pi <= t2: | |
| return min(0.5 * (pi - t1), 0.5 * ssb) | |
| base_amount = min(0.5 * (t2 - t1), 0.5 * ssb) | |
| candidate = base_amount + 0.85 * (pi - t2) | |
| return min(0.85 * ssb, candidate) | |
| def bracket_tax(taxable_income: float, cfg: TaxConfig) -> float: | |
| """Progressive tax from brackets.""" | |
| if taxable_income <= 0: | |
| return 0.0 | |
| tax = 0.0 | |
| for lower, upper, rate in cfg.brackets: | |
| if taxable_income <= lower: | |
| break | |
| amt = min(taxable_income, upper) - lower | |
| if amt > 0: | |
| tax += amt * rate | |
| if taxable_income <= upper: | |
| break | |
| return float(tax) | |
| def compute_baseline_tax(other_income: float, cfg: TaxConfig) -> float: | |
| """Tax ignoring Social Security (baseline reference).""" | |
| taxable_income = max(0.0, other_income - cfg.standard_deduction) | |
| return bracket_tax(taxable_income, cfg) | |
| def tax_with_ssb(other_income: float, ssb: float, cfg: TaxConfig) -> float: | |
| """Total federal tax including SSB taxation.""" | |
| taxable_ssb = ssb_tax(other_income, ssb, cfg) | |
| taxable_income = max(0.0, other_income + taxable_ssb - cfg.standard_deduction) | |
| return bracket_tax(taxable_income, cfg) | |
| def tax_with_ssb_detail(other_income: float, ssb: float, cfg: TaxConfig) -> Dict[str, float]: | |
| """ | |
| Full tax calculation returning all intermediate values. | |
| """ | |
| taxable_ssb_val = ssb_tax(other_income, ssb, cfg) | |
| pi = other_income + 0.5 * ssb | |
| agi = other_income + taxable_ssb_val | |
| taxable_income = max(0.0, agi - cfg.standard_deduction) | |
| tax = bracket_tax(taxable_income, cfg) | |
| return { | |
| "other_income": float(other_income), | |
| "ssb": float(ssb), | |
| "provisional_income": float(pi), | |
| "taxable_ssb": float(taxable_ssb_val), | |
| "pct_ssb_taxable": float(taxable_ssb_val / ssb * 100) if ssb > 0 else 0.0, | |
| "agi": float(agi), | |
| "standard_deduction": float(cfg.standard_deduction), | |
| "taxable_income": float(taxable_income), | |
| "tax": float(tax), | |
| "effective_rate": float(tax / other_income * 100) if other_income > 0 else 0.0, | |
| } | |
| def bracket_marginal_rate(other_income: float, cfg: TaxConfig) -> float: | |
| """Marginal bracket rate (step function, ignoring SSB effects).""" | |
| ti = float(max(0.0, other_income - cfg.standard_deduction)) | |
| for lower, upper, rate in cfg.brackets: | |
| if ti >= lower and ti < upper: | |
| return float(rate) | |
| return float(cfg.brackets[-1][2]) | |
| def total_marginal_rate( | |
| other_income: float, ssb: float, cfg: TaxConfig, delta: float = 100.0 | |
| ) -> float: | |
| """ | |
| Total marginal tax rate via finite difference (includes SSB torpedo effect). | |
| """ | |
| t1 = tax_with_ssb(other_income, ssb, cfg) | |
| t2 = tax_with_ssb(other_income + delta, ssb, cfg) | |
| return (t2 - t1) / delta | |
| def find_torpedo_bounds( | |
| cfg: TaxConfig, ssb: float, x_max: float = 200000 | |
| ) -> Tuple[Optional[float], Optional[float]]: | |
| """ | |
| Find the zero-point (where tax first > 0) and confluence point | |
| (where taxable SSB reaches 85% of SSB). | |
| The zero point is the highest Other Income where the marginal rate | |
| is still 0% (i.e. tax(OI) == 0 AND tax(OI + delta) == 0), so that | |
| it plots cleanly inside the No-Tax Zone. | |
| Returns (zero_point, confluence_point). Either may be None. | |
| """ | |
| delta = 100.0 # must match the delta used for marginal-rate computation | |
| # --- Coarse scan to bracket the transition points --- | |
| xs = np.linspace(0, x_max, 5000) | |
| ssb_vals = np.array([ssb_tax(x, ssb, cfg) for x in xs]) | |
| tax_vals = np.array([tax_with_ssb(x, ssb, cfg) for x in xs], dtype=float) | |
| zero_point = None | |
| confluence_point = None | |
| # Find approximate zero point (first x where tax > 0) | |
| approx_zp_lo = None | |
| approx_zp_hi = None | |
| prev_x = 0.0 | |
| for x, tv in zip(xs, tax_vals): | |
| if tv > 0 and approx_zp_lo is None: | |
| approx_zp_lo = prev_x | |
| approx_zp_hi = x | |
| prev_x = x | |
| # Binary search for the precise boundary where tax transitions from 0 to >0 | |
| if approx_zp_lo is not None: | |
| lo, hi = approx_zp_lo, approx_zp_hi | |
| for _ in range(50): # ~15 decimal digits of precision | |
| mid = (lo + hi) / 2 | |
| if tax_with_ssb(mid, ssb, cfg) > 0: | |
| hi = mid | |
| else: | |
| lo = mid | |
| # lo is the highest income where tax == 0 | |
| # Step back by delta so that marginal rate (tax(x+delta) - tax(x))/delta == 0 | |
| zero_point = max(0.0, lo - delta) | |
| # Find confluence point | |
| for x, sv in zip(xs, ssb_vals): | |
| if zero_point is not None and sv >= 0.85 * ssb: | |
| confluence_point = float(x) | |
| break | |
| return zero_point, confluence_point | |
| def classify_zone( | |
| other_income: float, ssb: float, cfg: TaxConfig, | |
| zero_point: Optional[float] = None, confluence_point: Optional[float] = None, | |
| ) -> str: | |
| """ | |
| Classify the user's income into one of three zones: | |
| - 'No-Tax Zone' (green) -- tax == 0 | |
| - 'High-Tax Zone' (red) -- in the torpedo | |
| - 'Same-Old Zone' (blue) -- past the torpedo | |
| """ | |
| my_tax = tax_with_ssb(other_income, ssb, cfg) | |
| if my_tax <= 0: | |
| return "No-Tax Zone" | |
| if confluence_point is not None and other_income >= confluence_point: | |
| return "Same-Old Zone" | |
| return "High-Tax Zone" | |
| def bracket_breakdown(taxable_income: float, cfg: TaxConfig) -> pd.DataFrame: | |
| """DataFrame showing tax in each bracket.""" | |
| rows = [] | |
| ti = float(max(0.0, taxable_income)) | |
| for lower, upper, rate in cfg.brackets: | |
| if ti <= lower: | |
| amt = 0.0 | |
| else: | |
| amt = max(0.0, min(ti, upper) - lower) | |
| rows.append({ | |
| "lower": lower, | |
| "upper": upper if np.isfinite(upper) else np.inf, | |
| "rate": rate, | |
| "taxed_amount": amt, | |
| "tax_in_bracket": amt * rate, | |
| }) | |
| if ti <= upper: | |
| break | |
| df = pd.DataFrame(rows) | |
| df["tax_in_bracket"] = df["tax_in_bracket"].astype(float) | |
| df["cum_tax"] = df["tax_in_bracket"].cumsum() | |
| return df | |