"""Upbit 자동 트레이딩 봇 진입점.""" import logging import threading import time from dotenv import load_dotenv load_dotenv() logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[ logging.StreamHandler(), logging.FileHandler("logs/trading.log", encoding="utf-8"), ], ) from core.monitor import run_monitor from core.notify import notify_error, notify_status from core.price_collector import backfill_prices, run_collector from core.price_db import get_cumulative_krw_profit from core.trader import get_positions, get_budget_info, restore_positions from daemon.runner import run_scanner def run_status_reporter() -> None: """매 정각마다 1시간 이상 보유 포지션 현황 전송.""" import datetime as _dt logger = logging.getLogger("status") logger.info("상태 리포터 시작 (매 정각 트리거)") while True: now = _dt.datetime.now() # 다음 정각까지 대기 secs_to_next_hour = (60 - now.minute) * 60 - now.second time.sleep(secs_to_next_hour) try: budget = get_budget_info() cum = get_cumulative_krw_profit() notify_status( dict(get_positions()), max_budget=budget["max_budget"], per_position=budget["per_position"], cum_profit=cum, ) except Exception as e: logger.error(f"상태 리포트 오류: {e}") def main() -> None: logger = logging.getLogger("main") # 재시작 시 기존 잔고 복원 (이중 매수 방지) restore_positions() # 과거 가격 백필 (추세 판단용 DB 데이터가 없는 경우 채움) logger.info("과거 가격 백필 시작 (48시간)...") backfill_prices(hours=48) # 트레일링 스탑 감시 스레드 (10초 주기) monitor_thread = threading.Thread( target=run_monitor, args=(10,), daemon=True, name="monitor" ) monitor_thread.start() # 매 정각 상태 리포트 스레드 (1시간 이상 보유 포지션만) status_thread = threading.Thread( target=run_status_reporter, daemon=True, name="status" ) status_thread.start() # 가격 수집 스레드 (10분 주기 → Oracle DB price_history 저장, 추세 판단용) collector_thread = threading.Thread( target=run_collector, daemon=True, name="collector" ) collector_thread.start() # 매수 스캔 루프 (60초 주기, 메인 스레드) — 예외 발생 시 Telegram 알림 후 재시작 while True: try: run_scanner() except Exception as e: logger.error(f"스캐너 비정상 종료: {e}", exc_info=True) notify_error(f"스캐너 비정상 종료: {e}\n5초 후 재시작합니다.") time.sleep(5) if __name__ == "__main__": main()