feat: add Telegram bot for manual trade commands

- Added telegram_command_bot.py with slash commands (/buySOL, /sellBTC, etc)
- Docker compose setup with DNS configuration
- Sends trades as plain text to n8n webhook (same format as TradingView)
- Improved Telegram success message formatting
- Only responds to authorized chat ID (579304651)
- Commands: /buySOL, /sellSOL, /buyBTC, /sellBTC, /buyETH, /sellETH
This commit is contained in:
mindesbunister
2025-10-27 00:23:09 +01:00
parent c2842f88c0
commit 3e2cf2eec2
24 changed files with 2573 additions and 6 deletions

12
.env
View File

@@ -61,11 +61,11 @@ PYTH_HERMES_URL=https://hermes.pyth.network
# Position sizing
# Base position size in USD (default: 50 for safe testing)
# Example: 50 with 10x leverage = $500 notional position
MAX_POSITION_SIZE_USD=20
MAX_POSITION_SIZE_USD=80
# Leverage multiplier (1-20, default: 10)
# Higher leverage = bigger gains AND bigger losses
LEVERAGE=5
LEVERAGE=10
# Risk parameters (as percentages)
# Stop Loss: Close 100% of position when price drops this much
@@ -93,19 +93,19 @@ HARD_STOP_PERCENT=-2.5
# Take Profit 1: Close 50% of position at this profit level
# Example: +0.7% on 10x = +7% account gain
TAKE_PROFIT_1_PERCENT=0.7
TAKE_PROFIT_1_PERCENT=0.4
# Take Profit 1 Size: What % of position to close at TP1
# Example: 50 = close 50% of position
TAKE_PROFIT_1_SIZE_PERCENT=50
TAKE_PROFIT_1_SIZE_PERCENT=75
# Take Profit 2: Close remaining 50% at this profit level
# Example: +1.5% on 10x = +15% account gain
TAKE_PROFIT_2_PERCENT=1.5
TAKE_PROFIT_2_PERCENT=0.8
# Take Profit 2 Size: What % of remaining position to close at TP2
# Example: 100 = close all remaining position
TAKE_PROFIT_2_SIZE_PERCENT=50
TAKE_PROFIT_2_SIZE_PERCENT=100
# Emergency Stop: Hard stop if this level is breached
# Example: -2.0% on 10x = -20% account loss (rare but protects from flash crashes)

11
.env.telegram-bot Normal file
View File

@@ -0,0 +1,11 @@
# Telegram Trade Bot Configuration
# Get your bot token from @BotFather on Telegram
TELEGRAM_BOT_TOKEN=8240234365:AAEm6hg_XOm54x8ctnwpNYreFKRAEvWU3uY
# Your n8n webhook URL (after importing telegram-manual-trade-FINAL.json, activate it and replace this URL)
# Your n8n webhook URL (public HTTPS)
N8N_WEBHOOK_URL=https://flow.egonetix.de/webhook/3371ad7c-0866-4161-90a4-f251de4aceb8
# Your Telegram chat ID (already set to 579304651)
TELEGRAM_CHAT_ID=579304651

262
.github/copilot-instructions.md vendored Normal file
View File

