cleanup: Remove backup files, add .gitignore, create production-ready repository structure

- Delete 20+ backup/test files and old versions
- Add comprehensive .gitignore for Python development
- Update requirements.txt with only essential dependencies
- Add ARCHITECTURE.md for system documentation
- Keep only production files: main_ml.py, web_dashboard.py, README.md

Repository is now clean, lean, and production-ready for Version 0.2
This commit is contained in:
Marc Blatter 2026-07-07 22:16:09 +02:00
parent edf23f9a4f
commit 0973dace06
27 changed files with 313 additions and 5193 deletions

56
.gitignore vendored Normal file
View File

@ -0,0 +1,56 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store
# Environment
.env
.env.local
.env.*.local
# Logs
*.log
logs/
# Temp files
*.bak
*.tmp
*.backup
*~
# OS
.DS_Store
Thumbs.db
# Bot-specific
state/
cache/
*.pickle

238
ARCHITECTURE.md Normal file
View File

@ -0,0 +1,238 @@
# Trading Bot V0.2 — System Architecture
## Overview
Trading Bot V0.2 is a production-ready cryptocurrency trading bot with adaptive strategy learning. The bot makes autonomous trading decisions based on hourly performance evaluation and currently manages a live Binance portfolio.
## Core Components
### 1. Trading Engine (`src/main_ml.py`)
**Purpose:** Autonomous trading bot with risk management and adaptive strategy learning.
**Key Features:**
- **Signal Generation**: Random 5-10% probability per cycle (adapts based on win rate)
- **Position Management**: Max 1 position (scales to 2 in full-throttle mode)
- **Risk Controls**:
- Stop Loss: -1.0 to -2.2% (adaptive)
- Take Profit: +1.5 to +3.5% (adaptive)
- Daily Loss Limit: -5% (stops trading if exceeded)
- Cooldown: 30min after 3 consecutive losses
- **Adaptive Learning**: Evaluates win rate hourly, adjusts strategy (5 levels)
**Strategy Levels (based on Win Rate):**
| Level | WR | Signal | Investment | TP | SL | Max Trades |
|-------|----|----|-----------|----|----|------|
| Emergency | <45% | 5.0% | 50% | 1.5% | 1.0% | 5/day |
| Conservative | 45-50% | 6.5% | 50% | 2.2% | 1.5% | 10/day |
| Standard | 50-60% | 7.5% | 50% | 2.8% | 1.8% | 15/day |
| Aggressive | 60-70% | 8.5% | 55% | 3.2% | 2.0% | 20/day |
| Full Throttle | >70% | 10.0% | 55% | 3.5% | 2.2% | 25/day |
**Input/Output:**
- **Input**: Binance API (market data, account state, order status)
- **Output**: Market buy/sell orders, stop loss orders, Telegram alerts
**Run Cycle:** 5-second loop (async)
### 2. Dashboard (`src/web_dashboard.py`)
**Purpose:** Real-time portfolio monitoring and P&L display.
**Endpoints:**
- `/` (HTTP) — HTML dashboard
- `/api/state` (JSON) — Market data, holdings, P&L, strategy status
**Features:**
- **Portfolio Metrics**: Total value, USDT free, locked positions
- **P&L Display**: Realized + unrealized, color-coded (green/red/neutral)
- **Live Prices**: Real-time cryptoommodity quotes
- **Holdings Table**: Asset balances with locked coin tracking
- **Strategy Status**: Current win rate, strategy mode, next adaptation time
**Refresh Rate:** 10 seconds (user-configurable)
**Tech Stack:**
- Framework: FastAPI
- Server: Uvicorn (async)
- Template: Jinja2 (server-side rendering)
- Port: 7000
## Data Flow
```
┌─────────────────────────────────────────────────────────┐
│ Binance API │
│ (Market Data, Account, Orders) │
└────────────────┬──────────────────────────────────────┘
┌────────▼────────┐
│ Trading Bot │
│ (main_ml.py) │
│ │
│ • Signal Gen │
│ • Order Place │
│ • Risk Mgmt │
│ • Adaptive Learn│
└────────┬────────┘
┌────────▼────────┐
│ Dashboard │
│ (web_dashboard) │
│ │
│ • /api/state │
│ • HTML UI │
└────────┬────────┘
┌────────▼────────┐
│ User Interface │
│ (HTTP Browser) │
└─────────────────┘
```
## Adaptive Learning Loop (Option 2)
**Evaluation Cycle:** Every hour
```
1. Calculate Win Rate
win_rate = total_wins / total_trades * 100
2. Compare to Thresholds
- <45% Emergency mode
- 45-50% → Conservative
- 50-60% → Standard
- 60-70% → Aggressive
- >70% → Full Throttle
3. Update Parameters
- SIGNAL_THRESHOLD (5-10%)
- INVESTMENT_PERCENT (50-55%)
- TAKE_PROFIT_PERCENT (1.5-3.5%)
- STOP_LOSS_PERCENT (1.0-2.2%)
- MAX_TRADES_PER_DAY (5-25)
- MAX_OPEN_POSITIONS (1-2)
4. Send Notification
- Telegram alert with old↔new parameters
- Log strategy change
- Store strategy_version for tracking
```
**Minimum Trades to Adapt:** 5 (prevents noise in early phase)
## Performance Tracking
**Tracked Metrics:**
- `total_trades` — All trades ever executed
- `total_wins` — Winning trades (TP hit)
- `total_losses` — Losing trades (SL hit)
- `daily_pnl` — Today's profit/loss (resets daily)
- `trades_today` — Count reset daily at UTC 00:00
- `portfolio_value` — Current liquid value (real-time)
- `pnl_usdt` — Total P&L in USD
- `pnl_pct` — Total P&L in percentage
**Reporting:**
- 3-hour summaries via Telegram (win rate, P&L, status)
- Real-time alerts on strategy changes
- Dashboard updates every 10 seconds
## Security & Risk
**API Key Management:**
- Stored in `.env` file (never committed)
- API key requires `TRADING` permission on Binance
- All read/write operations over HTTPS (Binance)
**Order Validation:**
- Minimum notional: $5.00 per order
- Quantity rounded to Binance step size (using Decimal, no precision loss)
- Price rounded to Binance tick size
- Daily loss limit enforces hard stop at -5%
**Position Limits:**
- Max 1 position (standard) / 2 positions (full throttle)
- Max 3 consecutive losses → 30min cooldown
- No pyramid trading (one trade at a time)
## Deployment
**Requirements:**
- Python 3.10+
- Binance API key with SPOT trading permission
- Telegram bot token (for alerts)
**Installation:**
```bash
pip install -r requirements.txt
```
**Start Bot:**
```bash
python3 src/main_ml.py
```
**Start Dashboard:**
```bash
uvicorn src/web_dashboard:app --host 0.0.0.0 --port 7000
```
**Access Dashboard:**
```
http://localhost:7000
```
## File Structure
```
BrainDock/
├── src/
│ ├── __init__.py (Package marker)
│ ├── main_ml.py (Trading bot engine - 512 lines)
│ └── web_dashboard.py (Dashboard API - 650+ lines)
├── README.md (User documentation)
├── ARCHITECTURE.md (This file)
├── requirements.txt (Python dependencies)
└── .gitignore (Git exclusions)
```
## Future Enhancements
**Phase 2: Machine Learning**
- Train model on historical OHLCV data
- Replace random signal with ML probability
- Feature engineering: RSI, MACD, Bollinger Bands, etc.
**Phase 3: Portfolio Optimization**
- Multi-pair trading (BTC, ETH, SOL, BNB, XRP)
- Dynamic position sizing by Sharpe ratio
- Kelly Criterion for capital allocation
**Phase 4: Advanced Risk**
- Correlation-based hedging
- Volatility clustering detection
- Dynamic stop loss based on ATR
## Monitoring & Debugging
**Logs:**
```bash
journalctl -u trading-bot.service -f # Real-time logs
```
**API Health Check:**
```bash
curl http://localhost:7000/api/state | jq .
```
**Database State:**
- No persistent database; all state in-memory
- Recovery from Binance API on bot restart
---
**Last Updated:** 2026-07-07
**Version:** V0.2
**Status:** Production Ready ✅

View File

@ -1,53 +0,0 @@
# Trading Bot Strategy V2 — 2026-07-06
## Signal Generation
- **Threshold:** 7.5% (instead of 5%)
- **Frequency:** Every 5 seconds
- **Confidence Range:** 30-95% (modeled)
## Position Sizing
- **Standard:** 25% of free USDT
- **High Confidence (>85%):** 35% of free USDT
- **NOTIONAL Min:** $5.00 (Binance requirement)
## Exit Rules
- **Take Profit:** +2.8% (was +3%)
- **Stop Loss:** -1.8% (was -2.5%)
- **Trailing Stop:** Active at +1.5% profit, distance 0.6%
## Risk Management
- **Max Concurrent Positions:** 3 (was 5)
- **Max Consecutive Losses:** 3 → triggers 30min cooldown
- **Daily Loss Limit:** -5% (unchanged)
- **Max Trades/Day:** 15
- **Min Win Probability Check:** 75%
- **Volatility Filter:** Rejects trades if 1h volatility > 5%
## Expected Behavior
- Fewer but more selective trades (7.5% signal threshold)
- Tighter SL/TP (1.8% / 2.8%)
- Better capital efficiency (25% standard)
- Cooldown protection after 3 losses
- Max 15 trades/day prevents over-trading
---
Deployed: 2026-07-06 21:48 UTC
Git: a815dfe (reference point)
---
## UPDATED (2026-07-06 21:54)
### Liquidity Optimization
- **Max Concurrent Positions:** Reduced to **1** (was 3)
- **Investment %:** **50%** (single position auto-closes before next entry)
- **Rationale:** With 1 position max, 50% × remaining USDT ensures next cycle has ~$9+ USDT
- **Trade Cadence:** Wait for SL/TP hit before next entry (no queue)
**Result:**
- Start: $17.35 USDT → Trade 1: 50% = $8.68 → Close → $17 back + gains
- Maintains minimum $5 NOTIONAL per trade
- Max 15 trades/day still enforced
- Single position reduces capital lock
---

View File

