chore: Organize workspace structure - move docs, workflows, scripts to subdirectories
Organization: - Created docs/ with setup/, guides/, history/ subdirectories - Created workflows/ with trading/, analytics/, telegram/, archive/ subdirectories - Created scripts/ with docker/, setup/, testing/ subdirectories - Created tests/ for TypeScript test files - Created archive/ for unused reference files Moved files: - 17 documentation files → docs/ - 16 workflow JSON files → workflows/ - 10 shell scripts → scripts/ - 4 test files → tests/ - 5 unused files → archive/ Updated: - README.md with new file structure and documentation paths Deleted: - data/ (empty directory) - screenshots/ (empty directory) Critical files remain in root: - telegram_command_bot.py (active bot - used by Dockerfile) - watch-restart.sh (systemd service dependency) - All Dockerfiles and docker-compose files - All environment files Validation: Containers running (trading-bot-v4, telegram-trade-bot, postgres) API responding (positions endpoint tested) Telegram bot functional (/status command tested) All critical files present in root No code changes - purely organizational. System continues running without interruption. Recovery: git revert HEAD or git reset --hard cleanup-before
This commit is contained in:
11
archive/CREATE_NEW_BOT.md
Normal file
11
archive/CREATE_NEW_BOT.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Steps to Create a NEW Telegram Bot for Manual Trades
|
||||
|
||||
1. Open Telegram on your phone
|
||||
2. Search for @BotFather
|
||||
3. Send: /newbot
|
||||
4. Name it: "Manual Trader Bot" (or anything you want)
|
||||
5. Username: something like "my_manual_trader_bot" (must end in _bot)
|
||||
6. Copy the TOKEN you get
|
||||
7. Send that token to me and I'll configure everything
|
||||
|
||||
This will be a SEPARATE bot from your n8n bot, so no conflicts!
|
||||
105
archive/quick-trade.html
Normal file
105
archive/quick-trade.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Quick Trade</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 500px;
|
||||
margin: 50px auto;
|
||||
padding: 20px;
|
||||
background: #1a1a1a;
|
||||
color: #fff;
|
||||
}
|
||||
button {
|
||||
width: 48%;
|
||||
padding: 30px;
|
||||
margin: 5px 1%;
|
||||
font-size: 20px;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
.buy { background: #10b981; color: white; }
|
||||
.sell { background: #ef4444; color: white; }
|
||||
.row { margin: 20px 0; }
|
||||
h2 { text-align: center; color: #10b981; }
|
||||
#status {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
.success { background: #10b981; }
|
||||
.error { background: #ef4444; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>📱 Quick Trade</h2>
|
||||
|
||||
<div class="row">
|
||||
<button class="buy" onclick="trade('buy', 'sol')">BUY SOL</button>
|
||||
<button class="sell" onclick="trade('sell', 'sol')">SELL SOL</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button class="buy" onclick="trade('buy', 'btc')">BUY BTC</button>
|
||||
<button class="sell" onclick="trade('sell', 'btc')">SELL BTC</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button class="buy" onclick="trade('buy', 'eth')">BUY ETH</button>
|
||||
<button class="sell" onclick="trade('sell', 'eth')">SELL ETH</button>
|
||||
</div>
|
||||
|
||||
<div id="status"></div>
|
||||
|
||||
<script>
|
||||
// SECRET TOKEN - Keep this file private! Only accessible from localhost
|
||||
const SECRET_TOKEN = 'YOUR_SECRET_HERE_' + Math.random().toString(36).substring(7);
|
||||
|
||||
// Only allow from localhost/internal network
|
||||
if (!window.location.hostname.match(/^(localhost|127\.0\.0\.1|10\.|192\.168\.|172\.)/)) {
|
||||
document.body.innerHTML = '<h2 style="color:red">Access Denied</h2>';
|
||||
}
|
||||
|
||||
async function trade(action, symbol) {
|
||||
const status = document.getElementById('status');
|
||||
status.style.display = 'block';
|
||||
status.className = '';
|
||||
status.textContent = '⏳ Sending...';
|
||||
|
||||
try {
|
||||
const response = await fetch('http://10.0.0.48:8098/webhook/3371ad7c-0866-4161-90a4-f251de4aceb8', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Secret-Token': SECRET_TOKEN
|
||||
},
|
||||
body: JSON.stringify({
|
||||
body: `${action} ${symbol}`,
|
||||
secret: SECRET_TOKEN
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
status.className = 'success';
|
||||
status.textContent = `✅ ${action.toUpperCase()} ${symbol.toUpperCase()} sent!`;
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
status.className = 'error';
|
||||
status.textContent = `❌ Error: ${error.message}`;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
status.style.display = 'none';
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
80
archive/telegram-to-webhook.py
Normal file
80
archive/telegram-to-webhook.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Telegram to n8n Webhook Bridge
|
||||
Monitors your Telegram chat for trade commands and forwards to n8n webhook
|
||||
"""
|
||||
import os
|
||||
import requests
|
||||
from telegram import Update
|
||||
from telegram.ext import Application, MessageHandler, filters, ContextTypes
|
||||
|
||||
# Configuration
|
||||
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', 'YOUR_BOT_TOKEN')
|
||||
N8N_WEBHOOK_URL = os.getenv('N8N_WEBHOOK_URL', 'https://your-n8n.com/webhook/manual-telegram-trade')
|
||||
ALLOWED_CHAT_ID = int(os.getenv('TELEGRAM_CHAT_ID', '579304651'))
|
||||
|
||||
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Handle incoming Telegram messages"""
|
||||
|
||||
# Only process messages from your chat
|
||||
if update.message.chat_id != ALLOWED_CHAT_ID:
|
||||
return
|
||||
|
||||
message_text = update.message.text.lower()
|
||||
|
||||
# Only process trade commands (containing buy/sell/long/short)
|
||||
if not any(word in message_text for word in ['buy', 'sell', 'long', 'short']):
|
||||
return
|
||||
|
||||
print(f"📨 Received trade command: {message_text}")
|
||||
|
||||
# Forward to n8n webhook
|
||||
try:
|
||||
response = requests.post(
|
||||
N8N_WEBHOOK_URL,
|
||||
json={'message': message_text},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
print(f"✅ Forwarded to n8n: {message_text}")
|
||||
# Send confirmation
|
||||
await update.message.reply_text(
|
||||
f"🤖 Processing: {message_text}\n"
|
||||
f"Forwarded to trading bot..."
|
||||
)
|
||||
else:
|
||||
print(f"❌ Webhook error: {response.status_code}")
|
||||
await update.message.reply_text(f"❌ Error: Webhook returned {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error forwarding to webhook: {e}")
|
||||
await update.message.reply_text(f"❌ Error: {str(e)}")
|
||||
|
||||
def main():
|
||||
"""Start the bot"""
|
||||
|
||||
if TELEGRAM_BOT_TOKEN == 'YOUR_BOT_TOKEN':
|
||||
print("❌ Error: Set TELEGRAM_BOT_TOKEN environment variable")
|
||||
return
|
||||
|
||||
if N8N_WEBHOOK_URL == 'https://your-n8n.com/webhook/manual-telegram-trade':
|
||||
print("❌ Error: Set N8N_WEBHOOK_URL environment variable")
|
||||
return
|
||||
|
||||
print(f"🚀 Starting Telegram to n8n bridge...")
|
||||
print(f"📱 Monitoring chat ID: {ALLOWED_CHAT_ID}")
|
||||
print(f"🔗 Webhook URL: {N8N_WEBHOOK_URL}")
|
||||
|
||||
# Create application
|
||||
application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
|
||||
|
||||
# Add message handler
|
||||
application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
|
||||
|
||||
# Start polling
|
||||
print("✅ Bot started! Send trade commands to your Telegram chat.")
|
||||
application.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
166
archive/telegram_trade_bot.py
Normal file
166
archive/telegram_trade_bot.py
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple Telegram bot that forwards trade commands to n8n webhook
|
||||
Install: pip3 install python-telegram-bot requests
|
||||
Run: python3 telegram_trade_bot.py
|
||||
"""
|
||||
import os
|
||||
import requests
|
||||
from telegram import Update
|
||||
from telegram.ext import Application, MessageHandler, CommandHandler, filters, ContextTypes
|
||||
|
||||
# Configuration - SET THESE!
|
||||
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN', 'YOUR_BOT_TOKEN_HERE')
|
||||
N8N_WEBHOOK_URL = os.getenv('N8N_WEBHOOK_URL', 'https://your-n8n.com/webhook/manual-trade')
|
||||
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'))
|
||||
|
||||
async def handle_status_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Handle /status command - show current open positions"""
|
||||
|
||||
# Only your chat
|
||||
if update.message.chat_id != ALLOWED_CHAT_ID:
|
||||
return
|
||||
|
||||
print(f"📊 /status command received")
|
||||
|
||||
try:
|
||||
# Fetch positions from trading bot API
|
||||
response = requests.get(
|
||||
f"{TRADING_BOT_URL}/api/trading/positions",
|
||||
headers={'Authorization': f'Bearer {API_SECRET_KEY}'},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if not response.ok:
|
||||
await update.message.reply_text(f"❌ Error fetching positions: {response.status_code}")
|
||||
return
|
||||
|
||||
data = response.json()
|
||||
|
||||
if not data.get('success'):
|
||||
await update.message.reply_text("❌ Failed to fetch positions")
|
||||
return
|
||||
|
||||
# Check if there are active positions
|
||||
positions = data.get('positions', [])
|
||||
|
||||
if not positions:
|
||||
await update.message.reply_text("📊 *No open positions*\n\nAll clear! Ready for new signals.", parse_mode='Markdown')
|
||||
return
|
||||
|
||||
# Format position information
|
||||
for pos in positions:
|
||||
symbol = pos['symbol']
|
||||
direction = pos['direction'].upper()
|
||||
entry = pos['entryPrice']
|
||||
current = pos['currentPrice']
|
||||
size = pos['currentSize']
|
||||
leverage = pos['leverage']
|
||||
|
||||
# P&L
|
||||
pnl_pct = pos['profitPercent']
|
||||
account_pnl = pos['accountPnL']
|
||||
unrealized_pnl = pos['unrealizedPnL']
|
||||
|
||||
# Targets
|
||||
sl = pos['stopLoss']
|
||||
tp1 = pos['takeProfit1']
|
||||
tp2 = pos['takeProfit2']
|
||||
tp1_hit = pos['tp1Hit']
|
||||
|
||||
# Age
|
||||
age_min = pos['ageMinutes']
|
||||
|
||||
# Build status message
|
||||
emoji = "🟢" if account_pnl > 0 else "🔴" if account_pnl < 0 else "⚪"
|
||||
direction_emoji = "📈" if direction == "LONG" else "📉"
|
||||
|
||||
message = f"{emoji} *{symbol}* {direction_emoji} {direction}\n\n"
|
||||
message += f"💰 *P&L:* ${unrealized_pnl:.2f} ({account_pnl:+.2f}% account)\n"
|
||||
message += f"📊 *Price Change:* {pnl_pct:+.2f}%\n\n"
|
||||
message += f"*Entry:* ${entry:.4f}\n"
|
||||
message += f"*Current:* ${current:.4f}\n\n"
|
||||
message += f"*Targets:*\n"
|
||||
message += f" TP1: ${tp1:.4f} {'✅' if tp1_hit else '⏳'}\n"
|
||||
message += f" TP2: ${tp2:.4f}\n"
|
||||
message += f" SL: ${sl:.4f}\n\n"
|
||||
message += f"*Position:* ${size:.2f} @ {leverage}x\n"
|
||||
message += f"*Age:* {age_min} min"
|
||||
|
||||
await update.message.reply_text(message, parse_mode='Markdown')
|
||||
|
||||
print(f"✅ Status sent: {len(positions)} position(s)")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Error: {e}")
|
||||
await update.message.reply_text(f"❌ Error: {str(e)}")
|
||||
|
||||
async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Forward trade commands to n8n"""
|
||||
|
||||
# Only your chat
|
||||
if update.message.chat_id != ALLOWED_CHAT_ID:
|
||||
return
|
||||
|
||||
text = update.message.text.lower().strip()
|
||||
|
||||
# Only process trade commands: buy/sell followed by symbol
|
||||
if not any(text.startswith(cmd) for cmd in ['buy', 'sell', 'long', 'short']):
|
||||
return
|
||||
|
||||
print(f"📨 {text}")
|
||||
|
||||
# Forward to n8n
|
||||
try:
|
||||
response = requests.post(
|
||||
N8N_WEBHOOK_URL,
|
||||
json={'text': text},
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.ok:
|
||||
print(f"✅ Sent to n8n")
|
||||
else:
|
||||
print(f"❌ Error {response.status_code}")
|
||||
await update.message.reply_text(f"❌ Error: {response.status_code}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ {e}")
|
||||
await update.message.reply_text(f"❌ Error: {str(e)}")
|
||||
|
||||
def main():
|
||||
"""Start bot"""
|
||||
|
||||
if TELEGRAM_BOT_TOKEN == 'YOUR_BOT_TOKEN_HERE':
|
||||
print("❌ Set TELEGRAM_BOT_TOKEN environment variable")
|
||||
print("Example: export TELEGRAM_BOT_TOKEN='your_token'")
|
||||
return
|
||||
|
||||
if N8N_WEBHOOK_URL == 'https://your-n8n.com/webhook/manual-trade':
|
||||
print("❌ Set N8N_WEBHOOK_URL environment variable")
|
||||
print("Example: export N8N_WEBHOOK_URL='https://n8n.yourdomain.com/webhook/manual-trade'")
|
||||
return
|
||||
|
||||
print(f"🚀 Telegram Trade Bot Starting...")
|
||||
print(f"📱 Chat ID: {ALLOWED_CHAT_ID}")
|
||||
print(f"🔗 Webhook: {N8N_WEBHOOK_URL}")
|
||||
print(f"🤖 Trading Bot: {TRADING_BOT_URL}")
|
||||
print(f"\n✅ Commands:")
|
||||
print(f" /status - Show open positions")
|
||||
print(f"\n✅ Trade messages:")
|
||||
print(f" buy sol")
|
||||
print(f" sell btc")
|
||||
print(f" buy eth")
|
||||
print(f" sell sol")
|
||||
|
||||
app = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
|
||||
app.add_handler(CommandHandler("status", handle_status_command))
|
||||
app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
|
||||
|
||||
print(f"\n🤖 Bot ready! Listening for commands...\n")
|
||||
app.run_polling(allowed_updates=Update.ALL_TYPES)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
120
archive/webapp-trade.html
Normal file
120
archive/webapp-trade.html
Normal file
@@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<script src="https://telegram.org/js/telegram-web-app.js"></script>
|
||||
<title>Quick Trade</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
padding: 20px;
|
||||
background: var(--tg-theme-bg-color, #1a1a1a);
|
||||
color: var(--tg-theme-text-color, #fff);
|
||||
min-height: 100vh;
|
||||
}
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
color: var(--tg-theme-accent-text-color, #10b981);
|
||||
}
|
||||
button {
|
||||
width: 48%;
|
||||
padding: 40px 10px;
|
||||
margin: 5px 1%;
|
||||
font-size: 18px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: transform 0.1s;
|
||||
}
|
||||
button:active { transform: scale(0.95); }
|
||||
.buy { background: #10b981; color: white; }
|
||||
.sell { background: #ef4444; color: white; }
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 15px 0;
|
||||
}
|
||||
#status {
|
||||
margin-top: 30px;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
display: none;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.success { background: #10b981; color: white; }
|
||||
.error { background: #ef4444; color: white; }
|
||||
.loading { background: #3b82f6; color: white; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>📊 Quick Trade</h2>
|
||||
|
||||
<div class="row">
|
||||
<button class="buy" onclick="trade('buy', 'SOL')">🟢 BUY<br>SOL</button>
|
||||
<button class="sell" onclick="trade('sell', 'SOL')">🔴 SELL<br>SOL</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button class="buy" onclick="trade('buy', 'BTC')">🟢 BUY<br>BTC</button>
|
||||
<button class="sell" onclick="trade('sell', 'BTC')">🔴 SELL<br>BTC</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<button class="buy" onclick="trade('buy', 'ETH')">🟢 BUY<br>ETH</button>
|
||||
<button class="sell" onclick="trade('sell', 'ETH')">🔴 SELL<br>ETH</button>
|
||||
</div>
|
||||
|
||||
<div id="status"></div>
|
||||
|
||||
<script>
|
||||
let tg = window.Telegram.WebApp;
|
||||
tg.ready();
|
||||
tg.expand();
|
||||
|
||||
async function trade(action, symbol) {
|
||||
const status = document.getElementById('status');
|
||||
status.style.display = 'block';
|
||||
status.className = 'loading';
|
||||
status.textContent = '⏳ Sending trade...';
|
||||
|
||||
// Haptic feedback
|
||||
if (tg.HapticFeedback) {
|
||||
tg.HapticFeedback.impactOccurred('medium');
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('http://10.0.0.48:8098/webhook/3371ad7c-0866-4161-90a4-f251de4aceb8', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ body: `${action} ${symbol}` })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
status.className = 'success';
|
||||
status.textContent = `✅ ${action.toUpperCase()} ${symbol} executed!`;
|
||||
if (tg.HapticFeedback) {
|
||||
tg.HapticFeedback.notificationOccurred('success');
|
||||
}
|
||||
} else {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
} catch (error) {
|
||||
status.className = 'error';
|
||||
status.textContent = `❌ Error: ${error.message}`;
|
||||
if (tg.HapticFeedback) {
|
||||
tg.HapticFeedback.notificationOccurred('error');
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
status.style.display = 'none';
|
||||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user