@@ -0,0 +1,262 @@
# AI Agent Instructions for Trading Bot v4
## Architecture Overview
**Type:** Autonomous cryptocurrency trading bot with Next.js 15 frontend + Solana/Drift Protocol backend
**Data Flow:** TradingView → n8n webhook → Next.js API → Drift Protocol (Solana DEX) → Real-time monitoring → Auto-exit
**Key Design Principle:** Dual-layer redundancy - every trade has both on-chain orders (Drift) AND software monitoring (Position Manager) as backup.
## Critical Components
### 1. Position Manager (`lib/trading/position-manager.ts`)
**Purpose:** Software-based monitoring loop that checks prices every 2 seconds and closes positions via market orders
**Singleton pattern:** Always use `getPositionManager()` - never instantiate directly
```typescript
const positionManager = getPositionManager()
await positionManager.addTrade(activeTrade)
```
**Key behaviors:**
- Tracks `ActiveTrade` objects in a Map
- Dynamic SL adjustments: Moves to breakeven at +0.5%, locks profit at +1.2%
- Closes positions via `closePosition()` market orders when targets hit
- Acts as backup if on-chain orders don't fill
### 2. Drift Client (`lib/drift/client.ts`)
**Purpose:** Solana/Drift Protocol SDK wrapper for order execution
**Singleton pattern:** Use `initializeDriftService()` and `getDriftService()` - maintains single connection
```typescript
const driftService = await initializeDriftService()
const health = await driftService.getAccountHealth()
```
**Wallet handling:** Supports both JSON array `[91,24,...]` and base58 string formats from Phantom wallet
### 3. Order Placement (`lib/drift/orders.ts`)
**Critical function:** `placeExitOrders()` - places TP/SL orders on-chain
**Dual Stop System** (USE_DUAL_STOPS=true):
```typescript
// Soft stop: TRIGGER_LIMIT at -1.5% (avoids wicks)
// Hard stop: TRIGGER_MARKET at -2.5% (guarantees exit)
```
**Order types:**
- Entry: MARKET (immediate execution)
- TP1/TP2: LIMIT reduce-only orders
- Soft SL: TRIGGER_LIMIT reduce-only
- Hard SL: TRIGGER_MARKET reduce-only
### 4. Database (`lib/database/trades.ts` + `prisma/schema.prisma`)
**Purpose:** PostgreSQL via Prisma ORM for trade history and analytics
**Models:** Trade, PriceUpdate, SystemEvent, DailyStats
**Singleton pattern:** Use `getPrismaClient()` - never instantiate PrismaClient directly
**Key functions:**
- `createTrade()` - Save trade after execution (includes dual stop TX signatures)
- `updateTradeExit()` - Record exit with P&L
- `addPriceUpdate()` - Track price movements (called by Position Manager)
- `getTradeStats()` - Win rate, profit factor, avg win/loss
## Configuration System
**Three-layer merge:**
1. `DEFAULT_TRADING_CONFIG` (config/trading.ts)
2. Environment variables (.env) via `getConfigFromEnv()`
3. Runtime overrides via `getMergedConfig(overrides)`
**Always use:** `getMergedConfig()` to get final config - never read env vars directly in business logic
**Symbol normalization:** TradingView sends "SOLUSDT" → must convert to "SOL-PERP" for Drift
```typescript
const driftSymbol = normalizeTradingViewSymbol(body.symbol)
```
## API Endpoints Architecture
**Authentication:** All `/api/trading/*` endpoints (except `/test`) require `Authorization: Bearer API_SECRET_KEY`
**Pattern:** Each endpoint follows same flow:
1. Auth check
2. Get config via `getMergedConfig()`
3. Initialize Drift service
4. Check account health
5. Execute operation
6. Save to database
7. Add to Position Manager if applicable
**Key endpoints:**
- `/api/trading/execute` - Main entry point from n8n (production)
- `/api/trading/test` - Test trades from settings UI (no auth required)
- `/api/trading/close` - Manual position closing
- `/api/trading/positions` - Query open positions
- `/api/settings` - Get/update config (writes to .env file)
## Critical Workflows
### Execute Trade (Production)
```
n8n webhook → /api/trading/execute
↓ normalize symbol (SOLUSDT → SOL-PERP)
↓ getMergedConfig()
↓ openPosition() [MARKET order]
↓ calculate dual stop prices if enabled
↓ placeExitOrders() [on-chain TP/SL orders]
↓ createTrade() [save to database]
↓ positionManager.addTrade() [start monitoring]
```
### Position Monitoring Loop
```
Position Manager every 2s:
↓ getPythPriceMonitor().getLatestPrice()
↓ Calculate current P&L
↓ Check TP1 hit → closePosition(75%)
↓ Check TP2 hit → closePosition(100%)
↓ Check SL hit → closePosition(100%)
↓ Check dynamic adjustments (breakeven, profit lock)
↓ addPriceUpdate() [save to database]
```
### Settings Update
```
Web UI → /api/settings POST
↓ Validate new settings
↓ Write to .env file using string replacement
↓ Return success
↓ User clicks "Restart Bot" → /api/restart
↓ Creates /tmp/trading-bot-restart.flag
↓ watch-restart.sh detects flag
↓ Executes: docker restart trading-bot-v4
```
## Docker Context
**Multi-stage build:** deps → builder → runner (Node 20 Alpine)
**Critical Dockerfile steps:**
1. Install deps with `npm install --production`
2. Copy source and `npx prisma generate` (MUST happen before build)
3. `npm run build` (Next.js standalone output)
4. Runner stage copies standalone + static + node_modules + Prisma client
**Container networking:**
- External: `trading-bot-v4` on port 3001
- Internal: Next.js on port 3000
- Database: `trading-bot-postgres` on 172.28.0.0/16 network
**DATABASE_URL caveat:** Use `trading-bot-postgres` (container name) in .env for runtime, but `localhost:5432` for Prisma CLI migrations from host
## Project-Specific Patterns
### 1. Singleton Services
Never create multiple instances - always use getter functions:
```typescript
const driftService = await initializeDriftService() // NOT: new DriftService()
const positionManager = getPositionManager() // NOT: new PositionManager()
const prisma = getPrismaClient() // NOT: new PrismaClient()
```
### 2. Price Calculations
Direction matters for long vs short:
```typescript
function calculatePrice(entry: number, percent: number, direction: 'long' | 'short') {
if (direction === 'long') {
return entry * (1 + percent / 100) // Long: +1% = higher price
} else {
return entry * (1 - percent / 100) // Short: +1% = lower price
}
}
```
### 3. Error Handling
Database failures should not fail trades - always wrap in try/catch:
```typescript
try {
await createTrade(params)
console.log('💾 Trade saved to database')
} catch (dbError) {
console.error('❌ Failed to save trade:', dbError)
// Don't fail the trade if database save fails
}
```
### 4. Reduce-Only Orders
All exit orders MUST be reduce-only (can only close, not open positions):
```typescript
const orderParams = {
reduceOnly: true, // CRITICAL for TP/SL orders
// ... other params
}
```
## Testing Commands
```bash
# Local development
npm run dev
# Build production
npm run build && npm start
# Docker build and restart
docker compose build trading-bot
docker compose up -d --force-recreate trading-bot
docker logs -f trading-bot-v4
# Database operations
npx prisma generate # Generate client
DATABASE_URL="postgresql://...@localhost:5432/..." npx prisma migrate dev
docker exec trading-bot-postgres psql -U postgres -d trading_bot_v4 -c "\dt"
# Test trade from UI
# Go to http://localhost:3001/settings
# Click "Test LONG" or "Test SHORT"
```
## Common Pitfalls
1. **Prisma not generated in Docker:** Must run `npx prisma generate` in Dockerfile BEFORE `npm run build`
2. **Wrong DATABASE_URL:** Container runtime needs `trading-bot-postgres`, Prisma CLI from host needs `localhost:5432`
3. **Symbol format mismatch:** Always normalize with `normalizeTradingViewSymbol()` before calling Drift
4. **Missing reduce-only flag:** Exit orders without `reduceOnly: true` can accidentally open new positions
5. **Singleton violations:** Creating multiple DriftClient or Position Manager instances causes connection/state issues
6. **Type errors with Prisma:** The Trade type from Prisma is only available AFTER `npx prisma generate` - use explicit types or `// @ts-ignore` carefully
## File Conventions
- **API routes:** `app/api/[feature]/[action]/route.ts` (Next.js 15 App Router)
- **Services:** `lib/[service]/[module].ts` (drift, pyth, trading, database)
- **Config:** Single source in `config/trading.ts` with env merging
- **Types:** Define interfaces in same file as implementation (not separate types directory)
- **Console logs:** Use emojis for visual scanning: 🎯 🚀 ✅ ❌ 💰 📊 🛡️
## When Making Changes
1. **Adding new config:** Update DEFAULT_TRADING_CONFIG + getConfigFromEnv() + .env file
2. **Adding database fields:** Update prisma/schema.prisma → migrate → regenerate client → rebuild Docker
3. **Changing order logic:** Test with DRY_RUN=true first, use small position sizes ($10)
4. **API endpoint changes:** Update both endpoint + corresponding n8n workflow JSON
5. **Docker changes:** Rebuild with `docker compose build trading-bot` then restart container
## Integration Points
- **n8n:** Expects exact response format from `/api/trading/execute` (see n8n-complete-workflow.json)
- **Drift Protocol:** Uses SDK v2.75.0 - check docs at docs.drift.trade for API changes
- **Pyth Network:** WebSocket + HTTP fallback for price feeds (handles reconnection)
- **PostgreSQL:** Version 16-alpine, must be running before bot starts
---
**Key Mental Model:** Think of this as two parallel systems (on-chain orders + software monitoring) working together. The Position Manager is the "backup brain" that constantly watches and acts if on-chain orders fail. Both write to the same database for complete trade history.

11
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!

