Browse Source

feat: add wealth gap visualizer with Fed Reserve benchmarks

Made-with: Cursor
pull/6453/head
Priyanka Punukollu 1 month ago
parent
commit
6c30617afb
  1. 66
      agent/evals/test_wealth_visualizer.py
  2. 174
      agent/tools/wealth_visualizer.py

66
agent/evals/test_wealth_visualizer.py

@ -0,0 +1,66 @@
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'tools'))
from wealth_visualizer import analyze_wealth_position
def test_wealth_above_median():
result = analyze_wealth_position(
portfolio_value=94000, age=34, annual_income=120000
)
assert result["current_position"]["total_net_worth"] == 94000
assert "above median" in result["current_position"]["you_vs_median"]
assert result["retirement_projection"]["monthly_income_at_retirement"] > 0
assert "honest_assessment" in result
def test_wealth_below_median():
result = analyze_wealth_position(
portfolio_value=15000, age=45, annual_income=80000
)
# 45-54 median is $247k, $15k is well below
assert result["current_position"]["total_net_worth"] == 15000
assert result["current_position"]["total_net_worth"] < 247000
assert "honest_assessment" in result
def test_wealth_includes_real_estate():
result = analyze_wealth_position(
portfolio_value=94000, age=40,
annual_income=150000, real_estate_equity=140000
)
assert result["current_position"]["total_net_worth"] == 234000
assert result["current_position"]["real_estate_equity"] == 140000
def test_early_retirement_scenario():
result = analyze_wealth_position(
portfolio_value=500000, age=40,
annual_income=200000, target_retirement_age=55
)
assert result["retirement_projection"]["years_to_retirement"] == 15
assert len(result["what_if_scenarios"]) >= 2
def test_retirement_math_reasonable():
result = analyze_wealth_position(
portfolio_value=100000, age=35,
annual_income=100000, annual_savings=15000,
target_retirement_age=65
)
projected = result["retirement_projection"]["projected_total_at_retirement"]
assert projected > 700000
assert projected < 5000000
def test_savings_grade_low_vs_high():
result_low = analyze_wealth_position(
50000, 30, 100000, annual_savings=5000
)
result_high = analyze_wealth_position(
50000, 30, 100000, annual_savings=30000
)
low_grade = result_low["savings_analysis"]["savings_grade"]
high_grade = result_high["savings_analysis"]["savings_grade"]
assert low_grade in ["critical", "minimum", "low"]
assert high_grade in ["excellent", "exceptional"]

174
agent/tools/wealth_visualizer.py