@ -1 +0,0 @@
<!DOCTYPE html><html><head><meta charset=UTF-8><title>Bot P&L</title><style>body{background:#1e1e1e;color:#d0d0d0;font-family:monospace;padding:20px}.pnl-value{font-size:48px;font-weight:700;margin:15px 0}.profit{color:#00ff88}.loss{color:#ff4444}.details{display:grid;grid-template-columns:1fr 1fr;gap:15px}</style></head><body><div id=c><div style=text-align:center>Läd…</div></div><script>setInterval(async()=>{const d=await(await fetch('http://172.16.1.168:7000/api/pnl')).json();const p=d.total_pnl_usdt,c=p>0?'profit':p<0?'loss':'';document.getElementById('c').innerHTML=`<h1>Bot P&L</h1><div class='pnl-value ${c}'>${p>=0?'+':''}$${p.toFixed(2)}</div><div>${d.total_pnl_percent>=0?'+':''}${d.total_pnl_percent.toFixed(2)}%</div>`},5000)</script></body></html>

View File

@ -1,6 +1,20 @@
python-binance==1.0.17
aiohttp==3.8.6
python-telegram-bot==20.1
pydantic==2.4.2
# Trading Bot V0.2 Dependencies
# Crypto Trading & Market Data
python-binance==1.0.20
requests==2.31.0
# Web Framework & Dashboard
fastapi==0.104.1
uvicorn==0.24.0
Jinja2==3.1.2
# Async & Utilities
aiohttp==3.9.1
python-dotenv==1.0.0
pyyaml==6.0.1
# Optional: ML/Data Analysis (for future enhancements)
numpy==1.24.3
pandas==2.0.3
# Logging & Monitoring
python-telegram-bot==20.2

View File

@ -1,13 +0,0 @@
#!/bin/bash
cd /home/marc/bot-deploy
# Generate report
REPORT=$(python3 src/frigate_report.py)
# Send to Telegram via Hermes
echo "$REPORT" | hermes send-message telegram --message-file /dev/stdin || true
# Also save to log
echo "[$(date)]" >> /tmp/frigate-reports.log
echo "$REPORT" >> /tmp/frigate-reports.log
echo "" >> /tmp/frigate-reports.log

View File

@ -1,3 +0,0 @@
#!/bin/bash
cd /home/marc/bot-deploy
python3 src/report_generator.py | hermes send-message telegram --message-file /dev/stdin

View File

@ -1,60 +0,0 @@
import logging
import os
from dotenv import load_dotenv
from pydantic import BaseModel
load_dotenv()
class BotConfig(BaseModel):
"""Bot configuration from environment variables."""
# Binance API
binance_api_key_testnet: str = os.getenv("BINANCE_API_KEY_TESTNET", "")
binance_api_secret_testnet: str = os.getenv("BINANCE_API_SECRET_TESTNET", "")
binance_api_key_live: str = os.getenv("BINANCE_API_KEY_LIVE", "")
binance_api_secret_live: str = os.getenv("BINANCE_API_SECRET_LIVE", "")
# Bot
dry_run: bool = os.getenv("DRY_RUN", "false").lower() == "true"
environment: str = os.getenv("ENVIRONMENT", "testnet") # "testnet" or "live"
trading_pair: str = os.getenv("TRADING_PAIR", "BTCUSDT")
dca_amount_usd: float = float(os.getenv("DCA_AMOUNT", "10"))
dca_interval_hours: float = float(os.getenv("DCA_INTERVAL_HOURS", "1"))
stop_loss_percent: float = float(os.getenv("STOP_LOSS_PERCENT", "2"))
# Telegram
telegram_bot_token: str = os.getenv("TELEGRAM_BOT_TOKEN", "")
telegram_chat_id: str = os.getenv("TELEGRAM_CHAT_ID", "")
# Obsidian
obsidian_vault_path: str = os.getenv("OBSIDIAN_VAULT_PATH", "/opt/obsidian/config/Vault/Test/")
obsidian_trade_log_file: str = os.getenv("OBSIDIAN_TRADE_LOG_FILE", "BrainDock/trading-log.md")
# Database
db_path: str = os.getenv("DB_PATH", "/data/bot_state.db")
class Config:
env_file = ".env"
case_sensitive = False
def validate(self):
"""Validate required config"""
if self.environment not in ("testnet", "live"):
raise ValueError("ENVIRONMENT must be 'testnet' or 'live'")
if self.environment == "testnet":
if not self.binance_api_key_testnet or not self.binance_api_secret_testnet:
raise ValueError("Testnet API credentials required")
else:
if not self.binance_api_key_live or not self.binance_api_secret_live:
raise ValueError("Live API credentials required")
if not self.telegram_bot_token or not self.telegram_chat_id:
logger.warning("Telegram credentials not configured - notifications disabled")
return self
def get_config() -> BotConfig:
"""Get validated config"""
config = BotConfig()
return config.validate()

View File

@ -1 +0,0 @@
<!DOCTYPE html><html><head><meta charset=UTF-8><title>Bot P&L</title><style>body{background:#1e1e1e;color:#d0d0d0;font-family:monospace;padding:20px}.pnl-value{font-size:48px;font-weight:700;margin:15px 0}.profit{color:#00ff88}.loss{color:#ff4444}.details{display:grid;grid-template-columns:1fr 1fr;gap:15px}</style></head><body><div id=c><div style=text-align:center>Läd…</div></div><script>setInterval(async()=>{const d=await(await fetch('http://172.16.1.168:7000/api/pnl')).json();const p=d.total_pnl_usdt,c=p>0?'profit':p<0?'loss':'';document.getElementById('c').innerHTML=`<h1>Bot P&L</h1><div class='pnl-value ${c}'>${p>=0?'+':''}$${p.toFixed(2)}</div><div>${d.total_pnl_percent>=0?'+':''}${d.total_pnl_percent.toFixed(2)}%</div>`},5000)</script></body></html>

View File

@ -1,86 +0,0 @@
#!/usr/bin/env python3
"""
Frigate Daily Report Generator
Sends to Telegram every evening at 20:30 CET
"""
import os, json, requests
from datetime import datetime, timedelta
from collections import defaultdict
FRIGATE_URL = "http://localhost:5000"
def get_frigate_events():
"""Get events from last 24 hours"""
try:
resp = requests.get(f"{FRIGATE_URL}/api/events", timeout=5)
events = resp.json()
# Filter for last 24h
now = datetime.now().timestamp()
yesterday = now - (24 * 3600)
recent = [e for e in events if e.get('start_time', 0) > yesterday]
return recent
except Exception as e:
print(f"Error fetching events: {e}")
return []
def generate_report():
"""Generate Frigate daily summary"""
events = get_frigate_events()
if not events:
return "🎥 **Frigate Daily Report** — Keine Events heute\n\nStatus: ✅ Alle Kameras aktiv\nEvents: 0"
# Group by camera & label
by_camera = defaultdict(lambda: defaultdict(int))
by_label = defaultdict(int)
people = set()
for event in events:
camera = event.get('camera', 'Unknown')
label = event.get('label', 'Unknown')
sub_label = event.get('sub_label', None)
by_camera[camera][label] += 1
by_label[label] += 1
if label == 'person' and sub_label:
people.add(sub_label)
# Format report
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M CET')
report = f"""🎥 **Frigate Daily Report** — {timestamp}
📊 **ZUSAMMENFASSUNG**
Gesamt Events: {len(events)}
Detektierte Personen: {len(people)}
Kameras aktiv: {len(by_camera)}
👥 **Erkannte Personen**
"""
for person in sorted(people):
report += f"{person}\n"
report += f"\n📹 **Nach Kamera**\n"
for camera in sorted(by_camera.keys()):
events_count = sum(by_camera[camera].values())
labels = ", ".join(by_camera[camera].keys())
report += f" 🟢 {camera}: {events_count} Events ({labels})\n"
report += f"\n🏷️ **Nach Objekttyp**\n"
for label in sorted(by_label.keys()):
count = by_label[label]
report += f"{label.upper()}: {count}\n"
report += f"\n✅ **Status**: Alle Kameras aktiv\n"
report += f"*Report: {datetime.now().strftime('%H:%M:%S UTC')}*"
return report
if __name__ == "__main__":
report = generate_report()
print(report)

View File

@ -1,99 +0,0 @@
import asyncio
import logging
import signal
from src.config import get_config
from src.bot.binance_client import BinanceClientWrapper
from src.bot.engine import TradingEngine
from src.integrations.telegram_notifier import TelegramNotifier
from src.integrations.obsidian_logger import ObsidianLogger
from src.strategies.dca import DCAStrategy
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
async def main():
"""Main bot entry point"""
# Load config
config = get_config()
logger.info(f"Starting bot | Environment: {config.environment} | Pair: {config.trading_pair}")
# Select credentials based on environment
if config.environment == "testnet":
api_key = config.binance_api_key_testnet
api_secret = config.binance_api_secret_testnet
else:
api_key = config.binance_api_key_live
api_secret = config.binance_api_secret_live
# Initialize components
binance_client = BinanceClientWrapper(
api_key=api_key,
api_secret=api_secret,
testnet=(config.environment == "testnet")
)
telegram = TelegramNotifier(
bot_token=config.telegram_bot_token,
chat_id=config.telegram_chat_id
)
obsidian = ObsidianLogger(
vault_path=config.obsidian_vault_path,
trade_log_file=config.obsidian_trade_log_file
)
strategy = DCAStrategy(
trading_pair=config.trading_pair,
dca_amount_usd=config.dca_amount_usd,
interval_hours=config.dca_interval_hours,
stop_loss_percent=config.stop_loss_percent
)
# Create engine
engine = TradingEngine(
strategy=strategy,
db_path=config.db_path,
binance_client=binance_client,
telegram_notifier=telegram
)
engine.dry_run = config.dry_run # Enable dry-run mode if configured
# Initialize
await engine.init()
# Setup signal handlers for graceful shutdown
def signal_handler(signum, frame):
logger.info("Shutdown signal received")
asyncio.create_task(engine.shutdown())
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
# Send startup message
startup_msg = f"""
<b>Bot Started</b>
Environment: {config.environment}
Pair: {config.trading_pair}
DCA Amount: ${config.dca_amount_usd}
Interval: {config.dca_interval_hours}h
Stop Loss: {config.stop_loss_percent}%
"""
await telegram.send_alert(startup_msg)
# Start trading
try:
await engine.start()
except Exception as e:
logger.error(f"Bot fatal error: {e}")
await telegram.send_alert(f"❌ Bot crashed: {str(e)}")
raise
finally:
await engine.shutdown()
if __name__ == "__main__":
asyncio.run(main())

View File

@ -1,190 +0,0 @@
import asyncio, logging, joblib, time
from datetime import datetime
from src.config import get_config
from src.bot.binance_client import BinanceClientWrapper
from src.integrations.telegram_notifier import TelegramNotifier
from src.integrations.obsidian_logger import ObsidianLogger
from src.strategies.ml_strategy import MLStrategy
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class MLTradingBot:
def __init__(self, config, binance, telegram, obsidian, model, scaler):
self.config = config
self.binance = binance
self.telegram = telegram
self.obsidian = obsidian
self.model = model
self.scaler = scaler
self.strategy = MLStrategy(trading_pair=config.trading_pair)
# Trading state
self.last_report_time = time.time()
self.report_interval = 10800 # 3 HOURS (10800 seconds)
self.trades_today = 0
self.wins_today = 0
self.losses_today = 0
self.daily_pnl = 0.0
self.report_count = 0
async def get_market_data(self):
"""Fetch current market price and stats"""
try:
ticker = self.config.trading_pair.split('/')[0] # BTC from BTCUSDT
symbol = f"{ticker}USDT"
# Get current price
price_data = await self.binance.get_ticker_price(symbol)
if not price_data:
return None
current_price = float(price_data)
return {
'ticker': ticker,
'current_price': current_price,
'symbol': symbol
}
except Exception as e:
logger.error(f"Market data fetch error: {e}")
return None
async def get_account_balance(self):
"""Get current account balance"""
try:
balance = self.binance.get_balance('USDT')
if balance:
return {'USDT': {'total': balance}}
return {}
except Exception as e:
logger.error(f"Balance fetch error: {e}")
return {}
async def send_performance_report(self):
"""Send 3-hourly performance report"""
try:
self.report_count += 1
# Get market data
market = await self.get_market_data()
if not market:
logger.warning("No market data available")
return
# Get account balance
balances = await self.get_account_balance()
usdt_balance = balances.get('USDT', {}).get('total', 0)
# Build report
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')
report = f"""
📊 **PERFORMANCE REPORT #{self.report_count}** — {timestamp}
🎯 **MARKET STATUS:**
├─ {market['ticker']}/USDT: ${market['current_price']:,.2f}
├─ Trades Today: {self.trades_today}
├─ Wins: {self.wins_today} | Losses: {self.losses_today}
└─ Daily P&L: ${self.daily_pnl:+.2f}
💰 **ACCOUNT STATUS:**
├─ USDT Balance: ${usdt_balance:,.2f}
├─ Device: CPU
├─ Mode: Live Trading
└─ Strategy: ML (92% accuracy, 60% threshold)
📈 **BOT STATUS: RUNNING ✅**
"""
# Send to Telegram (FIXED — now actually sends!)
success = await self.telegram.send_alert(report.strip())
if success:
logger.info(f"✅ Performance report #{self.report_count} sent to Telegram")
else:
logger.warning(f"❌ Failed to send report #{self.report_count} to Telegram")
except Exception as e:
logger.error(f"Report error: {e}")
async def monitor_trades(self):
"""Monitor open trades and check signals"""
try:
symbol = f"{self.config.trading_pair.split('/')[0]}USDT"
orders = self.binance.get_open_orders(symbol)
if orders and len(orders) > 0:
logger.info(f"📈 Open orders: {len(orders)}")
except Exception as e:
logger.debug(f"Trade monitoring: {e}")
async def run(self):
"""Main bot loop"""
logger.info(f"🤖 Starting ML Trading Bot — {self.config.trading_pair}")
startup_msg = f"""🤖 **BOT STARTED - V2 ML ADAPTIVE**
✅ Strategy: ML Adaptive (60% threshold)
✅ Models: BTC 92% accuracy
✅ Device: CPU (Live)
✅ Reporting: EVERY 3 HOURS
✅ Status: ACTIVE & MONITORING"""
await self.telegram.send_alert(startup_msg)
logger.info("✅ Startup message sent to Telegram")
logger.info("🟢 Bot running — sending reports every 3 hours...")
while True:
try:
current_time = time.time()
# Send 3-hourly performance report
if (current_time - self.last_report_time) >= self.report_interval:
logger.info(f"⏰ Time for Report #{self.report_count + 1}")
await self.send_performance_report()
self.last_report_time = current_time
# Monitor trades every 5 minutes
await self.monitor_trades()
# Sleep for 5 minutes
await asyncio.sleep(300)
except KeyboardInterrupt:
logger.info("Bot interrupted by user")
break
except Exception as e:
logger.error(f"Bot error: {e}")
try:
await self.telegram.send_alert(f"❌ Bot Error: {str(e)[:100]}")
except:
pass
await asyncio.sleep(60)
async def main():
config = get_config()
if config.environment == 'testnet':
api_key, api_secret = config.binance_api_key_testnet, config.binance_api_secret_testnet
else:
api_key, api_secret = config.binance_api_key_live, config.binance_api_secret_live
binance = BinanceClientWrapper(api_key=api_key, api_secret=api_secret, testnet=(config.environment=='testnet'))
telegram = TelegramNotifier(bot_token=config.telegram_bot_token, chat_id=config.telegram_chat_id)
obsidian = ObsidianLogger(vault_path=config.obsidian_vault_path, trade_log_file=config.obsidian_trade_log_file)
try:
# Load BTC model
model = joblib.load('/tmp/model_BTC.pkl')
scaler = joblib.load('/tmp/scaler_BTC.pkl')
logger.info(f'✅ ML Model loaded: BTC (92% accuracy)')
except Exception as e:
logger.error(f'❌ ML Model Error: {e}')
return
bot = MLTradingBot(config, binance, telegram, obsidian, model, scaler)
await bot.run()
if __name__ == '__main__':
asyncio.run(main())

View File

@ -1,431 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V5 ENHANCED - FULLY FIXED VERSION
Implementiert: SL, TP, Daily Limit, R:R Ratio
FIXED: Binance API method (order_take_profit → create_order)
FIXED: PRICE_FILTER für SL Orders durch Tick-Rounding
FIXED: Quantity rounding mit Decimal (no floating point errors)
FIXED: Quantity string formatting für Binance
NEW: Startup Message + 3h Performance Reports via Telegram
"""
import os, asyncio, logging, random, json, time, math, requests
from decimal import Decimal, ROUND_DOWN
from binance.client import Client
from binance.exceptions import BinanceAPIException
from datetime import datetime, timedelta
# Logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Load env
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
k,_,v = line.partition('=')
env[k.strip()] = v.strip()
class TradingBot:
def __init__(self):
self.client = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
self.PAIRS = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
self.SIGNAL_THRESHOLD = 5 # 5% random signal
self.INVESTMENT_PERCENT = 18 # 18% per trade (5 parallel = 90% max, 10% buffer)
self.STOP_LOSS_PERCENT = 2.5 # -2.5%
self.TAKE_PROFIT_PERCENT = 3.0 # +3%
self.DAILY_LOSS_LIMIT = -5 # -5% max
self.active_trades = {}
self.daily_pnl = 0
self.paused = False
self.start_time = datetime.now()
self.trades_today = 0
self.wins_today = 0
self.losses_today = 0
# Precision cache
self.pair_precision = {}
self._load_pair_precision()
# Telegram
self.telegram_token = env.get('TELEGRAM_BOT_TOKEN')
self.telegram_chat_id = env.get('TELEGRAM_CHAT_ID')
logger.info("✅ Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)")
# Send startup message
self._send_startup_message()
def _send_telegram(self, message):
"""Send message to Telegram"""
try:
if not self.telegram_token or not self.telegram_chat_id:
logger.warning("Telegram not configured")
return False
url = f"https://api.telegram.org/bot{self.telegram_token}/sendMessage"
data = {
'chat_id': self.telegram_chat_id,
'text': message,
'parse_mode': 'Markdown'
}
response = requests.post(url, data=data, timeout=5)
return response.status_code == 200
except Exception as e:
logger.error(f"Telegram Error: {e}")
return False
def _send_startup_message(self):
"""Send startup message with current strategy"""
message = """🤖 **TRADING BOT V5 — STARTED!**
⚙️ **AKTUELLE STRATEGIE:**
**Entry:**
• Signal: 5% Random (5 sec cycle)
• Investment: 18% USDT per trade ← FIXED!
• Pairs: BTC, ETH, SOL, BNB, XRP
• Max Parallel: 5 trades (5×18% = 90% max)
**Exit:**
• Take Profit: +3.0% ✅
• Stop Loss: -2.5% ✅
• Risk/Reward: 1:1.2
**Risk Management:**
• Daily Loss Limit: -5%
• Position Size Cap: 18%
• Buffer Reserve: 10% USDT
• SL Auto-Place: Ja (korrekt gerundet)
**Status:** 🟢 LIVE
• Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """
• Capital Ready: 100% USDT
---
Reports: Alle 3h via Telegram 📊"""
self._send_telegram(message)
logger.info("📱 Startup message sent to Telegram")
def _load_pair_precision(self):
"""Load Binance precision rules for each pair"""
for pair in self.PAIRS:
try:
info = self.client.get_symbol_info(symbol=pair)
for f in info['filters']:
if f['filterType'] == 'PRICE_FILTER':
tick = float(f['tickSize'])
self.pair_precision[pair] = {
'tick': tick,
'decimals': self._get_decimals(tick)
}
if f['filterType'] == 'LOT_SIZE':
step = float(f['stepSize'])
if pair not in self.pair_precision:
self.pair_precision[pair] = {}
self.pair_precision[pair]['step'] = step
self.pair_precision[pair]['step_decimals'] = self._get_decimals(step)
if f['filterType'] == 'NOTIONAL':
min_notional = float(f['minNotional'])
if pair not in self.pair_precision:
self.pair_precision[pair] = {}
self.pair_precision[pair]['min_notional'] = min_notional
except Exception as e:
logger.error(f"Precision load {pair}: {e}")
def _get_decimals(self, tick):
"""Get decimal places from tick size"""
s = str(tick)
if 'e' in s:
return int(s.split('e-')[1]) if 'e-' in s else 0
return len(s.split('.')[1]) if '.' in s else 0
def _round_to_tick(self, price, pair):
"""Round price to Binance tick size using Decimal"""
tick = self.pair_precision.get(pair, {}).get('tick', 0.01)
price_decimal = Decimal(str(price))
tick_decimal = Decimal(str(tick))
rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal
return float(rounded)
def _round_quantity(self, qty, pair):
"""Round quantity to Binance step size using Decimal - NO PRECISION LOSS"""
step = self.pair_precision.get(pair, {}).get('step', 0.00001)
step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5)
qty_decimal = Decimal(str(qty))
step_decimal = Decimal(str(step))
# Round down (safe side)
rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal
# Format as string with exactly the right decimals
format_str = f"0.{'':<{step_decimals}}"
if step_decimals == 0:
return int(rounded)
return float(rounded)
async def signal_buy(self, pair):
"""Generate random 5% buy signal"""
rand = random.randint(1, 100)
return rand <= self.SIGNAL_THRESHOLD
async def place_buy_order(self, pair):
"""Place market buy order"""
try:
# Get current price
ticker = self.client.get_ticker(symbol=pair)
entry_price = float(ticker['lastPrice'])
# Calculate quantity
account = self.client.get_account()
usdt_balance = next((b['free'] for b in account['balances'] if b['asset'] == 'USDT'), 0)
usdt = float(usdt_balance) * (self.INVESTMENT_PERCENT / 100)
qty = usdt / entry_price
# ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!)
qty = self._round_quantity(qty, pair)
# Check if qty is valid (not zero after rounding)
if qty <= 0:
logger.warning(f"Quantity too small for {pair}: {qty}")
return False
# VALIDATE NOTIONAL (order_value must be >= min_notional)
min_notional = self.pair_precision.get(pair, {}).get('min_notional', 10.0)
order_value = qty * entry_price
if order_value < min_notional:
logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${min_notional:.2f}")
return False
# Place market buy
order = self.client.order_market_buy(symbol=pair, quantity=qty)
logger.info(f"🟢 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})")
# Store trade
self.active_trades[pair] = {
'entry': entry_price,
'qty': qty,
'time': datetime.now()
}
# Place SL order (FIXED WITH CORRECT API METHOD)
await self.place_stop_loss(pair, entry_price, qty)
self.trades_today += 1
return True
except Exception as e:
logger.error(f"Buy Error {pair}: {e}")
return False
async def place_stop_loss(self, pair, entry_price, qty):
"""Place stop loss order with correct precision & API method"""
try:
# Calculate SL price with 2.5% loss
sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100)
# ROUND TO TICK SIZE (CRITICAL FIX!)
sl_price = self._round_to_tick(sl_price, pair)
# ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!)
qty_rounded = self._round_quantity(qty, pair)
# Place SL order using create_order (correct Binance API method)
order = self.client.create_order(
symbol=pair,
side='SELL',
type='STOP_LOSS_LIMIT',
timeInForce='GTC',
quantity=qty_rounded,
stopPrice=sl_price,
price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice
)
logger.info(f"🛡️ SL: {pair} x{qty_rounded} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)")
except BinanceAPIException as e:
logger.error(f"SL Error {pair}: {e}")
async def monitor_positions(self):
"""Monitor open positions for TP/SL"""
try:
account = self.client.get_account()
for pair in list(self.active_trades.keys()):
ticker = self.client.get_ticker(symbol=pair)
current = float(ticker['lastPrice'])
entry = self.active_trades[pair]['entry']
gain_percent = ((current - entry) / entry) * 100
# Check TP
if gain_percent >= self.TAKE_PROFIT_PERCENT:
await self.close_position(pair, 'TP', current)
# Check SL (secondary check)
elif gain_percent <= -self.STOP_LOSS_PERCENT:
await self.close_position(pair, 'SL', current)
except Exception as e:
logger.error(f"Monitor Error: {e}")
async def close_position(self, pair, reason, current_price):
"""Close position"""
if pair not in self.active_trades:
return
qty = self.active_trades[pair]['qty']
entry = self.active_trades[pair]['entry']
pnl = (current_price - entry) * qty
logger.info(f"📊 {reason}: {pair} closed @ ${current_price:.2f}, PnL: ${pnl:.2f}")
del self.active_trades[pair]
self.daily_pnl += pnl
if pnl > 0:
self.wins_today += 1
else:
self.losses_today += 1
# Check daily loss limit
if self.daily_pnl <= self.DAILY_LOSS_LIMIT:
logger.warning(f"⚠️ DAILY LOSS LIMIT REACHED: ${self.daily_pnl:.2f}")
self.paused = True
def get_performance_report(self):
"""Get current performance metrics"""
try:
account = self.client.get_account()
balance = {}
for asset_data in account['balances']:
asset = asset_data['asset']
free = float(asset_data['free'])
locked = float(asset_data['locked'])
total = free + locked
if total > 0.00001:
balance[asset] = {
'free': free,
'locked': locked,
'total': total
}
# Get prices
prices = {}
for pair in self.PAIRS:
try:
ticker = self.client.get_ticker(symbol=pair)
asset = pair.replace('USDT', '')
prices[asset] = float(ticker['lastPrice'])
except:
pass
prices['USDT'] = 1.0
# Calculate portfolio
portfolio = 0
tracked = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT']
for asset in tracked:
if asset in balance:
portfolio += balance[asset]['total'] * prices.get(asset, 0)
return {
'portfolio': round(portfolio, 2),
'usdt_free': balance.get('USDT', {}).get('free', 0),
'daily_pnl': self.daily_pnl,
'trades_today': self.trades_today,
'wins': self.wins_today,
'losses': self.losses_today,
'active_trades': len(self.active_trades),
'paused': self.paused
}
except Exception as e:
logger.error(f"Performance Report Error: {e}")
return None
def send_performance_report(self):
"""Send 3h performance report via Telegram"""
report = self.get_performance_report()
if not report:
return
win_rate = 0
if report['trades_today'] > 0:
win_rate = (report['wins'] / report['trades_today']) * 100
status = "🟢 RUNNING" if not report['paused'] else "⏸️ PAUSED"
message = f"""📊 **3H PERFORMANCE REPORT**
**Portfolio Status:**
• Total: ${report['portfolio']:.2f}
• USDT Free: ${report['usdt_free']:.2f}
• Status: {status}
**Today's Trading:**
• Trades Executed: {report['trades_today']}
• Wins: {report['wins']} ✅
• Losses: {report['losses']} ❌
• Win Rate: {win_rate:.1f}%
**P&L:**
• Daily P&L: ${report['daily_pnl']:.2f}
• Open Positions: {report['active_trades']}
**Risk Status:**
• Daily Loss Limit: -5%
• Current Daily Loss: ${report['daily_pnl']:.2f}
• Pause Active: {'Yes ⏸️' if report['paused'] else 'No ✅'}
---
Time: {datetime.now().strftime('%Y-%m-%d %H:%M UTC')}
Bot: V5 ENHANCED (FULLY FIXED)"""
self._send_telegram(message)
logger.info("📱 Performance report sent to Telegram")
async def run_cycle(self):
"""Main trading cycle"""
last_report_hour = None
while True:
try:
# Check if it's time for 3h report
current_hour = datetime.now().hour
if current_hour % 3 == 0 and last_report_hour != current_hour:
self.send_performance_report()
last_report_hour = current_hour
# Check daily loss limit pause
if self.paused:
logger.info("⏸️ Bot PAUSED (daily loss limit reached)")
await asyncio.sleep(60)
continue
# Signal generation
for pair in self.PAIRS:
if pair not in self.active_trades and await self.signal_buy(pair):
await self.place_buy_order(pair)
# Monitor positions
await self.monitor_positions()
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Cycle Error: {e}")
await asyncio.sleep(5)
async def main():
bot = TradingBot()
await bot.run_cycle()
if __name__ == '__main__':
asyncio.run(main())

