Kaynağa Gözat

Add /risk command to Telegram bot for advanced risk metrics - Implement a new command that provides users with detailed risk analysis, including risk-adjusted performance, drawdown analysis, and portfolio context. Enhance user experience with informative messages based on trading history and metrics, while ensuring proper error handling for unauthorized access and data retrieval issues.

Carles Sentis 2 hafta önce
ebeveyn
işleme
57b33be15b
1 değiştirilmiş dosya ile 113 ekleme ve 0 silme
  1. 113 0
      src/telegram_bot.py

+ 113 - 0
src/telegram_bot.py

@@ -1166,6 +1166,7 @@ Tap any button below for instant access to bot functions:
         self.application.add_handler(CommandHandler("daily", self.daily_command))
         self.application.add_handler(CommandHandler("weekly", self.weekly_command))
         self.application.add_handler(CommandHandler("monthly", self.monthly_command))
+        self.application.add_handler(CommandHandler("risk", self.risk_command))
         
         # Callback query handler for inline keyboards
         self.application.add_handler(CallbackQueryHandler(self.button_callback))
@@ -2869,6 +2870,118 @@ Will trigger when {token} price moves {alarm['direction']} ${target_price:,.2f}
             error_message = f"❌ Error processing monthly command: {str(e)}"
             await update.message.reply_text(error_message)
             logger.error(f"Error in monthly command: {e}")
+    
+    async def risk_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
+        """Handle the /risk command to show advanced risk metrics."""
+        if not self.is_authorized(update.effective_chat.id):
+            await update.message.reply_text("❌ Unauthorized access.")
+            return
+        
+        try:
+            # Get current balance for context
+            balance = self.client.get_balance()
+            current_balance = 0
+            if balance and balance.get('total'):
+                current_balance = float(balance['total'].get('USDC', 0))
+            
+            # Get risk metrics and basic stats
+            risk_metrics = self.stats.get_risk_metrics()
+            basic_stats = self.stats.get_basic_stats()
+            
+            # Check if we have enough data for risk calculations
+            if basic_stats['completed_trades'] < 2:
+                await update.message.reply_text(
+                    "📊 <b>Risk Analysis</b>\n\n"
+                    "📭 <b>Insufficient Data</b>\n\n"
+                    f"• Current completed trades: {basic_stats['completed_trades']}\n"
+                    f"• Required for risk analysis: 2+ trades\n"
+                    f"• Daily balance snapshots: {len(self.stats.data.get('daily_balances', []))}\n\n"
+                    "💡 <b>To enable risk analysis:</b>\n"
+                    "• Complete more trades to generate returns data\n"
+                    "• Bot automatically records daily balance snapshots\n"
+                    "• Risk metrics will be available after sufficient trading history\n\n"
+                    "📈 Use /stats for current performance metrics",
+                    parse_mode='HTML'
+                )
+                return
+            
+            # Format the risk analysis message
+            risk_text = f"""
+📊 <b>Risk Analysis & Advanced Metrics</b>
+
+🎯 <b>Risk-Adjusted Performance:</b>
+• Sharpe Ratio: {risk_metrics['sharpe_ratio']:.3f}
+• Sortino Ratio: {risk_metrics['sortino_ratio']:.3f}
+• Annual Volatility: {risk_metrics['volatility']:.2f}%
+
+📉 <b>Drawdown Analysis:</b>
+• Maximum Drawdown: {risk_metrics['max_drawdown']:.2f}%
+• Value at Risk (95%): {risk_metrics['var_95']:.2f}%
+
+💰 <b>Portfolio Context:</b>
+• Current Balance: ${current_balance:,.2f}
+• Initial Balance: ${basic_stats['initial_balance']:,.2f}
+• Total P&L: ${basic_stats['total_pnl']:,.2f}
+• Days Active: {basic_stats['days_active']}
+
+📊 <b>Risk Interpretation:</b>
+"""
+            
+            # Add interpretive guidance
+            sharpe = risk_metrics['sharpe_ratio']
+            if sharpe > 2.0:
+                risk_text += "• 🟢 <b>Excellent</b> risk-adjusted returns (Sharpe > 2.0)\n"
+            elif sharpe > 1.0:
+                risk_text += "• 🟡 <b>Good</b> risk-adjusted returns (Sharpe > 1.0)\n"
+            elif sharpe > 0.5:
+                risk_text += "• 🟠 <b>Moderate</b> risk-adjusted returns (Sharpe > 0.5)\n"
+            elif sharpe > 0:
+                risk_text += "• 🔴 <b>Poor</b> risk-adjusted returns (Sharpe > 0)\n"
+            else:
+                risk_text += "• ⚫ <b>Negative</b> risk-adjusted returns (Sharpe < 0)\n"
+            
+            max_dd = risk_metrics['max_drawdown']
+            if max_dd < 5:
+                risk_text += "• 🟢 <b>Low</b> maximum drawdown (< 5%)\n"
+            elif max_dd < 15:
+                risk_text += "• 🟡 <b>Moderate</b> maximum drawdown (< 15%)\n"
+            elif max_dd < 30:
+                risk_text += "• 🟠 <b>High</b> maximum drawdown (< 30%)\n"
+            else:
+                risk_text += "• 🔴 <b>Very High</b> maximum drawdown (> 30%)\n"
+            
+            volatility = risk_metrics['volatility']
+            if volatility < 10:
+                risk_text += "• 🟢 <b>Low</b> portfolio volatility (< 10%)\n"
+            elif volatility < 25:
+                risk_text += "• 🟡 <b>Moderate</b> portfolio volatility (< 25%)\n"
+            elif volatility < 50:
+                risk_text += "• 🟠 <b>High</b> portfolio volatility (< 50%)\n"
+            else:
+                risk_text += "• 🔴 <b>Very High</b> portfolio volatility (> 50%)\n"
+            
+            risk_text += f"""
+💡 <b>Risk Definitions:</b>
+• <b>Sharpe Ratio:</b> Risk-adjusted return (excess return / volatility)
+• <b>Sortino Ratio:</b> Return / downside volatility (focuses on bad volatility)
+• <b>Max Drawdown:</b> Largest peak-to-trough decline
+• <b>VaR 95%:</b> Maximum expected loss 95% of the time
+• <b>Volatility:</b> Annualized standard deviation of returns
+
+📈 <b>Data Based On:</b>
+• Completed Trades: {basic_stats['completed_trades']}
+• Daily Balance Records: {len(self.stats.data.get('daily_balances', []))}
+• Trading Period: {basic_stats['days_active']} days
+
+🔄 Use /stats for trading performance metrics
+            """
+            
+            await update.message.reply_text(risk_text.strip(), parse_mode='HTML')
+            
+        except Exception as e:
+            error_message = f"❌ Error processing risk command: {str(e)}"
+            await update.message.reply_text(error_message)
+            logger.error(f"Error in risk command: {e}")
 
     def _get_position_state(self, symbol: str) -> Dict[str, Any]:
         """Get current position state for a symbol."""