@ -0,0 +1,174 @@
"""
Wealth Gap Visualizer
Compares actual net worth against Federal Reserve median wealth by age group.
Projects retirement income and shows what-if scenarios.
Source: Federal Reserve Survey of Consumer Finances 2022
"""
FED_WEALTH_DATA = {
"under_35": {
"median": 39000, "p25": 7000,
"p75": 168000, "p90": 466000,
},
"35_to_44": {
"median": 135000, "p25": 22000,
"p75": 461000, "p90": 1100000,
},
"45_to_54": {
"median": 247000, "p25": 43000,
"p75": 791000, "p90": 1900000,
},
"55_to_64": {
"median": 365000, "p25": 71000,
"p75": 1200000, "p90": 2900000,
},
"65_to_74": {
"median": 409000, "p25": 83000,
"p75": 1380000, "p90": 3200000,
},
}
SAVINGS_GRADES = {
"exceptional": (0.30, "You are building wealth aggressively"),
"excellent": (0.20, "You are on track for most goals"),
"good": (0.15, "Solid progress"),
"minimum": (0.10, "Basic — consider increasing"),
"critical": (0.05, "Below recommended — increase urgently"),
"low": (0.0, "Saving very little — prioritize this"),
}
def _get_age_bracket(age: int) -> str:
if age < 35:
return "under_35"
elif age < 45:
return "35_to_44"
elif age < 55:
return "45_to_54"
elif age < 65:
return "55_to_64"
else:
return "65_to_74"
def analyze_wealth_position(
portfolio_value: float,
age: int,
annual_income: float,
annual_savings: float = None,
target_retirement_age: int = 65,
real_estate_equity: float = 0,
) -> dict:
"""Compare net worth against Fed Reserve benchmarks and project retirement."""
# Step 2: Total net worth
total_net_worth = portfolio_value + real_estate_equity
# Step 3: Percentile position
bracket_key = _get_age_bracket(age)
bracket = FED_WEALTH_DATA[bracket_key]
if total_net_worth >= bracket["p90"]:
position = "top 10%"
elif total_net_worth >= bracket["p75"]:
position = "75th-90th percentile"
elif total_net_worth >= bracket["median"]:
position = "50th-75th percentile"
elif total_net_worth >= bracket["p25"]:
position = "25th-50th percentile"
else:
position = "bottom 25%"
diff_from_median = total_net_worth - bracket["median"]
if diff_from_median >= 0:
vs_median = f"+${diff_from_median:,.0f} above median"
else:
vs_median = f"${abs(diff_from_median):,.0f} below median"
# Step 4: Savings analysis
savings = annual_savings if annual_savings is not None else annual_income * 0.15
savings_rate = savings / annual_income if annual_income > 0 else 0
grade = "low"
for g, (threshold, _) in SAVINGS_GRADES.items():
if savings_rate >= threshold:
grade = g
break
# Step 5: Retirement projection
years = max(1, target_retirement_age - age)
growth_rate = 0.07
future_portfolio = portfolio_value * ((1 + growth_rate) ** years)
future_savings = savings * (
((1 + growth_rate) ** years - 1) / growth_rate
)
total_at_retirement = future_portfolio + future_savings
monthly_retirement_income = (total_at_retirement * 0.04) / 12
# Step 6: What-if scenarios
# Scenario 1: save 5% more
extra_annual = annual_income * 0.05
extra_future = extra_annual * (
((1 + growth_rate) ** years - 1) / growth_rate
)
extra_monthly = (extra_future * 0.04) / 12
# Scenario 2: retire 5 years earlier
years_early = max(1, years - 5)
early_portfolio = portfolio_value * ((1 + growth_rate) ** years_early)
early_savings_val = savings * (
((1 + growth_rate) ** years_early - 1) / growth_rate
)
early_monthly = ((early_portfolio + early_savings_val) * 0.04) / 12
# Build honest assessment
peer_clause = f"You are in the {position} for your age group."
retirement_clause = (
f"At your current savings rate, you can expect "
f"${round(monthly_retirement_income):,}/mo at retirement."
)
honest_assessment = f"{peer_clause} {retirement_clause}"
return {
"current_position": {
"age": age,
"total_net_worth": total_net_worth,
"portfolio_value": portfolio_value,
"real_estate_equity": real_estate_equity,
"vs_peers": position,
"median_for_age": bracket["median"],
"you_vs_median": vs_median,
"percentile_estimate": position,
},
"savings_analysis": {
"annual_savings_used": savings,
"savings_rate": round(savings_rate, 3),
"savings_grade": grade,
"assessment": SAVINGS_GRADES[grade][1],
},
"retirement_projection": {
"target_retirement_age": target_retirement_age,
"years_to_retirement": years,
"projected_total_at_retirement": round(total_at_retirement),
"monthly_income_at_retirement": round(monthly_retirement_income),
"assumptions": "7% annual growth, 4% withdrawal rate",
},
"what_if_scenarios": [
{
"scenario": "Save 5% more per year",
"extra_monthly_at_retirement": round(extra_monthly),
"description": (
f"Adding ${extra_annual:,.0f}/yr gives "
f"${round(extra_monthly):,} more per month at retirement"
),
},
{
"scenario": f"Retire 5 years earlier (age {target_retirement_age - 5})",
"monthly_income": round(early_monthly),
"vs_normal_retirement": round(early_monthly - monthly_retirement_income),
},
],
"honest_assessment": honest_assessment,
"data_source": "Federal Reserve Survey of Consumer Finances 2022",
}
Loading…
Cancel
Save