View File

@ -1,431 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V5 ENHANCED - FULLY FIXED VERSION
Implementiert: SL, TP, Daily Limit, R:R Ratio
FIXED: Binance API method (order_take_profit → create_order)
FIXED: PRICE_FILTER für SL Orders durch Tick-Rounding
FIXED: Quantity rounding mit Decimal (no floating point errors)
FIXED: Quantity string formatting für Binance
NEW: Startup Message + 3h Performance Reports via Telegram
"""
import os, asyncio, logging, random, json, time, math, requests
from decimal import Decimal, ROUND_DOWN
from binance.client import Client
from binance.exceptions import BinanceAPIException
from datetime import datetime, timedelta
# Logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Load env
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
k,_,v = line.partition('=')
env[k.strip()] = v.strip()
class TradingBot:
def __init__(self):
self.client = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
self.PAIRS = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
self.SIGNAL_THRESHOLD = 5 # 5% random signal
self.INVESTMENT_PERCENT = 30 # 30% per trade (5 parallel = 90% max, 10% buffer)
self.STOP_LOSS_PERCENT = 2.5 # -2.5%
self.TAKE_PROFIT_PERCENT = 3.0 # +3%
self.DAILY_LOSS_LIMIT = -5 # -5% max
self.active_trades = {}
self.daily_pnl = 0
self.paused = False
self.start_time = datetime.now()
self.trades_today = 0
self.wins_today = 0
self.losses_today = 0
# Precision cache
self.pair_precision = {}
self._load_pair_precision()
# Telegram
self.telegram_token = env.get('TELEGRAM_BOT_TOKEN')
self.telegram_chat_id = env.get('TELEGRAM_CHAT_ID')
logger.info("✅ Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)")
# Send startup message
self._send_startup_message()
def _send_telegram(self, message):
"""Send message to Telegram"""
try:
if not self.telegram_token or not self.telegram_chat_id:
logger.warning("Telegram not configured")
return False
url = f"https://api.telegram.org/bot{self.telegram_token}/sendMessage"
data = {
'chat_id': self.telegram_chat_id,
'text': message,
'parse_mode': 'Markdown'
}
response = requests.post(url, data=data, timeout=5)
return response.status_code == 200
except Exception as e:
logger.error(f"Telegram Error: {e}")
return False
def _send_startup_message(self):
"""Send startup message with current strategy"""
message = """🤖 **TRADING BOT V5 — STARTED!**
⚙️ **AKTUELLE STRATEGIE:**
**Entry:**
• Signal: 5% Random (5 sec cycle)
• Investment: 18% USDT per trade ← FIXED!
• Pairs: BTC, ETH, SOL, BNB, XRP
• Max Parallel: 5 trades (5×18% = 90% max)
**Exit:**
• Take Profit: +3.0% ✅
• Stop Loss: -2.5% ✅
• Risk/Reward: 1:1.2
**Risk Management:**
• Daily Loss Limit: -5%
• Position Size Cap: 18%
• Buffer Reserve: 10% USDT
• SL Auto-Place: Ja (korrekt gerundet)
**Status:** 🟢 LIVE
• Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """
• Capital Ready: 100% USDT
---
Reports: Alle 3h via Telegram 📊"""
self._send_telegram(message)
logger.info("📱 Startup message sent to Telegram")
def _load_pair_precision(self):
"""Load Binance precision rules for each pair"""
for pair in self.PAIRS:
try:
info = self.client.get_symbol_info(symbol=pair)
for f in info['filters']:
if f['filterType'] == 'PRICE_FILTER':
tick = float(f['tickSize'])
self.pair_precision[pair] = {
'tick': tick,
'decimals': self._get_decimals(tick)
}
if f['filterType'] == 'LOT_SIZE':
step = float(f['stepSize'])
if pair not in self.pair_precision:
self.pair_precision[pair] = {}
self.pair_precision[pair]['step'] = step
self.pair_precision[pair]['step_decimals'] = self._get_decimals(step)
if f['filterType'] == 'NOTIONAL':
min_notional = float(f['minNotional'])
if pair not in self.pair_precision:
self.pair_precision[pair] = {}
self.pair_precision[pair]['min_notional'] = min_notional
except Exception as e:
logger.error(f"Precision load {pair}: {e}")
def _get_decimals(self, tick):
"""Get decimal places from tick size"""
s = str(tick)
if 'e' in s:
return int(s.split('e-')[1]) if 'e-' in s else 0
return len(s.split('.')[1]) if '.' in s else 0
def _round_to_tick(self, price, pair):
"""Round price to Binance tick size using Decimal"""
tick = self.pair_precision.get(pair, {}).get('tick', 0.01)
price_decimal = Decimal(str(price))
tick_decimal = Decimal(str(tick))
rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal
return float(rounded)
def _round_quantity(self, qty, pair):
"""Round quantity to Binance step size using Decimal - NO PRECISION LOSS"""
step = self.pair_precision.get(pair, {}).get('step', 0.00001)
step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5)
qty_decimal = Decimal(str(qty))
step_decimal = Decimal(str(step))
# Round down (safe side)
rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal
# Format as string with exactly the right decimals
format_str = f"0.{'':<{step_decimals}}"
if step_decimals == 0:
return int(rounded)
return float(rounded)
async def signal_buy(self, pair):
"""Generate random 5% buy signal"""
rand = random.randint(1, 100)
return rand <= self.SIGNAL_THRESHOLD
async def place_buy_order(self, pair):
"""Place market buy order"""
try:
# Get current price
ticker = self.client.get_ticker(symbol=pair)
entry_price = float(ticker['lastPrice'])
# Calculate quantity
account = self.client.get_account()
usdt_balance = next((b['free'] for b in account['balances'] if b['asset'] == 'USDT'), 0)
usdt = float(usdt_balance) * (self.INVESTMENT_PERCENT / 100)
qty = usdt / entry_price
# ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!)
qty = self._round_quantity(qty, pair)
# Check if qty is valid (not zero after rounding)
if qty <= 0:
logger.warning(f"Quantity too small for {pair}: {qty}")
return False
# VALIDATE NOTIONAL (order_value must be >= min_notional)
min_notional = self.pair_precision.get(pair, {}).get('min_notional', 10.0)
order_value = qty * entry_price
if order_value < min_notional:
logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${min_notional:.2f}")
return False
# Place market buy
order = self.client.order_market_buy(symbol=pair, quantity=qty)
logger.info(f"🟢 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})")
# Store trade
self.active_trades[pair] = {
'entry': entry_price,
'qty': qty,
'time': datetime.now()
}
# Place SL order (FIXED WITH CORRECT API METHOD)
await self.place_stop_loss(pair, entry_price, qty)
self.trades_today += 1
return True
except Exception as e:
logger.error(f"Buy Error {pair}: {e}")
return False
async def place_stop_loss(self, pair, entry_price, qty):
"""Place stop loss order with correct precision & API method"""
try:
# Calculate SL price with 2.5% loss
sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100)
# ROUND TO TICK SIZE (CRITICAL FIX!)
sl_price = self._round_to_tick(sl_price, pair)
# ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!)
qty_rounded = self._round_quantity(qty, pair)
# Place SL order using create_order (correct Binance API method)
order = self.client.create_order(
symbol=pair,
side='SELL',
type='STOP_LOSS_LIMIT',
timeInForce='GTC',
quantity=qty_rounded,
stopPrice=sl_price,
price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice
)
logger.info(f"🛡️ SL: {pair} x{qty_rounded} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)")
except BinanceAPIException as e:
logger.error(f"SL Error {pair}: {e}")
async def monitor_positions(self):
"""Monitor open positions for TP/SL"""
try:
account = self.client.get_account()
for pair in list(self.active_trades.keys()):
ticker = self.client.get_ticker(symbol=pair)
current = float(ticker['lastPrice'])
entry = self.active_trades[pair]['entry']
gain_percent = ((current - entry) / entry) * 100
# Check TP
if gain_percent >= self.TAKE_PROFIT_PERCENT:
await self.close_position(pair, 'TP', current)
# Check SL (secondary check)
elif gain_percent <= -self.STOP_LOSS_PERCENT:
await self.close_position(pair, 'SL', current)
except Exception as e:
logger.error(f"Monitor Error: {e}")
async def close_position(self, pair, reason, current_price):
"""Close position"""
if pair not in self.active_trades:
return
qty = self.active_trades[pair]['qty']
entry = self.active_trades[pair]['entry']
pnl = (current_price - entry) * qty
logger.info(f"📊 {reason}: {pair} closed @ ${current_price:.2f}, PnL: ${pnl:.2f}")
del self.active_trades[pair]
self.daily_pnl += pnl
if pnl > 0:
self.wins_today += 1
else:
self.losses_today += 1
# Check daily loss limit
if self.daily_pnl <= self.DAILY_LOSS_LIMIT:
logger.warning(f"⚠️ DAILY LOSS LIMIT REACHED: ${self.daily_pnl:.2f}")
self.paused = True
def get_performance_report(self):
"""Get current performance metrics"""
try:
account = self.client.get_account()
balance = {}
for asset_data in account['balances']:
asset = asset_data['asset']
free = float(asset_data['free'])
locked = float(asset_data['locked'])
total = free + locked
if total > 0.00001:
balance[asset] = {
'free': free,
'locked': locked,
'total': total
}
# Get prices
prices = {}
for pair in self.PAIRS:
try:
ticker = self.client.get_ticker(symbol=pair)
asset = pair.replace('USDT', '')
prices[asset] = float(ticker['lastPrice'])
except:
pass
prices['USDT'] = 1.0
# Calculate portfolio
portfolio = 0
tracked = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT']
for asset in tracked:
if asset in balance:
portfolio += balance[asset]['total'] * prices.get(asset, 0)
return {
'portfolio': round(portfolio, 2),
'usdt_free': balance.get('USDT', {}).get('free', 0),
'daily_pnl': self.daily_pnl,
'trades_today': self.trades_today,
'wins': self.wins_today,
'losses': self.losses_today,
'active_trades': len(self.active_trades),
'paused': self.paused
}
except Exception as e:
logger.error(f"Performance Report Error: {e}")
return None
def send_performance_report(self):
"""Send 3h performance report via Telegram"""
report = self.get_performance_report()
if not report:
return
win_rate = 0
if report['trades_today'] > 0:
win_rate = (report['wins'] / report['trades_today']) * 100
status = "🟢 RUNNING" if not report['paused'] else "⏸️ PAUSED"
message = f"""📊 **3H PERFORMANCE REPORT**
**Portfolio Status:**
• Total: ${report['portfolio']:.2f}
• USDT Free: ${report['usdt_free']:.2f}
• Status: {status}
**Today's Trading:**
• Trades Executed: {report['trades_today']}
• Wins: {report['wins']} ✅
• Losses: {report['losses']} ❌
• Win Rate: {win_rate:.1f}%
**P&L:**
• Daily P&L: ${report['daily_pnl']:.2f}
• Open Positions: {report['active_trades']}
**Risk Status:**
• Daily Loss Limit: -5%
• Current Daily Loss: ${report['daily_pnl']:.2f}
• Pause Active: {'Yes ⏸️' if report['paused'] else 'No ✅'}
---
Time: {datetime.now().strftime('%Y-%m-%d %H:%M UTC')}
Bot: V5 ENHANCED (FULLY FIXED)"""
self._send_telegram(message)
logger.info("📱 Performance report sent to Telegram")
async def run_cycle(self):
"""Main trading cycle"""
last_report_hour = None
while True:
try:
# Check if it's time for 3h report
current_hour = datetime.now().hour
if current_hour % 3 == 0 and last_report_hour != current_hour:
self.send_performance_report()
last_report_hour = current_hour
# Check daily loss limit pause
if self.paused:
logger.info("⏸️ Bot PAUSED (daily loss limit reached)")
await asyncio.sleep(60)
continue
# Signal generation
for pair in self.PAIRS:
if pair not in self.active_trades and await self.signal_buy(pair):
await self.place_buy_order(pair)
# Monitor positions
await self.monitor_positions()
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Cycle Error: {e}")
await asyncio.sleep(5)
async def main():
bot = TradingBot()
await bot.run_cycle()
if __name__ == '__main__':
asyncio.run(main())

