158 lines
6.1 KiB
Python
158 lines
6.1 KiB
Python
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())
|