diff --git a/docs/monitoring_and_dashboard.md b/docs/monitoring_and_dashboard.md new file mode 100644 index 000000000..8cc0dc9e2 --- /dev/null +++ b/docs/monitoring_and_dashboard.md @@ -0,0 +1,434 @@ +# šŸŒ™ Moon Dev Monitoring & Dashboard Guide + +Complete guide to the Agent Health Monitor and Portfolio Dashboard systems. + +--- + +## šŸ“‹ Table of Contents + +1. [Overview](#overview) +2. [Agent Health Monitor](#agent-health-monitor) +3. [Portfolio Dashboard](#portfolio-dashboard) +4. [Integration Guide](#integration-guide) +5. [Quick Start](#quick-start) + +--- + +## Overview + +Two powerful monitoring systems for Moon Dev's trading agents: + +1. **Agent Health Monitor** - Tracks which agents are running, when they last executed, and if APIs are responding +2. **Portfolio Dashboard** - Real-time PnL tracking, win rate analysis, and agent performance leaderboard + +--- + +## Agent Health Monitor + +### What It Does + +- šŸ¤– Tracks agent heartbeats (last run time, status) +- 🌐 Monitors API health (response time, up/down status) +- 🚨 Generates alerts for stale agents or down APIs +- šŸ“Š Web dashboard with auto-refresh +- šŸ’¾ Stores data in JSON files + +### Files + +- **Monitor**: `src/agents/agent_health_monitor.py` +- **Web Dashboard**: `src/scripts/health_dashboard.py` +- **Example Integration**: `src/agents/example_monitored_agent.py` +- **Data Directory**: `src/data/health_monitor/` + +### Quick Start + +```bash +# Option 1: CLI - One-time health report +python src/agents/agent_health_monitor.py + +# Option 2: CLI - Continuous monitoring (refreshes every 30s) +python src/agents/agent_health_monitor.py --continuous + +# Option 3: Web Dashboard (opens on http://localhost:8002) +python src/scripts/health_dashboard.py +``` + +### Web Dashboard + +Open `http://localhost:8002` in your browser to see: + +- āœ… All agents with last run time and status +- 🟢 All APIs with response time and health +- 🚨 Active alerts (stale agents, down APIs) +- Auto-refreshes every 10 seconds + +--- + +## Portfolio Dashboard + +### What It Does + +- šŸ“ˆ Tracks all trades (entry/exit, PnL, win/loss) +- šŸ’° Real-time PnL across all positions +- šŸ† Agent performance leaderboard +- šŸ“Š Win rate analysis by agent +- šŸŽÆ Open positions monitoring +- šŸ’¾ Stores data in CSV files + +### Files + +- **Tracker**: `src/agents/portfolio_tracker.py` +- **Web Dashboard**: `src/scripts/portfolio_dashboard.py` +- **Data Directory**: `src/data/portfolio_tracker/` + - `trades_history.csv` - All closed trades + - `open_positions.csv` - Current open positions + - `agent_performance.json` - Agent stats + +### Quick Start + +```bash +# Option 1: CLI - Print portfolio summary +python src/agents/portfolio_tracker.py + +# Option 2: Web Dashboard (opens on http://localhost:8003) +python src/scripts/portfolio_dashboard.py +``` + +### Web Dashboard + +Open `http://localhost:8003` in your browser to see: + +- šŸ“Š Total PnL, win rate, avg PnL/trade +- šŸ† Agent performance leaderboard (sorted by PnL) +- šŸ“ˆ All open positions +- šŸ“‰ Recent trades with PnL details +- Auto-refreshes every 10 seconds + +--- + +## Integration Guide + +### Add Health Monitoring to Your Agent + +**Step 1:** Import the monitor + +```python +from agent_health_monitor import get_monitor + +class YourAgent: + def __init__(self): + self.monitor = get_monitor() + self.agent_name = "your_agent_name" +``` + +**Step 2:** Log heartbeat at start + +```python +def run(self): + try: + # Log starting + self.monitor.heartbeat( + agent_name=self.agent_name, + status="running", + metadata={"action": "starting"} + ) + + # Your agent logic... + + # Log success + self.monitor.heartbeat( + agent_name=self.agent_name, + status="success" + ) + except Exception as e: + # Log error + self.monitor.heartbeat( + agent_name=self.agent_name, + status="error", + metadata={"error": str(e)} + ) +``` + +**Step 3:** Monitor API health + +```python +import time + +def call_api(self): + start = time.time() + try: + response = requests.get("https://api.example.com") + response_time = time.time() - start + + status = "slow" if response_time > 3 else "up" + + self.monitor.log_api_status( + api_name="example_api", + status=status, + response_time=response_time + ) + except Exception as e: + self.monitor.log_api_status( + api_name="example_api", + status="down", + error=str(e) + ) +``` + +### Add Portfolio Tracking to Your Agent + +**Step 1:** Import the tracker + +```python +from portfolio_tracker import get_tracker + +class YourAgent: + def __init__(self): + self.tracker = get_tracker() +``` + +**Step 2:** Log position open + +```python +def buy_token(self, token_address, token_symbol, price, quantity, usd_value): + # Execute buy order... + + # Log to portfolio tracker + position_id = self.tracker.log_position_open( + agent_name="trading_agent", + token_address=token_address, + token_symbol=token_symbol, + entry_price=price, + quantity=quantity, + usd_value=usd_value, + strategy="momentum_breakout", + notes="AI confidence: 85%" + ) + + # Save position_id for later + return position_id +``` + +**Step 3:** Log position close + +```python +def sell_token(self, position_id, exit_price, fees=0): + # Execute sell order... + + # Log to portfolio tracker + trade = self.tracker.log_position_close( + position_id=position_id, + exit_price=exit_price, + fees_usd=fees, + notes="Stop loss triggered" + ) + + # trade dict contains PnL info + print(f"PnL: ${trade['pnl_usd']:.2f}") +``` + +--- + +## Complete Integration Example + +```python +""" +Example Trading Agent with Full Monitoring +""" + +from agent_health_monitor import get_monitor +from portfolio_tracker import get_tracker +import time + +class MonitoredTradingAgent: + def __init__(self): + self.health_monitor = get_monitor() + self.portfolio_tracker = get_tracker() + self.agent_name = "example_trading_agent" + + def run(self): + """Main agent loop""" + try: + # Log agent starting + self.health_monitor.heartbeat( + agent_name=self.agent_name, + status="running" + ) + + # Check API health + self._check_birdeye_api() + + # Analyze market + signal = self.analyze_market() + + if signal == "BUY": + self.execute_buy() + elif signal == "SELL": + self.execute_sell() + + # Log success + self.health_monitor.heartbeat( + agent_name=self.agent_name, + status="success", + metadata={"signal": signal} + ) + + except Exception as e: + # Log error + self.health_monitor.heartbeat( + agent_name=self.agent_name, + status="error", + metadata={"error": str(e)} + ) + raise + + def _check_birdeye_api(self): + """Monitor BirdEye API health""" + import requests + + start = time.time() + try: + response = requests.get( + "https://public-api.birdeye.so/public/tokenlist", + timeout=5 + ) + response_time = time.time() - start + + status = "slow" if response_time > 3 else "up" + self.health_monitor.log_api_status( + api_name="birdeye", + status=status, + response_time=response_time + ) + except Exception as e: + self.health_monitor.log_api_status( + api_name="birdeye", + status="down", + error=str(e) + ) + + def execute_buy(self): + """Buy token and track in portfolio""" + # Your buy logic... + token_address = "..." + token_symbol = "TOKEN" + entry_price = 0.001 + quantity = 1000 + usd_value = 1.0 + + # Log position opening + position_id = self.portfolio_tracker.log_position_open( + agent_name=self.agent_name, + token_address=token_address, + token_symbol=token_symbol, + entry_price=entry_price, + quantity=quantity, + usd_value=usd_value, + strategy="momentum" + ) + + # Save position_id for later + self.current_position = position_id + + def execute_sell(self): + """Sell token and calculate PnL""" + # Your sell logic... + exit_price = 0.0012 + fees = 0.01 + + # Log position closing (calculates PnL automatically) + trade = self.portfolio_tracker.log_position_close( + position_id=self.current_position, + exit_price=exit_price, + fees_usd=fees + ) + + print(f"Trade closed: PnL = ${trade['pnl_usd']:.2f}") +``` + +--- + +## Quick Start - All Dashboards + +Run all monitoring dashboards in separate terminals: + +```bash +# Terminal 1: Agent Health Monitor +python src/scripts/health_dashboard.py +# Open: http://localhost:8002 + +# Terminal 2: Portfolio Dashboard +python src/scripts/portfolio_dashboard.py +# Open: http://localhost:8003 + +# Terminal 3: Backtest Dashboard (if you have it) +python src/scripts/backtestdashboard.py +# Open: http://localhost:8001 +``` + +--- + +## Data Storage + +### Agent Health Monitor + +``` +src/data/health_monitor/ +ā”œā”€ā”€ agent_heartbeats.json # Agent last run times +└── api_status.json # API health status +``` + +### Portfolio Tracker + +``` +src/data/portfolio_tracker/ +ā”œā”€ā”€ trades_history.csv # All closed trades +ā”œā”€ā”€ open_positions.csv # Current positions +└── agent_performance.json # Agent stats +``` + +--- + +## Alerts & Notifications + +### Agent Health Alerts + +- āš ļø Agent hasn't run in > 60 minutes +- āŒ Agent last run resulted in error +- šŸ”“ API is down +- 🟔 API response time > 3 seconds + +### Portfolio Alerts + +Currently displayed in dashboard: +- Total PnL (positive/negative) +- Win rate (above/below 50%) +- Individual agent performance + +--- + +## Tips + +1. **Start dashboards on system boot** - Add to cron/systemd +2. **Monitor on mobile** - Dashboards are responsive +3. **Export data** - CSVs can be opened in Excel/Sheets +4. **Add custom alerts** - Modify code for Telegram/Discord +5. **Backup data** - CSV/JSON files are in `src/data/` + +--- + +## Future Enhancements + +Potential additions: +- [ ] Telegram bot for alerts +- [ ] Discord webhook integration +- [ ] Email notifications for critical alerts +- [ ] Export reports (PDF/CSV) +- [ ] Historical performance charts +- [ ] Multi-timeframe analysis +- [ ] Sharpe ratio calculations +- [ ] Comparison with buy-and-hold + +--- + +Built with love by Moon Dev šŸŒ™šŸš€ diff --git a/src/agents/agent_health_monitor.py b/src/agents/agent_health_monitor.py new file mode 100644 index 000000000..1b25dea3f --- /dev/null +++ b/src/agents/agent_health_monitor.py @@ -0,0 +1,264 @@ +""" +šŸŒ™ Moon Dev's Agent Health Monitor +Tracks which agents are running, when they last executed, and if APIs are responding +Built with love by Moon Dev šŸš€ +""" + +import os +import json +import time +from datetime import datetime, timedelta +from pathlib import Path +from termcolor import cprint +import sys + +# Add project root to path +project_root = Path(__file__).parent.parent.parent +sys.path.append(str(project_root)) + +class AgentHealthMonitor: + """Monitors health of all trading agents""" + + def __init__(self, data_dir="src/data/health_monitor"): + self.data_dir = Path(data_dir) + self.data_dir.mkdir(parents=True, exist_ok=True) + self.heartbeat_file = self.data_dir / "agent_heartbeats.json" + self.api_status_file = self.data_dir / "api_status.json" + + # Initialize files if they don't exist + if not self.heartbeat_file.exists(): + self._save_json({}, self.heartbeat_file) + if not self.api_status_file.exists(): + self._save_json({}, self.api_status_file) + + def _save_json(self, data, filepath): + """Save JSON data to file""" + with open(filepath, 'w') as f: + json.dump(data, f, indent=2, default=str) + + def _load_json(self, filepath): + """Load JSON data from file""" + try: + with open(filepath, 'r') as f: + return json.load(f) + except: + return {} + + def heartbeat(self, agent_name, status="running", metadata=None): + """ + Record agent heartbeat + + Args: + agent_name: Name of the agent (e.g., 'trading_agent') + status: Status ('running', 'success', 'error') + metadata: Optional dict with additional info + """ + heartbeats = self._load_json(self.heartbeat_file) + + heartbeats[agent_name] = { + "last_run": datetime.now().isoformat(), + "status": status, + "metadata": metadata or {} + } + + self._save_json(heartbeats, self.heartbeat_file) + + def log_api_status(self, api_name, status, response_time=None, error=None): + """ + Log API health status + + Args: + api_name: Name of API (e.g., 'birdeye', 'moondev', 'coingecko') + status: 'up', 'down', 'slow' + response_time: Response time in seconds + error: Error message if any + """ + api_statuses = self._load_json(self.api_status_file) + + api_statuses[api_name] = { + "last_checked": datetime.now().isoformat(), + "status": status, + "response_time": response_time, + "error": str(error) if error else None + } + + self._save_json(api_statuses, self.api_status_file) + + def get_health_report(self): + """ + Generate comprehensive health report + + Returns: + dict with agent and API health status + """ + heartbeats = self._load_json(self.heartbeat_file) + api_statuses = self._load_json(self.api_status_file) + + now = datetime.now() + report = { + "timestamp": now.isoformat(), + "agents": {}, + "apis": {}, + "alerts": [] + } + + # Check agent health + for agent_name, data in heartbeats.items(): + last_run = datetime.fromisoformat(data["last_run"]) + time_since_run = (now - last_run).total_seconds() / 60 # minutes + + health_status = "healthy" + if time_since_run > 60: # No run in 1 hour + health_status = "stale" + report["alerts"].append(f"āš ļø {agent_name} hasn't run in {int(time_since_run)} minutes") + elif data["status"] == "error": + health_status = "error" + report["alerts"].append(f"āŒ {agent_name} last run resulted in error") + + report["agents"][agent_name] = { + "last_run": data["last_run"], + "minutes_ago": round(time_since_run, 1), + "status": data["status"], + "health": health_status, + "metadata": data.get("metadata", {}) + } + + # Check API health + for api_name, data in api_statuses.items(): + last_checked = datetime.fromisoformat(data["last_checked"]) + time_since_check = (now - last_checked).total_seconds() / 60 # minutes + + health_status = "healthy" + if data["status"] == "down": + health_status = "down" + report["alerts"].append(f"šŸ”“ {api_name} API is DOWN") + elif data["status"] == "slow": + health_status = "slow" + report["alerts"].append(f"🟔 {api_name} API is responding slowly") + + report["apis"][api_name] = { + "last_checked": data["last_checked"], + "minutes_ago": round(time_since_check, 1), + "status": data["status"], + "response_time": data.get("response_time"), + "error": data.get("error"), + "health": health_status + } + + return report + + def print_health_report(self): + """Print colorized health report to console""" + report = self.get_health_report() + + cprint("\n" + "="*80, "cyan") + cprint("šŸŒ™ MOON DEV AGENT HEALTH MONITOR", "white", "on_blue") + cprint("="*80 + "\n", "cyan") + + # Print alerts first + if report["alerts"]: + cprint("🚨 ALERTS:", "red", attrs=["bold"]) + for alert in report["alerts"]: + cprint(f" {alert}", "red") + print() + else: + cprint("āœ… All systems healthy!", "green", attrs=["bold"]) + print() + + # Print agent status + cprint("šŸ¤– AGENTS:", "cyan", attrs=["bold"]) + if not report["agents"]: + cprint(" No agents have reported yet", "yellow") + else: + for agent_name, data in sorted(report["agents"].items()): + status_color = { + "healthy": "green", + "stale": "yellow", + "error": "red" + }.get(data["health"], "white") + + status_icon = { + "healthy": "āœ…", + "stale": "āš ļø", + "error": "āŒ" + }.get(data["health"], "ā“") + + cprint(f" {status_icon} {agent_name:25s} ", status_color, end="") + cprint(f"Last run: {data['minutes_ago']}m ago ({data['status']})", "white") + + print() + + # Print API status + cprint("🌐 APIs:", "cyan", attrs=["bold"]) + if not report["apis"]: + cprint(" No API checks yet", "yellow") + else: + for api_name, data in sorted(report["apis"].items()): + status_color = { + "healthy": "green", + "slow": "yellow", + "down": "red" + }.get(data["health"], "white") + + status_icon = { + "healthy": "🟢", + "slow": "🟔", + "down": "šŸ”“" + }.get(data["health"], "ā“") + + cprint(f" {status_icon} {api_name:25s} ", status_color, end="") + if data.get("response_time"): + cprint(f"{data['response_time']:.2f}s", "white", end="") + if data.get("error"): + cprint(f" - Error: {data['error']}", "red") + else: + cprint("", "white") + + cprint("\n" + "="*80, "cyan") + cprint(f"Report generated at: {report['timestamp']}", "white") + cprint("="*80 + "\n", "cyan") + + def continuous_monitor(self, interval=30): + """ + Continuously print health report + + Args: + interval: Seconds between reports + """ + try: + while True: + os.system('clear' if os.name == 'posix' else 'cls') + self.print_health_report() + time.sleep(interval) + except KeyboardInterrupt: + cprint("\nšŸ‘‹ Health monitor stopped", "yellow") + + +# Singleton instance +_monitor_instance = None + +def get_monitor(): + """Get singleton monitor instance""" + global _monitor_instance + if _monitor_instance is None: + _monitor_instance = AgentHealthMonitor() + return _monitor_instance + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="Moon Dev Agent Health Monitor") + parser.add_argument("--continuous", "-c", action="store_true", help="Run continuous monitoring") + parser.add_argument("--interval", "-i", type=int, default=30, help="Refresh interval (seconds)") + args = parser.parse_args() + + monitor = get_monitor() + + if args.continuous: + cprint("šŸŒ™ Starting continuous health monitoring...", "cyan") + cprint(f"šŸ“Š Refreshing every {args.interval} seconds", "cyan") + cprint("Press Ctrl+C to stop\n", "yellow") + monitor.continuous_monitor(interval=args.interval) + else: + monitor.print_health_report() diff --git a/src/agents/example_monitored_agent.py b/src/agents/example_monitored_agent.py new file mode 100644 index 000000000..3bcf40e01 --- /dev/null +++ b/src/agents/example_monitored_agent.py @@ -0,0 +1,111 @@ +""" +šŸŒ™ Example: How to Add Health Monitoring to Your Agents +This shows how to integrate the health monitor into existing agents +""" + +from agent_health_monitor import get_monitor +import time + +class ExampleMonitoredAgent: + """Example agent with health monitoring""" + + def __init__(self): + self.monitor = get_monitor() + self.agent_name = "example_agent" + + def run(self): + """Main agent logic with monitoring""" + try: + # Log that agent is starting + self.monitor.heartbeat( + agent_name=self.agent_name, + status="running", + metadata={"action": "starting_analysis"} + ) + + # Your actual agent logic here + result = self._do_trading_logic() + + # Log successful completion + self.monitor.heartbeat( + agent_name=self.agent_name, + status="success", + metadata={ + "action": "completed", + "result": result + } + ) + + return result + + except Exception as e: + # Log error + self.monitor.heartbeat( + agent_name=self.agent_name, + status="error", + metadata={"error": str(e)} + ) + raise + + def _do_trading_logic(self): + """Placeholder for actual trading logic""" + # Example: Check API health + self._check_api_health() + + # Your logic here + return {"trades": 0, "analysis": "completed"} + + def _check_api_health(self): + """Example API health check""" + import requests + from time import time as timer + + # Example: Check BirdEye API + start = timer() + try: + response = requests.get("https://public-api.birdeye.so/public/tokenlist", timeout=5) + response_time = timer() - start + + if response_time > 3: + status = "slow" + else: + status = "up" + + self.monitor.log_api_status( + api_name="birdeye", + status=status, + response_time=response_time + ) + except Exception as e: + self.monitor.log_api_status( + api_name="birdeye", + status="down", + error=str(e) + ) + + +# HOW TO INTEGRATE INTO EXISTING AGENTS: +""" +1. Add import at top of agent file: + from agent_health_monitor import get_monitor + +2. In __init__ or at module level: + self.monitor = get_monitor() + +3. At start of run() method: + self.monitor.heartbeat(agent_name="my_agent", status="running") + +4. After successful execution: + self.monitor.heartbeat(agent_name="my_agent", status="success") + +5. In exception handlers: + self.monitor.heartbeat(agent_name="my_agent", status="error", metadata={"error": str(e)}) + +6. For API calls, wrap with timing: + start = time.time() + try: + response = api_call() + self.monitor.log_api_status("api_name", "up", time.time() - start) + except: + self.monitor.log_api_status("api_name", "down", error=str(e)) +""" diff --git a/src/agents/portfolio_tracker.py b/src/agents/portfolio_tracker.py new file mode 100644 index 000000000..6fc3d10ca --- /dev/null +++ b/src/agents/portfolio_tracker.py @@ -0,0 +1,313 @@ +""" +šŸŒ™ Moon Dev's Portfolio Tracker +Tracks all trades, PnL, win rate, and agent performance +Built with love by Moon Dev šŸš€ +""" + +import pandas as pd +from datetime import datetime +from pathlib import Path +import json +from termcolor import cprint +import sys + +# Add project root to path +project_root = Path(__file__).parent.parent.parent +sys.path.append(str(project_root)) + +class PortfolioTracker: + """Comprehensive portfolio and trade tracking""" + + def __init__(self, data_dir="src/data/portfolio_tracker"): + self.data_dir = Path(data_dir) + self.data_dir.mkdir(parents=True, exist_ok=True) + + self.trades_file = self.data_dir / "trades_history.csv" + self.positions_file = self.data_dir / "open_positions.csv" + self.performance_file = self.data_dir / "agent_performance.json" + + # Initialize files + self._init_files() + + def _init_files(self): + """Initialize CSV files with headers if they don't exist""" + if not self.trades_file.exists(): + df = pd.DataFrame(columns=[ + 'trade_id', 'timestamp_open', 'timestamp_close', 'agent_name', + 'token_address', 'token_symbol', 'entry_price', 'exit_price', + 'quantity', 'pnl_usd', 'pnl_percent', 'fees_usd', 'duration_minutes', + 'win', 'strategy', 'notes' + ]) + df.to_csv(self.trades_file, index=False) + + if not self.positions_file.exists(): + df = pd.DataFrame(columns=[ + 'position_id', 'timestamp_open', 'agent_name', 'token_address', + 'token_symbol', 'entry_price', 'quantity', 'usd_value', + 'strategy', 'notes' + ]) + df.to_csv(self.positions_file, index=False) + + if not self.performance_file.exists(): + with open(self.performance_file, 'w') as f: + json.dump({}, f) + + def log_position_open(self, agent_name, token_address, token_symbol, + entry_price, quantity, usd_value, strategy="", notes=""): + """ + Log a new position opening + + Args: + agent_name: Which agent opened this position + token_address: Token contract address + token_symbol: Token symbol + entry_price: Entry price + quantity: Quantity purchased + usd_value: Total USD value + strategy: Strategy name (optional) + notes: Additional notes (optional) + + Returns: + position_id + """ + positions_df = pd.read_csv(self.positions_file) + + position_id = f"{agent_name}_{token_symbol}_{datetime.now().strftime('%Y%m%d_%H%M%S')}" + + new_position = { + 'position_id': position_id, + 'timestamp_open': datetime.now().isoformat(), + 'agent_name': agent_name, + 'token_address': token_address, + 'token_symbol': token_symbol, + 'entry_price': entry_price, + 'quantity': quantity, + 'usd_value': usd_value, + 'strategy': strategy, + 'notes': notes + } + + positions_df = pd.concat([positions_df, pd.DataFrame([new_position])], ignore_index=True) + positions_df.to_csv(self.positions_file, index=False) + + cprint(f"šŸ“ˆ Position opened: {token_symbol} by {agent_name}", "green") + return position_id + + def log_position_close(self, position_id, exit_price, fees_usd=0, notes=""): + """ + Log a position closing + + Args: + position_id: ID of position to close + exit_price: Exit price + fees_usd: Trading fees in USD + notes: Additional notes + + Returns: + trade dict with PnL info + """ + # Get position + positions_df = pd.read_csv(self.positions_file) + position = positions_df[positions_df['position_id'] == position_id] + + if len(position) == 0: + cprint(f"āŒ Position {position_id} not found", "red") + return None + + position = position.iloc[0] + + # Calculate PnL + entry_price = position['entry_price'] + quantity = position['quantity'] + entry_value = position['usd_value'] + + exit_value = exit_price * quantity + pnl_usd = exit_value - entry_value - fees_usd + pnl_percent = (pnl_usd / entry_value) * 100 if entry_value > 0 else 0 + + # Calculate duration + timestamp_open = pd.to_datetime(position['timestamp_open']) + timestamp_close = datetime.now() + duration_minutes = (timestamp_close - timestamp_open).total_seconds() / 60 + + # Create trade record + trade = { + 'trade_id': position_id, + 'timestamp_open': position['timestamp_open'], + 'timestamp_close': timestamp_close.isoformat(), + 'agent_name': position['agent_name'], + 'token_address': position['token_address'], + 'token_symbol': position['token_symbol'], + 'entry_price': entry_price, + 'exit_price': exit_price, + 'quantity': quantity, + 'pnl_usd': pnl_usd, + 'pnl_percent': pnl_percent, + 'fees_usd': fees_usd, + 'duration_minutes': duration_minutes, + 'win': pnl_usd > 0, + 'strategy': position['strategy'], + 'notes': notes + } + + # Append to trades history + trades_df = pd.read_csv(self.trades_file) + trades_df = pd.concat([trades_df, pd.DataFrame([trade])], ignore_index=True) + trades_df.to_csv(self.trades_file, index=False) + + # Remove from open positions + positions_df = positions_df[positions_df['position_id'] != position_id] + positions_df.to_csv(self.positions_file, index=False) + + # Update agent performance + self._update_agent_performance(position['agent_name'], trade) + + result = "WIN" if pnl_usd > 0 else "LOSS" + cprint(f"šŸ“‰ Position closed: {position['token_symbol']} by {position['agent_name']} | {result} | PnL: ${pnl_usd:.2f} ({pnl_percent:.2f}%)", "green" if pnl_usd > 0 else "red") + + return trade + + def _update_agent_performance(self, agent_name, trade): + """Update agent performance stats""" + with open(self.performance_file, 'r') as f: + performance = json.load(f) + + if agent_name not in performance: + performance[agent_name] = { + 'total_trades': 0, + 'wins': 0, + 'losses': 0, + 'total_pnl_usd': 0, + 'total_pnl_percent': 0, + 'avg_win_usd': 0, + 'avg_loss_usd': 0, + 'largest_win_usd': 0, + 'largest_loss_usd': 0, + 'win_rate': 0 + } + + stats = performance[agent_name] + stats['total_trades'] += 1 + + if trade['win']: + stats['wins'] += 1 + stats['avg_win_usd'] = ((stats['avg_win_usd'] * (stats['wins'] - 1)) + trade['pnl_usd']) / stats['wins'] + if trade['pnl_usd'] > stats['largest_win_usd']: + stats['largest_win_usd'] = trade['pnl_usd'] + else: + stats['losses'] += 1 + stats['avg_loss_usd'] = ((stats['avg_loss_usd'] * (stats['losses'] - 1)) + trade['pnl_usd']) / stats['losses'] + if trade['pnl_usd'] < stats['largest_loss_usd']: + stats['largest_loss_usd'] = trade['pnl_usd'] + + stats['total_pnl_usd'] += trade['pnl_usd'] + stats['total_pnl_percent'] += trade['pnl_percent'] + stats['win_rate'] = (stats['wins'] / stats['total_trades']) * 100 if stats['total_trades'] > 0 else 0 + + with open(self.performance_file, 'w') as f: + json.dump(performance, f, indent=2, default=str) + + def get_open_positions(self): + """Get all open positions""" + return pd.read_csv(self.positions_file) + + def get_trade_history(self, limit=None, agent_name=None): + """ + Get trade history + + Args: + limit: Max number of trades to return + agent_name: Filter by agent name + + Returns: + DataFrame of trades + """ + df = pd.read_csv(self.trades_file) + + if agent_name: + df = df[df['agent_name'] == agent_name] + + if limit: + df = df.tail(limit) + + return df + + def get_agent_performance(self): + """Get performance stats for all agents""" + with open(self.performance_file, 'r') as f: + return json.load(f) + + def get_portfolio_summary(self): + """Get comprehensive portfolio summary""" + trades_df = pd.read_csv(self.trades_file) + positions_df = pd.read_csv(self.positions_file) + performance = self.get_agent_performance() + + summary = { + 'total_trades': len(trades_df), + 'open_positions': len(positions_df), + 'total_pnl': trades_df['pnl_usd'].sum() if len(trades_df) > 0 else 0, + 'total_wins': len(trades_df[trades_df['win'] == True]) if len(trades_df) > 0 else 0, + 'total_losses': len(trades_df[trades_df['win'] == False]) if len(trades_df) > 0 else 0, + 'overall_win_rate': (len(trades_df[trades_df['win'] == True]) / len(trades_df) * 100) if len(trades_df) > 0 else 0, + 'avg_pnl_per_trade': trades_df['pnl_usd'].mean() if len(trades_df) > 0 else 0, + 'best_trade': trades_df['pnl_usd'].max() if len(trades_df) > 0 else 0, + 'worst_trade': trades_df['pnl_usd'].min() if len(trades_df) > 0 else 0, + 'agent_performance': performance + } + + return summary + + def print_portfolio_summary(self): + """Print colorized portfolio summary""" + summary = self.get_portfolio_summary() + + cprint("\n" + "="*80, "cyan") + cprint("šŸŒ™ MOON DEV PORTFOLIO SUMMARY", "white", "on_blue") + cprint("="*80, "cyan") + + cprint(f"\nšŸ“Š Overall Performance:", "cyan", attrs=["bold"]) + cprint(f" Total Trades: {summary['total_trades']}", "white") + cprint(f" Open Positions: {summary['open_positions']}", "white") + pnl_color = "green" if summary['total_pnl'] > 0 else "red" + cprint(f" Total PnL: ${summary['total_pnl']:.2f}", pnl_color, attrs=["bold"]) + cprint(f" Win Rate: {summary['overall_win_rate']:.1f}% ({summary['total_wins']}W / {summary['total_losses']}L)", "white") + cprint(f" Avg PnL/Trade: ${summary['avg_pnl_per_trade']:.2f}", "white") + cprint(f" Best Trade: ${summary['best_trade']:.2f}", "green") + cprint(f" Worst Trade: ${summary['worst_trade']:.2f}", "red") + + cprint(f"\nšŸ† Agent Performance Leaderboard:", "cyan", attrs=["bold"]) + if not summary['agent_performance']: + cprint(" No agent data yet", "yellow") + else: + # Sort by total PnL + sorted_agents = sorted( + summary['agent_performance'].items(), + key=lambda x: x[1]['total_pnl_usd'], + reverse=True + ) + + for i, (agent_name, stats) in enumerate(sorted_agents, 1): + medal = ["šŸ„‡", "🄈", "šŸ„‰"][i-1] if i <= 3 else f"{i}." + pnl_color = "green" if stats['total_pnl_usd'] > 0 else "red" + + cprint(f" {medal} {agent_name}:", "white", attrs=["bold"]) + cprint(f" PnL: ${stats['total_pnl_usd']:.2f} | Win Rate: {stats['win_rate']:.1f}% | Trades: {stats['total_trades']}", pnl_color) + + cprint("\n" + "="*80, "cyan") + + +# Singleton instance +_tracker_instance = None + +def get_tracker(): + """Get singleton tracker instance""" + global _tracker_instance + if _tracker_instance is None: + _tracker_instance = PortfolioTracker() + return _tracker_instance + + +if __name__ == "__main__": + tracker = get_tracker() + tracker.print_portfolio_summary() diff --git a/src/scripts/health_dashboard.py b/src/scripts/health_dashboard.py new file mode 100644 index 000000000..7565ab633 --- /dev/null +++ b/src/scripts/health_dashboard.py @@ -0,0 +1,355 @@ +""" +šŸŒ™ Moon Dev's Agent Health Dashboard +Web UI for monitoring agent and API health in real-time +Built with love by Moon Dev šŸš€ + +Usage: + python src/scripts/health_dashboard.py + +Then open: http://localhost:8002 +""" + +from flask import Flask, render_template_string, jsonify +from pathlib import Path +import sys + +# Add project root to path +project_root = Path(__file__).parent.parent.parent +sys.path.append(str(project_root)) + +from src.agents.agent_health_monitor import get_monitor + +app = Flask(__name__) +monitor = get_monitor() + +HTML_TEMPLATE = """ + + + + šŸŒ™ Moon Dev Agent Health Monitor + + + +
+
+

