Bankbot / backend /app /ai /coaching.py
mohsin-devs's picture
Deploy to HF Space
7a30d73
Raw
History Blame Contribute Delete
10.8 kB
import os
from datetime import datetime, timedelta
from sqlalchemy.orm import Session
from app.database.models import User, Account, Transaction, Goal, Investment, Subscription
from app.ai.forecasting import get_cashflow_metrics
from app.ai.ollama_integration import get_ai_response
def calculate_financial_health_score(db: Session, user_id: str):
"""
Computes a multi-dimensional Financial Health Score (0-100) based on real database records.
"""
accounts = db.query(Account).filter(Account.user_id == user_id).all()
total_balance = sum(acc.balance for acc in accounts)
savings_balance = sum(acc.balance for acc in accounts if acc.type.lower() == "savings")
# Cashflow
current_balance, daily_income, daily_spending = get_cashflow_metrics(db, user_id)
monthly_income = max(1000.0, daily_income * 30.4)
monthly_spending = daily_spending * 30.4
# 1. Savings Consistency (20 pts)
# Check frequency of saving transactions or goal additions
txns = db.query(Transaction).join(Account).filter(
Account.user_id == user_id,
Transaction.type == "credit",
Transaction.category == "Income"
).count()
# Let's say if they have active goals with current_amount > 0, they get higher points
goals = db.query(Goal).filter(Goal.user_id == user_id).all()
goal_savings = sum(g.current_amount for g in goals)
savings_score = 10.0
if goal_savings > 1000:
savings_score += 10.0
elif goal_savings > 0:
savings_score += 5.0
# 2. Debt Ratio (20 pts)
# Estimate EMIs or goals with "debt"
debt_goals = sum(g.target_amount - g.current_amount for g in goals if "debt" in g.title.lower() or "loan" in g.title.lower())
# Standard monthly debt service (estimate 10% of debt or $150 minimum if debt exists)
est_monthly_debt = max(0.0, debt_goals * 0.05)
debt_to_income = est_monthly_debt / monthly_income
debt_score = 20.0
if debt_to_income > 0.40:
debt_score = 5.0
elif debt_to_income > 0.20:
debt_score = 12.0
elif debt_to_income > 0.05:
debt_score = 18.0
# 3. Spending Discipline (20 pts)
# Ratio of monthly spending to monthly income
savings_rate = (monthly_income - monthly_spending) / monthly_income if monthly_income > 0 else 0
discipline_score = 10.0
if savings_rate >= 0.30:
discipline_score = 20.0
elif savings_rate >= 0.15:
discipline_score = 16.0
elif savings_rate >= 0.0:
discipline_score = 12.0
# 4. Emergency Fund (20 pts)
# Do they have 3-6 months of expenses in savings?
monthly_expenses = max(500.0, monthly_spending)
months_buffer = savings_balance / monthly_expenses
emergency_score = 0.0
if months_buffer >= 6.0:
emergency_score = 20.0
elif months_buffer >= 3.0:
emergency_score = 15.0
elif months_buffer >= 1.0:
emergency_score = 8.0
# 5. Investment Index (10 pts)
investments = db.query(Investment).filter(Investment.user_id == user_id).all()
inv_total = sum(i.current_value for i in investments)
investment_score = 0.0
if inv_total > 5000:
investment_score = 10.0
elif inv_total > 0:
investment_score = 6.0
# 6. Subscription Efficiency (10 pts)
subs = db.query(Subscription).filter(Subscription.user_id == user_id, Subscription.active == True).all()
sub_cost = sum(s.amount if s.billing_cycle.lower() == "monthly" else (s.amount / 12) for s in subs)
sub_ratio = sub_cost / monthly_income
sub_score = 10.0
if sub_ratio > 0.10: # More than 10% of income on subscriptions
sub_score = 3.0
elif sub_ratio > 0.05: # More than 5%
sub_score = 7.0
# Calculate Overall Score
overall_score = savings_score + debt_score + discipline_score + emergency_score + investment_score + sub_score
overall_score = min(100.0, max(0.0, overall_score))
# Actionable improvements list
improvements = []
if savings_score < 15:
improvements.append("Set up automated transfers to your Savings account right after payday.")
if debt_score < 15:
improvements.append("Prioritize high-interest debt payoffs using the debt avalanche method.")
if discipline_score < 15:
improvements.append("Discretionary spending (shopping & dining) is high. Try implementing a $50 weekly limit.")
if emergency_score < 15:
improvements.append(f"Build savings buffer. Try to accumulate at least ${monthly_expenses * 3:,.2f} (3 months of expenses).")
if investment_score < 6:
improvements.append("Start a low-cost stock index fund SIP to counter inflation.")
if sub_score < 8:
improvements.append("Conduct an audit of active subscriptions. Cancel duplicate/unused memberships.")
if not improvements:
improvements.append("Maintain your current financial habits; your portfolio is highly optimized!")
# AI Explanation
user = db.query(User).filter(User.id == user_id).first()
persona = user.financial_personality if user else "Saver"
ai_prompt = f"""
The user is a '{persona}' with a Financial Health Score of {overall_score:.0f}/100.
Sub-scores:
- Savings Consistency: {savings_score:.0f}/20
- Debt Management: {debt_score:.0f}/20
- Spending Discipline: {discipline_score:.0f}/20
- Emergency Fund: {emergency_score:.0f}/20
- Investment Allocation: {investment_score:.0f}/10
- Subscription Management: {sub_score:.0f}/10
Write a concise, professional financial analyst explanation of this score. Detail the primary strengths and key weaknesses.
Do NOT write a generic chatbot reply. Keep it to 3-4 sentences. Format like a Bloomberg analyst report.
"""
from app.ai.ollama_integration import has_active_ai_backend
explanation = None
if has_active_ai_backend():
try:
# Hard 8-second timeout so the health score endpoint never hangs
import threading
result = [None]
def _call():
result[0] = get_ai_response(ai_prompt)
t = threading.Thread(target=_call, daemon=True)
t.start()
t.join(timeout=8)
explanation = result[0]
except Exception:
pass
if not explanation:
explanation = f"As a {persona}, your financial health score of {overall_score:.0f} reflects solid fundamentals with opportunities to optimize emergency allocations and subscription efficiencies. Focus on automating savings and expanding investments."
return {
"overall_score": round(overall_score, 0),
"categories": {
"savings_consistency": {"score": round(savings_score, 0), "max": 20},
"debt_ratio": {"score": round(debt_score, 0), "max": 20},
"spending_discipline": {"score": round(discipline_score, 0), "max": 20},
"emergency_funds": {"score": round(emergency_score, 0), "max": 20},
"investments": {"score": round(investment_score, 0), "max": 10},
"subscription_management": {"score": round(sub_score, 0), "max": 10}
},
"explanation": explanation,
"actionable_improvements": improvements
}
def generate_daily_briefing(db: Session, user_id: str):
"""
Pulls complete financial context and generates a personalized daily financial briefing.
"""
user = db.query(User).filter(User.id == user_id).first()
if not user:
return {"briefing": "User not found."}
# Collect data
accounts = db.query(Account).filter(Account.user_id == user_id).all()
total_balance = sum(acc.balance for acc in accounts)
goals = db.query(Goal).filter(Goal.user_id == user_id).all()
goals_summary = [f"{g.title}: {g.current_amount}/{g.target_amount}" for g in goals]
investments = db.query(Investment).filter(Investment.user_id == user_id).all()
inv_summary = [f"{i.asset_name} ({i.type}): Current Value ${i.current_value:,.2f}" for i in investments]
# Cashflow
current_balance, daily_income, daily_spending = get_cashflow_metrics(db, user_id)
monthly_income = daily_income * 30.4
monthly_spending = daily_spending * 30.4
# Format AI Prompt
ai_prompt = f"""
You are an AI Wealth Advisor and Predictive Banking Engine. Generate a personalized daily financial briefing for {user.profile_data.get('name', 'User')}.
Financial Summary:
- User Personality: {user.financial_personality}
- Total Account Balance: ${total_balance:,.2f}
- Estimated Monthly Income: ${monthly_income:,.2f}
- Estimated Monthly Spending: ${monthly_spending:,.2f}
- Active Goals: {', '.join(goals_summary) if goals_summary else 'None'}
- Investments: {', '.join(inv_summary) if inv_summary else 'None'}
Generate a 3-paragraph daily briefing.
Paragraph 1: Summary of their current liquidity and portfolio health.
Paragraph 2: One specific recommendation regarding their savings goals or investment potential.
Paragraph 3: A behavioral spending insight warning based on their spending velocity.
Style: Bloomberg Terminal style, highly intelligent, concise, financially meaningful, human-like.
Avoid boilerplate generic remarks (e.g. 'You should try saving more money'). Use exact figures.
"""
from app.ai.ollama_integration import has_active_ai_backend
briefing = None
if has_active_ai_backend():
try:
import threading
result = [None]
def _call():
result[0] = get_ai_response(ai_prompt)
t = threading.Thread(target=_call, daemon=True)
t.start()
t.join(timeout=10)
briefing = result[0]
except Exception:
pass
if not briefing:
briefing = f"DAILY BRIEFING:\n\nYour liquid capital stands at ${total_balance:,.2f}. Portfolio indicators suggest regular cashflow velocity. Based on your {user.financial_personality} profile, we advise dedicating a portion of your net surplus to your active goals to optimize compound growth. Avoid non-essential weekend dining and retail spikes to maintain your target trajectory."
return {
"date": datetime.utcnow().strftime("%Y-%m-%d"),
"user_name": user.profile_data.get('name', 'User'),
"briefing": briefing,
"metrics": {
"total_liquid_capital": round(total_balance, 2),
"monthly_income_projection": round(monthly_income, 2),
"monthly_burn_rate": round(monthly_spending, 2)
}
}