Cleanup: Remove all backup versions, keep only V0.3 production code (main_ml.py + main_ml_v03.py reference)

This commit is contained in:
Marc Blatter 2026-07-09 20:19:28 +02:00
parent 51220ba8cf
commit 095800adf3
12 changed files with 0 additions and 3933 deletions

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

View File

@ -1,590 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V0.2 — Adaptive Strategy Learning
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 = 7.5 # 7-8% range (midpoint 7.5%) # 5% random signal
self.INVESTMENT_PERCENT = 50 # 50% (single position for liquidity) (single position)
self.INVESTMENT_PERCENT_HIGH = 55 # 55% when confidence > 85% > 85%
self.CONFIDENCE_THRESHOLD = 85 # Min confidence for high investment # 35% per trade (5 parallel = 90% max, 10% buffer)
self.NOTIONAL_MIN = 5.0 # Override Binance minimum to $3
self.STOP_LOSS_PERCENT = 1.8 # -2.5%
self.TAKE_PROFIT_PERCENT = 2.8 # +3%
self.DAILY_LOSS_LIMIT = -5
# Trailing Stop
self.TRAILING_STOP_ENTRY = 1.5 # Activate trailing stop at +1.5%
self.TRAILING_STOP_DISTANCE = 0.6 # 0.6% distance
# Position & Trade Limits
self.MAX_OPEN_POSITIONS = 1 # Single position for max liquidity # Max concurrent trades
self.MAX_CONSECUTIVE_LOSSES = 3 # Stop after 3 losses
self.CONSECUTIVE_LOSS_COOLDOWN = 30 * 60 # 30 minutes in seconds
self.MAX_TRADES_PER_DAY = 15
self.MIN_WIN_PROBABILITY = 75 # Min expected win %
# Tracking
self.consecutive_losses = 0
self.last_loss_time = None
self.trades_today = 0
self.last_trade_reset = None # -5% max
# Profit tracking
self.entry_price_history = {} # symbol -> entry price
self.closed_trades = [] # list of {symbol, entry, exit, profit_pct, profit_usdt}
self.session_start_balance = None
self.active_trades = {}
self.daily_pnl = 0
self.paused = False
# ADAPTIVE TRACKING (Option 2: Win Rate based Strategy)
self.total_trades = 0
self.total_wins = 0
self.total_losses = 0
self.last_win_rate = 50.0 # Start neutral
self.strategy_version = 1
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(f"✅ Bot initialized with Risk Management (SL {self.STOP_LOSS_PERCENT}%, TP {self.TAKE_PROFIT_PERCENT}%, Daily Limit {-self.DAILY_LOSS_LIMIT}%, Max Pos: {self.MAX_OPEN_POSITIONS})")
# 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 V0.2 — 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 {self.STOP_LOSS_PERCENT}% 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: V0.2 Adaptive"""
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())
def get_signal_confidence(self):
"""Calculate confidence level for current signal (0-100%)"""
# This can be enhanced with actual ML model
# For now: random 30-95%
import random
return random.uniform(30, 95)
def get_investment_percent(self, confidence):
"""Select investment % based on confidence"""
return self.INVESTMENT_PERCENT_HIGH if confidence > self.CONFIDENCE_THRESHOLD else self.INVESTMENT_PERCENT
def check_consecutive_loss_cooldown(self):
"""Check if bot is in cooldown after 3 consecutive losses"""
if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES:
if self.last_loss_time is None:
return False # First loss, no cooldown
time_elapsed = time.time() - self.last_loss_time
if time_elapsed < self.CONSECUTIVE_LOSS_COOLDOWN:
logger.warning(f"🚫 Cooldown active: {int(self.CONSECUTIVE_LOSS_COOLDOWN - time_elapsed)}s remaining")
return False
else:
# Cooldown expired, reset counter
self.consecutive_losses = 0
logger.info("✅ Cooldown expired, consecutive loss counter reset")
return True
return True
def check_volatility(self, pair):
"""Check market volatility (simplified)"""
try:
ticker = self.client.get_symbol_ticker(symbol=pair)
current_price = float(ticker['price'])
# Get 1h candle for volatility estimate
candles = self.client.get_klines(symbol=pair, interval='1h', limit=5)
high_prices = [float(c[2]) for c in candles]
low_prices = [float(c[3]) for c in candles]
volatility = (max(high_prices) - min(low_prices)) / min(low_prices) * 100
# Flag as extreme if > 5% 1h volatility
if volatility > 5:
logger.warning(f"⚠️ High volatility {pair}: {volatility:.2f}% (skipping trade)")
return False
return True
except:
return True # If check fails, allow trade
def check_daily_trade_limit(self):
"""Check if daily trade limit reached"""
import datetime
now = datetime.datetime.now()
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
if self.last_trade_reset is None or self.last_trade_reset < today_start:
self.trades_today = 0
self.last_trade_reset = now
if self.trades_today >= self.MAX_TRADES_PER_DAY:
logger.warning(f"⚠️ Daily limit reached: {self.trades_today}/{self.MAX_TRADES_PER_DAY} trades")
return False
return True
def update_trailing_stop(self, pair, current_price, entry_price):
"""Update trailing stop for an open position"""
if pair not in self.active_trades:
return False
trade_data = self.active_trades[pair]
profit_pct = ((current_price - entry_price) / entry_price) * 100
# Activate trailing stop when profit >= 1.5%
if profit_pct >= self.TRAILING_STOP_ENTRY:
trailing_stop_price = current_price * (1 - self.TRAILING_STOP_DISTANCE / 100)
trade_data['trailing_stop'] = trailing_stop_price
# If price falls below trailing stop, close position
if current_price < trailing_stop_price:
logger.info(f"🛑 Trailing stop triggered {pair}: Sell @ ${current_price:.2f}")
return True
return False
def record_entry(self, pair, price, quantity):
"""Record entry price for profit calculation"""
self.entry_price_history[pair] = {
'price': price,
'qty': quantity,
'value': price * quantity,
'timestamp': time.time()
}
def calculate_unrealized_pnl(self):
"""Calculate unrealized P&L for open positions"""
try:
prices = get_live_prices()
total_unrealized = 0
for pair, entry_data in self.entry_price_history.items():
asset = pair.replace('USDT', '')
current_price = prices.get(asset, 0)
if current_price > 0:
current_value = entry_data['qty'] * current_price
unrealized = current_value - entry_data['value']
total_unrealized += unrealized
return total_unrealized
except:
return 0
def calculate_realized_pnl(self):
"""Sum all closed trades realized P&L"""
return sum(t.get('profit_usdt', 0) for t in self.closed_trades)
def get_total_pnl(self):
"""Total P&L = realized + unrealized"""
return self.calculate_realized_pnl() + self.calculate_unrealized_pnl()

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,682 +0,0 @@
#!/usr/bin/env python3
"""
Trading Bot V0.2 Adaptive Strategy Learning
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 = 7.5 # 7-8% range (midpoint 7.5%) # 5% random signal
self.INVESTMENT_PERCENT = 50 # 50% (single position for liquidity) (single position)
self.INVESTMENT_PERCENT_HIGH = 55 # 55% when confidence > 85% > 85%
self.CONFIDENCE_THRESHOLD = 85 # Min confidence for high investment # 35% per trade (5 parallel = 90% max, 10% buffer)
self.NOTIONAL_MIN = 5.0 # Override Binance minimum to $3
self.STOP_LOSS_PERCENT = 1.8 # -2.5%
self.TAKE_PROFIT_PERCENT = 2.8 # +3%
self.DAILY_LOSS_LIMIT = -5
# Trailing Stop
self.TRAILING_STOP_ENTRY = 1.5 # Activate trailing stop at +1.5%
self.TRAILING_STOP_DISTANCE = 0.6 # 0.6% distance
# Position & Trade Limits
self.MAX_OPEN_POSITIONS = 1 # Single position for max liquidity # Max concurrent trades
self.MAX_CONSECUTIVE_LOSSES = 3 # Stop after 3 losses
self.CONSECUTIVE_LOSS_COOLDOWN = 30 * 60 # 30 minutes in seconds
self.MAX_TRADES_PER_DAY = 15
self.MIN_WIN_PROBABILITY = 75 # Min expected win %
# Tracking
self.consecutive_losses = 0
self.last_loss_time = None
self.trades_today = 0
self.last_trade_reset = None # -5% max
# Profit tracking
self.entry_price_history = {} # symbol -> entry price
self.closed_trades = [] # list of {symbol, entry, exit, profit_pct, profit_usdt}
self.session_start_balance = None
self.active_trades = {}
self.daily_pnl = 0
self.paused = False
# ADAPTIVE TRACKING (Option 2: Win Rate based Strategy)
self.total_trades = 0
self.total_wins = 0
self.total_losses = 0
self.last_win_rate = 50.0 # Start neutral
self.strategy_version = 1
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(f"✅ Bot initialized with Risk Management (SL {self.STOP_LOSS_PERCENT}%, TP {self.TAKE_PROFIT_PERCENT}%, Daily Limit {-self.DAILY_LOSS_LIMIT}%, Max Pos: {self.MAX_OPEN_POSITIONS})")
# 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 V0.2 — 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 {self.STOP_LOSS_PERCENT}% 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 swap_coins_to_usdt(self):
"""
AUTO-SWAP: Konvertiere alle freien (unlocked) Coins USDT
Ignoriert locked Coins (von aktiven Trades)
Skip-list: LDBTTC (shitcoin), LDDOGE (shitcoin), USDC (dust)
"""
skip_coins = ['USDT', 'LDBTTC', 'LDDOGE', 'USDC'] # Never swap these
try:
balance = self.client.get_account()
swapped_total_usdt = 0
swap_log = []
for asset in balance['balances']:
coin = asset['asset']
free_qty = float(asset['free'])
# Skip: small amounts, USDT, locked coins, skip-list
if free_qty < 0.00001 or coin in skip_coins:
continue
try:
symbol = f"{coin}USDT"
# Get current price to estimate value
ticker = self.client.get_symbol_info(symbol)
if not ticker:
logger.warning(f"No ticker for {symbol}")
continue
# Round quantity to step size
qty_to_sell = self._round_quantity(free_qty, symbol)
if qty_to_sell < 0.00001:
continue
# MARKET SELL (immediate)
order = self.client.order_market_sell(symbol=symbol, quantity=qty_to_sell)
# Calculate USDT received
fills = order.get('fills', [])
usdt_received = sum(float(f['qty']) * float(f['price']) for f in fills)
swapped_total_usdt += usdt_received
swap_log.append(f"{coin}: {qty_to_sell:.6f} → ${usdt_received:.2f}")
logger.info(f"Sweep: Sold {qty_to_sell} {coin} for ${usdt_received:.2f}")
except BinanceAPIException as e:
logger.warning(f"Sweep {coin}: Binance Error {e.status_code} - {e.message}")
swap_log.append(f"{coin}: {e.message}")
except Exception as e:
logger.warning(f"Sweep {coin}: {e}")
swap_log.append(f"{coin}: {str(e)}")
# RESULT
result = {
'success': True,
'total_usdt_acquired': swapped_total_usdt,
'swaps_attempted': len(swap_log),
'log': swap_log
}
# Send Telegram notification
msg = f"""🔄 **COINS TO USDT SWAP COMPLETE**
**Total Converted:** ${swapped_total_usdt:.2f} USDT
{chr(10).join(swap_log)}
**New USDT Balance:** ${self.get_usdt_balance():.2f}
"""
self._send_telegram(msg)
logger.info(f"Swap complete: ${swapped_total_usdt:.2f} converted")
return result
except Exception as e:
logger.error(f"Swap error: {e}")
self._send_telegram(f"❌ **SWAP FAILED**: {e}")
return {'success': False, 'error': str(e)}
def get_usdt_balance(self):
"""Get current USDT balance"""
try:
balance = self.client.get_account()
for asset in balance['balances']:
if asset['asset'] == 'USDT':
return float(asset['free'])
return 0.0
except:
return 0.0
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: V0.2 Adaptive"""
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())
def get_signal_confidence(self):
"""Calculate confidence level for current signal (0-100%)"""
# This can be enhanced with actual ML model
# For now: random 30-95%
import random
return random.uniform(30, 95)
def get_investment_percent(self, confidence):
"""Select investment % based on confidence"""
return self.INVESTMENT_PERCENT_HIGH if confidence > self.CONFIDENCE_THRESHOLD else self.INVESTMENT_PERCENT
def check_consecutive_loss_cooldown(self):
"""Check if bot is in cooldown after 3 consecutive losses"""
if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES:
if self.last_loss_time is None:
return False # First loss, no cooldown
time_elapsed = time.time() - self.last_loss_time
if time_elapsed < self.CONSECUTIVE_LOSS_COOLDOWN:
logger.warning(f"🚫 Cooldown active: {int(self.CONSECUTIVE_LOSS_COOLDOWN - time_elapsed)}s remaining")
return False
else:
# Cooldown expired, reset counter
self.consecutive_losses = 0
logger.info("✅ Cooldown expired, consecutive loss counter reset")
return True
return True
def check_volatility(self, pair):
"""Check market volatility (simplified)"""
try:
ticker = self.client.get_symbol_ticker(symbol=pair)
current_price = float(ticker['price'])
# Get 1h candle for volatility estimate
candles = self.client.get_klines(symbol=pair, interval='1h', limit=5)
high_prices = [float(c[2]) for c in candles]
low_prices = [float(c[3]) for c in candles]
volatility = (max(high_prices) - min(low_prices)) / min(low_prices) * 100
# Flag as extreme if > 5% 1h volatility
if volatility > 5:
logger.warning(f"⚠️ High volatility {pair}: {volatility:.2f}% (skipping trade)")
return False
return True
except:
return True # If check fails, allow trade
def check_daily_trade_limit(self):
"""Check if daily trade limit reached"""
import datetime
now = datetime.datetime.now()
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
if self.last_trade_reset is None or self.last_trade_reset < today_start:
self.trades_today = 0
self.last_trade_reset = now
if self.trades_today >= self.MAX_TRADES_PER_DAY:
logger.warning(f"⚠️ Daily limit reached: {self.trades_today}/{self.MAX_TRADES_PER_DAY} trades")
return False
return True
def update_trailing_stop(self, pair, current_price, entry_price):
"""Update trailing stop for an open position"""
if pair not in self.active_trades:
return False
trade_data = self.active_trades[pair]
profit_pct = ((current_price - entry_price) / entry_price) * 100
# Activate trailing stop when profit >= 1.5%
if profit_pct >= self.TRAILING_STOP_ENTRY:
trailing_stop_price = current_price * (1 - self.TRAILING_STOP_DISTANCE / 100)
trade_data['trailing_stop'] = trailing_stop_price
# If price falls below trailing stop, close position
if current_price < trailing_stop_price:
logger.info(f"🛑 Trailing stop triggered {pair}: Sell @ ${current_price:.2f}")
return True
return False
def record_entry(self, pair, price, quantity):
"""Record entry price for profit calculation"""
self.entry_price_history[pair] = {
'price': price,
'qty': quantity,
'value': price * quantity,
'timestamp': time.time()
}
def calculate_unrealized_pnl(self):
"""Calculate unrealized P&L for open positions"""
try:
prices = get_live_prices()
total_unrealized = 0
for pair, entry_data in self.entry_price_history.items():
asset = pair.replace('USDT', '')
current_price = prices.get(asset, 0)
if current_price > 0:
current_value = entry_data['qty'] * current_price
unrealized = current_value - entry_data['value']
total_unrealized += unrealized
return total_unrealized
except:
return 0
def calculate_realized_pnl(self):
"""Sum all closed trades realized P&L"""
return sum(t.get('profit_usdt', 0) for t in self.closed_trades)
def get_total_pnl(self):
"""Total P&L = realized + unrealized"""
return self.calculate_realized_pnl() + self.calculate_unrealized_pnl()

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