12
Dockerfile.telegram-bot Normal file
View File

@@ -0,0 +1,12 @@
FROM python:3.11-slim
WORKDIR /app
# Install dependencies
RUN pip install --no-cache-dir python-telegram-bot==20.7 requests
# Copy bot script
COPY telegram_command_bot.py .
# Run bot
CMD ["python3", "telegram_command_bot.py"]

24
GET_BOT_TOKEN.sh Executable file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
# One-time setup to get your Telegram Bot Token
# Run this and follow the instructions
echo "🤖 Telegram Bot Setup Instructions"
echo "===================================="
echo ""
echo "1. Open Telegram on your phone"
echo "2. Search for '@BotFather'"
echo "3. Start a chat with BotFather"
echo "4. Send this message: /newbot"
echo "5. BotFather will ask for a name - choose anything like 'My Trading Bot'"
echo "6. BotFather will ask for a username - must end in 'bot', like 'mytrading_bot'"
echo "7. BotFather will give you a TOKEN that looks like:"
echo " 123456789:ABCdefGHIjklMNOpqrsTUVwxyz"
echo ""
echo "8. Copy that token and run:"
echo " nano .env.telegram-bot"
echo ""
echo "9. Replace 'your_bot_token_here' with your actual token"
echo ""
echo "10. Save (Ctrl+O, Enter, Ctrl+X)"
echo ""
echo "Then run: ./setup_telegram_bot.sh"

486
Money_Machine.json Normal file
View File