View File

@ -1,434 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V5 ENHANCED - FULLY FIXED VERSION
Implementiert: SL, TP, Daily Limit, R:R Ratio
FIXED: Binance API method (order_take_profit → create_order)
FIXED: PRICE_FILTER für SL Orders durch Tick-Rounding
FIXED: Quantity rounding mit Decimal (no floating point errors)
FIXED: Quantity string formatting für Binance
NEW: Startup Message + 3h Performance Reports via Telegram
"""
import os, asyncio, logging, random, json, time, math, requests
from decimal import Decimal, ROUND_DOWN
from binance.client import Client
from binance.exceptions import BinanceAPIException
from datetime import datetime, timedelta
# Logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Load env
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
k,_,v = line.partition('=')
env[k.strip()] = v.strip()
class TradingBot:
def __init__(self):
self.client = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
self.PAIRS = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
self.SIGNAL_THRESHOLD = 5 # 5% random signal
self.INVESTMENT_PERCENT = 35 # 35% per trade (5 parallel = 90% max, 10% buffer)
self.NOTIONAL_MIN = 5.0 # Override Binance minimum to $3
self.STOP_LOSS_PERCENT = 2.5 # -2.5%
self.TAKE_PROFIT_PERCENT = 3.0 # +3%
self.DAILY_LOSS_LIMIT = -5 # -5% max
self.active_trades = {}
self.daily_pnl = 0
self.paused = False
self.start_time = datetime.now()
self.trades_today = 0
self.wins_today = 0
self.losses_today = 0
# Precision cache
self.pair_precision = {}
self._load_pair_precision()
# Telegram
self.telegram_token = env.get('TELEGRAM_BOT_TOKEN')
self.telegram_chat_id = env.get('TELEGRAM_CHAT_ID')
logger.info("✅ Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)")
# Send startup message
self._send_startup_message()
def _send_telegram(self, message):
"""Send message to Telegram"""
try:
if not self.telegram_token or not self.telegram_chat_id:
logger.warning("Telegram not configured")
return False
url = f"https://api.telegram.org/bot{self.telegram_token}/sendMessage"
data = {
'chat_id': self.telegram_chat_id,
'text': message,
'parse_mode': 'Markdown'
}
response = requests.post(url, data=data, timeout=5)
return response.status_code == 200
except Exception as e:
logger.error(f"Telegram Error: {e}")
return False
def _send_startup_message(self):
"""Send startup message with current strategy"""
message = """🤖 **TRADING BOT V5 — STARTED!**
⚙️ **AKTUELLE STRATEGIE:**
**Entry:**
• Signal: 5% Random (5 sec cycle)
• Investment: 18% USDT per trade ← FIXED!
• Pairs: BTC, ETH, SOL, BNB, XRP
• Max Parallel: 5 trades (5×18% = 90% max)
**Exit:**
• Take Profit: +3.0% ✅
• Stop Loss: -2.5% ✅
• Risk/Reward: 1:1.2
**Risk Management:**
• Daily Loss Limit: -5%
• Position Size Cap: 18%
• Buffer Reserve: 10% USDT
• SL Auto-Place: Ja (korrekt gerundet)
**Status:** 🟢 LIVE
• Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """
• Capital Ready: 100% USDT
---
Reports: Alle 3h via Telegram 📊"""
self._send_telegram(message)
logger.info("📱 Startup message sent to Telegram")
def _load_pair_precision(self):
"""Load Binance precision rules for each pair"""
for pair in self.PAIRS:
try:
info = self.client.get_symbol_info(symbol=pair)
for f in info['filters']:
if f['filterType'] == 'PRICE_FILTER':
tick = float(f['tickSize'])
self.pair_precision[pair] = {
'tick': tick,
'decimals': self._get_decimals(tick)
}
if f['filterType'] == 'LOT_SIZE':
step = float(f['stepSize'])
if pair not in self.pair_precision:
self.pair_precision[pair] = {}
self.pair_precision[pair]['step'] = step
self.pair_precision[pair]['step_decimals'] = self._get_decimals(step)
if f['filterType'] == 'NOTIONAL':
min_notional = float(f['minNotional'])
if pair not in self.pair_precision:
self.pair_precision[pair] = {}
self.pair_precision[pair]['min_notional'] = min_notional
except Exception as e:
logger.error(f"Precision load {pair}: {e}")
def _get_decimals(self, tick):
"""Get decimal places from tick size"""
s = str(tick)
if 'e' in s:
return int(s.split('e-')[1]) if 'e-' in s else 0
return len(s.split('.')[1]) if '.' in s else 0
def _round_to_tick(self, price, pair):
"""Round price to Binance tick size using Decimal"""
tick = self.pair_precision.get(pair, {}).get('tick', 0.01)
price_decimal = Decimal(str(price))
tick_decimal = Decimal(str(tick))
rounded = (price_decimal / tick_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * tick_decimal
return float(rounded)
def _round_quantity(self, qty, pair):
"""Round quantity to Binance step size using Decimal - NO PRECISION LOSS"""
step = self.pair_precision.get(pair, {}).get('step', 0.00001)
step_decimals = self.pair_precision.get(pair, {}).get('step_decimals', 5)
qty_decimal = Decimal(str(qty))
step_decimal = Decimal(str(step))
# Round down (safe side)
rounded = (qty_decimal / step_decimal).quantize(Decimal('1'), rounding=ROUND_DOWN) * step_decimal
# Format as string with exactly the right decimals
format_str = f"0.{'':<{step_decimals}}"
if step_decimals == 0:
return int(rounded)
return float(rounded)
async def signal_buy(self, pair):
"""Generate random 5% buy signal"""
rand = random.randint(1, 100)
return rand <= self.SIGNAL_THRESHOLD
async def place_buy_order(self, pair):
"""Place market buy order"""
try:
# Get current price
ticker = self.client.get_ticker(symbol=pair)
entry_price = float(ticker['lastPrice'])
# Calculate quantity
account = self.client.get_account()
usdt_balance = next((b['free'] for b in account['balances'] if b['asset'] == 'USDT'), 0)
usdt = float(usdt_balance) * (self.INVESTMENT_PERCENT / 100)
qty = usdt / entry_price
# ROUND QUANTITY TO STEP SIZE (CRITICAL FIX WITH DECIMAL!)
qty = self._round_quantity(qty, pair)
# Check if qty is valid (not zero after rounding)
if qty <= 0:
logger.warning(f"Quantity too small for {pair}: {qty}")
return False
# VALIDATE NOTIONAL (order_value must be >= 3.0 MINIMUM)
order_value = qty * entry_price
NOTIONAL_MIN = 5.0 # Minimum $3
if order_value < NOTIONAL_MIN:
logger.warning(f"Order value too small {pair}: ${order_value:.2f} < ${NOTIONAL_MIN:.2f} (qty={qty}, price={entry_price})")
return False
logger.info(f"✅ NOTIONAL Check Passed: {pair} ${order_value:.2f} >= ${NOTIONAL_MIN:.2f}")
# Place market buy
order = self.client.order_market_buy(symbol=pair, quantity=qty)
logger.info(f"🟢 BUY: {pair} x{qty} @ ${entry_price:.2f} (value: ${order_value:.2f})")
# Store trade
self.active_trades[pair] = {
'entry': entry_price,
'qty': qty,
'time': datetime.now()
}
# Place SL order (FIXED WITH CORRECT API METHOD)
await self.place_stop_loss(pair, entry_price, qty)
self.trades_today += 1
return True
except Exception as e:
logger.error(f"Buy Error {pair}: {e}")
return False
async def place_stop_loss(self, pair, entry_price, qty):
"""Place stop loss order with correct precision & API method"""
try:
# Calculate SL price with 2.5% loss
sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100)
# ROUND TO TICK SIZE (CRITICAL FIX!)
sl_price = self._round_to_tick(sl_price, pair)
# ROUND QUANTITY TO STEP SIZE (WITH DECIMAL!)
qty_rounded = self._round_quantity(qty, pair)
# Place SL order using create_order (correct Binance API method)
order = self.client.create_order(
symbol=pair,
side='SELL',
type='STOP_LOSS_LIMIT',
timeInForce='GTC',
quantity=qty_rounded,
stopPrice=sl_price,
price=sl_price # For STOP_LOSS_LIMIT, need price = stopPrice
)
logger.info(f"🛡️ SL: {pair} x{qty_rounded} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)")
except BinanceAPIException as e:
logger.error(f"SL Error {pair}: {e}")
async def monitor_positions(self):
"""Monitor open positions for TP/SL"""
try:
account = self.client.get_account()
for pair in list(self.active_trades.keys()):
ticker = self.client.get_ticker(symbol=pair)
current = float(ticker['lastPrice'])
entry = self.active_trades[pair]['entry']
gain_percent = ((current - entry) / entry) * 100
# Check TP
if gain_percent >= self.TAKE_PROFIT_PERCENT:
await self.close_position(pair, 'TP', current)
# Check SL (secondary check)
elif gain_percent <= -self.STOP_LOSS_PERCENT:
await self.close_position(pair, 'SL', current)
except Exception as e:
logger.error(f"Monitor Error: {e}")
async def close_position(self, pair, reason, current_price):
"""Close position"""
if pair not in self.active_trades:
return
qty = self.active_trades[pair]['qty']
entry = self.active_trades[pair]['entry']
pnl = (current_price - entry) * qty
logger.info(f"📊 {reason}: {pair} closed @ ${current_price:.2f}, PnL: ${pnl:.2f}")
del self.active_trades[pair]
self.daily_pnl += pnl
if pnl > 0:
self.wins_today += 1
else:
self.losses_today += 1
# Check daily loss limit
if self.daily_pnl <= self.DAILY_LOSS_LIMIT:
logger.warning(f"⚠️ DAILY LOSS LIMIT REACHED: ${self.daily_pnl:.2f}")
self.paused = True
def get_performance_report(self):
"""Get current performance metrics"""
try:
account = self.client.get_account()
balance = {}
for asset_data in account['balances']:
asset = asset_data['asset']
free = float(asset_data['free'])
locked = float(asset_data['locked'])
total = free + locked
if total > 0.00001:
balance[asset] = {
'free': free,
'locked': locked,
'total': total
}
# Get prices
prices = {}
for pair in self.PAIRS:
try:
ticker = self.client.get_ticker(symbol=pair)
asset = pair.replace('USDT', '')
prices[asset] = float(ticker['lastPrice'])
except:
pass
prices['USDT'] = 1.0
# Calculate portfolio
portfolio = 0
tracked = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT']
for asset in tracked:
if asset in balance:
portfolio += balance[asset]['total'] * prices.get(asset, 0)
return {
'portfolio': round(portfolio, 2),
'usdt_free': balance.get('USDT', {}).get('free', 0),
'daily_pnl': self.daily_pnl,
'trades_today': self.trades_today,
'wins': self.wins_today,
'losses': self.losses_today,
'active_trades': len(self.active_trades),
'paused': self.paused
}
except Exception as e:
logger.error(f"Performance Report Error: {e}")
return None
def send_performance_report(self):
"""Send 3h performance report via Telegram"""
report = self.get_performance_report()
if not report:
return
win_rate = 0
if report['trades_today'] > 0:
win_rate = (report['wins'] / report['trades_today']) * 100
status = "🟢 RUNNING" if not report['paused'] else "⏸️ PAUSED"
message = f"""📊 **3H PERFORMANCE REPORT**
**Portfolio Status:**
• Total: ${report['portfolio']:.2f}
• USDT Free: ${report['usdt_free']:.2f}
• Status: {status}
**Today's Trading:**
• Trades Executed: {report['trades_today']}
• Wins: {report['wins']} ✅
• Losses: {report['losses']} ❌
• Win Rate: {win_rate:.1f}%
**P&L:**
• Daily P&L: ${report['daily_pnl']:.2f}
• Open Positions: {report['active_trades']}
**Risk Status:**
• Daily Loss Limit: -5%
• Current Daily Loss: ${report['daily_pnl']:.2f}
• Pause Active: {'Yes ⏸️' if report['paused'] else 'No ✅'}
---
Time: {datetime.now().strftime('%Y-%m-%d %H:%M UTC')}
Bot: V5 ENHANCED (FULLY FIXED)"""
self._send_telegram(message)
logger.info("📱 Performance report sent to Telegram")
async def run_cycle(self):
"""Main trading cycle"""
last_report_hour = None
while True:
try:
# Check if it's time for 3h report
current_hour = datetime.now().hour
if current_hour % 3 == 0 and last_report_hour != current_hour:
self.send_performance_report()
last_report_hour = current_hour
# Check daily loss limit pause
if self.paused:
logger.info("⏸️ Bot PAUSED (daily loss limit reached)")
await asyncio.sleep(60)
continue
# Signal generation
for pair in self.PAIRS:
if pair not in self.active_trades and await self.signal_buy(pair):
await self.place_buy_order(pair)
# Monitor positions
await self.monitor_positions()
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Cycle Error: {e}")
await asyncio.sleep(5)
async def main():
bot = TradingBot()
await bot.run_cycle()
if __name__ == '__main__':
asyncio.run(main())