šŸŒ™ Moon Dev Agent Health Monitor

+

Real-time monitoring of trading agents and APIs

+
+ +
+

🚨 Active Alerts

+
+
+ +
+
+

šŸ¤– Trading Agents

+
Loading agents...
+
+ +
+

🌐 API Status

+
Loading APIs...
+
+
+ +
+ Auto-refreshing every 10 seconds | Last update: - +
+
+ + + + +""" + +@app.route('/') +def index(): + """Main dashboard page""" + return render_template_string(HTML_TEMPLATE) + +@app.route('/api/health') +def health_api(): + """API endpoint for health data""" + report = monitor.get_health_report() + return jsonify(report) + +if __name__ == '__main__': + print("šŸŒ™ Moon Dev Agent Health Dashboard") + print("=" * 60) + print("šŸš€ Starting web server on http://localhost:8002") + print("šŸ“Š Dashboard will auto-refresh every 10 seconds") + print("Press Ctrl+C to stop") + print("=" * 60 + "\n") + + app.run(host='0.0.0.0', port=8002, debug=False) diff --git a/src/scripts/portfolio_dashboard.py b/src/scripts/portfolio_dashboard.py new file mode 100644 index 000000000..730efb1dc --- /dev/null +++ b/src/scripts/portfolio_dashboard.py @@ -0,0 +1,502 @@ +""" +šŸŒ™ Moon Dev's Portfolio Dashboard +Real-time PnL tracking, win rate analysis, and agent performance leaderboard +Built with love by Moon Dev šŸš€ + +Usage: + python src/scripts/portfolio_dashboard.py + +Then open: http://localhost:8003 +""" + +from flask import Flask, render_template_string, jsonify +from pathlib import Path +import sys +import pandas as pd + +# Add project root to path +project_root = Path(__file__).parent.parent.parent +sys.path.append(str(project_root)) + +from src.agents.portfolio_tracker import get_tracker + +app = Flask(__name__) +tracker = get_tracker() + +HTML_TEMPLATE = """ + + + + šŸŒ™ Moon Dev Portfolio Dashboard + + + +
+
+

