"""매수 기회 스캔 루프 - 60초마다 전체 시장 스캔 + 신호 종목 빠른 폴링.""" import logging import os import threading import time from core import trader from core.market import get_top_tickers from core.market_regime import get_regime from core.strategy import get_active_signals, should_buy logger = logging.getLogger(__name__) SCAN_INTERVAL = 60 # 전체 시장 스캔 주기 (초) SIGNAL_POLL_INTERVAL = int(os.getenv("SIGNAL_POLL_INTERVAL", "15")) # 신호 종목 빠른 감시 주기 (초) def _fast_poll_loop() -> None: """활성 신호 종목을 SIGNAL_POLL_INTERVAL 초마다 빠르게 체크. 신호 감지 후 전체 스캔 60초를 기다리지 않고, 신호 종목만 빠르게 감시하여 목표 임계값 도달 시 즉시 매수한다. """ logger.info(f"신호 감시 시작 (주기={SIGNAL_POLL_INTERVAL}초)") while True: try: signals = get_active_signals() if signals: regime = get_regime() if regime["name"] != "bear": positions = trader.get_positions() for ticker in list(signals): if ticker in positions: continue if len(positions) >= trader.MAX_POSITIONS: break try: if should_buy(ticker): logger.info(f"[빠른감시] 매수 신호: {ticker}") trader.buy(ticker) time.sleep(0.1) except Exception as e: logger.error(f"[빠른감시] 오류 {ticker}: {e}") except Exception as e: logger.error(f"신호 감시 루프 오류: {e}") time.sleep(SIGNAL_POLL_INTERVAL) def run_scanner() -> None: """메인 스캔 루프.""" # 신호 종목 빠른 감시 스레드 시작 t = threading.Thread(target=_fast_poll_loop, daemon=True, name="signal-fast-poll") t.start() logger.info(f"스캐너 시작 (스캔={SCAN_INTERVAL}초 | 신호감시={SIGNAL_POLL_INTERVAL}초)") while True: try: # 포지션 꽉 찼으면 스캔 스킵 if len(trader.get_positions()) >= trader.MAX_POSITIONS: logger.info("포지션 최대치 도달, 스캔 스킵") time.sleep(SCAN_INTERVAL) continue # Bear 레짐 시 신규 매수 완전 차단 regime = get_regime() if regime["name"] == "bear": logger.info( f"[Bear차단] 레짐={regime['emoji']} BEAR " f"(score={regime['score']:+.2f}%) — 신규 매수 스킵" ) time.sleep(SCAN_INTERVAL) continue tickers = get_top_tickers() logger.info(f"스캔 시작: {len(tickers)}개 종목") for ticker in tickers: # 이미 보유 중인 종목 제외 if ticker in trader.get_positions(): continue try: if should_buy(ticker): logger.info(f"매수 신호: {ticker}") trader.buy(ticker) time.sleep(0.15) # API rate limit 방지 except Exception as e: logger.error(f"스캔 오류 {ticker}: {e}") time.sleep(0.3) except Exception as e: logger.error(f"스캐너 루프 오류: {e}") time.sleep(SCAN_INTERVAL)