View File

@ -1,190 +0,0 @@
import asyncio, logging, joblib, time
from datetime import datetime
from src.config import get_config
from src.bot.binance_client import BinanceClientWrapper
from src.integrations.telegram_notifier import TelegramNotifier
from src.integrations.obsidian_logger import ObsidianLogger
from src.strategies.ml_strategy import MLStrategy
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class MLTradingBot:
def __init__(self, config, binance, telegram, obsidian, model, scaler):
self.config = config
self.binance = binance
self.telegram = telegram
self.obsidian = obsidian
self.model = model
self.scaler = scaler
self.strategy = MLStrategy(trading_pair=config.trading_pair)
# Trading state
self.last_report_time = time.time()
self.report_interval = 10800 # 3 HOURS (10800 seconds)
self.trades_today = 0
self.wins_today = 0
self.losses_today = 0
self.daily_pnl = 0.0
self.report_count = 0
async def get_market_data(self):
"""Fetch current market price and stats"""
try:
ticker = self.config.trading_pair.split('/')[0] # BTC from BTCUSDT
symbol = f"{ticker}USDT"
# Get current price
price_data = await self.binance.get_ticker_price(symbol)
if not price_data:
return None
current_price = float(price_data)
return {
'ticker': ticker,
'current_price': current_price,
'symbol': symbol
}
except Exception as e:
logger.error(f"Market data fetch error: {e}")
return None
async def get_account_balance(self):
"""Get current account balance"""
try:
balance = self.binance.get_balance('USDT')
if balance:
return {'USDT': {'total': balance}}
return {}
except Exception as e:
logger.error(f"Balance fetch error: {e}")
return {}
async def send_performance_report(self):
"""Send 3-hourly performance report"""
try:
self.report_count += 1
# Get market data
market = await self.get_market_data()
if not market:
logger.warning("No market data available")
return
# Get account balance
balances = await self.get_account_balance()
usdt_balance = balances.get('USDT', {}).get('total', 0)
# Build report
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S UTC')
report = f"""
📊 **PERFORMANCE REPORT #{self.report_count}** — {timestamp}
🎯 **MARKET STATUS:**
├─ {market['ticker']}/USDT: ${market['current_price']:,.2f}
├─ Trades Today: {self.trades_today}
├─ Wins: {self.wins_today} | Losses: {self.losses_today}
└─ Daily P&L: ${self.daily_pnl:+.2f}
💰 **ACCOUNT STATUS:**
├─ USDT Balance: ${usdt_balance:,.2f}
├─ Device: CPU
├─ Mode: Live Trading
└─ Strategy: ML (92% accuracy, 60% threshold)
📈 **BOT STATUS: RUNNING ✅**
"""
# Send to Telegram (FIXED — now actually sends!)
success = await self.telegram.send_alert(report.strip())
if success:
logger.info(f"✅ Performance report #{self.report_count} sent to Telegram")
else:
logger.warning(f"❌ Failed to send report #{self.report_count} to Telegram")
except Exception as e:
logger.error(f"Report error: {e}")
async def monitor_trades(self):
"""Monitor open trades and check signals"""
try:
symbol = f"{self.config.trading_pair.split('/')[0]}USDT"
orders = self.binance.get_open_orders(symbol)
if orders and len(orders) > 0:
logger.info(f"📈 Open orders: {len(orders)}")
except Exception as e:
logger.debug(f"Trade monitoring: {e}")
async def run(self):
"""Main bot loop"""
logger.info(f"🤖 Starting ML Trading Bot — {self.config.trading_pair}")
startup_msg = f"""🤖 **BOT STARTED - V2 ML ADAPTIVE**
✅ Strategy: ML Adaptive (60% threshold)
✅ Models: BTC 92% accuracy
✅ Device: CPU (Live)
✅ Reporting: EVERY 3 HOURS
✅ Status: ACTIVE & MONITORING"""
await self.telegram.send_alert(startup_msg)
logger.info("✅ Startup message sent to Telegram")
logger.info("🟢 Bot running — sending reports every 3 hours...")
while True:
try:
current_time = time.time()
# Send 3-hourly performance report
if (current_time - self.last_report_time) >= self.report_interval:
logger.info(f"⏰ Time for Report #{self.report_count + 1}")
await self.send_performance_report()
self.last_report_time = current_time
# Monitor trades every 5 minutes
await self.monitor_trades()
# Sleep for 5 minutes
await asyncio.sleep(60) # Check every 1 min instead of 5 min for trading opportunities
except KeyboardInterrupt:
logger.info("Bot interrupted by user")
break
except Exception as e:
logger.error(f"Bot error: {e}")
try:
await self.telegram.send_alert(f"❌ Bot Error: {str(e)[:100]}")
except:
pass
await asyncio.sleep(60)
async def main():
config = get_config()
if config.environment == 'testnet':
api_key, api_secret = config.binance_api_key_testnet, config.binance_api_secret_testnet
else:
api_key, api_secret = config.binance_api_key_live, config.binance_api_secret_live
binance = BinanceClientWrapper(api_key=api_key, api_secret=api_secret, testnet=(config.environment=='testnet'))
telegram = TelegramNotifier(bot_token=config.telegram_bot_token, chat_id=config.telegram_chat_id)
obsidian = ObsidianLogger(vault_path=config.obsidian_vault_path, trade_log_file=config.obsidian_trade_log_file)
try:
# Load BTC model
model = joblib.load('/tmp/model_BTC.pkl')
scaler = joblib.load('/tmp/scaler_BTC.pkl')
logger.info(f'✅ ML Model loaded: BTC (92% accuracy)')
except Exception as e:
logger.error(f'❌ ML Model Error: {e}')
return
bot = MLTradingBot(config, binance, telegram, obsidian, model, scaler)
await bot.run()
if __name__ == '__main__':
asyncio.run(main())

File diff suppressed because it is too large Load Diff

View File

@ -1,220 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V5 ENHANCED - Mit kritischen Risk Management Fixes
Implementiert: SL, TP Anpassung, Daily Limit, R:R Ratio
"""
import os, asyncio, logging, random, json, time
from datetime import datetime, timedelta
from binance.client import Client
from binance.exceptions import BinanceAPIException
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Load config
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
k, _, v = line.partition('=')
env[k.strip()] = v.strip()
class TradingBotV5Enhanced:
def __init__(self):
self.binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
self.state_file = '/home/marc/bot-deploy/trades.json'
self.load_state()
# NEW: Risk Management Settings
self.STOP_LOSS_PERCENT = 2.5 # 2.5% SL (-2.5%)
self.TAKE_PROFIT_PERCENT = 3.0 # 3.0% TP (+3%) - was +1%
self.DAILY_LOSS_LIMIT = 5.0 # Max -5% daily
self.MIN_RISK_REWARD = 1.5 # Min R:R ratio
self.MAX_POSITION_PERCENT = 25 # Max 25% per trade
logger.info("✅ Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)")
def load_state(self):
if os.path.exists(self.state_file):
with open(self.state_file) as f:
self.state = json.load(f)
else:
self.state = {'current': {}, 'completed': [], 'daily_start_balance': 0}
def save_state(self):
with open(self.state_file, 'w') as f:
json.dump(self.state, f, indent=2)
def check_and_place_sl_orders(self, pair, qty, entry_price):
"""
NEW: Automatically place Stop Loss orders for existing positions
SL = Entry - 2.5%
"""
sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100)
try:
# Check if already has SL order
orders = self.binance.get_open_orders(symbol=pair)
has_sl = any(o['side'] == 'SELL' and float(o['price']) < entry_price for o in orders)
if not has_sl:
# Place SL order
order = self.binance.order_limit_sell(
symbol=pair,
quantity=qty,
price=round(sl_price, 8)
)
logger.info(f"🛡️ Stop Loss set: {pair} {qty} @ ${sl_price:.4f}")
return True
except Exception as e:
logger.error(f"SL Error {pair}: {e}")
return False
def place_buy(self, pair):
"""Place market buy with Risk Management checks"""
try:
# Get balance
balance = self.binance.get_account()
usdt_free = float([a['free'] for a in balance['balances'] if a['asset'] == 'USDT'][0])
# NEW: Daily loss check
daily_loss = self.calculate_daily_loss()
if daily_loss <= -self.DAILY_LOSS_LIMIT:
logger.warning(f"⛔ Daily loss limit hit: {daily_loss:.2f}% (limit: -{self.DAILY_LOSS_LIMIT}%)")
return None
# Calculate position size (25% of USDT)
qty_usdt = usdt_free * (self.MAX_POSITION_PERCENT / 100)
if qty_usdt < 10: # Binance minimum
return None
# Get current price
ticker = self.binance.get_symbol_info(pair)
price = float(self.binance.get_ticker(symbol=pair)['lastPrice'])
# Calculate quantity with LOT_SIZE filter
lot_filter = next(f for f in ticker['filters'] if f['filterType'] == 'LOT_SIZE')
step_size = float(lot_filter['stepSize'])
qty = float(int(qty_usdt / price / step_size) * step_size)
if qty < float(lot_filter['minQty']):
return None
# Place market buy
order = self.binance.order_market_buy(symbol=pair, quantity=qty)
logger.info(f"🟢 BUY: {pair} x{qty:.6f} @ ${price:.4f}")
# NEW: Auto-place Stop Loss
self.check_and_place_sl_orders(pair, qty, price)
return order
except Exception as e:
logger.error(f"Buy Error {pair}: {e}")
return None
def check_take_profit(self):
"""NEW: Check and close at +3% TP with SL protection"""
try:
balance = self.binance.get_account()
for pair in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']:
ticker = self.binance.get_ticker(symbol=pair)
current_price = float(ticker['lastPrice'])
# Check if we have open trade
if pair in self.state['current']:
entry_price = self.state['current'][pair]['buy_price']
gain_percent = (current_price - entry_price) / entry_price * 100
# TP at +3%
if gain_percent >= self.TAKE_PROFIT_PERCENT:
qty = self.state['current'][pair]['qty']
try:
order = self.binance.order_market_sell(symbol=pair, quantity=qty)
profit_usd = (current_price - entry_price) * qty
logger.info(f"💰 TP HIT: {pair} +{gain_percent:.2f}% = ${profit_usd:.2f}")
# Record completion
self.state['completed'].append({
'pair': pair,
'qty': qty,
'buy_price': entry_price,
'sell_price': current_price,
'profit_percent': gain_percent,
'profit_usd': profit_usd
})
del self.state['current'][pair]
self.save_state()
except Exception as e:
logger.error(f"TP sell error {pair}: {e}")
# SL at -2.5% (auto-cancelled by limit order but check anyway)
elif gain_percent <= -self.STOP_LOSS_PERCENT:
qty = self.state['current'][pair]['qty']
try:
order = self.binance.order_market_sell(symbol=pair, quantity=qty)
loss_usd = (current_price - entry_price) * qty
logger.warning(f"🛑 SL HIT: {pair} {gain_percent:.2f}% = ${loss_usd:.2f}")
self.state['completed'].append({
'pair': pair,
'qty': qty,
'buy_price': entry_price,
'sell_price': current_price,
'profit_percent': gain_percent,
'profit_usd': loss_usd
})
del self.state['current'][pair]
self.save_state()
except Exception as e:
logger.error(f"SL sell error {pair}: {e}")
except Exception as e:
logger.error(f"TP check error: {e}")
def calculate_daily_loss(self):
"""Calculate daily loss percentage"""
try:
if not self.state['completed']:
return 0
today_trades = [t for t in self.state['completed']
if datetime.fromisoformat(t.get('timestamp', datetime.now().isoformat())).date() == datetime.now().date()]
daily_loss = sum(t.get('profit_usd', 0) for t in today_trades)
balance = self.binance.get_account()
portfolio = sum(float(a['free']) for a in balance['balances'])
loss_percent = (daily_loss / portfolio * 100) if portfolio > 0 else 0
return loss_percent
except:
return 0
async def run(self):
"""Main trading loop"""
logger.info("🚀 Trading Bot V5 ENHANCED started (SL+TP+DailyLimit)")
while True:
try:
# Check exits first (TP/SL)
self.check_take_profit()
# Generate signal (5% probability)
if random.random() < 0.05:
pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
for pair in pairs:
if pair not in self.state['current']:
self.place_buy(pair)
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Loop error: {e}")
await asyncio.sleep(5)
if __name__ == "__main__":
bot = TradingBotV5Enhanced()
asyncio.run(bot.run())

View File

@ -1,220 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V5 ENHANCED - Mit kritischen Risk Management Fixes
Implementiert: SL, TP Anpassung, Daily Limit, R:R Ratio
"""
import os, asyncio, logging, random, json, time
from datetime import datetime, timedelta
from binance.client import Client
from binance.exceptions import BinanceAPIException
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Load config
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
k, _, v = line.partition('=')
env[k.strip()] = v.strip()
class TradingBotV5Enhanced:
def __init__(self):
self.binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
self.state_file = '/home/marc/bot-deploy/trades.json'
self.load_state()
# NEW: Risk Management Settings
self.STOP_LOSS_PERCENT = 2.5 # 2.5% SL (-2.5%)
self.TAKE_PROFIT_PERCENT = 3.0 # 3.0% TP (+3%) - was +1%
self.DAILY_LOSS_LIMIT = 5.0 # Max -5% daily
self.MIN_RISK_REWARD = 1.5 # Min R:R ratio
self.MAX_POSITION_PERCENT = 25 # Max 25% per trade
logger.info("✅ Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)")
def load_state(self):
if os.path.exists(self.state_file):
with open(self.state_file) as f:
self.state = json.load(f)
else:
self.state = {'current': {}, 'completed': [], 'daily_start_balance': 0}
def save_state(self):
with open(self.state_file, 'w') as f:
json.dump(self.state, f, indent=2)
def check_and_place_sl_orders(self, pair, qty, entry_price):
"""
NEW: Automatically place Stop Loss orders for existing positions
SL = Entry - 2.5%
"""
sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100)
try:
# Check if already has SL order
orders = self.binance.get_open_orders(symbol=pair)
has_sl = any(o['side'] == 'SELL' and float(o['price']) < entry_price for o in orders)
if not has_sl:
# Place SL order
order = self.binance.order_limit_sell(
symbol=pair,
quantity=qty,
price=round(sl_price, 8)
)
logger.info(f"🛡️ Stop Loss set: {pair} {qty} @ ${sl_price:.4f}")
return True
except Exception as e:
logger.error(f"SL Error {pair}: {e}")
return False
def place_buy(self, pair):
"""Place market buy with Risk Management checks"""
try:
# Get balance
balance = self.binance.get_account()
usdt_free = float([a['free'] for a in balance['balances'] if a['asset'] == 'USDT'][0])
# NEW: Daily loss check
daily_loss = self.calculate_daily_loss()
if daily_loss <= -self.DAILY_LOSS_LIMIT:
logger.warning(f"⛔ Daily loss limit hit: {daily_loss:.2f}% (limit: -{self.DAILY_LOSS_LIMIT}%)")
return None
# Calculate position size (25% of USDT)
qty_usdt = usdt_free * (self.MAX_POSITION_PERCENT / 100)
if qty_usdt < 10: # Binance minimum
return None
# Get current price
ticker = self.binance.get_symbol_info(pair)
price = float(self.binance.get_ticker(symbol=pair)['lastPrice'])
# Calculate quantity with LOT_SIZE filter
lot_filter = next(f for f in ticker['filters'] if f['filterType'] == 'LOT_SIZE')
step_size = float(lot_filter['stepSize'])
qty = float(int(qty_usdt / price / step_size) * step_size)
if qty < float(lot_filter['minQty']):
return None
# Place market buy
order = self.binance.order_market_buy(symbol=pair, quantity=qty)
logger.info(f"🟢 BUY: {pair} x{qty:.6f} @ ${price:.4f}")
# NEW: Auto-place Stop Loss
self.check_and_place_sl_orders(pair, qty, price)
return order
except Exception as e:
logger.error(f"Buy Error {pair}: {e}")
return None
def check_take_profit(self):
"""NEW: Check and close at +3% TP with SL protection"""
try:
balance = self.binance.get_account()
for pair in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']:
ticker = self.binance.get_ticker(symbol=pair)
current_price = float(ticker['lastPrice'])
# Check if we have open trade
if pair in self.state['current']:
entry_price = self.state['current'][pair]['buy_price']
gain_percent = (current_price - entry_price) / entry_price * 100
# TP at +3%
if gain_percent >= self.TAKE_PROFIT_PERCENT:
qty = self.state['current'][pair]['qty']
try:
order = self.binance.order_market_sell(symbol=pair, quantity=qty)
profit_usd = (current_price - entry_price) * qty
logger.info(f"💰 TP HIT: {pair} +{gain_percent:.2f}% = ${profit_usd:.2f}")
# Record completion
self.state['completed'].append({
'pair': pair,
'qty': qty,
'buy_price': entry_price,
'sell_price': current_price,
'profit_percent': gain_percent,
'profit_usd': profit_usd
})
del self.state['current'][pair]
self.save_state()
except Exception as e:
logger.error(f"TP sell error {pair}: {e}")
# SL at -2.5% (auto-cancelled by limit order but check anyway)
elif gain_percent <= -self.STOP_LOSS_PERCENT:
qty = self.state['current'][pair]['qty']
try:
order = self.binance.order_market_sell(symbol=pair, quantity=qty)
loss_usd = (current_price - entry_price) * qty
logger.warning(f"🛑 SL HIT: {pair} {gain_percent:.2f}% = ${loss_usd:.2f}")
self.state['completed'].append({
'pair': pair,
'qty': qty,
'buy_price': entry_price,
'sell_price': current_price,
'profit_percent': gain_percent,
'profit_usd': loss_usd
})
del self.state['current'][pair]
self.save_state()
except Exception as e:
logger.error(f"SL sell error {pair}: {e}")
except Exception as e:
logger.error(f"TP check error: {e}")
def calculate_daily_loss(self):
"""Calculate daily loss percentage"""
try:
if not self.state['completed']:
return 0
today_trades = [t for t in self.state['completed']
if datetime.fromisoformat(t.get('timestamp', datetime.now().isoformat())).date() == datetime.now().date()]
daily_loss = sum(t.get('profit_usd', 0) for t in today_trades)
balance = self.binance.get_account()
portfolio = sum(float(a['free']) for a in balance['balances'])
loss_percent = (daily_loss / portfolio * 100) if portfolio > 0 else 0
return loss_percent
except:
return 0
async def run(self):
"""Main trading loop"""
logger.info("🚀 Trading Bot V5 ENHANCED started (SL+TP+DailyLimit)")
while True:
try:
# Check exits first (TP/SL)
self.check_take_profit()
# Generate signal (5% probability)
if random.random() < 0.05:
pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
for pair in pairs:
if pair not in self.state['current']:
self.place_buy(pair)
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Loop error: {e}")
await asyncio.sleep(5)
if __name__ == "__main__":
bot = TradingBotV5Enhanced()
asyncio.run(bot.run())