@@ -0,0 +1,486 @@
{
"name": "Money Machine",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "3371ad7c-0866-4161-90a4-f251de4aceb8",
"options": {}
},
"id": "35b54214-9761-49dc-97b6-df39543f0a7b",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
-840,
660
],
"webhookId": "3371ad7c-0866-4161-90a4-f251de4aceb8"
},
{
"parameters": {
"fields": {
"values": [
{
"name": "rawMessage",
"stringValue": "={{ $json.body }}"
},
{
"name": "symbol",
"stringValue": "={{ ($json.body || '').toString().match(/\\bSOL\\b/i) ? 'SOL-PERP' : (($json.body || '').toString().match(/\\bBTC\\b/i) ? 'BTC-PERP' : (($json.body || '').toString().match(/\\bETH\\b/i) ? 'ETH-PERP' : 'SOL-PERP')) }}"
},
{
"name": "direction",
"stringValue": "={{ ($json.body || '').toString().match(/\\b(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"stringValue": "5"
}
]
},
"options": {}
},
"id": "99336995-2326-4575-9970-26afcf957132",
"name": "Parse Signal",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
-660,
660
]
},
{
"parameters": {
"method": "POST",
"url": "http://10.0.0.48:3001/api/trading/check-risk",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer 2a344f0149442c857fb56c038c0c7d1b113883b830bec792c76f1e0efa15d6bb"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"symbol\": \"{{ $json.symbol }}\",\n \"direction\": \"{{ $json.direction }}\"\n}",
"options": {}
},
"id": "d42e7897-eadd-4202-8565-ac60759b46e1",
"name": "Check Risk",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
-340,
660
],
"credentials": {
"httpHeaderAuth": {
"id": "MATuNdkZclq5ISbr",
"name": "Header Auth account"
}
}
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.allowed }}",
"value2": true
}
]
}
},
"id": "a60bfecb-d2f4-4165-a609-e6ed437aa2aa",
"name": "Risk Passed?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
-140,
660
]
},
{
"parameters": {
"method": "POST",
"url": "http://10.0.0.48:3001/api/trading/execute",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendHeaders": true,
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "Bearer 2a344f0149442c857fb56c038c0c7d1b113883b830bec792c76f1e0efa15d6bb"
},
{
"name": "Content-Type",
"value": "application/json"
}
]
},
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={\n \"symbol\": \"{{ $('Parse Signal').item.json.symbol }}\",\n \"direction\": \"{{ $('Parse Signal').item.json.direction }}\",\n \"timeframe\": \"{{ $('Parse Signal').item.json.timeframe }}\",\n \"signalStrength\": \"strong\"\n}",
"options": {
"timeout": 30000
}
},
"id": "95c46846-4b6a-4f9e-ad93-be223b73a618",
"name": "Execute Trade",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
60,
560
],
"credentials": {
"httpHeaderAuth": {
"id": "MATuNdkZclq5ISbr",
"name": "Header Auth account"
}
}
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.success }}",
"value2": true
}
]
}
},
"id": "18342642-e76f-484f-b532-d29846536a9c",
"name": "Trade Success?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
260,
560
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "message",
"stringValue": "🟢 TRADE OPENED\\n\\n📊 Symbol: {{ $('Parse Signal').item.json.symbol }}\\n{{ $('Parse Signal').item.json.direction === 'long' ? '📈' : '📉' }} Direction: {{ $('Parse Signal').item.json.direction.toUpperCase() }}\\n⏰ Time: {{ $now.toFormat('HH:mm:ss') }}\\n\\n✅ Position is being monitored"
}
]
},
"options": {}
},
"id": "9da40e3d-b855-4c65-a032-c6fcf88245d4",
"name": "Format Success",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
460,
460
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "message",
"stringValue": "🔴 TRADE FAILED\\n\\n{{ $('Parse Signal').item.json.rawMessage }}\\n\\n❌ Error: {{ $json.error || $json.message }}\\n⏰ {{ $now.toFormat('HH:mm') }}"
}
]
},
"options": {}
},
"id": "500751c7-21bb-4351-8a6a-d43a1bfb9eaa",
"name": "Format Error",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
460,
660
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "message",
"stringValue": "⚠️ TRADE BLOCKED\\n\\n{{ $('Parse Signal').item.json.rawMessage }}\\n\\n🛑 Risk limits exceeded\\n⏰ {{ $now.toFormat('HH:mm') }}"
}
]
},
"options": {}
},
"id": "dec6cbc4-7550-40d3-9195-c4cc4f787b9b",
"name": "Format Risk",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
60,
760
]
},
{
"parameters": {
"chatId": "579304651",
"text": "={{ $json.message }}",
"additionalFields": {
"appendAttribution": false
}
},
"id": "6267b604-d39b-4cb7-98a5-2342cdced33b",
"name": "Telegram Success",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
660,
460
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"chatId": "579304651",
"text": "🟢 TRADE OPENED\n\n📊 Symbol: {{ $('Parse Signal').item.json.symbol }}\n{{ $('Parse Signal').item.json.direction === 'long' ? '📈' : '📉' }} Direction: {{ $('Parse Signal').item.json.direction.toUpperCase() }}\n⏰ Time: {{ $now.toFormat('HH:mm:ss') }}\n\n✅ Position is being monitored",
"additionalFields": {
"appendAttribution": false
}
},
"id": "88224fac-ef7a-41ec-b68a-e4bc1a5e3f31",
"name": "Telegram Error",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
660,
660
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"chatId": "579304651",
"text": "={{ $json.message }}",
"additionalFields": {
"appendAttribution": false
}
},
"id": "4eccaca4-a5e7-407f-aab9-663a98a8323b",
"name": "Telegram Risk",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
260,
760
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"chatId": "579304651",
"text": "={{ $json.signal.startsWith(\"Buy\") ? \"🟢 \" + $json.signal : \"🔴 \" + $json.signal }}\n",
"additionalFields": {
"appendAttribution": false
}
},
"id": "5a8eda4d-8945-4144-8672-022c9ee68bf6",
"name": "Telegram",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
-340,
840
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"fields": {
"values": [
{
"name": "signal",
"stringValue": "={{ $json.body.split('|')[0].trim() }}"
}
]
},
"options": {}
},
"id": "cce16424-fbb1-4191-b719-79ccfd59ec12",
"name": "Edit Fields",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
-660,
840
]
}
],
"pinData": {},
"connections": {
"Webhook": {
"main": [
[
{
"node": "Parse Signal",
"type": "main",
"index": 0
}
]
]
},
"Parse Signal": {
"main": [
[
{
"node": "Check Risk",
"type": "main",
"index": 0
},
{
"node": "Telegram",
"type": "main",
"index": 0
}
]
]
},
"Check Risk": {
"main": [
[
{
"node": "Risk Passed?",
"type": "main",
"index": 0
}
]
]
},
"Risk Passed?": {
"main": [
[
{
"node": "Execute Trade",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Risk",
"type": "main",
"index": 0
}
]
]
},
"Execute Trade": {
"main": [
[
{
"node": "Trade Success?",
"type": "main",
"index": 0
}
]
]
},
"Trade Success?": {
"main": [
[
{
"node": "Format Success",
"type": "main",
"index": 0
}
],
[
{
"node": "Format Error",
"type": "main",
"index": 0
}
]
]
},
"Format Success": {
"main": [
[
{
"node": "Telegram Success",
"type": "main",
"index": 0
}
]
]
},
"Format Error": {
"main": [
[
{
"node": "Telegram Error",
"type": "main",
"index": 0
}
]
]
},
"Format Risk": {
"main": [
[
{
"node": "Telegram Risk",
"type": "main",
"index": 0
}
]
]
},
"Edit Fields": {
"main": [
[]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "2cc10693-953a-4b97-8c86-750b3063096b",
"id": "xTCaxlyI02bQLxun",
"meta": {
"instanceId": "e766d4f0b5def8ee8cb8561cd9d2b9ba7733e1907990b6987bca40175f82c379"
},
"tags": []
}

115
TELEGRAM_BOT_README.md Normal file
View File

@@ -0,0 +1,115 @@
# Telegram Trading Bot - Quick Reference
## 🚀 Quick Setup (3 steps)
### 1. Import n8n workflow
- Open: http://10.0.0.48:8098
- Import: `telegram-manual-trade-FINAL.json`
- Connect last node to "Check Risk" in Money Machine
- Activate workflow
### 2. Get Telegram Bot Token
Run on your phone:
```
Open Telegram → @BotFather → /newbot → follow instructions
```
You'll get a token like: `123456789:ABCdefGHIjklMNOpqrsTUVwxyz`
### 3. Run setup
```bash
./complete_telegram_setup.sh
```
Paste your bot token when asked.
---
## 📱 Using it on your phone
Just send messages to your Telegram chat:
```
buy sol
sell btc
buy eth
sell sol
```
The bot will:
1. ✅ Parse your message
2. ✅ Forward to n8n webhook
3. ✅ n8n sends to your trading bot
4. ✅ Trade executes with risk checks
5. ✅ You get confirmation in Telegram
---
## 🔧 Management
**View logs:**
```bash
docker logs -f telegram-trade-bot
```
**Restart bot:**
```bash
docker restart telegram-trade-bot
```
**Stop bot:**
```bash
docker-compose -f docker-compose.telegram-bot.yml down
```
**Start bot:**
```bash
docker-compose -f docker-compose.telegram-bot.yml --env-file .env.telegram-bot up -d
```
---
## 📋 Configuration Files
- `.env.telegram-bot` - Bot token and webhook URL
- `docker-compose.telegram-bot.yml` - Docker configuration
- `telegram_trade_bot.py` - Bot source code
---
## 🐛 Troubleshooting
**Bot not responding?**
```bash
docker logs telegram-trade-bot
```
**Check if bot is running:**
```bash
docker ps | grep telegram
```
**Test webhook manually:**
```bash
curl -X POST http://10.0.0.48:8098/webhook/manual-trade \
-H "Content-Type: application/json" \
-d '{"text": "buy sol"}'
```
**Check n8n workflow:**
- Is it activated?
- Is the webhook URL correct?
- Is it connected to Check Risk node?
---
## ✅ Supported Commands
From your phone, send any of these:
- `buy sol` / `buy btc` / `buy eth`
- `sell sol` / `sell btc` / `sell eth`
- `long sol` / `short btc` (same as buy/sell)
The bot extracts:
- **Symbol**: SOL, BTC, or ETH (defaults to SOL)
- **Direction**: sell/short = short position, anything else = long position
Commands are case-insensitive: `BUY SOL`, `Buy Sol`, `buy sol` all work!

64
complete_telegram_setup.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/bin/bash
# Complete Telegram Trading Bot Setup
echo "🤖 Telegram Trading Bot - Complete Setup"
echo "=========================================="
echo ""
# Step 1: Check if workflow is imported
echo "📋 Step 1: n8n Workflow"
echo "-----------------------"
echo "1. Open n8n: http://10.0.0.48:8098"
echo "2. Import telegram-manual-trade-FINAL.json"
echo "3. Connect the last node '➡️ Connect to Check Risk Node' to your Money Machine's 'Check Risk' node"
echo "4. Activate the workflow"
echo ""
read -p "Done? (y/n) " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "❌ Complete step 1 first"
exit 1
fi
# Step 2: Get bot token
echo ""
echo "🤖 Step 2: Telegram Bot Token"
echo "------------------------------"
echo "Run: ./GET_BOT_TOKEN.sh for instructions"
echo ""
echo "Or if you already have a bot token:"
read -p "Paste your bot token here: " BOT_TOKEN
if [ -z "$BOT_TOKEN" ]; then
echo "❌ Bot token required"
exit 1
fi
# Update .env file
sed -i "s|TELEGRAM_BOT_TOKEN=.*|TELEGRAM_BOT_TOKEN=$BOT_TOKEN|" .env.telegram-bot
echo "✅ Bot token saved to .env.telegram-bot"
# Step 3: Build and start
echo ""
echo "🚀 Step 3: Starting Telegram Bot Container"
echo "-------------------------------------------"
docker-compose -f docker-compose.telegram-bot.yml --env-file .env.telegram-bot up -d --build
if [ $? -eq 0 ]; then
echo ""
echo "✅ SUCCESS! Telegram bot is running!"
echo ""
echo "📱 Test it now:"
echo " Open Telegram and send to your chat (579304651):"
echo " • buy sol"
echo " • sell btc"
echo " • buy eth"
echo ""
echo "📊 View logs: docker logs -f telegram-trade-bot"
echo ""
else
echo "❌ Failed to start container"
echo "Check logs: docker logs telegram-trade-bot"
fi

View File

@@ -0,0 +1,22 @@
version: '3.3'
services:
telegram-trade-bot:
build:
context: .
dockerfile: Dockerfile.telegram-bot
container_name: telegram-trade-bot
restart: unless-stopped
dns:
- 8.8.8.8
- 8.8.4.4
environment:
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
- N8N_WEBHOOK_URL=${N8N_WEBHOOK_URL}
- TELEGRAM_CHAT_ID=579304651
networks:
- traderv4_trading-net
networks:
traderv4_trading-net:
external: true

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

22
send_trade.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
# Quick script to trigger manual trades via n8n webhook
# Usage: ./send_trade.sh "buy SOL"
# ./send_trade.sh "sell BTC"
WEBHOOK_URL="https://YOUR_N8N_URL/webhook/manual-trade"
if [ -z "$1" ]; then
echo "Usage: $0 \"buy SOL\" or \"sell BTC\""
exit 1
fi
COMMAND="$1"
echo "📤 Sending command: $COMMAND"
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{\"text\": \"$COMMAND\"}"
echo ""
echo "✅ Command sent!"

60
setup_telegram_bot.sh Executable file
View File

@@ -0,0 +1,60 @@
#!/bin/bash
# Setup Telegram Trade Bot
echo "🤖 Telegram Trade Bot Setup"
echo "============================"
echo ""
# Check if .env.telegram-bot exists
if [ ! -f .env.telegram-bot ]; then
echo "❌ .env.telegram-bot not found!"
echo "Create it with your bot token and webhook URL"
exit 1
fi
# Source the env file
source .env.telegram-bot
# Check required vars
if [ "$TELEGRAM_BOT_TOKEN" = "your_bot_token_here" ]; then
echo "❌ Please set TELEGRAM_BOT_TOKEN in .env.telegram-bot"
echo ""
echo "Steps:"
echo "1. Message @BotFather on Telegram"
echo "2. Send /newbot"
echo "3. Follow instructions to get your bot token"
echo "4. Put the token in .env.telegram-bot"
exit 1
fi
if [[ "$N8N_WEBHOOK_URL" == *"your-n8n-url"* ]]; then
echo "❌ Please set N8N_WEBHOOK_URL in .env.telegram-bot"
echo ""
echo "Steps:"
echo "1. Import telegram-manual-trade-FINAL.json into n8n"
echo "2. Activate the workflow"
echo "3. Copy the webhook URL from the first node"
echo "4. Put the URL in .env.telegram-bot"
exit 1
fi
echo "✅ Configuration looks good!"
echo ""
echo "📱 Chat ID: $TELEGRAM_CHAT_ID"
echo "🔗 Webhook: $N8N_WEBHOOK_URL"
echo ""
echo "Building and starting bot..."
echo ""
# Build and start
docker-compose -f docker-compose.telegram-bot.yml --env-file .env.telegram-bot up -d --build
echo ""
echo "✅ Bot started!"
echo ""
echo "Test it by sending to your Telegram chat:"
echo " buy sol"
echo " sell btc"
echo " buy eth"
echo ""
echo "View logs: docker logs -f telegram-trade-bot"

View File

@@ -0,0 +1,161 @@
{
"name": "ADD THIS TO MONEY MACHINE - Manual Trade Listener",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"triggerAtMinute": 10
}
]
}
},
"id": "schedule-check-telegram",
"name": "Check Every 10 Seconds",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [
-1040,
280
]
},
{
"parameters": {
"resource": "message",
"operation": "getMany",
"chatId": "579304651",
"returnAll": false,
"limit": 5
},
"id": "get-recent-messages",
"name": "Get Recent Messages",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
-860,
280
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.text }}",
"operation": "regex",
"value2": "^(buy|sell|long|short)\\s+(SOL|BTC|ETH)"
}
]
}
},
"id": "filter-commands",
"name": "Is Trade Command?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
-680,
280
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "symbol",
"stringValue": "={{ $json.text.match(/\\b(SOL|BTC|ETH)\\b/i)[0].toUpperCase() }}-PERP"
},
{
"name": "direction",
"stringValue": "={{ $json.text.match(/^(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"stringValue": "5"
}
]
}
},
"id": "parse-trade",
"name": "Parse Trade",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
-500,
280
]
},
{
"parameters": {
"mode": "raw",
"jsonOutput": "={{ $json }}"
},
"id": "output-to-check-risk",
"name": "➡️ Merge with Check Risk",
"type": "n8n-nodes-base.set",
"typeVersion": 3.3,
"position": [
-320,
280
],
"notes": "Connect this to the SAME point where your TradingView webhook connects (before Check Risk node)"
}
],
"connections": {
"Check Every 10 Seconds": {
"main": [
[
{
"node": "Get Recent Messages",
"type": "main",
"index": 0
}
]
]
},
"Get Recent Messages": {
"main": [
[
{
"node": "Is Trade Command?",
"type": "main",
"index": 0
}
]
]
},
"Is Trade Command?": {
"main": [
[
{
"node": "Parse Trade",
"type": "main",
"index": 0
}
]
]
},
"Parse Trade": {
"main": [
[
{
"node": "➡️ Merge with Check Risk",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}

View File

@@ -0,0 +1,155 @@
{
"name": "Manual Trade Command - ADD TO MONEY MACHINE",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "manual-trade",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook-manual-trade",
"name": "Manual Trade Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1.1,
"position": [
-840,
280
],
"webhookId": "manual-trade-webhook"
},
{
"parameters": {
"fields": {
"values": [
{
"name": "command",
"stringValue": "={{ $json.body.text || $json.body.message || $json.body }}"
},
{
"name": "symbol",
"stringValue": "={{ ($json.body.text || $json.body.message || $json.body).toString().match(/\\b(SOL|BTC|ETH)\\b/i) ? ($json.body.text || $json.body.message || $json.body).toString().match(/\\b(SOL|BTC|ETH)\\b/i)[0].toUpperCase() + '-PERP' : 'SOL-PERP' }}"
},
{
"name": "direction",
"stringValue": "={{ ($json.body.text || $json.body.message || $json.body).toString().match(/\\b(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"stringValue": "5"
},
{
"name": "source",
"stringValue": "manual"
}
]
},
"options": {}
},
"id": "parse-manual-command",
"name": "Parse Command",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
-660,
280
]
},
{
"parameters": {
"chatId": "579304651",
"text": "🤖 Manual Trade Command\n\n📊 {{ $json.symbol }}\n{{ $json.direction === 'long' ? '📈' : '📉' }} {{ $json.direction.toUpperCase() }}\n⏰ {{ $now.toFormat('HH:mm:ss') }}\n\n✅ Sending to bot...",
"additionalFields": {
"appendAttribution": false
}
},
"id": "telegram-confirm",
"name": "Send Telegram Confirmation",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
-480,
280
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"status\": \"received\", \"symbol\": $json.symbol, \"direction\": $json.direction } }}"
},
"id": "webhook-response",
"name": "Webhook Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [
-480,
380
]
},
{
"parameters": {
"mode": "raw",
"jsonOutput": "={{ { \"symbol\": $json.symbol, \"direction\": $json.direction, \"timeframe\": $json.timeframe } }}"
},
"id": "output-to-check-risk",
"name": "➡️ Connect to Check Risk Node",
"type": "n8n-nodes-base.set",
"typeVersion": 3.3,
"position": [
-300,
280
],
"notes": "Connect this node to your Money Machine's Check Risk node (same place as TradingView webhook)"
}
],
"connections": {
"Manual Trade Webhook": {
"main": [
[
{
"node": "Parse Command",
"type": "main",
"index": 0
}
]
]
},
"Parse Command": {
"main": [
[
{
"node": "Send Telegram Confirmation",
"type": "main",
"index": 0
},
{
"node": "Webhook Response",
"type": "main",
"index": 0
}
]
]
},
"Send Telegram Confirmation": {
"main": [
[
{
"node": "➡️ Connect to Check Risk Node",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
}
}

219
telegram-n8n-listener.json Normal file
View File

@@ -0,0 +1,219 @@
{
"name": "Telegram Manual Trade - ADD TO MONEY MACHINE",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "telegram-trade",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook",
"name": "Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1.1,
"position": [200, 300],
"webhookId": "telegram-trade-webhook"
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.body.message || $json.body.text || '' }}",
"operation": "regex",
"value2": "^(buy|sell|long|short)\\s+(sol|btc|eth)"
}
]
}
},
"id": "filter-trades",
"name": "Is Trade Command?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [400, 300]
},
{
"parameters": {
"values": {
"string": [
{
"name": "symbol",
"value": "={{ ($json.body.message || $json.body.text).match(/\\b(SOL|BTC|ETH)\\b/i)[0].toUpperCase() }}-PERP"
},
{
"name": "direction",
"value": "={{ ($json.body.message || $json.body.text).match(/^(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"value": "5"
}
]
}
},
"id": "parse-command",
"name": "Parse Trade",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [600, 300]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { status: 'received', symbol: $json.symbol, direction: $json.direction } }}"
},
"id": "webhook-response",
"name": "Webhook Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [600, 450]
},
{
"parameters": {
"chatId": "579304651",
"text": "🤖 Manual Trade\n\n📊 {{ $json.symbol }}\n{{ $json.direction === 'long' ? '📈' : '📉' }} {{ $json.direction.toUpperCase() }}\n⏰ {{ $now.format('HH:mm:ss') }}\n\n✅ Executing...",
"additionalFields": {
"appendAttribution": false
}
},
"id": "confirm",
"name": "Send Confirmation",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [800, 300],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {},
"id": "output",
"name": "➡️ Connect to Check Risk",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1000, 300],
"notes": "Connect this to your Money Machine's Check Risk node"
}
],
"connections": {
"Webhook": {
"main": [[
{"node": "Is Trade Command?", "type": "main", "index": 0}
]]
},
"Is Trade Command?": {
"main": [[
{"node": "Parse Trade", "type": "main", "index": 0}
]]
},
"Parse Trade": {
"main": [[
{"node": "Webhook Response", "type": "main", "index": 0},
{"node": "Send Confirmation", "type": "main", "index": 0}
]]
},
"Send Confirmation": {
"main": [[
{"node": "➡️ Connect to Check Risk", "type": "main", "index": 0}
]]
}
},
"active": false,
"settings": {},
"versionId": "telegram-webhook-v2"
}
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.message.text }}",
"operation": "regex",
"value2": "^(buy|sell|long|short)\\s+(sol|btc|eth)$"
}
]
}
},
"id": "filter-trades",
"name": "Is Trade Command?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [400, 300]
},
{
"parameters": {
"values": {
"string": [
{
"name": "symbol",
"value": "={{ $json.message.text.match(/\\b(SOL|BTC|ETH)\\b/i)[0].toUpperCase() }}-PERP"
},
{
"name": "direction",
"value": "={{ $json.message.text.match(/^(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"value": "5"
}
]
}
},
"id": "parse-command",
"name": "Parse Trade",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [600, 300]
},
{
"parameters": {
"chatId": "579304651",
"text": "🤖 Manual Trade\n\n📊 {{ $json.symbol }}\n{{ $json.direction === 'long' ? '📈' : '📉' }} {{ $json.direction.toUpperCase() }}\n⏰ {{ $now.format('HH:mm:ss') }}\n\n✅ Executing...",
"additionalFields": {
"appendAttribution": false
}
},
"id": "confirm",
"name": "Send Confirmation",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [800, 300],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {},
"id": "output",
"name": "➡️ Connect to Check Risk",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1000, 300],
"notes": "Connect this to your Money Machine's Check Risk node"
}
],
"connections": {
"Telegram Messages": {
"main": [[{"node": "Is Trade Command?", "type": "main", "index": 0}]]
},
"Is Trade Command?": {
"main": [[{"node": "Parse Trade", "type": "main", "index": 0}]]
},
"Parse Trade": {
"main": [[{"node": "Send Confirmation", "type": "main", "index": 0}]]
},
"Send Confirmation": {
"main": [[{"node": "➡️ Connect to Check Risk", "type": "main", "index": 0}]]
}
},
"active": false,
"settings": {},
"versionId": "telegram-n8n-listener-v1"
}

