💰 MoneyMate
Your Personal Financial Assistant for Smart Money Management
import gradio as gr import requests import json import plotly.graph_objects as go import plotly.express as px from fastapi import FastAPI from typing import Dict, Any, Optional import os from datetime import datetime # Custom CSS for MoneyMate branding CUSTOM_CSS = """ .gradio-container { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); font-family: 'Inter', sans-serif; } .main-header { text-align: center; color: white; margin-bottom: 2rem; } .money-card { background: rgba(255, 255, 255, 0.95); border-radius: 15px; padding: 1.5rem; margin: 1rem 0; box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37); backdrop-filter: blur(4px); border: 1px solid rgba(255, 255, 255, 0.18); } .modal-branding { background: linear-gradient(45deg, #ff6b6b, #4ecdc4); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight: bold; text-align: center; margin-top: 1rem; } .advice-box { background: #f8f9ff; border-left: 4px solid #667eea; padding: 1rem; margin: 1rem 0; border-radius: 8px; } .quick-action-btn { background: linear-gradient(45deg, #667eea, #764ba2); color: white; border: none; border-radius: 25px; padding: 0.5rem 1rem; margin: 0.25rem; cursor: pointer; transition: all 0.3s ease; } .quick-action-btn:hover { transform: translateY(-2px); box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); } """ # MCP Server configuration class MCPServer: def __init__(self): self.tools = { "salary_breakdown": self.salary_breakdown, "investment_advice": self.investment_advice, "expense_analysis": self.expense_analysis, "savings_goal": self.savings_goal } def salary_breakdown(self, salary: float, expenses: Dict[str, float]) -> Dict[str, Any]: """Break down salary using 50/30/20 rule with Indian context""" needs = salary * 0.5 # 50% for needs wants = salary * 0.3 # 30% for wants savings = salary * 0.2 # 20% for savings/investments return { "breakdown": { "needs": needs, "wants": wants, "savings": savings }, "recommendations": self._get_indian_recommendations(salary) } def investment_advice(self, age: int, salary: float, risk_appetite: str) -> Dict[str, Any]: """Provide investment advice for Indian market""" equity_percentage = min(100 - age, 80) # Age-based equity allocation debt_percentage = 100 - equity_percentage return { "allocation": { "equity": equity_percentage, "debt": debt_percentage }, "instruments": self._get_indian_instruments(risk_appetite) } def expense_analysis(self, expenses: Dict[str, float]) -> Dict[str, Any]: """Analyze expenses and provide optimization suggestions""" total_expenses = sum(expenses.values()) analysis = {} for category, amount in expenses.items(): percentage = (amount / total_expenses) * 100 analysis[category] = { "amount": amount, "percentage": percentage, "status": self._categorize_expense(category, percentage) } return {"analysis": analysis, "suggestions": self._get_optimization_tips()} def savings_goal(self, goal_amount: float, timeline_months: int, current_savings: float) -> Dict[str, Any]: """Calculate monthly savings needed for a goal""" remaining_amount = goal_amount - current_savings monthly_required = remaining_amount / timeline_months if timeline_months > 0 else 0 return { "monthly_required": monthly_required, "total_goal": goal_amount, "timeline": timeline_months, "feasibility": "achievable" if monthly_required < 15000 else "challenging" } def _get_indian_recommendations(self, salary: float) -> list: """Get India-specific financial recommendations""" recommendations = [ "Build emergency fund of 6-12 months expenses", "Start SIP in diversified equity mutual funds", "Consider ELSS funds for tax saving under 80C", "Open PPF account for long-term tax-free returns" ] if salary > 50000: recommendations.append("Consider NPS for additional retirement planning") if salary > 100000: recommendations.append("Explore direct equity investment after gaining knowledge") return recommendations def _get_indian_instruments(self, risk_appetite: str) -> list: """Get Indian investment instruments based on risk appetite""" instruments = { "conservative": ["PPF", "NSC", "FD", "Debt Mutual Funds"], "moderate": ["Balanced Mutual Funds", "ELSS", "Gold ETF", "Corporate Bonds"], "aggressive": ["Large Cap Funds", "Mid Cap Funds", "Small Cap Funds", "Direct Equity"] } return instruments.get(risk_appetite.lower(), instruments["moderate"]) def _categorize_expense(self, category: str, percentage: float) -> str: """Categorize expense as optimal, high, or low""" thresholds = { "rent": (25, 35), "food": (15, 25), "transport": (10, 15), "utilities": (5, 10), "entertainment": (5, 15) } if category.lower() in thresholds: low, high = thresholds[category.lower()] if percentage < low: return "low" elif percentage > high: return "high" return "optimal" def _get_optimization_tips(self) -> list: """Get expense optimization tips""" return [ "Use public transport or carpool to reduce transport costs", "Cook at home more often to save on food expenses", "Use energy-efficient appliances to reduce utility bills", "Set a monthly entertainment budget and stick to it", "Review and cancel unused subscriptions" ] # Initialize MCP Server mcp_server = MCPServer() # Modal backend URL (replace with your actual Modal deployment URL) MODAL_BACKEND_URL = os.getenv("MODAL_BACKEND_URL", "https://kaustubhme0--moneymate-backend-fastapi-app.modal.run") def call_modal_backend(user_input: str, context: Dict[str, Any] = None) -> str: """Call Modal backend for AI-powered financial advice""" try: payload = { "user_input": user_input, "context": context or {} } response = requests.post( f"{MODAL_BACKEND_URL}/financial_advice", json=payload, timeout=30 ) if response.status_code == 200: return response.json().get("advice", "Unable to get advice at the moment.") else: return "Sorry, I'm having trouble connecting to the financial advisor. Please try again." except requests.exceptions.RequestException as e: return f"Connection error: {str(e)}. Please check your internet connection." def create_salary_breakdown_chart(salary: float, needs: float, wants: float, savings: float): """Create a pie chart for salary breakdown""" labels = ['Needs (50%)', 'Wants (30%)', 'Savings (20%)'] values = [needs, wants, savings] colors = ['#ff6b6b', '#4ecdc4', '#45b7d1'] fig = go.Figure(data=[go.Pie( labels=labels, values=values, hole=0.4, marker_colors=colors, textinfo='label+percent', textfont_size=12 )]) fig.update_layout( title=f"Salary Breakdown for ₹{salary:,.0f}", font=dict(size=14), showlegend=True, height=400 ) return fig def create_expense_analysis_chart(expenses: Dict[str, float]): """Create a bar chart for expense analysis""" categories = list(expenses.keys()) amounts = list(expenses.values()) fig = go.Figure([go.Bar( x=categories, y=amounts, marker_color='#667eea', text=[f"₹{amount:,.0f}" for amount in amounts], textposition='auto' )]) fig.update_layout( title="Monthly Expense Breakdown", xaxis_title="Categories", yaxis_title="Amount (₹)", font=dict(size=12), height=400 ) return fig def process_financial_query( salary: float, rent: float, food: float, transport: float, utilities: float, entertainment: float, other: float, savings_goal: str, user_question: str ) -> tuple: """Process user's financial query and return advice with visualizations""" # Calculate totals total_expenses = rent + food + transport + utilities + entertainment + other remaining_salary = salary - total_expenses # Create expense dictionary expenses = { "Rent": rent, "Food": food, "Transport": transport, "Utilities": utilities, "Entertainment": entertainment, "Other": other } # Get salary breakdown using 50/30/20 rule breakdown = mcp_server.salary_breakdown(salary, expenses) needs = breakdown["breakdown"]["needs"] wants = breakdown["breakdown"]["wants"] savings = breakdown["breakdown"]["savings"] # Create charts salary_chart = create_salary_breakdown_chart(salary, needs, wants, savings) expense_chart = create_expense_analysis_chart(expenses) # Prepare context for Modal backend context = { "salary": salary, "expenses": expenses, "total_expenses": total_expenses, "remaining_salary": remaining_salary, "savings_goal": savings_goal, "breakdown": breakdown } # Get AI advice if user_question.strip(): advice = call_modal_backend(user_question, context) else: advice = call_modal_backend(f"Analyze my finances: Salary ₹{salary}, Total expenses ₹{total_expenses}", context) # Create summary summary = f""" ## 💰 Financial Summary **Monthly Salary:** ₹{salary:,.0f} **Total Expenses:** ₹{total_expenses:,.0f} **Remaining Amount:** ₹{remaining_salary:,.0f} ### 📊 Recommended Allocation (50/30/20 Rule) - **Needs (50%):** ₹{needs:,.0f} - **Wants (30%):** ₹{wants:,.0f} - **Savings (20%):** ₹{savings:,.0f} ### 🎯 Status {'✅ Good job! You have money left over.' if remaining_salary > 0 else '⚠️ You are overspending. Consider reducing expenses.'} """ return salary_chart, expense_chart, summary, advice def handle_quick_question(question: str, salary: float = 50000) -> str: """Handle pre-defined quick questions""" context = {"salary": salary} return call_modal_backend(question, context) # Create Gradio interface def create_moneymate_app(): with gr.Blocks(css=CUSTOM_CSS, title="MoneyMate - Your Financial Assistant") as app: # Header gr.HTML("""
Your Personal Financial Assistant for Smart Money Management
Made with ❤️ for Agents & MCP Hackathon 2025
🏆 Track 1 — MCP Tool / Server