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:
mindesbunister
2025-10-27 12:59:25 +01:00
parent f8f289232a
commit 14d5de2c64
48 changed files with 37 additions and 14 deletions

11
archive/CREATE_NEW_BOT.md Normal file
View 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
View 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>

View 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()

View 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
View 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>