178
telegram-polling-addon.json Normal file
View File

@@ -0,0 +1,178 @@
{
"name": "Telegram Manual Trade (Polling) - Import to Money Machine",
"nodes": [
{
"parameters": {
"resource": "message",
"operation": "get",
"chatId": "579304651",
"returnAll": false,
"limit": 1,
"options": {}
},
"id": "telegram-poll-messages",
"name": "Poll Telegram Messages",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
-1020,
460
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.text }}",
"operation": "regex",
"value2": "\\b(buy|sell|long|short)\\b"
}
]
}
},
"id": "filter-trade-commands",
"name": "Filter Trade Commands",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [
-840,
460
]
},
{
"parameters": {
"fields": {
"values": [
{
"name": "rawMessage",
"stringValue": "={{ $json.text }}"
},
{
"name": "symbol",
"stringValue": "={{ $json.text.match(/\\b(SOL|BTC|ETH)\\b/i) ? $json.text.match(/\\b(SOL|BTC|ETH)\\b/i)[0].toUpperCase() + '-PERP' : 'SOL-PERP' }}"
},
{
"name": "direction",
"stringValue": "={{ $json.text.match(/\\b(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"stringValue": "5"
}
]
},
"options": {}
},
"id": "parse-command",
"name": "Parse Trade Command",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
-660,
460
]
},
{
"parameters": {
"chatId": "579304651",
"text": "🤖 Manual trade:\n\n📊 {{ $json.symbol }}\n{{ $json.direction === 'long' ? '📈' : '📉' }} {{ $json.direction.toUpperCase() }}\n⏰ {{ $now.toFormat('HH:mm:ss') }}\n\n⏳ Processing...",
"additionalFields": {
"appendAttribution": false
}
},
"id": "send-ack",
"name": "Send Acknowledgment",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
-480,
460
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"mode": "raw",
"jsonOutput": "={{ $json }}",
"options": {}
},
"id": "output-node",
"name": "➡️ Connect to Check Risk",
"type": "n8n-nodes-base.set",
"typeVersion": 3.3,
"position": [
-300,
460
],
"notes": "Connect this node's output to Money Machine's Check Risk node"
}
],
"pinData": {},
"connections": {
"Poll Telegram Messages": {
"main": [
[
{
"node": "Filter Trade Commands",
"type": "main",
"index": 0
}
]
]
},
"Filter Trade Commands": {
"main": [
[
{
"node": "Parse Trade Command",
"type": "main",
"index": 0
}
]
]
},
"Parse Trade Command": {
"main": [
[
{
"node": "Send Acknowledgment",
"type": "main",
"index": 0
}
]
]
},
"Send Acknowledgment": {
"main": [
[
{
"node": "➡️ Connect to Check Risk",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "telegram-polling-v1",
"meta": {
"instanceId": "e766d4f0b5def8ee8cb8561cd9d2b9ba7733e1907990b6987bca40175f82c379"
},
"tags": []
}

80
telegram-to-webhook.py Normal file
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()

138
telegram-trigger-addon.json Normal file
View File

@@ -0,0 +1,138 @@
{
"name": "Telegram Trade Trigger (Import to Money Machine)",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "manual-telegram-trade",
"options": {}
},
"id": "webhook-telegram-manual",
"name": "Telegram Command Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [
-840,
460
],
"webhookId": "manual-telegram-trade-webhook"
},
{
"parameters": {
"fields": {
"values": [
{
"name": "rawMessage",
"stringValue": "={{ $json.body.message || $json.body.text || $json.body }}"
},
{
"name": "symbol",
"stringValue": "={{ ($json.body.message || $json.body.text || $json.body).toString().match(/\\b(SOL|BTC|ETH)\\b/i) ? ($json.body.message || $json.body.text || $json.body).toString().match(/\\b(SOL|BTC|ETH)\\b/i)[0].toUpperCase() + '-PERP' : 'SOL-PERP' }}"
},
{
"name": "direction",
"stringValue": "={{ ($json.body.message || $json.body.text || $json.body).toString().match(/\\b(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"stringValue": "5"
}
]
},
"options": {}
},
"id": "parse-telegram-manual",
"name": "Parse Manual Command",
"type": "n8n-nodes-base.set",
"typeVersion": 3.2,
"position": [
-660,
460
]
},
{
"parameters": {
"chatId": "579304651",
"text": "🤖 Manual trade command:\n\n📊 {{ $json.symbol }}\n📈 {{ $json.direction.toUpperCase() }}\n⏰ {{ $now.toFormat('HH:mm') }}\n\n⏳ Processing...",
"additionalFields": {
"appendAttribution": false
}
},
"id": "ack-manual-command",
"name": "Send Acknowledgment",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [
-480,
460
],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {
"mode": "raw",
"jsonOutput": "={{ $json }}",
"options": {}
},
"id": "ready-for-check-risk",
"name": "➡️ Connect to Check Risk",
"type": "n8n-nodes-base.set",
"typeVersion": 3.3,
"position": [
-300,
460
],
"notes": "Connect this output to the 'Check Risk' node in Money Machine"
}
],
"pinData": {},
"connections": {
"Telegram Command Webhook": {
"main": [
[
{
"node": "Parse Manual Command",
"type": "main",
"index": 0
}
]
]
},
"Parse Manual Command": {
"main": [
[
{
"node": "Send Acknowledgment",
"type": "main",
"index": 0
}
]
]
},
"Send Acknowledgment": {
"main": [
[
{
"node": "➡️ Connect to Check Risk",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "telegram-webhook-v1",
"meta": {
"instanceId": "e766d4f0b5def8ee8cb8561cd9d2b9ba7733e1907990b6987bca40175f82c379"
},
"tags": []
}

128
telegram-webhook-FINAL.json Normal file
View File

@@ -0,0 +1,128 @@
{
"name": "Telegram Manual Trade - ADD TO MONEY MACHINE",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "telegram-trade",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook",
"name": "Webhook Receiver",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1.1,
"position": [200, 300],
"webhookId": "telegram-trade-webhook"
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{ $json.body.message || $json.body.text || '' }}",
"operation": "regex",
"value2": "(buy|sell|long|short).*(sol|btc|eth)"
}
]
}
},
"id": "filter-trades",
"name": "Is Trade Command?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [400, 300]
},
{
"parameters": {
"values": {
"string": [
{
"name": "symbol",
"value": "={{ ($json.body.message || $json.body.text).toString().match(/\\b(SOL|BTC|ETH)\\b/i)[0].toUpperCase() }}-PERP"
},
{
"name": "direction",
"value": "={{ ($json.body.message || $json.body.text).toString().match(/\\b(sell|short)\\b/i) ? 'short' : 'long' }}"
},
{
"name": "timeframe",
"value": "5"
}
]
}
},
"id": "parse-command",
"name": "Parse Trade",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [600, 300]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"status\": \"received\", \"symbol\": $json.symbol, \"direction\": $json.direction } }}"
},
"id": "webhook-response",
"name": "Webhook Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [800, 450]
},
{
"parameters": {
"chatId": "579304651",
"text": "🤖 Manual Trade\\n\\n📊 {{ $json.symbol }}\\n{{ $json.direction === 'long' ? '📈' : '📉' }} {{ $json.direction.toUpperCase() }}\\n⏰ {{ $now.format('HH:mm:ss') }}\\n\\n✅ Executing...",
"additionalFields": {
"appendAttribution": false
}
},
"id": "confirm",
"name": "Send Telegram Confirm",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.1,
"position": [800, 300],
"credentials": {
"telegramApi": {
"id": "Csk5cg4HtaSqP5jJ",
"name": "Telegram account"
}
}
},
{
"parameters": {},
"id": "output",
"name": "➡️ Connect to Check Risk",
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [1000, 300],
"notes": "Connect this output to your Money Machine's Check Risk node"
}
],
"connections": {
"Webhook Receiver": {
"main": [[
{"node": "Is Trade Command?", "type": "main", "index": 0}
]]
},
"Is Trade Command?": {
"main": [[
{"node": "Parse Trade", "type": "main", "index": 0}
]]
},
"Parse Trade": {
"main": [[
{"node": "Webhook Response", "type": "main", "index": 0},
{"node": "Send Telegram Confirm", "type": "main", "index": 0}
]]
},
"Send Telegram Confirm": {
"main": [[
{"node": "➡️ Connect to Check Risk", "type": "main", "index": 0}
]]
}
},
"active": false,
"settings": {},
"versionId": "telegram-webhook-final"
}