View File

@ -1,205 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V5 ENHANCED - Risk Management FIXED
Implementiert: SL (mit korrekter Precision), TP, Daily Limit, R:R Ratio
FIXED: PRICE_FILTER für SL Orders durch Tick-Rounding
"""
import os, asyncio, logging, random, json, time, math
from binance.client import Client
from binance.exceptions import BinanceAPIException
from datetime import datetime, timedelta
# Logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Load env
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
k,_,v = line.partition('=')
env[k.strip()] = v.strip()
class TradingBot:
def __init__(self):
self.client = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
self.PAIRS = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
self.SIGNAL_THRESHOLD = 5 # 5% random signal
self.INVESTMENT_PERCENT = 25 # 25% per trade
self.STOP_LOSS_PERCENT = 2.5 # -2.5%
self.TAKE_PROFIT_PERCENT = 3.0 # +3%
self.DAILY_LOSS_LIMIT = -5 # -5% max
self.active_trades = {}
self.daily_pnl = 0
self.paused = False
# Precision cache
self.pair_precision = {}
self._load_pair_precision()
logger.info("✅ Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)")
def _load_pair_precision(self):
"""Load Binance precision rules for each pair"""
for pair in self.PAIRS:
try:
info = self.client.get_symbol_info(symbol=pair)
for f in info['filters']:
if f['filterType'] == 'PRICE_FILTER':
tick = float(f['tickSize'])
self.pair_precision[pair] = {
'tick': tick,
'decimals': self._get_decimals(tick)
}
except Exception as e:
logger.error(f"Precision load {pair}: {e}")
def _get_decimals(self, tick):
"""Get decimal places from tick size"""
s = str(tick)
if 'e' in s:
return int(s.split('e-')[1]) if 'e-' in s else 0
return len(s.split('.')[1]) if '.' in s else 0
def _round_to_tick(self, price, pair):
"""Round price to Binance tick size"""
tick = self.pair_precision.get(pair, {}).get('tick', 0.01)
return round(price / tick) * tick
async def signal_buy(self, pair):
"""Generate random 5% buy signal"""
rand = random.randint(1, 100)
return rand <= self.SIGNAL_THRESHOLD
async def place_buy_order(self, pair):
"""Place market buy order"""
try:
# Get current price
ticker = self.client.get_ticker(symbol=pair)
entry_price = float(ticker['lastPrice'])
# Calculate quantity
account = self.client.get_account()
usdt_balance = next((b['free'] for b in account['balances'] if b['asset'] == 'USDT'), 0)
usdt = float(usdt_balance) * (self.INVESTMENT_PERCENT / 100)
qty = usdt / entry_price
# Place market buy
order = self.client.order_market_buy(symbol=pair, quantity=qty)
logger.info(f"🟢 BUY: {pair} x{qty:.6f} @ ${entry_price:.2f}")
# Store trade
self.active_trades[pair] = {
'entry': entry_price,
'qty': qty,
'time': datetime.now()
}
# Place SL order (FIXED WITH ROUNDING)
await self.place_stop_loss(pair, entry_price, qty)
return True
except Exception as e:
logger.error(f"Buy Error {pair}: {e}")
return False
async def place_stop_loss(self, pair, entry_price, qty):
"""Place stop loss order with correct precision"""
try:
# Calculate SL price with 2.5% loss
sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100)
# ROUND TO TICK SIZE (CRITICAL FIX!)
sl_price = self._round_to_tick(sl_price, pair)
# Place SL order
order = self.client.order_take_profit(
symbol=pair,
side='SELL',
type='STOP_LOSS',
timeInForce='GTC',
quantity=qty,
stopPrice=sl_price,
price=sl_price # Binance requires price = stopPrice for STOP_LOSS
)
logger.info(f"🛡️ SL: {pair} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)")
except BinanceAPIException as e:
logger.error(f"SL Error {pair}: {e}")
async def monitor_positions(self):
"""Monitor open positions for TP/SL"""
try:
account = self.client.get_account()
for pair in self.active_trades.keys():
ticker = self.client.get_ticker(symbol=pair)
current = float(ticker['lastPrice'])
entry = self.active_trades[pair]['entry']
gain_percent = ((current - entry) / entry) * 100
# Check TP
if gain_percent >= self.TAKE_PROFIT_PERCENT:
await self.close_position(pair, 'TP', current)
# Check SL (secondary check)
elif gain_percent <= -self.STOP_LOSS_PERCENT:
await self.close_position(pair, 'SL', current)
except Exception as e:
logger.error(f"Monitor Error: {e}")
async def close_position(self, pair, reason, current_price):
"""Close position"""
if pair not in self.active_trades:
return
qty = self.active_trades[pair]['qty']
entry = self.active_trades[pair]['entry']
pnl = (current_price - entry) * qty
logger.info(f"📊 {reason}: {pair} closed @ ${current_price:.2f}, PnL: ${pnl:.2f}")
del self.active_trades[pair]
self.daily_pnl += pnl
# Check daily loss limit
if self.daily_pnl <= self.DAILY_LOSS_LIMIT:
logger.warning(f"⚠️ DAILY LOSS LIMIT REACHED: ${self.daily_pnl:.2f}")
self.paused = True
async def run_cycle(self):
"""Main trading cycle"""
while True:
try:
# Check daily loss limit pause
if self.paused:
logger.info("⏸️ Bot PAUSED (daily loss limit reached)")
await asyncio.sleep(60)
continue
# Signal generation
for pair in self.PAIRS:
if pair not in self.active_trades and await self.signal_buy(pair):
await self.place_buy_order(pair)
# Monitor positions
await self.monitor_positions()
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Cycle Error: {e}")
await asyncio.sleep(5)
async def main():
bot = TradingBot()
await bot.run_cycle()
if __name__ == '__main__':
asyncio.run(main())

View File

@ -1,157 +0,0 @@
import asyncio, logging, joblib, time
from datetime import datetime
from src.config import get_config
from src.bot.binance_client import BinanceClientWrapper
from src.integrations.telegram_notifier import TelegramNotifier
from src.integrations.obsidian_logger import ObsidianLogger
from src.strategies.ml_strategy import MLStrategy
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class MLTradingBot:
def __init__(self, config, binance, telegram, obsidian, model, scaler):
self.config = config
self.binance = binance
self.telegram = telegram
self.obsidian = obsidian
self.model = model
self.scaler = scaler
self.strategy = MLStrategy(trading_pair=config.trading_pair)
self.last_report_time = time.time()
self.report_interval = 10800
self.trades_today = 0
self.wins_today = 0
self.losses_today = 0
self.daily_pnl = 0.0
self.report_count = 0
async def auto_swap_to_usdt(self):
"""Auto-swap holdings to USDT if needed"""
try:
balance = await self.binance.get_balance()
usdt_free = float(balance.get('USDT', {}).get('free', 0)) if balance else 0
# If low on USDT, sell any BTC/ETH/SOL holdings
for crypto in ['BTC', 'ETH', 'SOL']:
crypto_balance = float(balance.get(crypto, {}).get('free', 0)) if balance else 0
if usdt_free < 20 and crypto_balance > 0.0001:
pair = crypto + 'USDT'
logger.info(f'SWAP: Selling {crypto_balance:.6f} {crypto} for USDT')
try:
await self.binance.place_order(pair, 'SELL', 'MARKET', crypto_balance * 0.95)
await self.telegram.send_alert(f'SWAP: Sold {crypto_balance:.6f} {crypto}')
return True
except Exception as e:
logger.error(f'Swap failed: {e}')
except Exception as e:
logger.error(f'Auto-swap error: {e}')
return False
async def find_best_trade(self):
"""Scan multiple pairs for best signal"""
pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
for pair in pairs:
try:
price = await self.binance.get_ticker_price(pair)
signal = self.strategy.predict(price) if hasattr(self.strategy, 'predict') else 'HOLD'
if signal == 'BUY':
logger.info(f'BUY signal: {pair} at {price:.2f}')
return {'pair': pair, 'price': price, 'signal': signal}
except Exception as e:
logger.debug(f'{pair}: {e}')
return {'pair': None, 'signal': 'HOLD'}
async def monitor_trades(self):
"""Monitor & execute trades"""
try:
balance = await self.binance.get_balance()
usdt = float(balance.get('USDT', {}).get('free', 0)) if balance else 0
# Auto-swap if needed
if usdt < 15:
await self.auto_swap_to_usdt()
return
# Find best trade
trade = await self.find_best_trade()
if trade['signal'] == 'BUY' and usdt > 15:
pair = trade['pair']
price = trade['price']
qty = (usdt * 0.7) / price
logger.info(f'EXECUTE BUY: {qty:.6f} {pair} @ {price:.2f}')
try:
await self.binance.place_order(pair, 'BUY', 'MARKET', qty)
self.trades_today += 1
await self.telegram.send_alert(f'BUY {pair}\n{qty:.6f} @ {price:.2f}')
except Exception as e:
logger.error(f'Trade failed: {e}')
except Exception as e:
logger.debug(f'Monitor: {e}')
async def send_performance_report(self):
"""Send 3-hourly report"""
try:
self.report_count += 1
price = await self.binance.get_ticker_price(self.config.trading_pair)
balance = await self.binance.get_balance()
usdt = float(balance.get('USDT', {}).get('free', 0)) if balance else 0
report = f'''REPORT #{self.report_count}
BTC: {price:.2f}
Balance: {usdt:.2f} USDT
Trades: {self.trades_today}
Wins: {self.wins_today}'''
logger.info(report)
await self.telegram.send_alert(report)
except Exception as e:
logger.error(f'Report error: {e}')
async def run(self):
"""Main bot loop"""
logger.info('BOT STARTED - Multi-Crypto Auto-Trading')
await self.telegram.send_alert('BOT STARTED - Multi-Crypto Mode with Auto-Swap')
while True:
try:
current_time = time.time()
if (current_time - self.last_report_time) >= self.report_interval:
await self.send_performance_report()
self.last_report_time = current_time
await self.monitor_trades()
await asyncio.sleep(60)
except Exception as e:
logger.error(f'Bot error: {e}')
await asyncio.sleep(60)
async def main():
config = get_config()
binance = BinanceClientWrapper(
api_key=config.binance_api_key_live,
api_secret=config.binance_api_secret_live,
testnet=False
)
telegram = TelegramNotifier(bot_token=config.telegram_bot_token, chat_id=config.telegram_chat_id)
obsidian = ObsidianLogger(vault_path=config.obsidian_vault_path, trade_log_file=config.obsidian_trade_log_file)
model = joblib.load(config.model_path) if hasattr(config, 'model_path') else None
scaler = None
bot = MLTradingBot(config, binance, telegram, obsidian, model, scaler)
await bot.run()
if __name__ == '__main__':
asyncio.run(main())

View File

@ -1,173 +0,0 @@
#!/usr/bin/env python3
import os, asyncio, aiohttp, logging, random
from datetime import datetime
from binance.client import Client
from decimal import Decimal
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
with open("/home/marc/bot-deploy/.env") as f:
env = {}
for line in f:
k, _, v = line.partition("=")
env[k.strip()] = v.strip()
class Bot:
def __init__(self):
self.binance = Client(env.get("BINANCE_API_KEY_LIVE"), env.get("BINANCE_API_SECRET_LIVE"))
self.current_trades = {}
self.completed_trades = []
self.balance = {}
self.trades_today = 0
self.daily_pnl = 0.0
self.dashboard = "http://localhost:7000/api/update"
logger.info("🤖 Bot initialized")
def get_balance(self):
try:
acc = self.binance.get_account()
self.balance = {}
for a in acc["balances"]:
free, locked = float(a["free"]), float(a["locked"])
if free + locked > 0:
self.balance[a["asset"]] = {"free": free, "locked": locked, "total": free+locked}
logger.info(f"💰 Balance updated: USDT")
except Exception as e:
logger.error(f"Balance error: {e}")
def place_buy(self, pair):
try:
usdt_free = self.balance.get("USDT", {}).get("free", 0)
if usdt_free < 5:
return None
# Use 25% per trade
qty_usdt = usdt_free * 0.25
ticker = self.binance.get_symbol_ticker(symbol=pair)
price = float(ticker["price"])
# Get symbol info for filters
info = self.binance.get_symbol_info(pair)
filters = {f["filterType"]: f for f in info["filters"]}
# LOT_SIZE check
if "LOT_SIZE" in filters:
lot = filters["LOT_SIZE"]
min_qty = float(lot["minQty"])
step = float(lot["stepSize"])
# Calculate quantity
qty_calc = qty_usdt / price
# Round down to step
qty = round(qty_calc / step) * step
if qty < min_qty or qty <= 0:
return None
else:
qty = float(round(qty_usdt / price, 6))
# Format as string to avoid scientific notation
qty_str = f"{qty:.8f}".rstrip("0").rstrip(".")
try:
order = self.binance.order_market_buy(symbol=pair, quantity=qty_str)
logger.info(f"🟢 BUY: {pair} x{qty_str}")
self.current_trades[pair] = {
"qty": float(qty_str),
"buy_price": price,
"buy_time": datetime.now().isoformat(),
"order_id": order["orderId"]
}
self.trades_today += 1
return order
except Exception as e:
logger.error(f"Buy {pair} error: {e}")
return None
except Exception as e:
logger.error(f"place_buy error: {e}")
return None
def check_tp(self):
remove = []
for pair in list(self.current_trades.keys()):
try:
trade = self.current_trades[pair]
ticker = self.binance.get_symbol_ticker(symbol=pair)
current = float(ticker["price"])
profit_pct = (current / trade["buy_price"]) - 1
if profit_pct >= 0.01:
logger.info(f"🎯 TP HIT: {pair} +{profit_pct*100:.2f}%")
sell = self.binance.order_market_sell(symbol=pair, quantity=trade["qty"])
sell_price = float(sell["fills"][0]["price"]) if sell.get("fills") else current
profit = (sell_price - trade["buy_price"]) * trade["qty"]
self.completed_trades.append({
"pair": pair,
"buy_price": trade["buy_price"],
"sell_price": sell_price,
"qty": trade["qty"],
"profit_usd": profit,
"profit_pct": profit_pct,
"buy_time": trade["buy_time"],
"sell_time": datetime.now().isoformat()
})
self.daily_pnl += profit
remove.append(pair)
except Exception as e:
pass
for p in remove:
del self.current_trades[p]
async def send_dashboard(self):
try:
state = {
"current_trades": self.current_trades,
"completed_trades": self.completed_trades[-20:],
"balance": self.balance,
"trades_today": self.trades_today,
"daily_pnl": self.daily_pnl,
"total_pnl": self.daily_pnl,
"wins_today": len([t for t in self.completed_trades if t.get("profit_usd", 0) > 0]),
"losses_today": len([t for t in self.completed_trades if t.get("profit_usd", 0) < 0]),
"last_update": datetime.now().isoformat()
}
async with aiohttp.ClientSession() as s:
async with s.post(self.dashboard, json=state, timeout=2) as r:
pass
except:
pass
async def run(self):
logger.info("🎯 Bot started")
while True:
try:
self.get_balance()
self.check_tp()
pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
for pair in pairs:
if pair not in self.current_trades and random.random() < 0.05:
logger.info(f"🟢 Signal: {pair}")
self.place_buy(pair)
await self.send_dashboard()
await asyncio.sleep(5)
except Exception as e:
logger.error(f"Run error: {e}")
await asyncio.sleep(10)
if __name__ == "__main__":
bot = Bot()
asyncio.run(bot.run())

View File

@ -1,200 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V5 CLEAN Minimal, Reliable, Profitable
Architecture: Single trading loop, live dashboard updates
"""
import os
import asyncio
import aiohttp
from datetime import datetime
from binance.client import Client
from dotenv import load_dotenv
import logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
load_dotenv()
class TradingBotClean:
def __init__(self):
self.binance = Client(
os.getenv('BINANCE_API_KEY'),
os.getenv('BINANCE_API_SECRET')
)
self.pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
# Trading state - SINGLE SOURCE OF TRUTH
self.current_trades = {}
self.completed_trades = []
self.balance = {}
self.trades_today = 0
self.daily_pnl = 0.0
self.total_pnl = 0.0
self.wins_today = 0
self.losses_today = 0
self.dashboard_url = 'http://localhost:7000/api/update'
self.TP = 1.01
self.SL = 0.97
self.BUY_AMOUNT = 0.5
self.MIN_ORDER = 10
logger.info('🤖 Bot CLEAN initialized')
async def update_balance(self):
"""Get current balance from Binance"""
try:
account = self.binance.get_account()
self.balance = {}
for asset in account['balances']:
free = float(asset['free'])
locked = float(asset['locked'])
if free + locked > 0:
self.balance[asset['asset']] = {
'free': free,
'locked': locked,
'total': free + locked
}
except Exception as e:
logger.error(f'Balance error: {e}')
async def get_ml_signal(self, pair, price):
"""Get ML trading signal"""
import random
return 'BUY' if random.random() > 0.95 else None
async def place_buy_order(self, pair, price):
"""Place BUY order"""
try:
usdt_free = self.balance.get('USDT', {}).get('free', 0)
qty_usdt = usdt_free * self.BUY_AMOUNT
if qty_usdt < self.MIN_ORDER:
return None
qty = qty_usdt / price
order = self.binance.order_market_buy(symbol=pair, quantity=qty)
logger.info(f'🟢 BUY: {pair} x{qty:.4f} @ ${price:.2f}')
self.current_trades[pair] = {
'qty': qty,
'buy_price': price,
'buy_time': datetime.now().isoformat(),
'order_id': order['orderId'],
}
self.trades_today += 1
return order
except Exception as e:
logger.error(f'Buy error {pair}: {e}')
return None
async def check_take_profit(self):
"""Check for +1% take profit"""
pairs_to_remove = []
for pair in list(self.current_trades.keys()):
try:
trade = self.current_trades[pair]
ticker = self.binance.get_symbol_ticker(symbol=pair)
current_price = float(ticker['price'])
profit_pct = (current_price / trade['buy_price']) - 1
if profit_pct >= (self.TP - 1): # +1%
logger.info(f'🎯 TP HIT: {pair} +{profit_pct*100:.2f}%')
sell_order = self.binance.order_market_sell(symbol=pair, quantity=trade['qty'])
sell_price = float(sell_order['fills'][0]['price']) if sell_order.get('fills') else current_price
profit_usd = (sell_price - trade['buy_price']) * trade['qty']
self.completed_trades.append({
'pair': pair,
'buy_price': trade['buy_price'],
'sell_price': sell_price,
'qty': trade['qty'],
'profit_usd': profit_usd,
'profit_pct': profit_pct,
'buy_time': trade['buy_time'],
'sell_time': datetime.now().isoformat()
})
self.daily_pnl += profit_usd
self.total_pnl += profit_usd
self.wins_today += 1
pairs_to_remove.append(pair)
except Exception as e:
logger.warning(f'TP check error {pair}: {e}')
for pair in pairs_to_remove:
del self.current_trades[pair]
async def send_to_dashboard(self):
"""Send state to dashboard"""
try:
state = {
'current_trades': self.current_trades,
'completed_trades': self.completed_trades[-20:],
'balance': self.balance,
'trades_today': self.trades_today,
'daily_pnl': self.daily_pnl,
'total_pnl': self.total_pnl,
'wins_today': self.wins_today,
'losses_today': self.losses_today,
'last_update': datetime.now().isoformat()
}
async with aiohttp.ClientSession() as session:
async with session.post(self.dashboard_url, json=state, timeout=2) as resp:
pass
except Exception as e:
logger.warning(f'Dashboard send error: {e}')
async def run(self):
"""Main trading loop"""
logger.info('🎯 Bot started')
while True:
try:
await self.update_balance()
for pair in self.pairs:
if pair in self.current_trades:
continue
try:
ticker = self.binance.get_symbol_ticker(symbol=pair)
price = float(ticker['price'])
signal = await self.get_ml_signal(pair, price)
if signal == 'BUY':
logger.info(f'🟢 BUY signal: {pair}')
await self.place_buy_order(pair, price)
except Exception as e:
pass
await self.check_take_profit()
await self.send_to_dashboard()
await asyncio.sleep(1)
except Exception as e:
logger.error(f'Loop error: {e}')
await asyncio.sleep(5)
async def main():
bot = TradingBotClean()
await bot.run()
if __name__ == '__main__':
asyncio.run(main())

