Bot V5 ENHANCED: Telegram Notifications + Quantity Precision

- NEW: Startup message with strategy summary (sent to Telegram)
- NEW: 3h performance reports (automatic every 3 hours)
- NEW: Telegram integration for notifications + metrics
- FIXED: Quantity rounding to Binance step size
- FIXED: All buy orders now precision-safe
- NEW: Performance metrics tracked per day
- NEW: Win rate + P&L reporting
- NEW: Daily PnL tracking + pause at -5%
- FEATURE: Auto-reporting every 3 hours
- VERSION: V5 ENHANCED PRODUCTION
- STATUS: Ready for 100% USDT trading with Telegram alerts
This commit is contained in:
Marc Blatter 2026-07-04 23:44:09 +02:00
parent 0fd2bdc647
commit aacbeaf6df
3 changed files with 637 additions and 7 deletions

View File

@ -1,10 +1,11 @@
#!/usr/bin/env python3
"""
Trading Bot V5 ENHANCED - Risk Management FIXED
Implementiert: SL (mit korrekter Precision), TP, Daily Limit, R:R Ratio
Trading Bot V5 ENHANCED - Risk Management + Telegram Notifications
Implementiert: SL, TP, Daily Limit, R:R Ratio
FIXED: PRICE_FILTER für SL Orders durch Tick-Rounding
NEW: Startup Message + 3h Performance Reports via Telegram
"""
import os, asyncio, logging, random, json, time, math
import os, asyncio, logging, random, json, time, math, requests
from binance.client import Client
from binance.exceptions import BinanceAPIException
from datetime import datetime, timedelta
@ -34,12 +35,74 @@ class TradingBot:
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: 25% USDT per trade
Pairs: BTC, ETH, SOL, BNB, XRP
Max Parallel: 5 trades
**Exit:**
Take Profit: +3.0%
Stop Loss: -2.5%
Risk/Reward: 1:1.2
**Risk Management:**
Daily Loss Limit: -5%
Position Size Cap: 25%
SL Auto-Place: Ja (korrekt gerundet)
**Status:** 🟢 LIVE
Time: """ + datetime.now().strftime('%Y-%m-%d %H:%M UTC') + """
Capital Ready: ~$135 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"""
@ -63,11 +126,36 @@ class TradingBot:
return int(s.split('e-')[1]) if 'e-' in s else 0
return len(s.split('.')[1]) if '.' in s else 0
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
except Exception as e:
logger.error(f"Precision load {pair}: {e}")
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
def _round_quantity(self, qty, pair):
"""Round quantity to Binance step size"""
step = self.pair_precision.get(pair, {}).get('step', 0.00001)
return round(qty / step) * step
async def signal_buy(self, pair):
"""Generate random 5% buy signal"""
rand = random.randint(1, 100)
@ -87,9 +175,17 @@ class TradingBot:
qty = usdt / entry_price
# ROUND QUANTITY TO STEP SIZE (CRITICAL FIX!)
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
# Place market buy
order = self.client.order_market_buy(symbol=pair, quantity=qty)
logger.info(f"🟢 BUY: {pair} x{qty:.6f} @ ${entry_price:.2f}")
logger.info(f"🟢 BUY: {pair} x{qty:.8f} @ ${entry_price:.2f}")
# Store trade
self.active_trades[pair] = {
@ -101,6 +197,7 @@ class TradingBot:
# Place SL order (FIXED WITH ROUNDING)
await self.place_stop_loss(pair, entry_price, qty)
self.trades_today += 1
return True
except Exception as e:
@ -116,17 +213,20 @@ class TradingBot:
# ROUND TO TICK SIZE (CRITICAL FIX!)
sl_price = self._round_to_tick(sl_price, pair)
# ROUND QUANTITY TO STEP SIZE
qty_rounded = self._round_quantity(qty, pair)
# Place SL order
order = self.client.order_take_profit(
symbol=pair,
side='SELL',
type='STOP_LOSS',
timeInForce='GTC',
quantity=qty,
quantity=qty_rounded,
stopPrice=sl_price,
price=sl_price # Binance requires price = stopPrice for STOP_LOSS
price=sl_price
)
logger.info(f"🛡️ SL: {pair} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)")
logger.info(f"🛡️ SL: {pair} x{qty_rounded:.8f} @ ${sl_price:.4f} (-{self.STOP_LOSS_PERCENT}%)")
except BinanceAPIException as e:
logger.error(f"SL Error {pair}: {e}")
@ -168,15 +268,120 @@ class TradingBot:
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"""
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)")

View File

@ -0,0 +1,220 @@
#!/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())

205
src/main_ml_fixed.py Normal file
View File

@ -0,0 +1,205 @@
#!/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())