← Назад
""" Squeeze-VWAP Bot β€” Main Entry Point ======================================= Π“Π»Π°Π²Π½Ρ‹ΠΉ Ρ†ΠΈΠΊΠ»: 1. Nuclear cleanup ΠΏΡ€ΠΈ стартС 2. Recovery ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ (Squeeze + Zatochki) 3. Squeeze: PAUSED (6 Apr 2026 β€” replaced by Zatochki) 4. Zatochki: ΠΊΠ°ΠΆΠ΄Ρ‹Π΅ 60 сСк β€” скан + entry 5. ΠšΠ°ΠΆΠ΄Ρ‹Π΅ 5 сСк β€” ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ (both strategies) Π‘Ρ‚Ρ€Π°Ρ‚Π΅Π³ΠΈΠΈ: - Squeeze Momentum + Z-VWAP + Waddah Attar + ADX (PAUSED) - Zatochki (Knife Catcher) β€” Volume exhaustion reversal 1m (ACTIVE) """ import asyncio import logging import sys import os import time # Add project root to path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from src.config import SCAN_INTERVAL_SEC, DATA_DIR from src.zatochki_config import SCAN_INTERVAL_SEC as Z_SCAN_INTERVAL_SEC from src.exchange import Exchange from src.screener import Screener from src.manager import TradeManager from src.zatochki_screener import ZatochkiScreener from src.zatochki_manager import ZatochkiManager from src.bot import TelegramBot from src.tmm_client import TMMClient # Squeeze ON/OFF SQUEEZE_ENABLED = False # PAUSED 6 Apr 2026 β€” Zatochki replaces it ZATOCHKI_ENABLED = True # ============================================================ # LOGGING # ============================================================ logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(name)s] %(levelname)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) logger = logging.getLogger("main") # Suppress noisy libs logging.getLogger("urllib3").setLevel(logging.WARNING) logging.getLogger("httpx").setLevel(logging.WARNING) logging.getLogger("telegram").setLevel(logging.WARNING) # ============================================================ # MAIN # ============================================================ async def main(): logger.info("=" * 50) logger.info("Squeeze-VWAP Bot starting...") logger.info(f"Squeeze: {'ACTIVE' if SQUEEZE_ENABLED else 'PAUSED'}") logger.info(f"Zatochki: {'ACTIVE' if ZATOCHKI_ENABLED else 'PAUSED'}") logger.info("=" * 50) # Ensure data dir exists os.makedirs(DATA_DIR, exist_ok=True) # Init components exchange = Exchange() notify_queue = asyncio.Queue() def sync_notify(msg): """Sync wrapper β€” ΠΊΠ»Π°Π΄Ρ‘Ρ‚ сообщСниС Π² ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ для async ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ.""" try: notify_queue.put_nowait(msg) except Exception: pass tmm = TMMClient() # Squeeze components (even if paused β€” needed for recovery) screener = Screener(exchange, notifier=sync_notify) manager = TradeManager(exchange, screener, notifier=sync_notify, tmm=tmm) screener.get_open_positions = lambda: manager.positions # Zatochki components z_screener = ZatochkiScreener(exchange, notifier=sync_notify) z_manager = ZatochkiManager(exchange, z_screener, notifier=sync_notify, tmm=tmm) bot = TelegramBot(screener=screener, exchange=exchange, tmm=tmm) bot.manager = manager bot.z_manager = z_manager # for /zstatus, /zstats commands # Start Telegram bot await bot.start() logger.info("Telegram bot started") # Startup notification balance = exchange.get_balance() squeeze_status = "ACTIVE" if SQUEEZE_ENABLED else "PAUSED" zatochki_status = "ACTIVE" if ZATOCHKI_ENABLED else "PAUSED" await bot.send_message( f"\U0001f680 *Bot started*\n" f"Balance: ${balance:.2f}\n" f"Squeeze: {squeeze_status} ({len(manager.positions)} pos)\n" f"\U0001f52a Zatochki: {zatochki_status} ({len(z_manager.positions)} pos)\n" f"$5\\*10x | SL cap 1\\.2% | TP1 0\\.7%" ) # Nuclear cleanup (only if no open positions from either strategy) if not manager.positions and not z_manager.positions: logger.info("Nuclear cleanup...") exchange.nuclear_cleanup() else: logger.info("Skipping nuclear cleanup (open positions exist)") # Recovery logger.info("Recovery check (Squeeze)...") manager.recovery() logger.info("Recovery check (Zatochki)...") z_manager.recovery() # Main loop last_squeeze_scan = 0 last_zatochki_scan = 0 check_interval = 5 # seconds between position checks logger.info("Entering main loop...") try: while True: now = time.time() # === SQUEEZE SCAN (ΠΊΠ°ΠΆΠ΄Ρ‹Π΅ 5 ΠΌΠΈΠ½) β€” PAUSED === if SQUEEZE_ENABLED and now - last_squeeze_scan >= SCAN_INTERVAL_SEC: try: screener.run_scan() last_squeeze_scan = now except Exception as e: logger.error(f"Squeeze scan error: {e}") last_squeeze_scan = now # === SQUEEZE ENTRIES === if SQUEEZE_ENABLED: try: manager.check_watchlist_for_entries() except Exception as e: logger.error(f"Squeeze entry error: {e}") # === SQUEEZE MONITOR (always β€” might have recovery positions) === if manager.positions: try: manager.check_positions() except Exception as e: logger.error(f"Squeeze position error: {e}") # === ZATOCHKI SCAN (ΠΊΠ°ΠΆΠ΄Ρ‹Π΅ 60 сСк) === if ZATOCHKI_ENABLED and now - last_zatochki_scan >= Z_SCAN_INTERVAL_SEC: try: signals = z_screener.run_scan() if signals: z_manager.process_signals(signals) last_zatochki_scan = now except Exception as e: logger.error(f"Zatochki scan error: {e}") last_zatochki_scan = now # === ZATOCHKI MONITOR === if z_manager.positions: try: z_manager.check_positions() except Exception as e: logger.error(f"Zatochki position error: {e}") # === TMM RETRY PENDING TAGS === try: tmm.retry_pending_tags() except Exception: pass # === PROCESS NOTIFY QUEUE === while not notify_queue.empty(): try: msg = notify_queue.get_nowait() await bot.send_message(msg) except Exception: break # Sleep await asyncio.sleep(check_interval) except KeyboardInterrupt: logger.info("Shutting down...") except Exception as e: logger.error(f"Fatal error: {e}", exc_info=True) await bot.send_message(f"\U0001f480 *Bot crashed:* {str(e)[:200]}") finally: await bot.stop() logger.info("Bot stopped") if __name__ == "__main__": asyncio.run(main())