Spaces:
Build error
Build error
| from sqlalchemy.orm import Session | |
| from app.database.models import Account, Goal, Investment, Subscription | |
| from app.ai.forecasting import get_cashflow_metrics | |
| def simulate_purchase_impact(db: Session, user_id: str, amount: float, category: str, merchant: str): | |
| """ | |
| Simulates buying a large asset or item (e.g. a car) and assesses risk. | |
| """ | |
| accounts = db.query(Account).filter(Account.user_id == user_id).all() | |
| total_balance = sum(acc.balance for acc in accounts) | |
| checking_acc = next((a for a in accounts if a.type.lower() == "checking"), None) | |
| # Target emergency fund amount | |
| goals = db.query(Goal).filter(Goal.user_id == user_id).all() | |
| emergency_goal = next((g for g in goals if "emergency" in g.title.lower()), None) | |
| emergency_threshold = emergency_goal.target_amount if emergency_goal else 3000.0 | |
| new_balance = total_balance - amount | |
| # Cashflow metrics | |
| _, daily_income, daily_spending = get_cashflow_metrics(db, user_id) | |
| monthly_net = (daily_income - daily_spending) * 30.4 | |
| # Risk Analysis | |
| risk_level = "low" | |
| reasons = [] | |
| if amount > total_balance: | |
| risk_level = "critical" | |
| reasons.append("Purchase exceeds your total available balance, requiring debt.") | |
| elif new_balance < emergency_threshold: | |
| risk_level = "high" | |
| reasons.append(f"This purchase depletes your emergency buffer (threshold of ${emergency_threshold:,.2f}).") | |
| elif amount > total_balance * 0.3: | |
| risk_level = "medium" | |
| reasons.append("Single purchase consumes more than 30% of your total liquid cash.") | |
| if monthly_net < 0 and amount > 500: | |
| risk_level = "high" | |
| reasons.append("You have a negative monthly cashflow; making large purchases increases financial strain.") | |
| # Recommendations | |
| recommendation = "" | |
| if risk_level == "critical": | |
| recommendation = "❌ Strongly advise against this purchase. Consider financing options, delaying, or establishing a dedicated goal." | |
| elif risk_level == "high": | |
| recommendation = "⚠️ Refrain from this purchase if possible. Rebuilding your emergency fund should be prioritized." | |
| elif risk_level == "medium": | |
| recommendation = "💡 Proceed with caution. Consider trimming discretionary expenses next month to offset the cost." | |
| else: | |
| recommendation = "✅ Purchase is safe. It fits within your financial profile without impacting key safety buffers." | |
| return { | |
| "purchase_amount": amount, | |
| "merchant": merchant, | |
| "category": category, | |
| "current_balance": round(total_balance, 2), | |
| "projected_balance": round(max(0.0, new_balance), 2), | |
| "savings_impact": { | |
| "immediate_reduction": round(amount, 2), | |
| "emergency_buffer_breached": new_balance < emergency_threshold, | |
| "emergency_threshold": round(emergency_threshold, 2) | |
| }, | |
| "risk_analysis": { | |
| "risk_level": risk_level, | |
| "reasons": reasons | |
| }, | |
| "recommendation": recommendation | |
| } | |
| def simulate_investment_impact(db: Session, user_id: str, monthly_sip: float, asset_type: str, lump_sum: float = 0.0): | |
| """ | |
| Simulates investment growth and evaluates opportunity cost. | |
| """ | |
| # Expected annual returns based on asset type | |
| returns_map = { | |
| "stock": 0.10, # 10% | |
| "crypto": 0.20, # 20% | |
| "mutual_fund": 0.08, # 8% | |
| "fd": 0.05, # 5% | |
| "bond": 0.04 # 4% | |
| } | |
| apr = returns_map.get(asset_type.lower(), 0.07) | |
| # Calculate current balance | |
| accounts = db.query(Account).filter(Account.user_id == user_id).all() | |
| total_balance = sum(acc.balance for acc in accounts) | |
| # Cashflow metrics | |
| _, daily_income, daily_spending = get_cashflow_metrics(db, user_id) | |
| monthly_net = (daily_income - daily_spending) * 30.4 | |
| # Check if SIP is affordable | |
| is_affordable = monthly_net >= monthly_sip | |
| growth_projection = [] | |
| current_value = lump_sum | |
| total_invested = lump_sum | |
| # 5-year monthly projection | |
| for month in range(0, 61): | |
| if month > 0: | |
| current_value = (current_value + monthly_sip) * (1 + apr / 12) | |
| total_invested += monthly_sip | |
| if month in [12, 36, 60]: # Save 1, 3, 5 year markers | |
| growth_projection.append({ | |
| "year": month // 12, | |
| "total_invested": round(total_invested, 2), | |
| "future_value": round(current_value, 2), | |
| "earnings": round(max(0.0, current_value - total_invested), 2) | |
| }) | |
| risk_level = "low" | |
| if asset_type.lower() == "crypto": | |
| risk_level = "high" | |
| elif asset_type.lower() in ["stock", "mutual_fund"] and monthly_sip > monthly_net * 0.5: | |
| risk_level = "medium" | |
| recommendation = "" | |
| if not is_affordable: | |
| recommendation = f"⚠️ Your monthly net surplus (${monthly_net:,.2f}) is lower than the planned SIP (${monthly_sip:,.2f}). This may lead to checking overdrafts." | |
| else: | |
| recommendation = f"✅ Excellent choice. Investing ${monthly_sip:,.2f} monthly in {asset_type} is fully supported by your net cashflow." | |
| return { | |
| "asset_type": asset_type, | |
| "monthly_sip": round(monthly_sip, 2), | |
| "lump_sum": round(lump_sum, 2), | |
| "is_affordable": is_affordable, | |
| "growth_projection": growth_projection, | |
| "risk_analysis": { | |
| "risk_level": risk_level, | |
| "expected_annual_return": apr | |
| }, | |
| "savings_impact": { | |
| "opportunity_cost_yearly": round(monthly_sip * 12, 2), | |
| "monthly_surplus_retaining": round(max(0.0, monthly_net - monthly_sip), 2) | |
| }, | |
| "recommendation": recommendation | |
| } | |
| def simulate_subscription_cancellation(db: Session, user_id: str, subscription_ids: list): | |
| """ | |
| Simulates the financial benefit of cancelling one or more subscriptions. | |
| """ | |
| subs = db.query(Subscription).filter( | |
| Subscription.user_id == user_id, | |
| Subscription.id.in_(subscription_ids) | |
| ).all() | |
| if not subs: | |
| return {"message": "No matching subscriptions found for cancellation simulation."} | |
| monthly_savings = 0.0 | |
| yearly_savings = 0.0 | |
| cancelled_details = [] | |
| for sub in subs: | |
| cost = sub.amount | |
| is_monthly = sub.billing_cycle.lower() == "monthly" | |
| m_cost = cost if is_monthly else (cost / 12) | |
| y_cost = (cost * 12) if is_monthly else cost | |
| monthly_savings += m_cost | |
| yearly_savings += y_cost | |
| cancelled_details.append({ | |
| "id": sub.id, | |
| "merchant": sub.merchant, | |
| "amount": sub.amount, | |
| "billing_cycle": sub.billing_cycle | |
| }) | |
| # Relate savings to user's Goals | |
| goals = db.query(Goal).filter(Goal.user_id == user_id).all() | |
| first_goal = goals[0] if goals else None | |
| goal_impact = None | |
| if first_goal: | |
| months_saved = 0.0 | |
| remaining_needed = max(0.0, first_goal.target_amount - first_goal.current_amount) | |
| if monthly_savings > 0: | |
| months_saved = remaining_needed / (remaining_needed / 12 if remaining_needed > 0 else 1) # simple logic | |
| # Let's say if they direct this money to goal, it reduces target time by: | |
| months_saved = (remaining_needed / monthly_savings) if remaining_needed > 0 else 0 | |
| goal_impact = { | |
| "goal_title": first_goal.title, | |
| "target_amount": round(first_goal.target_amount, 2), | |
| "months_to_reach_with_savings": round(months_saved, 1) if monthly_savings > 0 else 0 | |
| } | |
| # Recommendations | |
| recommendation = f"Cancelling these subscriptions yields ${monthly_savings:,.2f} per month (${yearly_savings:,.2f} annually). Reinvesting these funds into high-yield savings or mutual funds is recommended." | |
| return { | |
| "cancelled_subscriptions": cancelled_details, | |
| "monthly_savings": round(monthly_savings, 2), | |
| "yearly_savings": round(yearly_savings, 2), | |
| "goal_impact": goal_impact, | |
| "recommendation": recommendation | |
| } | |