96
telegram_command_bot.py Normal file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""
Telegram Trade Bot - SECURE Command-based
Only responds to YOUR commands in YOUR chat
"""
import os
import requests
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
# Configuration
TELEGRAM_BOT_TOKEN = os.getenv('TELEGRAM_BOT_TOKEN')
N8N_WEBHOOK_URL = os.getenv('N8N_WEBHOOK_URL')
ALLOWED_CHAT_ID = int(os.getenv('TELEGRAM_CHAT_ID', '579304651'))
async def trade_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Handle trade commands like /buySOL, /sellBTC, etc."""
# Only process from YOUR chat
if update.message.chat_id != ALLOWED_CHAT_ID:
await update.message.reply_text("❌ Unauthorized")
return
# Extract command (remove the /)
command = update.message.text[1:].lower() # e.g., "buysol"
# Parse action and symbol
if command.startswith('buy'):
action = 'buy'
symbol = command[3:] # e.g., "sol"
elif command.startswith('sell'):
action = 'sell'
symbol = command[4:] # e.g., "btc"
else:
await update.message.reply_text("❓ Unknown command")
return
message = f"{action} {symbol}"
print(f"📨 Command: {message}", flush=True)
# Forward to n8n webhook - send as plain text body like TradingView does
try:
print(f"📤 Sending: {message}", flush=True)
response = requests.post(
N8N_WEBHOOK_URL,
data=message, # Plain text, not JSON
headers={'Content-Type': 'text/plain'},
timeout=10
)
print(f"📥 Response status: {response.status_code}", flush=True)
print(f"📥 Response body: {response.text[:200]}", flush=True)
if response.ok:
print(f"✅ Sent: {message}", flush=True)
await update.message.reply_text(
f"🤖 {action.upper()} {symbol.upper()}\n"
f"✅ Trade command sent!"
)
else:
print(f"❌ Error: {response.status_code}", flush=True)
await update.message.reply_text(f"❌ Error: {response.status_code}")
except Exception as e:
print(f"❌ Error: {e}", flush=True)
await update.message.reply_text(f"❌ Error: {str(e)}")
def main():
"""Start the bot"""
print(f"🚀 Telegram Trade Bot Starting...", flush=True)
print(f"📱 Allowed Chat ID: {ALLOWED_CHAT_ID}", flush=True)
print(f"🔗 Webhook: {N8N_WEBHOOK_URL}", flush=True)
print(f"\n✅ Commands:", flush=True)
print(f" /buySOL, /sellSOL", flush=True)
print(f" /buyBTC, /sellBTC", flush=True)
print(f" /buyETH, /sellETH", flush=True)
# Create application
application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
# Add command handlers
application.add_handler(CommandHandler("buySOL", trade_command))
application.add_handler(CommandHandler("sellSOL", trade_command))
application.add_handler(CommandHandler("buyBTC", trade_command))
application.add_handler(CommandHandler("sellBTC", trade_command))
application.add_handler(CommandHandler("buyETH", trade_command))
application.add_handler(CommandHandler("sellETH", trade_command))
# Start polling
print("\n🤖 Bot ready! Send commands to your Telegram.\n", flush=True)
application.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == '__main__':
main()

79
telegram_trade_bot.py Normal file
View File

@@ -0,0 +1,79 @@
#!/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, 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')
ALLOWED_CHAT_ID = int(os.getenv('TELEGRAM_CHAT_ID', '579304651'))
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"\n✅ Send messages like:")
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(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_message))
print(f"\n🤖 Bot ready! Listening for trade commands...\n")
app.run_polling(allowed_updates=Update.ALL_TYPES)
if __name__ == '__main__':
main()

19
trade.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/bash
# Send manual trade command
# Usage: ./trade.sh buy sol
# ./trade.sh sell btc
COMMAND="$1 $2"
if [ -z "$COMMAND" ]; then
echo "Usage: $0 <buy|sell> <sol|btc|eth>"
echo "Example: $0 buy sol"
exit 1
fi
curl -X POST http://10.0.0.48:8098/webhook/3371ad7c-0866-4161-90a4-f251de4aceb8 \
-H "Content-Type: application/json" \
-d "{\"body\": \"$COMMAND\"}"
echo ""
echo "✅ Trade command sent: $COMMAND"

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