|
@@ -108,6 +108,11 @@ Tap the buttons below for instant access to key functions.
|
|
|
• /sl BTC 44000 - Set stop loss for BTC at $44,000
|
|
|
• /tp BTC 50000 - Set take profit for BTC at $50,000
|
|
|
|
|
|
+<b>🚨 Automatic Stop Loss:</b>
|
|
|
+• Enabled: {risk_enabled}
|
|
|
+• Stop Loss: {stop_loss}% (automatic execution)
|
|
|
+• Monitoring: Every {heartbeat} seconds
|
|
|
+
|
|
|
<b>📋 Order Management:</b>
|
|
|
• /orders - Show all open orders
|
|
|
• /orders BTC - Show open orders for BTC only
|
|
@@ -166,7 +171,10 @@ For support, contact your bot administrator.
|
|
|
""".format(
|
|
|
symbol=Config.DEFAULT_TRADING_SYMBOL,
|
|
|
amount=Config.DEFAULT_TRADE_AMOUNT,
|
|
|
- network="Testnet" if Config.HYPERLIQUID_TESTNET else "Mainnet"
|
|
|
+ network="Testnet" if Config.HYPERLIQUID_TESTNET else "Mainnet",
|
|
|
+ risk_enabled=Config.RISK_MANAGEMENT_ENABLED,
|
|
|
+ stop_loss=Config.STOP_LOSS_PERCENTAGE,
|
|
|
+ heartbeat=Config.BOT_HEARTBEAT_SECONDS
|
|
|
)
|
|
|
|
|
|
keyboard = [
|
|
@@ -222,6 +230,11 @@ For support, contact your bot administrator.
|
|
|
• /sl BTC 44000 - Set stop loss for BTC at $44,000
|
|
|
• /tp BTC 50000 - Set take profit for BTC at $50,000
|
|
|
|
|
|
+<b>🚨 Automatic Stop Loss:</b>
|
|
|
+• Enabled: {risk_enabled}
|
|
|
+• Stop Loss: {stop_loss}% (automatic execution)
|
|
|
+• Monitoring: Every {heartbeat} seconds
|
|
|
+
|
|
|
<b>📋 Order Management:</b>
|
|
|
• /orders - Show all open orders
|
|
|
• /orders BTC - Show open orders for BTC only
|
|
@@ -264,7 +277,10 @@ For support, contact your bot administrator.
|
|
|
""".format(
|
|
|
symbol=Config.DEFAULT_TRADING_SYMBOL,
|
|
|
amount=Config.DEFAULT_TRADE_AMOUNT,
|
|
|
- network="Testnet" if Config.HYPERLIQUID_TESTNET else "Mainnet"
|
|
|
+ network="Testnet" if Config.HYPERLIQUID_TESTNET else "Mainnet",
|
|
|
+ risk_enabled=Config.RISK_MANAGEMENT_ENABLED,
|
|
|
+ stop_loss=Config.STOP_LOSS_PERCENTAGE,
|
|
|
+ heartbeat=Config.BOT_HEARTBEAT_SECONDS
|
|
|
)
|
|
|
|
|
|
await update.message.reply_text(help_text, parse_mode='HTML')
|
|
@@ -1844,6 +1860,10 @@ This will place a limit {exit_side} order at ${profit_price:,.2f} to capture pro
|
|
|
# Check external trades (trades made outside the bot)
|
|
|
await self._check_external_trades()
|
|
|
|
|
|
+ # Check stop losses (if risk management is enabled)
|
|
|
+ if Config.RISK_MANAGEMENT_ENABLED:
|
|
|
+ await self._check_stop_losses(current_positions)
|
|
|
+
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Error checking order fills: {e}")
|
|
|
|
|
@@ -2000,6 +2020,159 @@ This will place a limit {exit_side} order at ${profit_price:,.2f} to capture pro
|
|
|
except Exception as e:
|
|
|
logger.error(f"❌ Error sending external trade notification: {e}")
|
|
|
|
|
|
+ async def _check_stop_losses(self, current_positions: list):
|
|
|
+ """Check all positions for stop loss triggers and execute automatic exits."""
|
|
|
+ try:
|
|
|
+ if not current_positions:
|
|
|
+ return
|
|
|
+
|
|
|
+ stop_loss_triggers = []
|
|
|
+
|
|
|
+ for position in current_positions:
|
|
|
+ symbol = position.get('symbol')
|
|
|
+ contracts = float(position.get('contracts', 0))
|
|
|
+ entry_price = float(position.get('entryPx', 0))
|
|
|
+
|
|
|
+ if not symbol or contracts == 0 or entry_price == 0:
|
|
|
+ continue
|
|
|
+
|
|
|
+ # Get current market price
|
|
|
+ market_data = self.client.get_market_data(symbol)
|
|
|
+ if not market_data or not market_data.get('ticker'):
|
|
|
+ continue
|
|
|
+
|
|
|
+ current_price = float(market_data['ticker'].get('last', 0))
|
|
|
+ if current_price == 0:
|
|
|
+ continue
|
|
|
+
|
|
|
+ # Calculate current P&L percentage
|
|
|
+ if contracts > 0: # Long position
|
|
|
+ pnl_percent = ((current_price - entry_price) / entry_price) * 100
|
|
|
+ else: # Short position
|
|
|
+ pnl_percent = ((entry_price - current_price) / entry_price) * 100
|
|
|
+
|
|
|
+ # Check if stop loss should trigger
|
|
|
+ if pnl_percent <= -Config.STOP_LOSS_PERCENTAGE:
|
|
|
+ token = symbol.split('/')[0] if '/' in symbol else symbol
|
|
|
+ stop_loss_triggers.append({
|
|
|
+ 'symbol': symbol,
|
|
|
+ 'token': token,
|
|
|
+ 'contracts': contracts,
|
|
|
+ 'entry_price': entry_price,
|
|
|
+ 'current_price': current_price,
|
|
|
+ 'pnl_percent': pnl_percent
|
|
|
+ })
|
|
|
+
|
|
|
+ # Execute stop losses
|
|
|
+ for trigger in stop_loss_triggers:
|
|
|
+ await self._execute_automatic_stop_loss(trigger)
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ Error checking stop losses: {e}")
|
|
|
+
|
|
|
+ async def _execute_automatic_stop_loss(self, trigger: Dict[str, Any]):
|
|
|
+ """Execute an automatic stop loss order."""
|
|
|
+ try:
|
|
|
+ symbol = trigger['symbol']
|
|
|
+ token = trigger['token']
|
|
|
+ contracts = trigger['contracts']
|
|
|
+ entry_price = trigger['entry_price']
|
|
|
+ current_price = trigger['current_price']
|
|
|
+ pnl_percent = trigger['pnl_percent']
|
|
|
+
|
|
|
+ # Determine the exit side (opposite of position)
|
|
|
+ exit_side = 'sell' if contracts > 0 else 'buy'
|
|
|
+ contracts_abs = abs(contracts)
|
|
|
+
|
|
|
+ # Send notification before executing
|
|
|
+ await self._send_stop_loss_notification(trigger, "triggered")
|
|
|
+
|
|
|
+ # Execute the stop loss order (market order for immediate execution)
|
|
|
+ try:
|
|
|
+ if exit_side == 'sell':
|
|
|
+ order = self.client.create_market_sell_order(symbol, contracts_abs)
|
|
|
+ else:
|
|
|
+ order = self.client.create_market_buy_order(symbol, contracts_abs)
|
|
|
+
|
|
|
+ if order:
|
|
|
+ logger.info(f"🛑 Stop loss executed: {token} {exit_side} {contracts_abs} @ ${current_price}")
|
|
|
+
|
|
|
+ # Record the trade in stats
|
|
|
+ self.stats.record_trade(symbol, exit_side, contracts_abs, current_price, order.get('id', 'stop_loss'))
|
|
|
+
|
|
|
+ # Send success notification
|
|
|
+ await self._send_stop_loss_notification(trigger, "executed", order)
|
|
|
+ else:
|
|
|
+ logger.error(f"❌ Stop loss order failed for {token}")
|
|
|
+ await self._send_stop_loss_notification(trigger, "failed")
|
|
|
+
|
|
|
+ except Exception as order_error:
|
|
|
+ logger.error(f"❌ Stop loss order execution failed for {token}: {order_error}")
|
|
|
+ await self._send_stop_loss_notification(trigger, "failed", error=str(order_error))
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ Error executing automatic stop loss: {e}")
|
|
|
+
|
|
|
+ async def _send_stop_loss_notification(self, trigger: Dict[str, Any], status: str, order: Dict = None, error: str = None):
|
|
|
+ """Send notification for stop loss events."""
|
|
|
+ try:
|
|
|
+ token = trigger['token']
|
|
|
+ contracts = trigger['contracts']
|
|
|
+ entry_price = trigger['entry_price']
|
|
|
+ current_price = trigger['current_price']
|
|
|
+ pnl_percent = trigger['pnl_percent']
|
|
|
+
|
|
|
+ position_type = "LONG" if contracts > 0 else "SHORT"
|
|
|
+ contracts_abs = abs(contracts)
|
|
|
+
|
|
|
+ if status == "triggered":
|
|
|
+ title = "🛑 Stop Loss Triggered"
|
|
|
+ status_text = f"Stop loss triggered at {Config.STOP_LOSS_PERCENTAGE}% loss"
|
|
|
+ emoji = "🚨"
|
|
|
+ elif status == "executed":
|
|
|
+ title = "✅ Stop Loss Executed"
|
|
|
+ status_text = "Position closed automatically"
|
|
|
+ emoji = "🛑"
|
|
|
+ elif status == "failed":
|
|
|
+ title = "❌ Stop Loss Failed"
|
|
|
+ status_text = f"Stop loss execution failed{': ' + error if error else ''}"
|
|
|
+ emoji = "⚠️"
|
|
|
+ else:
|
|
|
+ return
|
|
|
+
|
|
|
+ # Calculate loss
|
|
|
+ loss_value = contracts_abs * abs(current_price - entry_price)
|
|
|
+
|
|
|
+ message = f"""
|
|
|
+{title}
|
|
|
+
|
|
|
+{emoji} <b>Risk Management Alert</b>
|
|
|
+
|
|
|
+📊 <b>Position Details:</b>
|
|
|
+• Token: {token}
|
|
|
+• Direction: {position_type}
|
|
|
+• Size: {contracts_abs} contracts
|
|
|
+• Entry Price: ${entry_price:,.2f}
|
|
|
+• Current Price: ${current_price:,.2f}
|
|
|
+
|
|
|
+🔴 <b>Loss Details:</b>
|
|
|
+• Loss: ${loss_value:,.2f} ({pnl_percent:.2f}%)
|
|
|
+• Stop Loss Threshold: {Config.STOP_LOSS_PERCENTAGE}%
|
|
|
+
|
|
|
+📋 <b>Action:</b> {status_text}
|
|
|
+⏰ <b>Time:</b> {datetime.now().strftime('%H:%M:%S')}
|
|
|
+ """
|
|
|
+
|
|
|
+ if order and status == "executed":
|
|
|
+ order_id = order.get('id', 'N/A')
|
|
|
+ message += f"\n🆔 <b>Order ID:</b> {order_id}"
|
|
|
+
|
|
|
+ await self.send_message(message.strip())
|
|
|
+ logger.info(f"📢 Sent stop loss notification: {token} {status}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"❌ Error sending stop loss notification: {e}")
|
|
|
+
|
|
|
async def _process_filled_orders(self, filled_order_ids: set, current_positions: list):
|
|
|
"""Process filled orders and determine if they opened or closed positions."""
|
|
|
try:
|
|
@@ -2219,12 +2392,18 @@ This will place a limit {exit_side} order at ${profit_price:,.2f} to capture pro
|
|
|
• Auto Stats Update: ✅ Enabled
|
|
|
• External Notifications: ✅ Enabled
|
|
|
|
|
|
+🛡️ <b>Risk Management:</b>
|
|
|
+• Automatic Stop Loss: {'✅ Enabled' if Config.RISK_MANAGEMENT_ENABLED else '❌ Disabled'}
|
|
|
+• Stop Loss Threshold: {Config.STOP_LOSS_PERCENTAGE}%
|
|
|
+• Position Monitoring: {'✅ Active' if Config.RISK_MANAGEMENT_ENABLED else '❌ Inactive'}
|
|
|
+
|
|
|
📈 <b>Notifications:</b>
|
|
|
• 🚀 Position Opened/Increased
|
|
|
• 📉 Position Partially/Fully Closed
|
|
|
• 🎯 P&L Calculations
|
|
|
• 🔔 Price Alarm Triggers
|
|
|
• 🔄 External Trade Detection
|
|
|
+• 🛑 Automatic Stop Loss Triggers
|
|
|
|
|
|
⏰ <b>Last Check:</b> {datetime.now().strftime('%H:%M:%S')}
|
|
|
|