Add TP1/SL consistency check on trade restore
This commit is contained in:
@@ -6,7 +6,7 @@ Only responds to YOUR commands in YOUR chat
|
||||
import os
|
||||
import requests
|
||||
from telegram import Update
|
||||
from telegram.ext import Application, CommandHandler, ContextTypes
|
||||
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters
|
||||
|
||||
# Configuration
|
||||
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
|
||||
@@ -15,6 +15,38 @@ TRADING_BOT_URL = os.getenv('TRADING_BOT_URL', 'http://trading-bot-v4:3000')
|
||||
API_SECRET_KEY = os.getenv('API_SECRET_KEY', '')
|
||||
ALLOWED_CHAT_ID = int(os.getenv('TELEGRAM_CHAT_ID', '579304651'))
|
||||
|
||||
SYMBOL_MAP = {
|
||||
'sol': {
|
||||
'tradingview': 'SOLUSDT',
|
||||
'label': 'SOL'
|
||||
},
|
||||
'eth': {
|
||||
'tradingview': 'ETHUSDT',
|
||||
'label': 'ETH'
|
||||
},
|
||||
'btc': {
|
||||
'tradingview': 'BTCUSDT',
|
||||
'label': 'BTC'
|
||||
},
|
||||
}
|
||||
|
||||
MANUAL_METRICS = {
|
||||
'long': {
|
||||
'atr': 0.45,
|
||||
'adx': 32,
|
||||
'rsi': 58,
|
||||
'volumeRatio': 1.25,
|
||||
'pricePosition': 55,
|
||||
},
|
||||
'short': {
|
||||
'atr': 0.45,
|
||||
'adx': 32,
|
||||
'rsi': 42,
|
||||
'volumeRatio': 1.25,
|
||||
'pricePosition': 45,
|
||||
},
|
||||
}
|
||||
|
||||
async def status_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Handle /status command - show current open positions"""
|
||||
|
||||
@@ -496,6 +528,100 @@ async def trade_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
print(f"❌ Error: {e}", flush=True)
|
||||
await update.message.reply_text(f"❌ Error: {str(e)}")
|
||||
|
||||
|
||||
async def manual_trade_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Execute manual long/short commands sent as plain text."""
|
||||
|
||||
if update.message is None:
|
||||
return
|
||||
|
||||
if update.message.chat_id != ALLOWED_CHAT_ID:
|
||||
return
|
||||
|
||||
text = update.message.text.strip().lower()
|
||||
|
||||
parts = text.split()
|
||||
if len(parts) != 2:
|
||||
return
|
||||
|
||||
direction, symbol_key = parts[0], parts[1]
|
||||
|
||||
if direction not in ('long', 'short'):
|
||||
return
|
||||
|
||||
symbol_info = SYMBOL_MAP.get(symbol_key)
|
||||
if not symbol_info:
|
||||
return
|
||||
|
||||
metrics = MANUAL_METRICS[direction]
|
||||
|
||||
payload = {
|
||||
'symbol': symbol_info['tradingview'],
|
||||
'direction': direction,
|
||||
'timeframe': 'manual',
|
||||
'signalStrength': 'manual',
|
||||
'atr': metrics['atr'],
|
||||
'adx': metrics['adx'],
|
||||
'rsi': metrics['rsi'],
|
||||
'volumeRatio': metrics['volumeRatio'],
|
||||
'pricePosition': metrics['pricePosition'],
|
||||
}
|
||||
|
||||
try:
|
||||
print(f"🚀 Manual trade: {direction.upper()} {symbol_info['label']}", flush=True)
|
||||
|
||||
response = requests.post(
|
||||
f"{TRADING_BOT_URL}/api/trading/execute",
|
||||
headers={'Authorization': f'Bearer {API_SECRET_KEY}'},
|
||||
json=payload,
|
||||
timeout=30,
|
||||
)
|
||||
|
||||
print(f"📥 Manual trade response: {response.status_code}", flush=True)
|
||||
|
||||
if not response.ok:
|
||||
await update.message.reply_text(
|
||||
f"❌ Execution error ({response.status_code})"
|
||||
)
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
|
||||
if not data.get('success'):
|
||||
message = data.get('message') or data.get('error') or 'Trade rejected'
|
||||
await update.message.reply_text(f"❌ {message}")
|
||||
return
|
||||
|
||||
entry_price = data.get('entryPrice')
|
||||
notional = data.get('positionSize')
|
||||
leverage = data.get('leverage')
|
||||
tp1 = data.get('takeProfit1')
|
||||
tp2 = data.get('takeProfit2')
|
||||
sl = data.get('stopLoss')
|
||||
|
||||
entry_text = f"${entry_price:.4f}" if entry_price is not None else 'n/a'
|
||||
size_text = (
|
||||
f"${notional:.2f} @ {leverage}x"
|
||||
if notional is not None and leverage is not None
|
||||
else 'n/a'
|
||||
)
|
||||
tp1_text = f"${tp1:.4f}" if tp1 is not None else 'n/a'
|
||||
tp2_text = f"${tp2:.4f}" if tp2 is not None else 'n/a'
|
||||
sl_text = f"${sl:.4f}" if sl is not None else 'n/a'
|
||||
|
||||
success_message = (
|
||||
f"✅ OPENED {direction.upper()} {symbol_info['label']}\n"
|
||||
f"Entry: {entry_text}\n"
|
||||
f"Size: {size_text}\n"
|
||||
f"TP1: {tp1_text}\nTP2: {tp2_text}\nSL: {sl_text}"
|
||||
)
|
||||
|
||||
await update.message.reply_text(success_message)
|
||||
|
||||
except Exception as exc:
|
||||
print(f"❌ Manual trade failed: {exc}", flush=True)
|
||||
await update.message.reply_text(f"❌ Error: {exc}")
|
||||
|
||||
def main():
|
||||
"""Start the bot"""
|
||||
|
||||
@@ -511,6 +637,7 @@ def main():
|
||||
print(f" /buySOL, /sellSOL", flush=True)
|
||||
print(f" /buyBTC, /sellBTC", flush=True)
|
||||
print(f" /buyETH, /sellETH", flush=True)
|
||||
print(f" long sol | short btc (plain text)", flush=True)
|
||||
|
||||
# Create application
|
||||
application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
|
||||
@@ -527,6 +654,10 @@ def main():
|
||||
application.add_handler(CommandHandler("sellBTC", trade_command))
|
||||
application.add_handler(CommandHandler("buyETH", trade_command))
|
||||
application.add_handler(CommandHandler("sellETH", trade_command))
|
||||
application.add_handler(MessageHandler(
|
||||
filters.TEXT & (~filters.COMMAND),
|
||||
manual_trade_handler,
|
||||
))
|
||||
|
||||
# Start polling
|
||||
print("\n🤖 Bot ready! Send commands to your Telegram.\n", flush=True)
|
||||
|
||||
Reference in New Issue
Block a user