View File

@ -1,99 +0,0 @@
#!/usr/bin/env python3
"""
Bot Persistence & Auto-Recovery System
- Saves all trades to persistent storage (JSON)
- On restart: Loads all trades + binance positions
- Dashboard syncs with persistent storage
- Bot operates autonomously even after restart
"""
import json
import os
import sys
sys.path.insert(0, '/home/marc/bot-deploy')
# Paths
TRADES_FILE = '/home/marc/bot-deploy/data/trades_persistent.json'
BOT_STATE_FILE = '/home/marc/bot-deploy/data/bot_state.json'
DATA_DIR = '/home/marc/bot-deploy/data'
# Ensure data directory exists
os.makedirs(DATA_DIR, exist_ok=True)
def init_persistence():
"""Initialize persistence files if they don't exist"""
if not os.path.exists(TRADES_FILE):
with open(TRADES_FILE, 'w') as f:
json.dump({
'current_trades': {},
'completed_trades': [],
'swaps': []
}, f, indent=2)
if not os.path.exists(BOT_STATE_FILE):
with open(BOT_STATE_FILE, 'w') as f:
json.dump({
'last_restart': None,
'total_capital_deployed': 0.0,
'session_start': None
}, f, indent=2)
def load_persistent_trades():
"""Load trades from persistent storage"""
try:
with open(TRADES_FILE, 'r') as f:
data = json.load(f)
return data.get('current_trades', {}), data.get('completed_trades', []), data.get('swaps', [])
except:
return {}, [], []
def save_persistent_trades(current_trades, completed_trades, swaps):
"""Save trades to persistent storage"""
data = {
'current_trades': current_trades,
'completed_trades': completed_trades,
'swaps': swaps
}
with open(TRADES_FILE, 'w') as f:
json.dump(data, f, indent=2)
def load_binance_positions_on_startup():
"""Load current open positions from Binance on startup"""
from src.bot.binance_client import BinanceClient
import asyncio
async def _load():
client = BinanceClient()
positions = {}
# Get account balances
balances = await client.get_balance()
# Scan for open positions (non-zero balances excluding USDT)
for symbol, amount in balances.items():
if symbol != 'USDT' and amount > 0.00001:
# Get current price for this asset
price = await client.get_price(f'{symbol}USDT')
positions[f'{symbol}USDT'] = {
'qty': amount,
'buy_price': price, # Current price as reference
'entry_time': None, # Lost on restart
'status': 'open'
}
print(f'✅ Loaded from Binance: {symbol}USDT - Qty: {amount} @ ${price}')
return positions
try:
loop = asyncio.get_event_loop()
except:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(_load())
# Initialize on import
init_persistence()
print('✅ Persistence module initialized')

View File

@ -1,58 +0,0 @@
#!/usr/bin/env python3
import os, json, subprocess
from datetime import datetime
from binance.client import Client
with open('/home/marc/bot-deploy/.env') as f:
env = {}
for line in f:
k, _, v = line.partition('=')
env[k.strip()] = v.strip()
# Load bot state
with open('/home/marc/bot-deploy/trades.json') as f:
bot_state = json.load(f)
# Get balance from Binance
c = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
acc = c.get_account()
balance = {a['asset']: float(a['free']) for a in acc['balances']}
# Calculate metrics
portfolio_value = balance.get('USDT', 0)
for asset in ['ETH', 'BTC', 'SOL', 'BNB', 'XRP']:
if asset in balance:
# Rough values (should use ticker for precision)
prices = {'ETH': 1790, 'BTC': 63000, 'SOL': 83.5, 'BNB': 578, 'XRP': 2.5}
portfolio_value += balance.get(asset, 0) * prices.get(asset, 0)
completed = bot_state.get('completed', [])
daily_pnl = sum(t.get('profit_usd', 0) for t in completed)
wins = len([t for t in completed if t.get('profit_usd', 0) > 0])
losses = len([t for t in completed if t.get('profit_usd', 0) < 0])
# Format report
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M UTC')
report = f'''📊 **TRADING BOT REPORT** — {timestamp}
💰 **PORTFOLIO**
Total: ${portfolio_value:.2f}
USDT Free: ${balance.get('USDT', 0):.2f}
Open Trades: {len(bot_state.get('current', {}))}
📈 **TODAY'S PERFORMANCE**
Trades: {len(completed)}
Wins: {wins} | Losses: {losses}
Win Rate: {(wins/(wins+losses)*100) if (wins+losses) > 0 else 0:.1f}%
Daily P&L: ${daily_pnl:.2f}
🟢 **BOT STATUS**: OPERATIONAL
🔗 Dashboard: https://bot.bizmark.cloud
---
*Next report in 3 hours*
'''
# Send via Telegram using Hermes send_message
print(report)

View File

@ -1,139 +0,0 @@
#!/usr/bin/env python3
"""
State Manager V3: Ultra-Simple Binance Direct
- Uses environment variables directly
- No .env nonsense, uses os.environ
"""
import asyncio
import json
import logging
import os
import sys
from datetime import datetime
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from binance.client import Client
# Read .env directly into os.environ BEFORE importing anything else
env_file = '/home/marc/bot-deploy/.env'
for line in open(env_file).readlines():
line = line.strip()
if line and not line.startswith('#') and '=' in line:
k, v = line.split('=', 1)
os.environ[k] = v.strip('"').strip("'")
API_KEY = os.environ.get('BINANCE_API_KEY')
API_SECRET = os.environ.get('BINANCE_API_SECRET')
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
if not API_KEY or not API_SECRET:
logger.error(f"Missing credentials: key={bool(API_KEY)}, secret={bool(API_SECRET)}")
sys.exit(1)
logger.info(f"✅ API credentials loaded")
client = Client(API_KEY, API_SECRET)
app = FastAPI()
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
state = {
'current_trades': {},
'completed_trades': [],
'swaps': [],
'balance': {},
'portfolio_value_usd': 0.0,
'daily_pnl': 0.0,
'total_pnl': 0.0,
'last_sync': datetime.now().isoformat()
}
def load_from_binance():
"""Load real data from Binance"""
global state
try:
logger.info('🔄 Syncing with Binance...')
account = client.get_account()
balances = {b['asset']: float(b['free']) for b in account['balances'] if float(b['free']) > 0.00001}
state['balance'] = balances
logger.info(f"Balance: USDT={balances.get('USDT', 0):.2f}")
open_trades = {}
for symbol in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']:
try:
orders = client.get_open_orders(symbol=symbol)
if orders:
o = orders[0]
qty = float(o['origQty'])
buy_price = float(o['price'])
current_price = float(client.get_symbol_ticker(symbol=symbol)['price'])
profit = (current_price - buy_price) * qty
profit_pct = ((current_price - buy_price) / buy_price * 100) if buy_price > 0 else 0
open_trades[symbol] = {
'qty': qty,
'buy_price': buy_price,
'current_price': current_price,
'buy_time': datetime.fromtimestamp(o['time']/1000).isoformat(),
'profit': profit,
'profit_pct': profit_pct,
'order_id': o['orderId']
}
logger.info(f" {symbol}: {qty:.8f} → ${current_price:.2f} P&L: {profit_pct:.2f}%")
except Exception as e:
logger.debug(f"Error {symbol}: {e}")
state['current_trades'] = open_trades
usdt = balances.get('USDT', 0)
portfolio = usdt + sum(t['qty']*t['current_price'] for t in open_trades.values())
pnl = sum(t['profit'] for t in open_trades.values())
state['portfolio_value_usd'] = portfolio
state['daily_pnl'] = pnl
state['total_pnl'] = pnl
state['last_sync'] = datetime.now().isoformat()
logger.info(f"✅ Portfolio: ${portfolio:.2f}, Trades: {len(open_trades)}, P&L: ${pnl:.2f}")
return True
except Exception as e:
logger.error(f"❌ Error: {e}")
import traceback
traceback.print_exc()
return False
async def background_sync():
while True:
try:
load_from_binance()
await asyncio.sleep(10)
except Exception as e:
logger.error(f"Sync loop: {e}")
await asyncio.sleep(10)
@app.on_event("startup")
async def startup():
logger.info("🚀 Starting State Manager...")
load_from_binance()
asyncio.create_task(background_sync())
logger.info("✅ Sync active")
@app.get("/state")
async def get_state():
return state
@app.get("/health")
async def health():
return {"status": "ok", "trades": len(state['current_trades']), "portfolio": state['portfolio_value_usd']}
if __name__ == "__main__":
logger.info("Starting on :8001")
uvicorn.run(app, host="0.0.0.0", port=8001, log_level="error")

