From 99fa508db7ea8d6896c0f88d141af5e7a07d8900 Mon Sep 17 00:00:00 2001 From: 21in7 Date: Mon, 2 Mar 2026 20:01:18 +0900 Subject: [PATCH] feat: add CLAUDE.md and settings.json for project documentation and plugin configuration Introduced CLAUDE.md to provide comprehensive guidance on the CoinTrader project, including architecture, common commands, testing, and deployment details. Added settings.json to enable the superpowers plugin for Claude. This enhances the project's documentation and configuration management. --- .claude/settings.json | 5 + CLAUDE.md | 83 ++++++++++++ docs/plans/2026-03-02-adx-filter-design.md | 150 +++++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 .claude/settings.json create mode 100644 CLAUDE.md create mode 100644 docs/plans/2026-03-02-adx-filter-design.md diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..0f243d7 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "superpowers@claude-plugins-official": true + } +} diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..68a08fe --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,83 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +CoinTrader is a Python asyncio-based automated cryptocurrency trading bot for Binance Futures. It trades XRPUSDT on 15-minute candles, using BTC/ETH as correlation features. The system has 5 layers: Data (WebSocket streams) → Signal (technical indicators) → ML Filter (ONNX/LightGBM) → Execution & Risk → Event/Alert (Discord). + +## Common Commands + +```bash +# venv +source .venv/bin/activate + +# Run the bot +python main.py + +# Run full test suite +bash scripts/run_tests.sh + +# Run filtered tests +bash scripts/run_tests.sh -k "bot" + +# Run pytest directly +pytest tests/ -v --tb=short + +# ML training pipeline (LightGBM default) +bash scripts/train_and_deploy.sh + +# MLX GPU training (macOS Apple Silicon) +bash scripts/train_and_deploy.sh mlx + +# Hyperparameter tuning (50 trials, 5-fold walk-forward) +python scripts/tune_hyperparams.py + +# Fetch historical data +python scripts/fetch_history.py --symbols XRPUSDT BTCUSDT ETHUSDT --interval 15m --days 365 + +# Deploy models to production +bash scripts/deploy_model.sh +``` + +## Architecture + +**Entry point**: `main.py` → creates `Config` (dataclass from env vars) → runs `TradingBot` + +**5-layer data flow on each 15m candle close:** +1. `src/data_stream.py` — Combined WebSocket for XRP/BTC/ETH, deque buffers (200 candles each) +2. `src/indicators.py` — RSI, MACD, BB, EMA, StochRSI, ATR; weighted signal aggregation → LONG/SHORT/HOLD +3. `src/ml_filter.py` + `src/ml_features.py` — 23-feature extraction, ONNX priority > LightGBM fallback, threshold ≥ 0.60 +4. `src/exchange.py` + `src/risk_manager.py` — Dynamic margin, MARKET orders with SL/TP, daily loss limit (5%) +5. `src/user_data_stream.py` + `src/notifier.py` — Real-time TP/SL detection via WebSocket, Discord webhooks + +**Parallel execution**: `user_data_stream` runs independently via `asyncio.gather()` alongside candle processing. + +## Key Patterns + +- **Async-first**: All I/O operations use `async/await`; parallel tasks via `asyncio.gather()` +- **Reverse signal re-entry**: While holding LONG, if SHORT signal appears → close position, cancel SL/TP, open SHORT. `_is_reentering` flag prevents race conditions with User Data Stream +- **ML hot reload**: `ml_filter.check_and_reload()` compares file mtime on every candle, reloads model without restart +- **Active Config pattern**: Best hyperparams stored in `models/active_lgbm_params.json`, must be manually approved before retraining +- **Graceful degradation**: Missing model → all signals pass; API failure → use fallback values (0.0 for OI/funding) +- **Walk-forward validation**: Time-series CV with undersampling (1:1 class balance, preserving time order) +- **Label generation**: Binary labels based on 24-candle (6h) lookahead — check SL hit first (conservative), then TP + +## Testing + +- All external APIs (Binance, Discord) are mocked with `unittest.mock.AsyncMock` +- Async tests use `@pytest.mark.asyncio` +- 14 test files, 80+ test cases covering all layers +- Testing is done in actual terminal, not IDE sandbox + +## Configuration + +Environment variables via `.env` file (see `.env.example`). Key vars: `BINANCE_API_KEY`, `BINANCE_API_SECRET`, `SYMBOL` (default XRPUSDT), `LEVERAGE`, `DISCORD_WEBHOOK_URL`, `MARGIN_MAX_RATIO`, `MARGIN_MIN_RATIO`, `NO_ML_FILTER`. + +`src/config.py` uses `@dataclass` with `__post_init__` to load and validate all env vars. + +## Deployment + +- **Docker**: `Dockerfile` (Python 3.12-slim) + `docker-compose.yml` +- **CI/CD**: Jenkins pipeline (Gitea → Docker registry → LXC production server) +- Models stored in `models/`, data cache in `data/`, logs in `logs/` diff --git a/docs/plans/2026-03-02-adx-filter-design.md b/docs/plans/2026-03-02-adx-filter-design.md new file mode 100644 index 0000000..863f549 --- /dev/null +++ b/docs/plans/2026-03-02-adx-filter-design.md @@ -0,0 +1,150 @@ +# ADX 횡보장 필터 구현 계획 + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** ADX < 25일 때 get_signal()에서 즉시 HOLD를 반환하여 횡보장 진입을 차단한다. + +**Architecture:** `calculate_all()`에서 `pandas_ta.adx()`로 ADX 컬럼을 추가하고, `get_signal()`에서 가중치 계산 전 ADX < 25이면 early-return HOLD. NaN(초기 캔들)은 기존 로직으로 폴백. + +**Tech Stack:** pandas-ta (이미 사용 중), pytest + +--- + +### Task 1: ADX 계산 테스트 추가 + +**Files:** +- Test: `tests/test_indicators.py` + +**Step 1: Write the failing test** + +```python +def test_adx_column_exists(sample_df): + """calculate_all()이 adx 컬럼을 생성하는지 확인.""" + ind = Indicators(sample_df) + df = ind.calculate_all() + assert "adx" in df.columns + valid = df["adx"].dropna() + assert (valid >= 0).all() +``` + +`tests/test_indicators.py`에 위 테스트 함수를 추가한다. + +**Step 2: Run test to verify it fails** + +Run: `pytest tests/test_indicators.py::test_adx_column_exists -v` +Expected: FAIL — `"adx" not in df.columns` + +--- + +### Task 2: calculate_all()에 ADX 계산 추가 + +**Files:** +- Modify: `src/indicators.py:46-48` (vol_ma20 계산 바로 앞에 추가) + +**Step 3: Write minimal implementation** + +`calculate_all()`의 Stochastic RSI 계산 뒤, `vol_ma20` 계산 앞에 추가: + +```python + # ADX (14) — 횡보장 필터 + adx_df = ta.adx(df["high"], df["low"], df["close"], length=14) + df["adx"] = adx_df["ADX_14"] +``` + +**Step 4: Run test to verify it passes** + +Run: `pytest tests/test_indicators.py::test_adx_column_exists -v` +Expected: PASS + +**Step 5: Commit** + +```bash +git add src/indicators.py tests/test_indicators.py +git commit -m "feat: add ADX calculation to indicators" +``` + +--- + +### Task 3: ADX 필터 테스트 추가 (차단 케이스) + +**Files:** +- Test: `tests/test_indicators.py` + +**Step 6: Write the failing test** + +```python +def test_adx_filter_blocks_low_adx(sample_df): + """ADX < 25일 때 가중치와 무관하게 HOLD를 반환해야 한다.""" + ind = Indicators(sample_df) + df = ind.calculate_all() + # ADX를 강제로 낮은 값으로 설정 + df["adx"] = 15.0 + signal = ind.get_signal(df) + assert signal == "HOLD" +``` + +**Step 7: Run test to verify it fails** + +Run: `pytest tests/test_indicators.py::test_adx_filter_blocks_low_adx -v` +Expected: FAIL — signal이 LONG 또는 SHORT 반환 (ADX 필터 미구현) + +--- + +### Task 4: ADX 필터 테스트 추가 (NaN 폴백 케이스) + +**Files:** +- Test: `tests/test_indicators.py` + +**Step 8: Write the failing test** + +```python +def test_adx_nan_falls_through(sample_df): + """ADX가 NaN(초기 캔들)이면 기존 가중치 로직으로 폴백해야 한다.""" + ind = Indicators(sample_df) + df = ind.calculate_all() + df["adx"] = float("nan") + signal = ind.get_signal(df) + # NaN이면 차단하지 않고 기존 로직 실행 → LONG/SHORT/HOLD 중 하나 + assert signal in ("LONG", "SHORT", "HOLD") +``` + +**Step 9: Run test to verify it passes (이 테스트는 현재도 통과)** + +Run: `pytest tests/test_indicators.py::test_adx_nan_falls_through -v` +Expected: PASS (ADX 컬럼이 무시되므로 기존 로직 그대로) + +--- + +### Task 5: get_signal()에 ADX early-return 구현 + +**Files:** +- Modify: `src/indicators.py:51-56` (get_signal 메서드 시작부) + +**Step 10: Write minimal implementation** + +`get_signal()` 메서드의 `last = df.iloc[-1]` 바로 다음에 추가: + +```python + # ADX 횡보장 필터: ADX < 25이면 추세 부재로 판단하여 진입 차단 + adx = last.get("adx", None) + if adx is not None and not pd.isna(adx) and adx < 25: + logger.debug(f"ADX 필터: {adx:.1f} < 25 — HOLD") + return "HOLD" +``` + +**Step 11: Run all ADX-related tests** + +Run: `pytest tests/test_indicators.py -k "adx" -v` +Expected: 3 tests PASS + +**Step 12: Run full test suite to check for regressions** + +Run: `pytest tests/ -v --tb=short` +Expected: All tests PASS + +**Step 13: Commit** + +```bash +git add src/indicators.py tests/test_indicators.py +git commit -m "feat: add ADX filter to block sideways market entries" +```