Skip to main content
Glama
get_past_performance.py13.6 kB
""" Track historical portfolio performance and returns analysis. """ from datetime import datetime, timedelta from typing import Any, Dict, List, Optional from pydantic import BaseModel from ...constants import ODOS_ROUTER_ADDRESS from ...helpers import make_zerion_request from ...mcp import mcp from .get_past_transactions import apply_filters, parse_transaction class OdosPerformanceArgs(BaseModel): wallet_address: str days_back: int = 30 input_tokens: Optional[List[str]] = None output_tokens: Optional[List[str]] = None token_addresses: Optional[List[str]] = None min_trade_value: Optional[float] = None max_trade_value: Optional[float] = None methods: Optional[List[str]] = None @mcp.tool( name="get_past_performance", description="Analyze Odos trading performance with optional filtering", ) async def get_past_performance(args: OdosPerformanceArgs) -> str: """Analyze Odos performance with filtering""" try: cutoff_timestamp = int( (datetime.now() - timedelta(days=args.days_back)).timestamp() * 1000 ) params: Dict[str, str] = { "filter[operation_types]": "trade", "filter[contract_addresses]": ODOS_ROUTER_ADDRESS, "filter[min_mined_at]": str(cutoff_timestamp), "page[size]": "50", } data = await make_zerion_request( f"/wallets/{args.wallet_address}/transactions", params ) if not data or "data" not in data: return "❌ No transaction data found" trades = data["data"] if not trades: return f"📊 No Odos trades found in the last {args.days_back} days" # Apply filters # We've verified that OdosPerformanceArgs is structurally compatible # with the subset of TransactionArgs fields used by apply_filters. filtered_trades = apply_filters(trades, args) # type: ignore[arg-type] if not filtered_trades: return f"📊 No trades match your filters. Found {len(trades)} total Odos trades." # Analyze performance analysis = analyze_trades(filtered_trades) return format_performance(args.wallet_address, analysis, args.days_back) except ConnectionError as e: return f"❌ API Error in get_past_performance: {str(e)}" except Exception as e: # pylint: disable=broad-except return f"❌ Unexpected Error in get_past_performance: {str(e)}" def _process_trade_status(parsed: Dict[str, Any]) -> Dict[str, int]: """Process trade status and return counts""" status = parsed.get("status", "unknown") counts = {"confirmed": 0, "failed": 0, "pending": 0} if status == "confirmed": counts["confirmed"] = 1 elif status == "failed": counts["failed"] = 1 else: counts["pending"] = 1 return counts def _process_gas_info(parsed: Dict[str, Any]) -> Dict[str, float]: """Process gas information from parsed transaction""" fee_info = parsed.get("fee", {}) gas_data: Dict[str, float] = {"gas_usd": 0.0, "gas_eth": 0.0} if fee_info: gas_usd = fee_info.get("value_usd", 0) gas_eth = fee_info.get("amount_eth", 0) if gas_usd > 0: gas_data["gas_usd"] = float(gas_usd) if gas_eth > 0: gas_data["gas_eth"] = float(gas_eth) return gas_data def _process_volume_info(parsed: Dict[str, Any]) -> Dict[str, float]: """Process volume information from parsed transaction""" trade_summary = parsed.get("trade_summary", {}) volume_data: Dict[str, float] = { "input_vol": 0.0, "output_vol": 0.0, "net_change": 0.0, } if trade_summary: volume_data["input_vol"] = float(trade_summary.get("input_total_usd", 0)) volume_data["output_vol"] = float(trade_summary.get("output_total_usd", 0)) volume_data["net_change"] = float(trade_summary.get("net_change_usd", 0)) return volume_data def _process_method_info(parsed: Dict[str, Any]) -> str: """Process method information from parsed transaction""" app_info = parsed.get("application", {}) if app_info: method_info = app_info.get("method", {}) if method_info: return str(method_info.get("name", "Unknown")) return "Unknown" def _process_tokens_info(parsed: Dict[str, Any]) -> set[str]: """Process token information from parsed transaction""" tokens_traded: set[str] = set() transfers = parsed.get("transfers", []) for transfer in transfers: token = transfer.get("token", {}) if token: symbol = token.get("symbol", "UNK") tokens_traded.add(str(symbol)) return tokens_traded def _process_chain_info(parsed: Dict[str, Any]) -> str: """Process chain information from parsed transaction""" chain_info = parsed.get("chain", {}) if chain_info: return str(chain_info.get("id", "unknown")) return "unknown" def _calculate_statistics(data: Dict[str, Any]) -> Dict[str, float]: """Calculate various statistics for the analysis""" stats: Dict[str, float] = {} total = data["total"] confirmed = data["confirmed"] failed = data["failed"] gas_costs = data["gas_costs"] trade_volumes = data["trade_volumes"] total_gas_usd = data["total_gas_usd"] total_gas_eth = data["total_gas_eth"] total_input_volume = data["total_input_volume"] # Confirmation and failure rates stats["confirmation_rate"] = (confirmed / total * 100) if total > 0 else 0 stats["failure_rate"] = (failed / total * 100) if total > 0 else 0 # Gas statistics stats["average_gas_cost_usd"] = total_gas_usd / total if total > 0 else 0 stats["average_gas_cost_eth"] = total_gas_eth / total if total > 0 else 0 stats["min_gas_cost_usd"] = min(gas_costs) if gas_costs else 0 stats["max_gas_cost_usd"] = max(gas_costs) if gas_costs else 0 # Volume statistics stats["average_trade_size_usd"] = ( sum(trade_volumes) / len(trade_volumes) if trade_volumes else 0 ) stats["min_trade_size_usd"] = min(trade_volumes) if trade_volumes else 0 stats["max_trade_size_usd"] = max(trade_volumes) if trade_volumes else 0 # Gas to volume ratio stats["gas_to_volume_ratio"] = ( (total_gas_usd / total_input_volume * 100) if total_input_volume > 0 else 0 ) return stats def _process_single_trade( trade: Dict, totals: Dict[str, Any], collections: Dict[str, Any] ) -> None: """Process a single trade and update totals and collections""" try: parsed = parse_transaction(trade) if not parsed: return # Process status status_counts = _process_trade_status(parsed) totals["confirmed"] += status_counts["confirmed"] totals["failed"] += status_counts["failed"] totals["pending"] += status_counts["pending"] # Process gas information gas_data = _process_gas_info(parsed) totals["total_gas_usd"] += gas_data["gas_usd"] totals["total_gas_eth"] += gas_data["gas_eth"] if gas_data["gas_usd"] > 0: collections["gas_costs"].append(gas_data["gas_usd"]) # Process volume information volume_data = _process_volume_info(parsed) totals["total_input_volume"] += volume_data["input_vol"] totals["total_output_volume"] += volume_data["output_vol"] totals["total_net_change"] += volume_data["net_change"] if volume_data["input_vol"] > 0: collections["trade_volumes"].append(volume_data["input_vol"]) # Process method information method = _process_method_info(parsed) collections["methods_used"][method] = ( collections["methods_used"].get(method, 0) + 1 ) # Process token information trade_tokens = _process_tokens_info(parsed) collections["tokens_traded"].update(trade_tokens) # Process chain information chain_id = _process_chain_info(parsed) collections["chains_used"].add(chain_id) except (KeyError, ValueError, TypeError) as e: print(f"Error parsing trade: {e}") def analyze_trades(trades: List[Dict]) -> Dict[str, Any]: """Comprehensive trade analysis with error handling""" # Initialize totals totals: Dict[str, Any] = { "confirmed": 0, "failed": 0, "pending": 0, "total_gas_usd": 0, "total_gas_eth": 0, "total_input_volume": 0, "total_output_volume": 0, "total_net_change": 0, } # Initialize collections collections: Dict[str, Any] = { "gas_costs": [], "trade_volumes": [], "methods_used": {}, "tokens_traded": set(), "chains_used": set(), } # Process each trade for trade in trades: _process_single_trade(trade, totals, collections) # Calculate statistics stats_data = { "total": len(trades), "confirmed": totals["confirmed"], "failed": totals["failed"], "gas_costs": collections["gas_costs"], "trade_volumes": collections["trade_volumes"], "total_gas_usd": totals["total_gas_usd"], "total_gas_eth": totals["total_gas_eth"], "total_input_volume": totals["total_input_volume"], } stats = _calculate_statistics(stats_data) return { "total_transactions": len(trades), "confirmed_transactions": totals["confirmed"], "failed_transactions": totals["failed"], "pending_transactions": totals["pending"], "confirmation_rate": stats["confirmation_rate"], "failure_rate": stats["failure_rate"], "total_gas_cost_usd": totals["total_gas_usd"], "total_gas_cost_eth": totals["total_gas_eth"], "average_gas_cost_usd": stats["average_gas_cost_usd"], "average_gas_cost_eth": stats["average_gas_cost_eth"], "min_gas_cost_usd": stats["min_gas_cost_usd"], "max_gas_cost_usd": stats["max_gas_cost_usd"], "total_input_volume_usd": totals["total_input_volume"], "total_output_volume_usd": totals["total_output_volume"], "total_net_change_usd": totals["total_net_change"], "average_trade_size_usd": stats["average_trade_size_usd"], "min_trade_size_usd": stats["min_trade_size_usd"], "max_trade_size_usd": stats["max_trade_size_usd"], "total_trade_count": len(collections["trade_volumes"]), "methods_used": dict(collections["methods_used"]), "unique_tokens_traded": list(collections["tokens_traded"]), "unique_chains_used": list(collections["chains_used"]), "gas_to_volume_ratio": stats["gas_to_volume_ratio"], } def format_performance( wallet_address: str, analysis: Dict[str, Any], days_back: int ) -> str: """Format comprehensive performance analysis""" output = "ODOS PERFORMANCE ANALYSIS\n" output += f"Wallet: {wallet_address}\n" output += f"Analysis period: {days_back} days\n" output += f"Router contract: {ODOS_ROUTER_ADDRESS}\n\n" output += "TRANSACTION STATISTICS:\n" output += f" Total transactions: {analysis['total_transactions']}\n" output += f" Confirmed transactions: {analysis['confirmed_transactions']}\n" output += f" Failed transactions: {analysis['failed_transactions']}\n" output += f" Pending transactions: {analysis['pending_transactions']}\n" output += f" Confirmation rate: {analysis['confirmation_rate']:.2f}%\n" output += f" Failure rate: {analysis['failure_rate']:.2f}%\n\n" output += "GAS COST ANALYSIS:\n" output += f" Total gas cost (USD): ${analysis['total_gas_cost_usd']:.6f}\n" output += f" Total gas cost (ETH): {analysis['total_gas_cost_eth']:.12f}\n" output += f" Average gas cost (USD): ${analysis['average_gas_cost_usd']:.6f}\n" output += f" Average gas cost (ETH): {analysis['average_gas_cost_eth']:.12f}\n" output += f" Minimum gas cost (USD): ${analysis['min_gas_cost_usd']:.6f}\n" output += f" Maximum gas cost (USD): ${analysis['max_gas_cost_usd']:.6f}\n\n" output += "TRADING VOLUME ANALYSIS:\n" output += f" Total input volume (USD): ${analysis['total_input_volume_usd']:.6f}\n" output += ( f" Total output volume (USD): ${analysis['total_output_volume_usd']:.6f}\n" ) output += f" Total net change (USD): ${analysis['total_net_change_usd']:.6f}\n" output += f" Average trade size (USD): ${analysis['average_trade_size_usd']:.6f}\n" output += f" Minimum trade size (USD): ${analysis['min_trade_size_usd']:.6f}\n" output += f" Maximum trade size (USD): ${analysis['max_trade_size_usd']:.6f}\n" output += f" Gas to volume ratio: {analysis['gas_to_volume_ratio']:.6f}%\n\n" output += "METHOD USAGE:\n" methods = analysis.get("methods_used", {}) for method, count in methods.items(): percentage = ( (count / analysis["total_transactions"] * 100) if analysis["total_transactions"] > 0 else 0 ) output += f" {method}: {count} transactions ({percentage:.2f}%)\n" output += "\n" output += "TOKENS TRADED:\n" tokens = analysis.get("unique_tokens_traded", []) output += f" Unique tokens: {len(tokens)}\n" output += f" Token symbols: {', '.join(sorted(tokens))}\n\n" output += "CHAINS USED:\n" chains = analysis.get("unique_chains_used", []) output += f" Unique chains: {len(chains)}\n" output += f" Chain IDs: {', '.join(sorted(chains))}\n" return output

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/odos-xyz/odos-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server