View File

@ -1,657 +0,0 @@
#!/usr/bin/env python3
from fastapi import FastAPI, Response
from binance.client import Client
import json, os, time
from datetime import datetime
app = FastAPI()
env = {}
with open('/home/marc/bot-deploy/.env') as f:
for line in f:
k,_,v = line.partition('=')
env[k.strip()] = v.strip()
binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE'))
price_cache = {'prices': {}, 'timestamp': 0}
def get_live_prices():
global price_cache
if time.time() - price_cache['timestamp'] < 5:
return price_cache['prices']
prices = {'USDT': 1.0}
pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']
for pair in pairs:
try:
ticker = binance.get_ticker(symbol=pair)
asset = pair.replace('USDT', '')
prices[asset] = float(ticker['lastPrice'])
except:
pass
price_cache['prices'] = prices
price_cache['timestamp'] = time.time()
return prices
def load_bot_state():
state_file = '/home/marc/bot-deploy/trades.json'
if os.path.exists(state_file):
try:
with open(state_file) as f:
return json.load(f)
except:
pass
return {'current': {}, 'completed': [], 'balance': {}}
@app.get('/api/state')
async def get_state():
try:
account = binance.get_account()
balance = {}
for asset_data in account['balances']:
asset = asset_data['asset']
free = float(asset_data['free'])
locked = float(asset_data['locked'])
total = free + locked
if total > 0.00001:
balance[asset] = {
'free': free,
'locked': locked,
'total': total
}
prices = get_live_prices()
portfolio_value = 0
tracked_assets = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT', 'USDC']
for asset in tracked_assets:
if asset in balance:
data = balance[asset]
price = prices.get(asset, 0)
portfolio_value += data['total'] * price
usdt_free = balance.get('USDT', {}).get('free', 0)
# P&L CALCULATION
initial_capital = 137.79
pnl_usdt = portfolio_value - initial_capital
pnl_pct = (pnl_usdt / initial_capital * 100) if initial_capital > 0 else 0
pnl_status = "🟢 PROFIT" if pnl_usdt > 0.01 else ("🔴 LOSS" if pnl_usdt < -0.01 else "⚪ BREAK")
pnl_color = "accent" if pnl_usdt > 0.01 else ("negative" if pnl_usdt < -0.01 else "neutral")
# Count active positions = locked coins (NOT trades.json)
active_positions = 0
for asset in ['BTC', 'ETH', 'SOL', 'BNB', 'XRP']:
if asset in balance and balance[asset]['locked'] > 0.00001:
active_positions += 1
trades = load_bot_state()
return {
'balance': balance,
'portfolio_value': round(portfolio_value, 2),
'usdt_free': round(usdt_free, 2),
'active_positions': active_positions, # ← NEW: Real count!
'current_trades': trades.get('current', {}),
'pnl_usdt': round(pnl_usdt, 2),
'pnl_pct': round(pnl_pct, 2),
'pnl_status': pnl_status,
'pnl_color': pnl_color,
'completed_trades': trades.get('completed', []),
'prices': prices,
'timestamp': datetime.now().isoformat()
}
except Exception as e:
return {'error': str(e), 'portfolio_value': 0, 'usdt_free': 0, 'active_positions': 0}
@app.get('/')
async def root():
state = await get_state()
portfolio_val = state.get('portfolio_value', 0)
usdt_free = state.get('usdt_free', 0)
trades_count = state.get('active_positions', 0) # ← FIXED: Use real count!
prices = state.get('prices', {})
# P&L from state
pnl_usdt = state.get("pnl_usdt", 0)
pnl_pct = state.get("pnl_pct", 0)
pnl_status = state.get("pnl_status", "⚪ BREAK")
pnl_color = state.get("pnl_color", "neutral")
html = f'''<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>Trading Bot V10</title>
<style>
:root {{
--bg-primary: #1a1a1a;
--bg-secondary: #252525;
--bg-tertiary: #2a2a2a;
--bg-hover: #303030;
--border: #404040;
--text-primary: #e0e0e0;
--text-secondary: #a0a0a0;
--accent: #00ff88;
--spacing: 1rem;
}}
* {{
margin: 0;
padding: 0;
box-sizing: border-box;
}}
html, body {{
width: 100%;
height: 100%;
}}
body {{
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Monaco', 'Menlo', monospace;
background: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
font-size: clamp(14px, 2vw, 16px);
overflow-x: hidden;
}}
.app-container {{
width: 100%;
min-height: 100vh;
padding: calc(var(--spacing) * 1.5);
}}
.header {{
margin-bottom: calc(var(--spacing) * 2.5);
}}
.logo {{
font-size: clamp(24px, 6vw, 32px);
font-weight: bold;
color: var(--accent);
margin-bottom: 0.5rem;
}}
.version {{
font-size: clamp(11px, 2vw, 13px);
color: var(--text-secondary);
}}
/* ===== METRICS GRID ===== */
.metrics-grid {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: calc(var(--spacing) * 1.5);
margin-bottom: calc(var(--spacing) * 3);
}}
.metric-card {{
background: var(--bg-tertiary);
border: 1px solid var(--border);
padding: calc(var(--spacing) * 1.5);
border-radius: 8px;
transition: all 0.3s ease;
cursor: pointer;
min-height: 140px;
display: flex;
flex-direction: column;
justify-content: space-between;
}}
.metric-card:active {{
transform: scale(0.98);
}}
.metric-card:hover {{
background: var(--bg-hover);
border-color: var(--accent);
box-shadow: 0 0 20px rgba(0, 255, 136, 0.1);
}}
.metric-label {{
font-size: clamp(11px, 1.5vw, 12px);
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.8px;
margin-bottom: 1rem;
}}
.metric-value {{
font-size: clamp(20px, 5vw, 32px);
font-weight: bold;
color: var(--text-primary);
word-break: break-word;
}}
.metric-value.accent {{
color: var(--accent);
}}
/* ===== SECTIONS ===== */
.section {{
margin-bottom: calc(var(--spacing) * 3);
}}
.section-header {{
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
padding: calc(var(--spacing) * 0.75) 0;
border-bottom: 1px solid var(--border);
margin-bottom: calc(var(--spacing) * 1.25);
user-select: none;
transition: all 0.2s ease;
}}
.section-header:hover {{
color: var(--accent);
}}
.section-title {{
font-size: clamp(13px, 2.5vw, 15px);
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 1.2px;
transition: color 0.2s ease;
}}
.section-toggle {{
font-size: clamp(14px, 2vw, 16px);
color: var(--text-secondary);
transition: transform 0.3s ease;
margin-left: 0.5rem;
}}
.section-toggle.expanded {{
transform: rotate(180deg);
}}
.section-content {{
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}}
.section-content.expanded {{
max-height: 2000px;
}}
/* ===== TABLES ===== */
.table-wrapper {{
overflow-x: auto;
-webkit-overflow-scrolling: touch;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--bg-tertiary);
}}
table {{
width: 100%;
border-collapse: collapse;
font-size: clamp(12px, 2vw, 14px);
}}
th {{
background: var(--bg-tertiary);
color: var(--text-secondary);
padding: calc(var(--spacing) * 1);
text-align: left;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.6px;
border-bottom: 1px solid var(--border);
white-space: nowrap;
font-size: clamp(10px, 1.5vw, 12px);
}}
td {{
padding: calc(var(--spacing) * 0.875);
border-bottom: 1px solid var(--border);
}}
tr:last-child td {{
border-bottom: none;
}}
tbody tr {{
transition: background 0.2s ease;
}}
tbody tr:hover {{
background: var(--bg-hover);
}}
tbody tr:active {{
background: var(--bg-secondary);
}}
.price-positive {{
color: var(--accent);
font-weight: 600;
}}
/* ===== RESPONSIVE ===== */
@media (max-width: 1200px) {{
.metrics-grid {{
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}}
}}
@media (max-width: 768px) {{
:root {{
--spacing: 0.875rem;
}}
.app-container {{
padding: calc(var(--spacing) * 1.25);
}}
.metrics-grid {{
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing);
}}
.metric-card {{
padding: var(--spacing);
min-height: 120px;
}}
.metric-label {{
margin-bottom: 0.75rem;
font-size: 10px;
}}
.metric-value {{
font-size: clamp(18px, 4vw, 26px);
}}
.section {{
margin-bottom: calc(var(--spacing) * 1.75);
}}
th, td {{
padding: calc(var(--spacing) * 0.75);
font-size: 11px;
}}
th {{
font-size: 10px;
}}
}}
@media (max-width: 480px) {{
:root {{
--spacing: 0.75rem;
}}
.app-container {{
padding: var(--spacing);
}}
.metrics-grid {{
grid-template-columns: repeat(2, 1fr);
gap: calc(var(--spacing) * 0.75);
}}
.metric-card {{
padding: calc(var(--spacing) * 0.875);
min-height: 110px;
}}
.metric-label {{
font-size: 9px;
margin-bottom: 0.5rem;
letter-spacing: 0.5px;
}}
.metric-value {{
font-size: clamp(16px, 3.5vw, 22px);
}}
.logo {{
font-size: clamp(20px, 5vw, 26px);
}}
.version {{
font-size: 10px;
}}
.section-title {{
font-size: 11px;
}}
th, td {{
padding: calc(var(--spacing) * 0.6);
font-size: 9px;
}}
th {{
font-size: 8px;
}}
.table-wrapper {{
border-radius: 6px;
}}
}}
/* ===== SCROLLBAR ===== */
::-webkit-scrollbar {{
width: 6px;
height: 6px;
}}
::-webkit-scrollbar-track {{
background: var(--bg-secondary);
}}
::-webkit-scrollbar-thumb {{
background: var(--border);
border-radius: 3px;
}}
::-webkit-scrollbar-thumb:hover {{
background: var(--text-secondary);
}}
/* ===== ANIMATIONS ===== */
@keyframes fadeIn {{
from {{
opacity: 0;
transform: translateY(10px);
}}
to {{
opacity: 1;
transform: translateY(0);
}}
}}
.metric-card {{
animation: fadeIn 0.5s ease forwards;
}}
.metric-card:nth-child(2) {{
animation-delay: 0.1s;
}}
.metric-card:nth-child(3) {{
animation-delay: 0.2s;
}}
</style>
</head>
<body>
<div class="app-container">
<div class="header">
<div class="logo">💰 Trading Bot</div>
<div class="version">V10 Real-time Portfolio Dashboard</div>
</div>
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-label">Portfolio Value</div>
<div class="metric-value">${portfolio_val:.2f}</div>
</div>
<div class="metric-card">
<div class="metric-label">USDT Available</div>
<div class="metric-value accent">${usdt_free:.2f}</div>
</div>
<div class="metric-card">
<div class="metric-label">Open Positions</div>
<div class="metric-value">{trades_count}</div>
</div>
<div class="metric-card">
<div class="metric-label">Total P&L</div>
<div class="metric-value {pnl_color}">${pnl_usdt:+.2f} ({pnl_pct:+.1f}%)</div>
</div>
<div class="metric-card">
<div class="metric-label">P&L Status</div>
<div class="metric-value {pnl_color}">{pnl_status}</div>
</div>
</div>
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<div class="section-title">Live Prices</div>
<div class="section-toggle"></div>
</div>
<div class="section-content">
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Asset</th>
<th>Price</th>
</tr>
</thead>
<tbody>'''
for asset, price in prices.items():
html += f'''<tr>
<td>{asset}</td>
<td class="price-positive">${price:.2f}</td>
</tr>'''
html += '''</tbody>
</table>
</div>
</div>
</div>
<div class="section">
<div class="section-header" onclick="toggleSection(this)">
<div class="section-title">Holdings</div>
<div class="section-toggle"></div>
</div>
<div class="section-content">
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Asset</th>
<th>Free</th>
<th>Total</th>
<th>Value</th>
</tr>
</thead>
<tbody>'''
tracked = ['BTC', 'ETH', 'SOL', 'BNB', 'XRP', 'USDT', 'USDC']
balance = state.get('balance', {})
for asset in tracked:
if asset in balance:
data = balance[asset]
price = prices.get(asset, 0)
value = data['total'] * price
html += f'''<tr>
<td>{asset}</td>
<td>{data['free']:.4f}</td>
<td>{data['total']:.4f}</td>
<td class="price-positive">${value:.2f}</td>
</tr>'''
html += '''</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
function toggleSection(header) {
const content = header.nextElementSibling;
const toggle = header.querySelector('.section-toggle');
content.classList.toggle('expanded');
toggle.classList.toggle('expanded');
}
// Refresh prices every 5 seconds
setInterval(function() {{
location.reload();
}}, 10000);
</script>
</body>
</html>'''
return Response(content=html, media_type='text/html')
@app.get('/api/pnl')
async def get_pnl():
"""Get live Profit & Loss (P&L) calculation"""
try:
account = binance.get_account()
# Get current account value
prices = get_live_prices()
current_value = 0
for asset_data in account['balances']:
asset = asset_data['asset']
total = float(asset_data['free']) + float(asset_data['locked'])
if total > 0.00001 and asset != 'LDDOGE' and asset != 'LDBTTC':
price = prices.get(asset, 1.0)
current_value += total * price
# Benchmark: Initial capital was $137.79 (before trading)
# This should be stored, but for now use a reference
initial_capital = 137.79
pnl_usdt = current_value - initial_capital
pnl_pct = (pnl_usdt / initial_capital * 100) if initial_capital > 0 else 0
# Get open trades for unrealized portion
state_file = '/home/marc/bot-deploy/trades.json'
open_trades = {}
if os.path.exists(state_file):
try:
data = json.load(state_file)
open_trades = data.get('current', {})
except:
pass
return {
'current_value': round(current_value, 2),
'initial_capital': initial_capital,
'total_pnl_usdt': round(pnl_usdt, 2),
'total_pnl_percent': round(pnl_pct, 2),
'status': '🟢 PROFIT' if pnl_usdt > 0 else ('🔴 LOSS' if pnl_usdt < 0 else '⚪ BREAK'),
'open_positions': len(open_trades),
'timestamp': datetime.now().isoformat()
}
except Exception as e:
return {'error': str(e)}
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host='0.0.0.0', port=7000)