CoolFace
Apppublic

cherrisai/wealth_AI

sourceHugging Faceupdated 6mo agoView on Hugging Face
1likes
app.py1253 linesDownload Raw Back to root
1"""2====================================================3  AI Personal Wealth Analyzer — Streamlit App4  Run: streamlit run app.py5  Requires: finance_model.pkl in same folder6====================================================7"""8 9import streamlit as st10import numpy as np11import pandas as pd12import pickle13import plotly.express as px14import plotly.graph_objects as go15from fpdf import FPDF16import time17import json18import os19from datetime import datetime20 21# ============================================================22# PAGE CONFIG23# ============================================================24st.set_page_config(25    page_title="AI Personal Wealth Analyzer",26    layout="wide"27)28 29# ============================================================30# GLOBAL CSS31# ============================================================32st.markdown("""33<style>34.section-title {35    background: linear-gradient(90deg, #0f2027, #2c5364);36    color: white;37    padding: 10px 20px;38    border-radius: 10px;39    font-size: 20px;40    font-weight: bold;41    margin-bottom: 15px;42    letter-spacing: 0.5px;43}44.kpi {45    background: linear-gradient(135deg, #2c5364, #0f2027);46    padding: 25px;47    border-radius: 15px;48    color: white;49    text-align: center;50    font-size: 22px;51    font-weight: bold;52    box-shadow: 0px 4px 15px rgba(0,0,0,0.3);53}54.info-card {55    background: #1e293b;56    border-left: 5px solid #38bdf8;57    padding: 15px 20px;58    border-radius: 8px;59    color: #e2e8f0;60    margin: 8px 0;61    font-size: 15px;62}63.loan-card {64    background: #0f172a;65    border: 1px solid #334155;66    padding: 12px 18px;67    border-radius: 10px;68    color: #cbd5e1;69    margin: 5px 0;70    font-size: 14px;71}72.pred-card {73    background: #0f172a;74    border: 1px solid #38bdf8;75    padding: 14px 18px;76    border-radius: 10px;77    color: #e2e8f0;78    margin: 6px 0;79    font-size: 14px;80}81.alert-gain {82    background: #052e16;83    border-left: 5px solid #22c55e;84    padding: 14px 18px;85    border-radius: 10px;86    color: #bbf7d0;87    margin: 6px 0;88    font-size: 15px;89}90.alert-loss {91    background: #2d0a0a;92    border-left: 5px solid #ef4444;93    padding: 14px 18px;94    border-radius: 10px;95    color: #fecaca;96    margin: 6px 0;97    font-size: 15px;98}99</style>100""", unsafe_allow_html=True)101 102 103# ============================================================104# HELPER — renders a styled section title105# ============================================================106def section(icon, title):107    st.markdown(f'<div class="section-title">{icon} {title}</div>', unsafe_allow_html=True)108 109 110# ============================================================111# SMART RUPEE FORMATTER112# Format: K = thousands, L = lakhs, Cr = crores113# ============================================================114def fmt(amount):115    """Format a rupee amount into K / L / Cr for readability."""116    try:117        amount = float(amount)118    except (TypeError, ValueError):119        return "Rs. 0"120    neg = amount < 0121    a   = abs(amount)122    if a >= 1_00_00_000:          # 1 crore+123        s = f"Rs. {a / 1_00_00_000:.2f} Cr"124    elif a >= 1_00_000:           # 1 lakh+125        s = f"Rs. {a / 1_00_000:.2f} L"126    elif a >= 1_000:              # 1 thousand+127        s = f"Rs. {a / 1_000:.1f} K"128    else:129        s = f"Rs. {a:,.0f}"130    return f"-{s}" if neg else s131 132 133# ============================================================134# LOAD MODEL135# ============================================================136@st.cache_resource137def load_model():138    return pickle.load(open("finance_model.pkl", "rb"))139 140model = load_model()141 142 143# ============================================================144# HISTORY HELPERS145# ============================================================146HISTORY_FILE = "analysis_history.json"147 148def load_history():149    if os.path.exists(HISTORY_FILE):150        with open(HISTORY_FILE, "r") as f:151            return json.load(f)152    return []153 154def save_history(data):155    with open(HISTORY_FILE, "w") as f:156        json.dump(data, f, indent=2)157 158 159# ============================================================160# APP HEADER161# ============================================================162st.markdown("""163<div style='text-align:center; padding:30px 0 10px 0;'>164    <h1 style='font-size:42px; color:#38bdf8;'> AI Personal Wealth Analyzer</h1>165    <p style='color:#94a3b8; font-size:17px;'>166    </p>167</div>168""", unsafe_allow_html=True)169st.divider()170 171 172# ============================================================173# SECTION — INCOME INFORMATION174# ============================================================175section("", "Income Information")176 177c1, c2, c3 = st.columns(3)178with c1:179    income = st.number_input("Monthly Salary (Rs.)", min_value=0, value=0, step=1000)180with c2:181    extra_income = st.number_input("Extra Income (Rs.)", min_value=0, value=0, step=500)182with c3:183    savings = st.number_input("Current Savings (Rs.)", min_value=0, value=0, step=1000)184 185total_income = income + extra_income186 187if total_income > 0:188    st.info(f"Total Monthly Income: {fmt(total_income)}")189 190st.divider()191 192 193# ============================================================194# SECTION — LOAN INFORMATION195# ============================================================196section("", "Loan Information")197 198if "loans" not in st.session_state:199    st.session_state.loans = []200 201if st.button("Add Loan"):202    st.session_state.loans.append({"name": "", "emi": 0, "principal": 0, "months": 0})203 204loan_data = []205 206for i in range(len(st.session_state.loans)):207    st.markdown(f"**Loan {i + 1}**")208    c1, c2, c3, c4 = st.columns(4)209    name      = c1.text_input("Loan Name",             key=f"name{i}", placeholder="e.g. Home Loan")210    emi       = c2.number_input("Monthly EMI (Rs.)",   min_value=0,    key=f"emi{i}")211    principal = c3.number_input("Principal Left (Rs.)", min_value=0,   key=f"principal{i}")212    months    = c4.number_input("Months Remaining",    min_value=0,    key=f"months{i}")213    loan_data.append({"name": name, "emi": emi, "principal": principal, "months": months})214 215df_loans = pd.DataFrame(loan_data) if loan_data else pd.DataFrame(216    columns=["name", "emi", "principal", "months"]217)218 219st.divider()220 221 222# ============================================================223# ANALYZE BUTTON224# ============================================================225_, mid_col, _ = st.columns([1, 2, 1])226with mid_col:227    if st.button("Analyze My Financial Health", use_container_width=True):228        st.session_state.analyzed = True229 230if "analyzed" not in st.session_state:231    st.session_state.analyzed = False232 233 234# ============================================================235# FULL ANALYSIS236# ============================================================237if st.session_state.analyzed:238 239    # Core values240    total_emi      = int(df_loans["emi"].sum())       if not df_loans.empty else 0241    principal_left = int(df_loans["principal"].sum()) if not df_loans.empty else 0242    months_left    = int(df_loans["months"].max())    if not df_loans.empty else 0243    loan_count     = len(df_loans)244    balance        = total_income - total_emi245    monthly_balance = balance246 247    st.divider()248 249    # ----------------------------------------------------------250    # FINANCIAL OVERVIEW — KPI Dashboard251    # ----------------------------------------------------------252    section("", "Financial Overview")253 254    c1, c2, c3, c4 = st.columns(4)255    c1.markdown(f'<div class="kpi">Income<br>{fmt(total_income)}</div>',   unsafe_allow_html=True)256    c2.markdown(f'<div class="kpi">Total EMI<br>{fmt(total_emi)}</div>',   unsafe_allow_html=True)257    c3.markdown(f'<div class="kpi">Balance<br>{fmt(balance)}</div>',        unsafe_allow_html=True)258    c4.markdown(f'<div class="kpi">Loans<br>{loan_count}</div>',            unsafe_allow_html=True)259 260    st.divider()261 262    # ----------------------------------------------------------263    # AI FINANCIAL STRESS PREDICTION264    # ----------------------------------------------------------265    section("", "AI Financial Stress Prediction")266 267    input_data = np.array([[income, extra_income, loan_count, total_emi, principal_left, months_left, savings]])268    pred = model.predict(input_data)269 270    bar = st.progress(0, text="Analyzing with AI model...")271    for i in range(100):272        time.sleep(0.008)273        bar.progress(i + 1, text=f"Analyzing... {i+1}%")274    bar.empty()275 276    if pred[0] == 0:277        st.success("Low Financial Stress")278    elif pred[0] == 1:279        st.warning("Moderate Financial Stress")280    else:281        st.error("High Financial Stress")282 283    st.divider()284 285    # ----------------------------------------------------------286    # DEBT RISK SCORE287    # ----------------------------------------------------------288    section("", "Debt Risk Score")289 290    risk_score = min(int((total_emi / max(total_income, 1)) * 100), 100)291 292    fig_gauge = go.Figure(go.Indicator(293        mode  = "gauge+number+delta",294        value = risk_score,295        title = {"text": "EMI-to-Income Risk %", "font": {"size": 18}},296        delta = {"reference": 40,297                 "increasing": {"color": "red"},298                 "decreasing": {"color": "green"}},299        gauge = {300            "axis":      {"range": [0, 100], "tickwidth": 1},301            "bar":       {"color": "crimson"},302            "steps":     [303                {"range": [0,  35], "color": "#22c55e"},304                {"range": [35, 60], "color": "#eab308"},305                {"range": [60,100], "color": "#ef4444"},306            ],307            "threshold": {"line": {"color": "white", "width": 3}, "value": 40}308        }309    ))310    fig_gauge.update_layout(height=300, margin=dict(t=50, b=0))311    st.plotly_chart(fig_gauge, use_container_width=True)312 313    if risk_score <= 35:314        st.success(f"Healthy — EMI is {risk_score}% of income (Safe zone is 35% or below)")315    elif risk_score <= 60:316        st.warning(f"Moderate — EMI is {risk_score}% of income (Caution zone 35–60%)")317    else:318        st.error(f"Danger — EMI is {risk_score}% of income (Critical above 60%)")319 320    st.divider()321 322    # ----------------------------------------------------------323    # FUTURE BALANCE PROJECTION324    # ----------------------------------------------------------325    section("", "Future Balance Prediction")326 327    years        = st.slider("Prediction Years", 1, 10, 3)328    months_total = years * 12329    cumulative   = float(savings)330    projection   = []331 332    for m in range(1, months_total + 1):333        cumulative += monthly_balance334        projection.append({"Month": m, "Prediction Balance (Rs.)": cumulative})335 336    df_proj  = pd.DataFrame(projection)337    fig_proj = px.area(338        df_proj, x="Month", y="Prediction Balance (Rs.)",339        color_discrete_sequence=["#38bdf8"],340        title=f"Balance Prediction over {years} Year(s)"341    )342    fig_proj.update_layout(343        plot_bgcolor="#0f172a", paper_bgcolor="#0f172a",344        font_color="white", height=350345    )346    st.plotly_chart(fig_proj, use_container_width=True)347 348    total_gain = monthly_balance * months_total349    if monthly_balance >= 0:350        st.success(351            f"SAFE ZONE — Saving {fmt(monthly_balance)}/month — "352            f"{fmt(total_gain)} total in {years} year(s)"353        )354    else:355        st.error(356            f"DANGER ZONE — Deficit {fmt(abs(monthly_balance))}/month — "357            f"Debt grows {fmt(abs(total_gain))} in {years} year(s)"358        )359 360    st.divider()361 362    # ----------------------------------------------------------363    # SECTION 1 — ADDITIONAL PAYMENT SIMULATOR (UPDATED)364    # ----------------------------------------------------------365    section("", "Additional Payment Simulator")366 367    sim_years     = st.slider("Simulation Period (Years)", 1, 10, 3, key="sim_years")368    sim_months    = sim_years * 12369    extra_payment = st.slider("Extra Monthly Payment Toward EMI (Rs.)", 0, 200000, 0, step=500)370 371    if total_emi > 0 and principal_left > 0:372 373        # Amortization — WITHOUT extra payment374        normal_months = 0375        bal = float(principal_left)376        while bal > 0 and normal_months < 99999:377            bal -= total_emi378            normal_months += 1379 380        # Amortization — WITH extra payment381        new_monthly  = total_emi + extra_payment382        extra_months = 0383        bal2         = float(principal_left)384        while bal2 > 0 and extra_months < 99999:385            bal2 -= new_monthly386            extra_months += 1387 388        months_saved = max(0, normal_months - extra_months)389 390        # ── Row 1: Loan Close Time | Time Saved ──391        c1, c2 = st.columns(2)392        c1.metric(393            "Loan Close Time (With Extra Pay)",394            f"{extra_months} months",395            delta=f"-{months_saved} months saved",396            delta_color="inverse"397        )398        c2.metric("Time Saved", f"{months_saved} months")399 400        # ── Row 2: Time Saved Amount — EMI freed + income kept ──401        time_saved_emi_amount    = months_saved * total_emi402        time_saved_income_amount = months_saved * total_income403 404        ts1, ts2 = st.columns(2)405        ts1.metric(406            "Time Saved — EMI Amount Freed",407            fmt(time_saved_emi_amount),408            help="Total EMI payments you avoid due to early closure"409        )410        ts2.metric(411            "Time Saved — Income You Keep",412            fmt(time_saved_income_amount),413            help="Total income in hand during months saved by closing early"414        )415 416        # ── Simulation: month-by-month balance — Normal vs Extra Pay ──417        sim_rows        = []418        bal_normal      = float(savings)419        bal_extra_path  = float(savings)420        loan_bal_normal = float(principal_left)421        loan_bal_extra  = float(principal_left)422 423        for m in range(1, sim_months + 1):424            # Normal path425            if loan_bal_normal > 0:426                loan_bal_normal = max(loan_bal_normal - total_emi, 0)427                bal_normal     += (total_income - total_emi)428            else:429                bal_normal += total_income  # loan done, full income kept430 431            # Extra payment path432            if loan_bal_extra > 0:433                pay_this_month  = min(new_monthly, loan_bal_extra)434                loan_bal_extra  = max(loan_bal_extra - new_monthly, 0)435                bal_extra_path += (total_income - pay_this_month)436            else:437                bal_extra_path += total_income  # loan done, full income kept438 439            sim_rows.append({440                "Month":                   m,441                "Balance Without Extra":   round(bal_normal, 0),442                "Balance With Extra Pay":  round(bal_extra_path, 0),443            })444 445        df_sim = pd.DataFrame(sim_rows)446 447        final_normal = df_sim["Balance Without Extra"].iloc[-1]448        final_extra  = df_sim["Balance With Extra Pay"].iloc[-1]449        balance_diff = final_extra - final_normal450 451        # ── DEBT AVOIDED — actual money saved = balance gained by extra pay ──452        # Debt avoided = what you would have paid in interest/EMI but won't453        debt_avoided = max(454            0,455            (total_emi * normal_months) - (new_monthly * extra_months) - principal_left456        )457        # Real balance gain = what you actually accumulate extra in your hand458        real_balance_gain = balance_diff  # from simulation459 460        st.markdown(f"**Balance Accumulation Over {sim_years} Year(s) — Normal vs Extra Payment**")461 462        fig_sim = px.line(463            df_sim, x="Month",464            y=["Balance Without Extra", "Balance With Extra Pay"],465            title=f"Balance Comparison Over {sim_years} Year(s)",466            color_discrete_map={467                "Balance Without Extra":  "#94a3b8",468                "Balance With Extra Pay": "#38bdf8",469            }470        )471        fig_sim.update_layout(472            plot_bgcolor="#0f172a", paper_bgcolor="#0f172a",473            font_color="white", height=340474        )475        st.plotly_chart(fig_sim, use_container_width=True)476 477        # ── KPI row: Balance Normal | Balance Extra | Debt Avoided (balance gain) ──478        ba1, ba2, ba3 = st.columns(3)479        ba1.metric(f"Balance After {sim_years}Y (Normal)",    fmt(final_normal))480        ba2.metric(f"Balance After {sim_years}Y (Extra Pay)", fmt(final_extra))481        ba3.metric(482            "Debt Avoided (Extra Balance Gained)",483            fmt(real_balance_gain),484            delta=fmt(real_balance_gain),485            delta_color="normal",486            help=(487                f"By paying extra, you close {months_saved} months early. "488                f"Those freed months mean your balance is {fmt(real_balance_gain)} "489                f"higher than without extra payment. Interest/excess saved: {fmt(debt_avoided)}."490            )491        )492 493        if extra_payment > 0:494            sign = "📈 GAIN" if real_balance_gain >= 0 else "📉 LOSS"495            st.info(496                f"{sign} — Extra {fmt(extra_payment)}/month closes loan {months_saved} months early | "497                f"EMI freed: {fmt(time_saved_emi_amount)} | "498                f"Balance gained vs normal path: {fmt(real_balance_gain)} over {sim_years} yr(s)"499            )500    else:501        st.info("Add at least one loan with EMI and Principal to simulate additional payments.")502 503    st.divider()504 505    # ----------------------------------------------------------506    # SECTION 2 — LOAN CLOSE ADVICE507    # ----------------------------------------------------------508    section("", "Loan Close Advice")509 510    if not df_loans.empty and total_income > 0:511        daily_spend        = total_income / 30512        family_expenses    = total_income * 0.40513        personal_lifestyle = total_income * 0.15514        required_total     = total_emi + family_expenses + personal_lifestyle515        extra_needed       = max(0, required_total - total_income)516 517        c1, c2, c3 = st.columns(3)518        c1.metric("Current Total EMI",          fmt(total_emi))519        c2.metric("Daily Spend (monthly basis)", f"{fmt(daily_spend)} per day")520        c3.metric("Extra Needed to Manage",      f"{fmt(extra_needed)} per month")521 522        st.markdown("**Monthly Budget Required to Manage Smoothly:**")523        b1, b2, b3, b4 = st.columns(4)524        b1.metric("EMI Payments",    fmt(total_emi))525        b2.metric("Family (40%)",    fmt(family_expenses))526        b3.metric("Lifestyle (15%)", fmt(personal_lifestyle))527        b4.metric("Total Required",  fmt(required_total))528 529        if extra_needed > 0:530            st.error(f"You need {fmt(extra_needed)} more per month to cover all obligations smoothly.")531        else:532            st.success("Your income comfortably covers EMI, family needs, and lifestyle.")533 534        top_loans = df_loans[df_loans["emi"] > 0].sort_values("emi", ascending=False)535        if not top_loans.empty:536            t = top_loans.iloc[0]537            st.warning(f"Close '{t['name']}' first — EMI relief of {fmt(t['emi'])} per month")538    else:539        st.info("Add loan and income data to see advice.")540 541    st.divider()542 543    # ----------------------------------------------------------544    # SECTION 3 — NET WORTH ANALYSIS (UPDATED)545    # ----------------------------------------------------------546    section("", "Net Worth Analysis")547 548    annual_income       = total_income * 12549    emi_paid_1year      = total_emi * 12550    principal_after_1yr = max(principal_left - emi_paid_1year, 0)551    balance_after_1yr   = savings + (monthly_balance * 12)552    net_gain_loss       = balance_after_1yr - savings553    current_networth    = savings - principal_left554    future_savings      = savings + monthly_balance * months_total555    future_networth     = future_savings - principal_left556 557    st.markdown("**1-Year Financial Summary:**")558    c1, c2, c3, c4 = st.columns(4)559    c1.metric("Total 1-Year Income",  fmt(annual_income))560    c2.metric("EMI Paid in 1 Year",   fmt(emi_paid_1year))561    c3.metric("Balance After 1 Year", fmt(balance_after_1yr))562    c4.metric("Net Gain / Loss",      fmt(net_gain_loss),563              delta=fmt(net_gain_loss), delta_color="normal")564 565    st.markdown("**Long-Term Net Worth (Principal Debt Included):**")566    n1, n2, n3 = st.columns(3)567    n1.metric(568        "Current Net Worth",569        fmt(current_networth),570        help=f"Savings {fmt(savings)} minus Principal Debt {fmt(principal_left)}"571    )572    n2.metric(573        "Net Worth After 1 Year",574        fmt(savings - principal_after_1yr),575        help=f"Principal remaining after 1 year: {fmt(principal_after_1yr)}"576    )577    n3.metric(578        f"Net Worth After {years} Years",579        fmt(future_networth),580        help=f"Projected future savings minus current principal"581    )582 583    st.markdown("**Principal Debt Breakdown:**")584    pd1, pd2, pd3 = st.columns(3)585    pd1.metric("Current Principal Debt",      fmt(principal_left))586    pd2.metric("Principal After 1 Year",      fmt(principal_after_1yr))587    pd3.metric(588        "Principal Reduced in 1 Year",589        fmt(principal_left - principal_after_1yr),590        delta=f"-{fmt(principal_left - principal_after_1yr)}",591        delta_color="inverse"592    )593 594    nw_df = pd.DataFrame({595        "Period":    ["Today", "1 Year", f"{years} Years"],596        "Net Worth": [current_networth, savings - principal_after_1yr, future_networth]597    })598    fig_nw = px.bar(599        nw_df, x="Period", y="Net Worth",600        color="Net Worth",601        color_continuous_scale="Blues",602        title="Net Worth Over Time"603    )604    fig_nw.update_layout(605        plot_bgcolor="#0f172a", paper_bgcolor="#0f172a",606        font_color="white", height=300607    )608    st.plotly_chart(fig_nw, use_container_width=True)609 610    st.divider()611 612    # ----------------------------------------------------------613    # SECTION 4 — LOAN CLOSING STRATEGY614    # ----------------------------------------------------------615    section("", "Loan Closing Strategy")616 617    if not df_loans.empty and total_income > 0:618        df_strat    = df_loans[df_loans["emi"] > 0].copy()619        df_strat    = df_strat.sort_values("emi", ascending=False).reset_index(drop=True)620        emi_limit   = total_income * 0.40621        running_emi = 0622        close_list  = []623        safe_list   = []624 625        for _, row in df_strat.iterrows():626            running_emi += row["emi"]627            if running_emi > emi_limit:628                close_list.append(row)629            else:630                safe_list.append(row)631 632        s1, s2 = st.columns(2)633        s1.metric("Safe EMI Limit (40% of Income)", fmt(emi_limit))634        over_under = "over" if total_emi > emi_limit else "under"635        s2.metric(636            "Your Current Total EMI",637            fmt(total_emi),638            delta=f"{fmt(abs(total_emi - emi_limit))} {over_under} limit",639            delta_color="inverse"640        )641 642        if close_list:643            st.error(644                f"{len(close_list)} loan(s) are pushing you beyond the 40% EMI safety limit. "645                f"Close these first:"646            )647            for row in close_list:648                mo_close     = round(row["principal"] / row["emi"]) if row["emi"] > 0 else 0649                stress_pct   = round((row["emi"] / max(total_emi, 1)) * 100, 1)650                extra_per_mo = max(0, row["emi"] - (emi_limit / max(len(df_strat), 1)))651                st.markdown(f"""652<div class="loan-card">653  <b>{row['name']}</b><br>654  EMI: {fmt(row['emi'])} &nbsp;|&nbsp;655  Principal Left: {fmt(row['principal'])} &nbsp;|&nbsp;656  Close in: ~{mo_close} months &nbsp;|&nbsp;657  Extra needed: {fmt(extra_per_mo)}/month &nbsp;|&nbsp;658  Stress reduction if closed: <b>{stress_pct}%</b>659</div>""", unsafe_allow_html=True)660        else:661            st.success("All loans are within the 40% EMI safety limit.")662 663        if safe_list:664            st.info(f"{len(safe_list)} loan(s) are within the safe EMI range:")665            for row in safe_list:666                st.markdown(f"""667<div class="loan-card">668  <b>{row['name']}</b> — EMI: {fmt(row['emi'])} (Safe — continue paying)669</div>""", unsafe_allow_html=True)670 671        monthly_extra_req = max(0, total_emi - emi_limit)672        if monthly_extra_req > 0:673            st.warning(674                f"You need {fmt(monthly_extra_req)} extra income per month "675                f"OR close high-EMI loans to reach the safe 40% zone."676            )677    else:678        st.info("Add loan and income data for strategy analysis.")679 680    st.divider()681 682    # ----------------------------------------------------------683    # SECTION — LOAN CLOSURE PREDICTION (REDESIGNED)684    # ----------------------------------------------------------685    section("", "Loan Closure Prediction")686 687    st.markdown(688        "This section uses the loans you already entered above. "689        "Select your prediction period and instantly see closure timelines, "690        "principal progress, balance impact, and smart suggestions."691    )692 693    # Use loans from the main Loan Information section (df_loans)694    valid_pred_loans = [695        {696            "name":       row["name"] or f"Loan {i+1}",697            "emi":        row["emi"],698            "principal":  row["principal"],699            "months_rem": row["months"],700        }701        for i, row in df_loans.iterrows()702        if row["emi"] > 0 and row["principal"] > 0703    ]704 705    pred_years_sel    = st.selectbox("Prediction Period", [1, 2, 3, 5], index=0, key="pred_years_sel")706    pred_months_total = pred_years_sel * 12707 708    if valid_pred_loans:709 710        # ── Per-loan closure summary ──711        st.markdown(f"**📋 Loan Closure Summary — {pred_years_sel} Year View:**")712 713        pred_summary_rows = []714        for loan in valid_pred_loans:715            m_to_close = int(loan["months_rem"]) if loan["months_rem"] > 0 \716                         else int(loan["principal"] / loan["emi"])717            closes_within = m_to_close <= pred_months_total718            if closes_within:719                status = f"✅ Closes at Month {m_to_close}"720            else:721                beyond = m_to_close - pred_months_total722                status = f"⚠️ {beyond} months beyond {pred_years_sel}yr"723 724            # Principal remaining after pred_months_total725            months_paid_so_far = min(m_to_close, pred_months_total)726            principal_remaining = max(loan["principal"] - loan["emi"] * months_paid_so_far, 0)727            principal_cleared   = loan["principal"] - principal_remaining728            pct_cleared         = round((principal_cleared / max(loan["principal"], 1)) * 100, 1)729 730            pred_summary_rows.append({731                "Loan":                    loan["name"],732                "Monthly EMI":             fmt(loan["emi"]),733                "Starting Principal":      fmt(loan["principal"]),734                "Months to Close":         m_to_close,735                "Principal Cleared":       fmt(principal_cleared),736                "Principal Remaining":     fmt(principal_remaining),737                "% Cleared":               f"{pct_cleared}%",738                "Status":                  status,739            })740 741        df_pred_table = pd.DataFrame(pred_summary_rows)742        st.dataframe(df_pred_table, use_container_width=True)743 744        # ── Principal Progress bar per loan ──745        st.markdown("** Principal Clearance Progress per Loan:**")746        for row in pred_summary_rows:747            pct_val = float(row["% Cleared"].replace("%", ""))748            colour  = "normal" if pct_val >= 100 else ("normal" if pct_val > 50 else "off")749            st.markdown(f"**{row['Loan']}** — Cleared: {row['Principal Cleared']}  |  Remaining: {row['Principal Remaining']}  ({row['% Cleared']})")750            st.progress(min(int(pct_val), 100))751 752        # ── Month-by-month 12-month rolling prediction ──753        st.markdown("** Month-by-Month 1-Year Prediction (Balance with Loan Closures):**")754 755        running_loans_pred = []756        for loan in valid_pred_loans:757            m_rem = int(loan["months_rem"]) if loan["months_rem"] > 0 \758                    else int(loan["principal"] / loan["emi"])759            running_loans_pred.append({760                "name":       loan["name"],761                "emi":        loan["emi"],762                "bal":        float(loan["principal"]),763                "months_rem": m_rem,764                "closed":     False,765                "closed_at":  None,766            })767 768        cum_balance_pred  = float(savings)769        monthly_pred_rows = []770 771        for m in range(1, 13):772            active_emi_pred = sum(l["emi"] for l in running_loans_pred if not l["closed"])773            net_this_month  = total_income - active_emi_pred774            cum_balance_pred += net_this_month775 776            closed_names_this = []777            for l in running_loans_pred:778                if not l["closed"]:779                    l["bal"]        -= l["emi"]780                    l["months_rem"] -= 1781                    if l["bal"] <= 0 or l["months_rem"] <= 0:782                        l["closed"]    = True783                        l["closed_at"] = m784                        closed_names_this.append(l["name"])785 786            monthly_pred_rows.append({787                "Month":            m,788                "Active EMI":       fmt(active_emi_pred),789                "Net This Month":   fmt(net_this_month),790                "Cumulative Balance": round(cum_balance_pred, 0),791                "Loan(s) Closed":   ", ".join(closed_names_this) if closed_names_this else "—",792            })793 794        df_monthly_pred = pd.DataFrame(monthly_pred_rows)795 796        # display with formatted balance797        df_display = df_monthly_pred.copy()798        df_display["Cumulative Balance"] = df_display["Cumulative Balance"].apply(fmt)799        st.dataframe(df_display, use_container_width=True)800 801        # ── Balance area chart ──802        df_chart_pred = pd.DataFrame({803            "Month":   [r["Month"] for r in monthly_pred_rows],804            "Balance": [r["Cumulative Balance"] for r in monthly_pred_rows],805        })806        fig_pred_bal = px.area(807            df_chart_pred, x="Month", y="Balance",808            color_discrete_sequence=["#38bdf8"],809            title="Predicted Balance Over 12 Months (Loan Closures Factored In)"810        )811        fig_pred_bal.update_layout(812            plot_bgcolor="#0f172a", paper_bgcolor="#0f172a",813            font_color="white", height=320814        )815        st.plotly_chart(fig_pred_bal, use_container_width=True)816 817        # ── 1-Year Summary KPIs ──818        final_balance_pred = df_chart_pred["Balance"].iloc[-1]819        loans_closed_pred  = [l for l in running_loans_pred if l["closed"]]820        loans_open_pred    = [l for l in running_loans_pred if not l["closed"]]821        freed_emi_pred     = sum(l["emi"] for l in loans_closed_pred)822 823        sk1, sk2, sk3, sk4 = st.columns(4)824        sk1.metric("Balance After 1 Year",              fmt(final_balance_pred))825        sk2.metric("Loans Closed Within 1 Year",        str(len(loans_closed_pred)))826        sk3.metric("Loans Still Active After 1 Year",   str(len(loans_open_pred)))827        sk4.metric("Monthly EMI Freed After Closures",  fmt(freed_emi_pred))828 829        # ── Multi-year balance impact with loan-closure waterfall ──830        st.markdown(f"**📈 {pred_years_sel}-Year Balance Projection (Dynamic — Closures Applied):**")831 832        # Re-run full multi-year simulation833        ml2 = []834        for loan in valid_pred_loans:835            m_rem = int(loan["months_rem"]) if loan["months_rem"] > 0 \836                    else int(loan["principal"] / loan["emi"])837            ml2.append({838                "name": loan["name"], "emi": loan["emi"],839                "bal": float(loan["principal"]),840                "months_rem": m_rem, "closed": False841            })842 843        cum2       = float(savings)844        proj_rows  = []845        for m in range(1, pred_months_total + 1):846            act_emi = sum(l["emi"] for l in ml2 if not l["closed"])847            cum2   += (total_income - act_emi)848            for l in ml2:849                if not l["closed"]:850                    l["bal"]        -= l["emi"]851                    l["months_rem"] -= 1852                    if l["bal"] <= 0 or l["months_rem"] <= 0:853                        l["closed"] = True854            proj_rows.append({"Month": m, "Balance": round(cum2, 0)})855 856        df_proj2   = pd.DataFrame(proj_rows)857        start_bal  = float(savings)858        end_bal    = df_proj2["Balance"].iloc[-1]859        delta_bal  = end_bal - start_bal860 861        fig_proj2 = px.area(862            df_proj2, x="Month", y="Balance",863            color_discrete_sequence=["#22c55e" if delta_bal >= 0 else "#ef4444"],864            title=f"{pred_years_sel}-Year Balance Projection with Actual Loan Closure Events"865        )866        fig_proj2.update_layout(867            plot_bgcolor="#0f172a", paper_bgcolor="#0f172a",868            font_color="white", height=340869        )870        st.plotly_chart(fig_proj2, use_container_width=True)871 872        # ── GAIN / LOSS ALERT ──873        gain_label = "📈 BALANCE GAIN" if delta_bal >= 0 else "📉 BALANCE LOSS"874        card_class  = "alert-gain"      if delta_bal >= 0 else "alert-loss"875        st.markdown(f"""876<div class="{card_class}">877  <b>{gain_label} over {pred_years_sel} Year(s)</b><br>878  Starting Balance: {fmt(start_bal)} &nbsp;→&nbsp;879  Ending Balance: {fmt(end_bal)}<br>880  Change: <b>{fmt(delta_bal)}</b>881  {"&nbsp; ✅ Your loans closing early free up cash, boosting your balance!" if delta_bal >= 0882   else "&nbsp; ⚠️ EMI burden exceeds income. Close high-EMI loans to reverse this."}883</div>""", unsafe_allow_html=True)884 885        # ── HOW LONG TO CLOSE TOTAL PRINCIPAL in 1 year ──886        st.markdown("** Principal Closure Timeline & Suggestions:**")887 888        total_principal_all  = sum(l["principal"] for l in valid_pred_loans)889        total_emi_all        = sum(l["emi"] for l in valid_pred_loans)890        months_to_clear_all  = int(total_principal_all / total_emi_all) if total_emi_all > 0 else 999891 892        pc1, pc2 = st.columns(2)893        pc1.metric("Total Principal (All Loans)", fmt(total_principal_all))894        pc2.metric(895            "Estimated Months to Clear All Principal",896            f"{months_to_clear_all} months ({months_to_clear_all/12:.1f} yrs)"897        )898 899        # Suggestion: how much extra to close all within 1 year900        if months_to_clear_all > 12:901            # Extra needed per month so principal clears in 12 months902            extra_to_close_1yr = max(0, int(total_principal_all / 12) - total_emi_all)903            st.markdown(f"""904<div class="info-card">905  💡 <b>Suggestion:</b> To close <b>all principal within 1 year</b>, you need to pay906  an extra <b>{fmt(extra_to_close_1yr)}/month</b> on top of your current EMI of {fmt(total_emi_all)}.907  <br>Total monthly payment required: <b>{fmt(total_emi_all + extra_to_close_1yr)}</b>908</div>""", unsafe_allow_html=True)909        else:910            st.success(f"✅ At current EMI rate, all principal clears within {months_to_clear_all} months — well within 1 year!")911 912        # Per-loan suggestion913        st.markdown("**Per-Loan Closure Suggestions:**")914        for loan in valid_pred_loans:915            m_rem = int(loan["months_rem"]) if loan["months_rem"] > 0 \916                    else int(loan["principal"] / loan["emi"])917            if m_rem > 12:918                extra_needed_1yr = max(0, int(loan["principal"] / 12) - loan["emi"])919                st.markdown(f"""920<div class="info-card">921   <b>{loan['name']}</b>: Closes in {m_rem} months. 922  Pay extra <b>{fmt(extra_needed_1yr)}/month</b> to close within 1 year.923  (Current EMI: {fmt(loan['emi'])} → Required: {fmt(loan['emi'] + extra_needed_1yr)}/month)924</div>""", unsafe_allow_html=True)925            else:926                st.markdown(f"""927<div class="alert-gain">928  ✅ <b>{loan['name']}</b>: Closes in {m_rem} months — within 1 year at current EMI. No extra payment needed.929</div>""", unsafe_allow_html=True)930 931        # ── Loan closure events summary ──932        if loans_closed_pred:933            st.success(934                f"Loans closing within 1 year: "935                f"{', '.join(l['name'] for l in loans_closed_pred)} — "936                f"EMI freed: {fmt(freed_emi_pred)}/month after closure"937            )938        if loans_open_pred:939            st.info(940                f"Loans still active after 1 year: "941                f"{', '.join(l['name'] for l in loans_open_pred)}"942            )943 944    else:945        st.info(946            "Add loans in the **Loan Information** section above (EMI + Principal required) "947            "to see closure predictions here."948        )949 950    st.divider()951 952    # ----------------------------------------------------------953    # SECTION 5 — EXTRA INCOME SUGGESTIONS954    # ----------------------------------------------------------955    section("", "Extra Income Suggestions")956 957    family_expenses     = total_income * 0.40958    personal_lifestyle  = total_income * 0.15959    required_total      = total_emi + family_expenses + personal_lifestyle960    extra_income_needed = max(0, required_total - total_income)961 962    st.markdown("**Monthly Obligation Breakdown:**")963    e1, e2, e3, e4 = st.columns(4)964    e1.metric("EMI Total",       fmt(total_emi))965    e2.metric("Family (40%)",    fmt(family_expenses))966    e3.metric("Lifestyle (15%)", fmt(personal_lifestyle))967    e4.metric("Total Required",  fmt(required_total))968 969    if extra_income_needed > 0:970        st.error(f"You need {fmt(extra_income_needed)} extra per month to cover all obligations.")971        allocations = [972            ("Freelancing / Consulting",            0.40),973            ("Online Business (Flipkart / Amazon)",  0.20),974            ("Teaching / Online Courses",            0.15),975            ("Investments (Index Funds / Stocks)",   0.15),976            ("Content Creation / YouTube",           0.10),977        ]978        st.markdown("**Income Target Allocation Plan:**")979        for label, share in allocations:980            target = round(extra_income_needed * share)981            st.markdown(982                f'<div class="info-card">'983                f'<b>{label}</b> — Target: {fmt(target)}/month ({int(share*100)}% of extra needed)'984                f'</div>',985                unsafe_allow_html=True986            )987    else:988        surplus = total_income - required_total989        st.success(f"Income covers all obligations. Monthly surplus: {fmt(surplus)}")990        st.write(f"Total obligations: {fmt(required_total)}  |  Your income: {fmt(total_income)}")991 992    st.divider()993 994    # ----------------------------------------------------------995    # STRESS SOURCE DETECTION996    # ----------------------------------------------------------997    section("", "Stress Source Detection")998 999    if not df_loans.empty:1000        stress_df = df_loans[df_loans["emi"] > 0].sort_values("emi", ascending=False)1001        if not stress_df.empty:1002            top = stress_df.iloc[0]1003            pct = round((top["emi"] / max(total_emi, 1)) * 100, 1)1004            st.error(1005                f"Highest stress loan: {top['name']} — "1006                f"EMI {fmt(top['emi'])} ({pct}% of total EMI)"1007            )1008            fig_pie = px.pie(1009                df_loans[df_loans["emi"] > 0],1010                values="emi", names="name",1011                title="EMI Distribution Across Loans",1012                color_discrete_sequence=px.colors.sequential.Blues_r1013            )1014            fig_pie.update_layout(1015                paper_bgcolor="#0f172a", font_color="white", height=3201016            )1017            st.plotly_chart(fig_pie, use_container_width=True)1018    else:1019        st.info("No loan data to detect stress source.")1020 1021    st.divider()1022 1023    # ----------------------------------------------------------1024    # AI FINANCIAL ADVISOR1025    # ----------------------------------------------------------1026    section("", "AI Financial Advisor")1027 1028    q = st.text_input(1029        "Ask a finance question:",1030        placeholder="e.g. How can I reduce my loan burden?"1031    )1032    if q:1033        ql = q.lower()1034        if any(w in ql for w in ["loan", "emi", "debt", "close", "pay"]):1035            st.write(1036                "Focus on clearing the highest-EMI loan first to reduce financial stress fastest. "1037                "Even an extra Rs. 1,000–2,000 per month toward the principal accelerates payoff."1038            )1039        elif any(w in ql for w in ["save", "saving", "savings", "emergency"]):1040            st.write(1041                "Build an emergency fund of 6 months of expenses before aggressive investing. "1042                "Keep it in a liquid fund or high-interest savings account."1043            )1044        elif any(w in ql for w in ["invest", "investment", "stock", "mutual", "fund"]):1045            st.write(1046                "Start with index funds (Nifty 50 / Sensex) for stable long-term growth. "1047                "Invest at least 10–15% of income monthly via SIP once EMI is under control."1048            )1049        elif any(w in ql for w in ["income", "earn", "salary", "extra"]):1050            st.write(1051                "Focus on one high-return side income stream first. Freelancing or consulting "1052                "in your primary skill is the fastest way to add Rs. 5,000–20,000 per month."1053            )1054        elif any(w in ql for w in ["budget", "plan", "manage", "spend"]):1055            st.write(1056                "Follow the 50/30/20 rule: 50% for needs (EMI + family), "1057                "30% for wants, 20% for savings and investments. "1058                "Automate EMI payments to avoid penalties."1059            )1060        else:1061            st.write(1062                "General rule: Keep total EMI below 40% of income. "1063                "Maintain 6 months emergency fund. "1064                "Invest 15% or more in diversified instruments once debt is managed."1065            )1066 1067    st.divider()1068 1069    # ----------------------------------------------------------1070    # SECTION 6 — ANALYSIS HISTORY & DELETE1071    # ----------------------------------------------------------1072    section("️", "Analysis History")1073 1074    history = load_history()1075 1076    if st.button("Save Current Analysis to History"):1077        entry = {1078            "id":             str(int(time.time())),1079            "date":           datetime.now().strftime("%d %b %Y, %H:%M"),1080            "income":         total_income,1081            "total_emi":      total_emi,1082            "balance":        balance,1083            "principal_left": principal_left,1084            "loan_count":     loan_count,1085            "risk_score":     risk_score,1086            "loans":          loan_data1087        }1088        history.append(entry)1089        save_history(history)1090        st.success("Analysis saved successfully!")1091        st.rerun()1092 1093    if history:1094        st.markdown(f"**{len(history)} saved record(s):**")1095        for entry in reversed(history):1096            label = (1097                f"{entry['date']}  |  "1098                f"Income: {fmt(entry['income'])}  |  "1099                f"EMI: {fmt(entry['total_emi'])}  |  "1100                f"Risk: {entry['risk_score']}%"1101            )1102            with st.expander(label):1103                h1, h2, h3, h4 = st.columns(4)1104                h1.metric("Income",     fmt(entry['income']))1105                h2.metric("Total EMI",  fmt(entry['total_emi']))1106                h3.metric("Balance",    fmt(entry['balance']))1107                h4.metric("Risk Score", f"{entry['risk_score']}%")1108                st.write(1109                    f"Loans: {entry['loan_count']}  |  "1110                    f"Principal Left: {fmt(entry['principal_left'])}"1111                )1112                if st.button("Delete this record", key=f"del_{entry['id']}"):1113                    history = [h for h in history if h["id"] != entry["id"]]1114                    save_history(history)1115                    st.warning("Record deleted.")1116                    st.rerun()1117    else:1118        st.info("No saved analyses yet. Click 'Save Current Analysis' above after analyzing.")1119 1120    st.divider()1121 1122    # ----------------------------------------------------------1123    # SECTION 7 — QUICK CALCULATOR1124    # ----------------------------------------------------------1125    section("", "Quick Calculator")1126 1127    tab_emi, tab_savings, tab_invest = st.tabs([1128        "EMI Calculator",1129        "Savings Goal",1130        "Investment Return (SIP)"1131    ])1132 1133    with tab_emi:1134        st.markdown("**Calculate EMI for any loan instantly**")1135        t1c1, t1c2, t1c3 = st.columns(3)1136        q_principal = t1c1.number_input("Loan Amount (Rs.)",         min_value=0,   value=500000, step=10000, key="qc_p")1137        q_rate      = t1c2.number_input("Annual Interest Rate (%)",  min_value=0.0, value=10.0,   step=0.1,   key="qc_r")1138        q_tenure    = t1c3.number_input("Tenure (Months)",           min_value=1,   value=60,                key="qc_t")1139        if q_principal > 0 and q_rate > 0 and q_tenure > 0:1140            r         = q_rate / (12 * 100)1141            emi_calc  = q_principal * r * (1 + r)**q_tenure / ((1 + r)**q_tenure - 1)1142            total_pay = emi_calc * q_tenure1143            total_int = total_pay - q_principal1144            rc1, rc2, rc3 = st.columns(3)1145            rc1.metric("Monthly EMI",    fmt(emi_calc))1146            rc2.metric("Total Interest", fmt(total_int))1147            rc3.metric("Total Payment",  fmt(total_pay))1148            fig_ep = px.pie(1149                values=[q_principal, total_int],1150                names=["Principal", "Interest"],1151                color_discrete_sequence=["#38bdf8", "#ef4444"],1152                title="Principal vs Interest Split"1153            )1154            fig_ep.update_layout(paper_bgcolor="#0f172a", font_color="white", height=280)1155            st.plotly_chart(fig_ep, use_container_width=True)1156 1157    with tab_savings:1158        st.markdown("**How long to reach your savings target?**")1159        sg1, sg2 = st.columns(2)1160        goal_amount  = sg1.number_input("Target Savings (Rs.)",  min_value=0, value=1000000, step=50000, key="sg_g")1161        monthly_save = sg2.number_input("Monthly Saving (Rs.)",  min_value=0, value=10000,   step=1000,  key="sg_m")1162        if monthly_save > 0 and goal_amount > 0:1163            months_to_goal = goal_amount / monthly_save1164            st.metric(1165                "Time to Reach Goal",1166                f"{months_to_goal:.0f} months  ({months_to_goal/12:.1f} years)"1167            )1168            progress_pct = min(int((savings / goal_amount) * 100), 100)1169            st.write(f"Current savings: {fmt(savings)} — {progress_pct}% of goal reached")1170            st.progress(progress_pct)1171 1172    with tab_invest:1173        st.markdown("**SIP compound return calculator**")1174        ir1, ir2, ir3 = st.columns(3)1175        inv_amount = ir1.number_input("Monthly Investment (Rs.)",    min_value=0,   value=5000,  step=500,  key="ir_a")1176        inv_rate   = ir2.number_input("Expected Annual Return (%)",  min_value=0.0, value=12.0,  step=0.5,  key="ir_r")1177        inv_years  = ir3.number_input("Investment Period (Years)",   min_value=1,   value=10,              key="ir_y")1178        if inv_amount > 0 and inv_rate > 0:1179            r_m      = inv_rate / (12 * 100)1180            n_m      = inv_years * 121181            fv       = inv_amount * ((1 + r_m)**n_m - 1) / r_m * (1 + r_m)1182            invested = inv_amount * n_m1183            ret      = fv - invested1184            i1, i2, i3 = st.columns(3)1185            i1.metric("Total Invested",    fmt(invested))1186            i2.metric("Estimated Returns", fmt(ret))1187            i3.metric("Future Value",      fmt(fv))1188            sip_rows = []1189            cum = 0.0; inv_cum = 0.01190            for m in range(1, n_m + 1):1191                cum     = cum * (1 + r_m) + inv_amount1192                inv_cum += inv_amount1193                sip_rows.append({"Month": m, "Portfolio Value": cum, "Amount Invested": inv_cum})1194            df_sip = pd.DataFrame(sip_rows)1195            fig_sip = px.line(1196                df_sip, x="Month", y=["Portfolio Value", "Amount Invested"],1197                title=f"SIP Growth over {inv_years} Year(s)",1198                color_discrete_map={"Portfolio Value": "#38bdf8", "Amount Invested": "#94a3b8"}1199            )1200            fig_sip.update_layout(

Showing the first 1,200 of 1253 lines. Download the file for the rest.