šŸŒ™ Moon Dev Portfolio Dashboard

+

Real-time PnL tracking and agent performance

+
+ +
+
+
Total PnL
+
$0.00
+
+
+
Total Trades
+
0
+
+
+
Win Rate
+
0%
+
+
+
Open Positions
+
0
+
+
+
Avg PnL/Trade
+
$0.00
+
+
+
Best Trade
+
$0.00
+
+
+ +
+
+

šŸ† Agent Performance Leaderboard

+
Loading...
+
+ +
+

šŸ“ˆ Open Positions

+
Loading...
+
+
+ +
+

šŸ“Š Recent Trades

+
Loading...
+
+ +
+ Auto-refreshing every 10 seconds | Last update: - +
+
+ + + + +""" + +@app.route('/') +def index(): + """Main dashboard page""" + return render_template_string(HTML_TEMPLATE) + +@app.route('/api/portfolio') +def portfolio_api(): + """API endpoint for portfolio data""" + summary = tracker.get_portfolio_summary() + positions = tracker.get_open_positions().to_dict('records') + recent_trades = tracker.get_trade_history(limit=20).to_dict('records') + + return jsonify({ + 'summary': summary, + 'positions': positions, + 'recent_trades': recent_trades + }) + +if __name__ == '__main__': + print("šŸŒ™ Moon Dev Portfolio Dashboard") + print("=" * 60) + print("šŸš€ Starting web server on http://localhost:8003") + print("šŸ“Š Dashboard will auto-refresh every 10 seconds") + print("Press Ctrl+C to stop") + print("=" * 60 + "\n") + + app.run(host='0.0.0.0', port=8003, debug=False)