diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/superpowers/specs/2026-04-01-operations-and-strategy-expansion-design.md | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-04-01-operations-and-strategy-expansion-design.md b/docs/superpowers/specs/2026-04-01-operations-and-strategy-expansion-design.md new file mode 100644 index 0000000..e1aea74 --- /dev/null +++ b/docs/superpowers/specs/2026-04-01-operations-and-strategy-expansion-design.md @@ -0,0 +1,458 @@ +# Operations Infrastructure & Strategy Expansion — Design Spec + +## Overview + +기존 Binance 현물 암호화폐 자동매매 플랫폼의 두 가지 영역을 강화한다: + +1. **운영 인프라** — DB 마이그레이션, 구조화된 로깅, Telegram 알림, 에러 복구, 메트릭 수집 +2. **전략 확장** — 추세 추종/스캘핑 전략 추가, 백테스트 고도화 + +접근 순서: 운영 인프라 먼저 완성 → 전략 추가. 안정적인 모니터링/알림 기반 위에서 새 전략을 검증할 수 있어야 한다. + +--- + +## Part 1: Operations Infrastructure + +### 1.1 DB Layer Migration (asyncpg → SQLAlchemy 2.0 Async + Alembic) + +**목표:** raw SQL과 asyncpg 직접 사용을 SQLAlchemy 2.0 async ORM으로 교체하고, Alembic으로 마이그레이션을 관리한다. + +**변경 사항:** + +- `shared/src/shared/db.py` — AsyncSession 기반으로 재작성 + - `create_async_engine()` + `async_sessionmaker()` 사용 + - asyncpg는 SQLAlchemy의 내부 드라이버로 유지 (`postgresql+asyncpg://`) + - 기존 raw SQL 함수들을 ORM 쿼리로 전환 + +- `shared/src/shared/sa_models.py` — SQLAlchemy ORM 모델 (신규) + - 기존 Pydantic 모델(models.py)과 1:1 매핑되는 SA 테이블 정의 + - `Candle`, `Signal`, `Order`, `Trade`, `Position`, `PortfolioSnapshot` 테이블 + - Pydantic 모델은 이벤트 직렬화/API 전용으로 유지 + +- `shared/alembic/` — Alembic 마이그레이션 환경 (신규) + - `alembic.ini` — 설정 파일 (DATABASE_URL 참조) + - `env.py` — async 엔진 설정, SA 모델 메타데이터 참조 + - `versions/` — 마이그레이션 파일들 + - 초기 마이그레이션: 기존 `db.py`의 CREATE TABLE 로직을 마이그레이션으로 이전 + +- `Makefile` 타겟 추가: + - `make migrate` — `alembic upgrade head` + - `make migrate-down` — `alembic downgrade -1` + - `make migrate-new MSG="description"` — `alembic revision --autogenerate -m "description"` + +- 각 서비스의 DB 접근 코드를 AsyncSession 기반으로 업데이트: + - `data-collector/storage.py` — bulk insert 쿼리를 SA ORM으로 + - `order-executor/executor.py` — order CRUD를 SA ORM으로 + - `portfolio-manager/portfolio.py` — position/snapshot 쿼리를 SA ORM으로 + - `backtester/engine.py` — candle 조회를 SA ORM으로 + +**의존성 추가:** `sqlalchemy[asyncio]>=2.0`, `alembic>=1.13` +**의존성 제거:** `asyncpg` (직접 의존 → SQLAlchemy 내부 의존으로 변경) + +--- + +### 1.2 Structured Logging (structlog) + +**목표:** 전 서비스에 JSON 구조화 로깅을 적용하고, 에러 로그를 Telegram 알림과 연결한다. + +**변경 사항:** + +- `shared/src/shared/logging.py` (신규) + - `setup_logging(service_name: str, log_level: str)` 함수 + - structlog 프로세서 체인: timestamp, log level, service_name 바인딩, JSON 렌더러 + - 개발 환경: 컬러 콘솔 출력 / 프로덕션: JSON stdout + - `LOG_FORMAT` 환경변수로 전환 (`console` | `json`, 기본값: `json`) + +- 각 서비스 `main.py`에서 `setup_logging()` 호출 +- 기존 `logging.getLogger()` 호출을 `structlog.get_logger()` 로 교체 +- 컨텍스트 바인딩 예시: + ```python + log = structlog.get_logger().bind(service="strategy-engine", symbol="BTCUSDT") + log.info("signal_generated", strategy="rsi", side="BUY", price=68500) + ``` + +- ERROR 이상 로그 → Telegram 알림 트리거 (1.3절 TelegramNotifier 연동) + - structlog 커스텀 프로세서로 구현 + - 알림 전송 실패 시 로그만 남기고 서비스 중단하지 않음 + +**의존성 추가:** `structlog>=24.0` + +--- + +### 1.3 Telegram Notification Service + +**목표:** 주요 이벤트(시그널, 주문, 에러, 일일 요약)를 Telegram으로 전송한다. + +**변경 사항:** + +- `shared/src/shared/notifier.py` (신규) + - `TelegramNotifier` 클래스 + - `__init__(bot_token: str, chat_id: str)` — aiohttp 세션 관리 + - `send(message: str, parse_mode: str = "HTML")` — 메시지 전송 + - `send_signal(signal: Signal)` — 시그널 포맷팅 후 전송 + - `send_order(order: Order)` — 주문 체결/실패 알림 + - `send_error(error: str, service: str)` — 에러 알림 + - `send_daily_summary(positions: list, pnl: Decimal)` — 일일 요약 + - Rate limiting: 초당 최대 1건 (asyncio.Semaphore + 큐) + - 연결 실패 시 최대 3회 재시도, 실패해도 서비스 중단하지 않음 + +- `shared/src/shared/config.py` — 설정 추가: + - `TELEGRAM_BOT_TOKEN: str = ""` + - `TELEGRAM_CHAT_ID: str = ""` + - `TELEGRAM_ENABLED: bool = False` (토큰 미설정 시 자동 비활성) + +- `.env.example` 업데이트: + ``` + TELEGRAM_BOT_TOKEN= + TELEGRAM_CHAT_ID= + TELEGRAM_ENABLED=false + ``` + +- 연동 포인트: + - `strategy-engine/engine.py` — 시그널 생성 시 `send_signal()` + - `order-executor/executor.py` — 주문 체결/실패 시 `send_order()` + - `shared/logging.py` — ERROR 로그 시 `send_error()` + - `portfolio-manager/main.py` — 매일 자정(UTC) `send_daily_summary()` + +**의존성:** aiohttp (이미 존재) + +--- + +### 1.4 Error Recovery & Health Checks + +**목표:** 서비스 장애 시 자동 복구하고, 헬스체크 엔드포인트로 상태를 모니터링한다. + +**변경 사항:** + +- `shared/src/shared/resilience.py` (신규) + - `retry_with_backoff(func, max_retries, base_delay)` — exponential backoff 데코레이터 + - 지터(jitter) 포함: `delay * (1 + random(0, 0.5))` + - 최대 지연: 60초 + - `CircuitBreaker` 클래스: + - 상태: CLOSED(정상) → OPEN(차단) → HALF_OPEN(시험) + - `failure_threshold`: 연속 실패 N회 시 OPEN (기본: 5) + - `recovery_timeout`: OPEN 후 N초 뒤 HALF_OPEN (기본: 60) + - OPEN 전환 시 Telegram 알림 전송 + +- `shared/src/shared/healthcheck.py` (신규) + - `HealthCheckServer` — aiohttp 기반 경량 HTTP 서버 + - `GET /health` → `{"status": "ok", "service": "...", "uptime": ..., "checks": {...}}` + - 체크 항목: Redis 연결, PostgreSQL 연결, Binance WS 연결(해당 서비스만) + - 포트: `HEALTH_PORT` 환경변수 (서비스별 다르게 설정) + +- 각 서비스에 적용: + - `data-collector` — Binance WS 재연결 (backoff), Redis/DB 재연결 + - `strategy-engine` — Redis 소비자 재연결 + - `order-executor` — 거래소 API 호출 재시도 (circuit breaker) + - `portfolio-manager` — Redis/DB 재연결 + +- `docker-compose.yml` — healthcheck를 `/health` 엔드포인트로 변경 + +- `shared/src/shared/config.py` — 설정 추가: + - `HEALTH_PORT: int = 8080` + - `CIRCUIT_BREAKER_THRESHOLD: int = 5` + - `CIRCUIT_BREAKER_TIMEOUT: int = 60` + +--- + +### 1.5 Prometheus Metrics + +**목표:** 각 서비스의 주요 지표를 Prometheus 포맷으로 노출한다. + +**변경 사항:** + +- `shared/src/shared/metrics.py` (신규) + - `MetricsServer` — prometheus_client 기반 + - `/metrics` 엔드포인트 (healthcheck 서버에 통합) + - 공통 메트릭: + - `service_up` (Gauge) — 서비스 상태 + - `errors_total` (Counter) — 에러 횟수 (label: service, error_type) + - `event_processing_seconds` (Histogram) — 이벤트 처리 시간 + +- 서비스별 메트릭: + - **data-collector:** + - `candles_received_total` (Counter) — 수신 캔들 수 + - `ws_reconnections_total` (Counter) — WS 재연결 횟수 + - **strategy-engine:** + - `signals_generated_total` (Counter, label: strategy, side) + - `strategy_execution_seconds` (Histogram, label: strategy) + - **order-executor:** + - `orders_total` (Counter, label: status, side) + - `risk_rejections_total` (Counter, label: reason) + - **portfolio-manager:** + - `portfolio_value` (Gauge) — 총 포트폴리오 가치 + - `unrealized_pnl` (Gauge, label: symbol) + +- `docker-compose.yml` — Prometheus + Grafana 서비스 추가 (선택적 프로필): + ```yaml + prometheus: + image: prom/prometheus:latest + profiles: ["monitoring"] + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + grafana: + image: grafana/grafana:latest + profiles: ["monitoring"] + ports: + - "3000:3000" + ``` + +- `monitoring/prometheus.yml` (신규) — 스크래핑 설정 +- `monitoring/grafana/` (신규) — 대시보드 프로비저닝 (선택적) + +**의존성 추가:** `prometheus-client>=0.20` + +--- + +## Part 2: Strategy Expansion + +### 2.1 Trend Following Strategies + +**MACD Strategy** (`strategies/macd_strategy.py`) +- MACD line = EMA(12) - EMA(26), Signal line = EMA(9) of MACD +- BUY: MACD가 Signal line 위로 교차 + 히스토그램 양전환 +- SELL: MACD가 Signal line 아래로 교차 + 히스토그램 음전환 +- 파라미터: `fast_period=12`, `slow_period=26`, `signal_period=9`, `quantity` +- warmup_period: `slow_period + signal_period` + +**Bollinger Bands Strategy** (`strategies/bollinger_strategy.py`) +- 중심선 = SMA(20), 상단 = 중심 + 2*std, 하단 = 중심 - 2*std +- BUY: 가격이 하단 밴드 아래로 이탈 후 복귀 +- SELL: 가격이 상단 밴드 위로 이탈 후 복귀 +- 변동성 필터: 밴드 폭이 임계값 미만이면 시그널 무시 (횡보장 필터) +- 파라미터: `period=20`, `num_std=2.0`, `min_bandwidth=0.02`, `quantity` +- warmup_period: `period` + +**EMA Crossover Strategy** (`strategies/ema_crossover_strategy.py`) +- 단기 EMA와 장기 EMA 교차 +- BUY: 단기 EMA가 장기 EMA 위로 교차 (Golden Cross) +- SELL: 단기 EMA가 장기 EMA 아래로 교차 (Death Cross) +- 파라미터: `short_period=9`, `long_period=21`, `quantity` +- warmup_period: `long_period` + +--- + +### 2.2 Scalping Strategies + +**VWAP Strategy** (`strategies/vwap_strategy.py`) +- VWAP = cumsum(price * volume) / cumsum(volume) +- BUY: 가격이 VWAP 아래에서 VWAP으로 복귀 (평균 회귀) +- SELL: 가격이 VWAP 위에서 VWAP으로 복귀 +- 일중 리셋: UTC 00:00에 VWAP 재계산 +- 파라미터: `deviation_threshold=0.002`, `quantity` +- warmup_period: 최소 30 캔들 + +**Volume Profile Strategy** (`strategies/volume_profile_strategy.py`) +- 최근 N개 캔들의 가격대별 거래량 분포 계산 +- POC (Point of Control): 가장 거래량이 많은 가격대 +- Value Area: 전체 거래량 70%가 집중된 구간 +- BUY: 가격이 Value Area 하단 지지선에서 반등 +- SELL: 가격이 Value Area 상단 저항선에서 거부 +- 파라미터: `lookback_period=100`, `num_bins=50`, `value_area_pct=0.7`, `quantity` +- warmup_period: `lookback_period` + +--- + +### 2.3 Strategy Common Improvements + +**BaseStrategy 확장:** +```python +class BaseStrategy(ABC): + @property + @abstractmethod + def warmup_period(self) -> int: + """지표 계산에 필요한 최소 캔들 수""" + pass + + @abstractmethod + def on_candle(self, candle: Candle) -> Signal | None: + pass + + @abstractmethod + def configure(self, params: dict) -> None: + pass + + def reset(self) -> None: + """전략 상태 초기화 (백테스트 간 재사용)""" + pass +``` + +**전략 파라미터 외부화:** +- `strategies/config/` 디렉토리에 YAML 설정 파일 +- 파일명: `{strategy_name}.yaml` (예: `rsi_strategy.yaml`) +- 구조: + ```yaml + # rsi_strategy.yaml + period: 14 + oversold: 30 + overbought: 70 + quantity: 0.001 + ``` +- `plugin_loader.py`가 전략 로드 시 자동으로 같은 이름의 YAML을 찾아 `configure()` 호출 +- CLI에서 `--param key=value`로 런타임 오버라이드 가능 + +**기존 전략 업데이트:** +- `RsiStrategy`, `GridStrategy`에 `warmup_period` 속성 추가 +- `reset()` 메서드 구현 + +**의존성 추가:** `pyyaml>=6.0` + +--- + +### 2.4 Backtest Enhancement + +**DetailedMetrics 데이터클래스** (`backtester/src/backtester/metrics.py`, 신규): +```python +@dataclass +class TradeRecord: + entry_time: datetime + exit_time: datetime + symbol: str + side: str + entry_price: Decimal + exit_price: Decimal + quantity: Decimal + pnl: Decimal + pnl_pct: float + holding_period: timedelta + +@dataclass +class DetailedMetrics: + # 기본 + total_return: float + total_trades: int + winning_trades: int + losing_trades: int + win_rate: float + profit_factor: float + + # 리스크 메트릭 + sharpe_ratio: float + sortino_ratio: float + calmar_ratio: float + max_drawdown: float + max_drawdown_duration: timedelta + + # 수익률 분석 + monthly_returns: dict[str, float] # "2025-01": 0.05 + avg_win: float + avg_loss: float + largest_win: float + largest_loss: float + avg_holding_period: timedelta + + # 개별 거래 + trades: list[TradeRecord] +``` + +**BacktestEngine 확장:** +- `engine.py`에 `DetailedMetrics` 계산 로직 추가 +- `simulator.py`에 `TradeRecord` 생성 로직 추가 (진입/청산 시점 기록) +- Sharpe ratio = `mean(daily_returns) / std(daily_returns) * sqrt(365)` (crypto는 365일) +- Sortino ratio = `mean(daily_returns) / downside_std * sqrt(365)` +- Calmar ratio = `annualized_return / max_drawdown` +- Max drawdown = `max(peak - trough) / peak` + +**Reporter 개선:** +- `reporter.py` — rich 라이브러리로 테이블 출력 + - 요약 테이블: 핵심 메트릭 + - 월별 수익률 테이블 + - 최고/최악 거래 Top 5 +- CSV/JSON 내보내기: `--output csv` / `--output json` 플래그 + +**CLI 확장:** +- `trading backtest run` — 기존 출력에 상세 메트릭 추가 +- `trading backtest run --output csv --file result.csv` — 결과 내보내기 + +**의존성 추가:** `rich>=13.0` + +--- + +## Updated Tech Stack + +| 용도 | 기존 | 변경 | +|------|------|------| +| DB ORM | asyncpg (raw SQL) | **SQLAlchemy 2.0 async** (asyncpg 드라이버) | +| 마이그레이션 | 없음 | **Alembic** | +| 로깅 | Python logging | **structlog** | +| 알림 | 없음 | **Telegram Bot API** (aiohttp) | +| 메트릭 | 없음 | **prometheus-client** | +| 전략 설정 | 하드코딩 | **YAML** (pyyaml) | +| 리포트 출력 | print | **rich** | + +--- + +## Updated .env.example + +```env +# Exchange +BINANCE_API_KEY= +BINANCE_API_SECRET= + +# Infrastructure +REDIS_URL=redis://localhost:6379 +DATABASE_URL=postgresql+asyncpg://trading:trading@localhost:5432/trading + +# Logging +LOG_LEVEL=INFO +LOG_FORMAT=json + +# Telegram +TELEGRAM_BOT_TOKEN= +TELEGRAM_CHAT_ID= +TELEGRAM_ENABLED=false + +# Risk Management +RISK_MAX_POSITION_SIZE=0.1 +RISK_STOP_LOSS_PCT=5 +RISK_DAILY_LOSS_LIMIT_PCT=10 +DRY_RUN=true + +# Health & Metrics +HEALTH_PORT=8080 +CIRCUIT_BREAKER_THRESHOLD=5 +CIRCUIT_BREAKER_TIMEOUT=60 +``` + +--- + +## New Files Summary + +| 파일 | 용도 | +|------|------| +| `shared/src/shared/sa_models.py` | SQLAlchemy ORM 모델 | +| `shared/src/shared/logging.py` | structlog 설정 | +| `shared/src/shared/notifier.py` | Telegram 알림 | +| `shared/src/shared/resilience.py` | retry, circuit breaker | +| `shared/src/shared/healthcheck.py` | 헬스체크 서버 | +| `shared/src/shared/metrics.py` | Prometheus 메트릭 | +| `shared/alembic/` | DB 마이그레이션 환경 | +| `strategies/config/*.yaml` | 전략 파라미터 설정 | +| `strategies/macd_strategy.py` | MACD 전략 | +| `strategies/bollinger_strategy.py` | Bollinger Bands 전략 | +| `strategies/ema_crossover_strategy.py` | EMA Crossover 전략 | +| `strategies/vwap_strategy.py` | VWAP 전략 | +| `strategies/volume_profile_strategy.py` | Volume Profile 전략 | +| `backtester/src/backtester/metrics.py` | 상세 백테스트 메트릭 | +| `monitoring/prometheus.yml` | Prometheus 설정 | + +--- + +## Scope Boundaries + +**포함:** +- SQLAlchemy 2.0 async 전환 + Alembic 마이그레이션 +- structlog JSON 로깅 +- Telegram 알림 (시그널, 주문, 에러, 일일 요약) +- 에러 복구 (retry, circuit breaker) + 헬스체크 +- Prometheus 메트릭 수집 +- 5개 신규 전략 (MACD, Bollinger, EMA Crossover, VWAP, Volume Profile) +- BaseStrategy에 warmup_period, reset() 추가 +- YAML 기반 전략 파라미터 +- 백테스트 상세 메트릭 + rich 리포트 + +**제외:** +- Grafana 대시보드 프로비저닝 (Prometheus만 설정, 대시보드는 수동) +- 멀티 거래소 지원 +- REST API / 웹 대시보드 +- 전략 조합 프레임워크 (향후 확장) |
