Add position reduction feature via Telegram
- New endpoint: /api/trading/reduce-position to take partial profits - Closes specified percentage at market price - Recalculates and places new TP/SL orders for remaining size - Entry price stays the same, only size is reduced - Telegram command: /reduce [percent] (default 50%, range 10-90%) - Shows realized P&L from the closed portion - Example: /reduce 25 closes 25% and updates orders for remaining 75%
This commit is contained in:
@@ -191,6 +191,99 @@ async def scale_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
print(f"❌ Error: {e}", flush=True)
|
||||
await update.message.reply_text(f"❌ Error: {str(e)}")
|
||||
|
||||
async def reduce_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Handle /reduce command - take partial profits and adjust TP/SL"""
|
||||
|
||||
# Only process from YOUR chat
|
||||
if update.message.chat_id != ALLOWED_CHAT_ID:
|
||||
await update.message.reply_text("❌ Unauthorized")
|
||||
return
|
||||
|
||||
print(f"📉 /reduce command received", flush=True)
|
||||
|
||||
try:
|
||||
# First, get the current open position
|
||||
pos_response = requests.get(
|
||||
f"{TRADING_BOT_URL}/api/trading/positions",
|
||||
headers={'Authorization': f'Bearer {API_SECRET_KEY}'},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if not pos_response.ok:
|
||||
await update.message.reply_text(f"❌ Error fetching positions: {pos_response.status_code}")
|
||||
return
|
||||
|
||||
pos_data = pos_response.json()
|
||||
positions = pos_data.get('positions', [])
|
||||
|
||||
if not positions:
|
||||
await update.message.reply_text("❌ No open positions to reduce")
|
||||
return
|
||||
|
||||
if len(positions) > 1:
|
||||
await update.message.reply_text("❌ Multiple positions open. Please close extras first.")
|
||||
return
|
||||
|
||||
position = positions[0]
|
||||
trade_id = position['id']
|
||||
|
||||
# Determine reduce percent from command argument
|
||||
reduce_percent = 50 # Default
|
||||
if context.args and len(context.args) > 0:
|
||||
try:
|
||||
reduce_percent = int(context.args[0])
|
||||
if reduce_percent < 10 or reduce_percent > 90:
|
||||
await update.message.reply_text("❌ Reduce percent must be between 10 and 90")
|
||||
return
|
||||
except ValueError:
|
||||
await update.message.reply_text("❌ Invalid reduce percent. Usage: /reduce [percent]")
|
||||
return
|
||||
|
||||
# Send reduce request
|
||||
response = requests.post(
|
||||
f"{TRADING_BOT_URL}/api/trading/reduce-position",
|
||||
headers={'Authorization': f'Bearer {API_SECRET_KEY}'},
|
||||
json={'tradeId': trade_id, 'reducePercent': reduce_percent},
|
||||
timeout=30
|
||||
)
|
||||
|
||||
print(f"📥 API Response: {response.status_code}", flush=True)
|
||||
|
||||
if not response.ok:
|
||||
data = response.json()
|
||||
await update.message.reply_text(f"❌ Error: {data.get('message', 'Unknown error')}")
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
|
||||
if not data.get('success'):
|
||||
await update.message.reply_text(f"❌ {data.get('message', 'Failed to reduce position')}")
|
||||
return
|
||||
|
||||
# Build success message
|
||||
message = f"✅ *Position Reduced by {reduce_percent}%*\n\n"
|
||||
message += f"*{position['symbol']} {position['direction'].upper()}*\n\n"
|
||||
message += f"*Closed:*\n"
|
||||
message += f" Size: ${data['closedSize']:.0f}\n"
|
||||
message += f" Price: ${data['closePrice']:.2f}\n"
|
||||
message += f" P&L: ${data['realizedPnL']:.2f}\n\n"
|
||||
message += f"*Remaining:*\n"
|
||||
message += f" Size: ${data['remainingSize']:.0f}\n"
|
||||
message += f" Entry: ${position['entryPrice']:.2f}\n\n"
|
||||
message += f"*Updated Targets:*\n"
|
||||
message += f" TP1: ${data['newTP1']:.2f}\n"
|
||||
message += f" TP2: ${data['newTP2']:.2f}\n"
|
||||
message += f" SL: ${data['newSL']:.2f}\n\n"
|
||||
message += f"🎯 TP/SL orders updated for remaining size!"
|
||||
|
||||
await update.message.reply_text(message, parse_mode='Markdown')
|
||||
|
||||
print(f"✅ Position reduced: {reduce_percent}%", flush=True)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}", flush=True)
|
||||
await update.message.reply_text(f"❌ Error: {str(e)}")
|
||||
|
||||
async def validate_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Handle /validate command - check if positions match settings"""
|
||||
|
||||
@@ -331,6 +424,7 @@ def main():
|
||||
print(f" /status - Show open positions", flush=True)
|
||||
print(f" /validate - Validate positions against settings", flush=True)
|
||||
print(f" /scale [percent] - Scale position (default 50%)", flush=True)
|
||||
print(f" /reduce [percent] - Take partial profits (default 50%)", flush=True)
|
||||
print(f" /buySOL, /sellSOL", flush=True)
|
||||
print(f" /buyBTC, /sellBTC", flush=True)
|
||||
print(f" /buyETH, /sellETH", flush=True)
|
||||
@@ -342,6 +436,7 @@ def main():
|
||||
application.add_handler(CommandHandler("status", status_command))
|
||||
application.add_handler(CommandHandler("validate", validate_command))
|
||||
application.add_handler(CommandHandler("scale", scale_command))
|
||||
application.add_handler(CommandHandler("reduce", reduce_command))
|
||||
application.add_handler(CommandHandler("buySOL", trade_command))
|
||||
application.add_handler(CommandHandler("sellSOL", trade_command))
|
||||
application.add_handler(CommandHandler("buyBTC", trade_command))
|
||||
|
||||
Reference in New Issue
Block a user