Files
AutoCoinTrader2/src/common.py
2025-12-09 21:39:23 +09:00

108 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import gzip
import logging
import logging.handlers
import os
import shutil
from pathlib import Path
LOG_DIR = os.getenv("LOG_DIR", "logs")
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper()
Path(LOG_DIR).mkdir(parents=True, exist_ok=True)
LOG_FILE = os.path.join(LOG_DIR, "AutoCoinTrader.log")
logger = logging.getLogger("macd_alarm")
_logger_configured = False
# 거래소 및 계산 상수
# 부동소수점 비교용 엡실론 (일반적 정밀도)
FLOAT_EPSILON = 1e-10
# 거래소별 최소 수량 (Upbit 기준)
MIN_TRADE_AMOUNT = 1e-8 # 0.00000001 (암호화폐 최소 단위)
# 최소 주문 금액 (KRW)
MIN_KRW_ORDER = 5000 # Upbit 최소 주문 금액
# 데이터 파일 경로 상수 (중앙 집중 관리)
DATA_DIR = Path("data")
DATA_DIR.mkdir(parents=True, exist_ok=True)
HOLDINGS_FILE = str(DATA_DIR / "holdings.json")
TRADES_FILE = str(DATA_DIR / "trades.json")
PENDING_ORDERS_FILE = str(DATA_DIR / "pending_orders.json")
class CompressedRotatingFileHandler(logging.handlers.RotatingFileHandler):
"""RotatingFileHandler with gzip compression for rotated logs."""
def rotation_filename(self, default_name):
"""Append .gz to rotated log files."""
return default_name + ".gz"
def rotate(self, source, dest):
"""Compress the rotated log file."""
if os.path.exists(source):
with open(source, "rb") as f_in:
with gzip.open(dest, "wb") as f_out:
shutil.copyfileobj(f_in, f_out)
os.remove(source)
def setup_logger(dry_run: bool):
"""
Configure logging with rotation and compression.
Args:
dry_run: If True, also output to console. If False, only to file.
Log Rotation Strategy:
- Size-based: 10MB per file, keep 7 backups (total ~80MB)
- Compression: Old logs are gzipped (saves ~70% space)
Log Levels (production recommendation):
- dry_run=True: INFO (development/testing)
- dry_run=False: INFO (production - retain important trading logs)
⚠️ CRITICAL: Production mode uses INFO level to ensure trading events are logged.
This is essential for auditing buy/sell orders and debugging issues.
For high-volume environments, adjust LOG_LEVEL via environment variable.
"""
global logger, _logger_configured
if _logger_configured:
return
logger.handlers.clear()
# Use INFO level for both dry_run and production to ensure trading events are logged
# Production systems can override via LOG_LEVEL environment variable if needed
effective_level = getattr(logging, LOG_LEVEL, logging.INFO)
logger.setLevel(effective_level)
formatter = logging.Formatter("%(asctime)s - %(levelname)s - [%(threadName)s] - %(message)s")
# Console handler (only in dry_run mode)
if dry_run:
ch = logging.StreamHandler()
ch.setLevel(effective_level)
ch.setFormatter(formatter)
logger.addHandler(ch)
# Size-based rotating file handler with compression (only one rotation strategy)
fh_size = CompressedRotatingFileHandler(
LOG_FILE,
maxBytes=10 * 1024 * 1024,
backupCount=7,
encoding="utf-8", # 10MB per file # Keep 7 backups
)
fh_size.setLevel(effective_level)
fh_size.setFormatter(formatter)
logger.addHandler(fh_size)
_logger_configured = True
logger.info(
"[SYSTEM] 로그 설정 완료: level=%s, size_rotation=%dMB×%d (일별 로테이션 제거됨)",
logging.getLevelName(effective_level),
10,
7,
)