Spaces:
Build error
Build error
File size: 8,324 Bytes
a282d4b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | 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
}
|