Files
cointrader/docs/plans/2026-03-03-testnet-1m-125x-plan.md
21in7 2b92fd0dce feat: implement testnet and demo trading setup for XRPUSDT with 125x leverage
- Added support for demo trading on Binance Futures with a new configuration for 1-minute candles and 125x leverage.
- Updated various components including Config, Exchange, DataStream, UserDataStream, and Bot to handle demo and testnet flags.
- Enhanced the training pipeline to collect 1-minute data and adjusted the lookahead for model training.
- Updated environment variables in .env and .env.example to include demo settings.

This commit lays the groundwork for testing ML-based automated trading strategies in a controlled environment.
2026-03-04 20:43:05 +09:00

427 lines
11 KiB
Markdown
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.
# Testnet 1분봉 125x 트레이딩 Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**Goal:** 바이낸스 테스트넷에서 XRPUSDT 1분봉, 125x 레버리지로 ML 기반 자동매매를 실행한다.
**Architecture:** Config에 `testnet` 플래그를 추가하고, Exchange/DataStream/UserDataStream에 `testnet=True`를 전달한다. 학습 파이프라인은 LOOKAHEAD=60(1시간)으로 조정하여 1분봉 데이터로 새 모델을 학습한다.
**Tech Stack:** python-binance (testnet=True), LightGBM, asyncio
---
## Task 1: Config에 testnet 지원 추가
**Files:**
- Modify: `src/config.py:8-33`
- Test: `tests/test_config.py` (기존 테스트 수정 필요시)
**Step 1: Config에 testnet, interval 필드 추가**
`src/config.py`에서 `Config` dataclass에 `testnet`, `interval` 필드를 추가하고, `__post_init__`에서 `BINANCE_TESTNET=true`이면 테스트넷 키를 사용하도록 변경:
```python
@dataclass
class Config:
api_key: str = ""
api_secret: str = ""
symbol: str = "XRPUSDT"
leverage: int = 10
testnet: bool = False
interval: str = "15m"
max_positions: int = 3
stop_loss_pct: float = 0.015 # 1.5%
take_profit_pct: float = 0.045 # 4.5% (3:1 RR)
trailing_stop_pct: float = 0.01 # 1%
discord_webhook_url: str = ""
margin_max_ratio: float = 0.50
margin_min_ratio: float = 0.20
margin_decay_rate: float = 0.0006
ml_threshold: float = 0.55
def __post_init__(self):
self.testnet = os.getenv("BINANCE_TESTNET", "").lower() in ("true", "1", "yes")
self.interval = os.getenv("INTERVAL", "15m")
if self.testnet:
self.api_key = os.getenv("BINANCE_TESTNET_API_KEY", "")
self.api_secret = os.getenv("BINANCE_TESTNET_API_SECRET", "")
else:
self.api_key = os.getenv("BINANCE_API_KEY", "")
self.api_secret = os.getenv("BINANCE_API_SECRET", "")
self.symbol = os.getenv("SYMBOL", "XRPUSDT")
self.leverage = int(os.getenv("LEVERAGE", "10"))
self.discord_webhook_url = os.getenv("DISCORD_WEBHOOK_URL", "")
self.margin_max_ratio = float(os.getenv("MARGIN_MAX_RATIO", "0.50"))
self.margin_min_ratio = float(os.getenv("MARGIN_MIN_RATIO", "0.20"))
self.margin_decay_rate = float(os.getenv("MARGIN_DECAY_RATE", "0.0006"))
self.ml_threshold = float(os.getenv("ML_THRESHOLD", "0.55"))
```
**Step 2: 테스트 실행**
Run: `pytest tests/ -v --tb=short -x`
Expected: 기존 테스트 모두 PASS (testnet 미설정 시 기존 동작 유지)
**Step 3: Commit**
```bash
git add src/config.py
git commit -m "feat: add testnet and interval support to Config"
```
---
## Task 2: Exchange에 testnet 전달
**Files:**
- Modify: `src/exchange.py:9-14`
**Step 1: Client 생성자에 testnet 전달**
`src/exchange.py`에서 `BinanceFuturesClient.__init__`을 수정:
```python
class BinanceFuturesClient:
def __init__(self, config: Config):
self.config = config
self.client = Client(
api_key=config.api_key,
api_secret=config.api_secret,
testnet=config.testnet,
)
```
**Step 2: 테스트 실행**
Run: `pytest tests/test_exchange.py -v --tb=short -x`
Expected: PASS
**Step 3: Commit**
```bash
git add src/exchange.py
git commit -m "feat: pass testnet flag to Binance Client"
```
---
## Task 3: DataStream에 testnet 전달
**Files:**
- Modify: `src/data_stream.py:78-82,185-189`
**Step 1: KlineStream.start()에 testnet 파라미터 추가**
`src/data_stream.py``KlineStream.start()` (line 78)을 수정:
```python
async def start(self, api_key: str, api_secret: str, testnet: bool = False):
client = await AsyncClient.create(
api_key=api_key,
api_secret=api_secret,
testnet=testnet,
)
```
**Step 2: MultiSymbolStream.start()에 testnet 파라미터 추가**
`src/data_stream.py``MultiSymbolStream.start()` (line 185)을 수정:
```python
async def start(self, api_key: str, api_secret: str, testnet: bool = False):
client = await AsyncClient.create(
api_key=api_key,
api_secret=api_secret,
testnet=testnet,
)
```
**Step 3: 테스트 실행**
Run: `pytest tests/test_data_stream.py -v --tb=short -x`
Expected: PASS (testnet 기본값 False이므로 기존 동작 유지)
**Step 4: Commit**
```bash
git add src/data_stream.py
git commit -m "feat: pass testnet flag to AsyncClient in data streams"
```
---
## Task 4: UserDataStream에 testnet 전달
**Files:**
- Modify: `src/user_data_stream.py:28-33`
**Step 1: start()에 testnet 파라미터 추가**
```python
async def start(self, api_key: str, api_secret: str, testnet: bool = False) -> None:
"""User Data Stream 메인 루프 — 봇 종료 시까지 실행."""
client = await AsyncClient.create(
api_key=api_key,
api_secret=api_secret,
testnet=testnet,
)
```
**Step 2: 테스트 실행**
Run: `pytest tests/test_user_data_stream.py -v --tb=short -x`
Expected: PASS
**Step 3: Commit**
```bash
git add src/user_data_stream.py
git commit -m "feat: pass testnet flag to AsyncClient in user data stream"
```
---
## Task 5: Bot에서 testnet/interval 전달
**Files:**
- Modify: `src/bot.py:27-31,300-322`
**Step 1: MultiSymbolStream에 config.interval 전달**
`src/bot.py` line 27-31을 수정:
```python
self.stream = MultiSymbolStream(
symbols=[config.symbol, "BTCUSDT", "ETHUSDT"],
interval=config.interval,
on_candle=self._on_candle_closed,
)
```
**Step 2: run()에서 testnet 전달**
`src/bot.py` line 300-322의 `run()` 메서드에서 stream.start() 호출 시 testnet 전달:
```python
async def run(self):
logger.info(f"봇 시작: {self.config.symbol}, 레버리지 {self.config.leverage}x, 테스트넷={self.config.testnet}")
await self._recover_position()
balance = await self.exchange.get_balance()
self.risk.set_base_balance(balance)
logger.info(f"기준 잔고 설정: {balance:.2f} USDT (동적 증거금 비율 기준점)")
user_stream = UserDataStream(
symbol=self.config.symbol,
on_order_filled=self._on_position_closed,
)
await asyncio.gather(
self.stream.start(
api_key=self.config.api_key,
api_secret=self.config.api_secret,
testnet=self.config.testnet,
),
user_stream.start(
api_key=self.config.api_key,
api_secret=self.config.api_secret,
testnet=self.config.testnet,
),
self._position_monitor(),
)
```
**Step 3: 테스트 실행**
Run: `pytest tests/test_bot.py -v --tb=short -x`
Expected: PASS
**Step 4: Commit**
```bash
git add src/bot.py
git commit -m "feat: pass testnet and interval from config to streams"
```
---
## Task 6: .env 설정 및 전체 테스트
**Files:**
- Modify: `.env`
- Modify: `.env.example`
**Step 1: .env.example 업데이트**
`.env.example`에 새 변수 추가:
```
BINANCE_API_KEY=
BINANCE_API_SECRET=
BINANCE_TESTNET=false
BINANCE_TESTNET_API_KEY=
BINANCE_TESTNET_API_SECRET=
SYMBOL=XRPUSDT
LEVERAGE=10
INTERVAL=15m
RISK_PER_TRADE=0.02
DISCORD_WEBHOOK_URL=
ML_THRESHOLD=0.55
```
**Step 2: 워크트리의 .env에 테스트넷 설정**
`.env` 파일에 테스트넷 키와 설정 적용:
```
BINANCE_TESTNET=true
BINANCE_TESTNET_API_KEY=<사용자의_테스트넷_키>
BINANCE_TESTNET_API_SECRET=<사용자의_테스트넷_시크릿>
SYMBOL=XRPUSDT
LEVERAGE=125
INTERVAL=1m
ML_THRESHOLD=0.55
```
**Step 3: 전체 테스트 실행**
Run: `bash scripts/run_tests.sh`
Expected: 모든 테스트 PASS
**Step 4: Commit**
```bash
git add .env.example
git commit -m "feat: add testnet and interval env vars to .env.example"
```
---
## Task 7: 학습 파이프라인 — LOOKAHEAD 조정 및 1분봉 데이터 수집
**Files:**
- Modify: `src/dataset_builder.py:14` (LOOKAHEAD 변경)
- Modify: `scripts/train_model.py:56` (LOOKAHEAD 변경)
- Modify: `scripts/train_and_deploy.sh:32-50` (1분봉 데이터 경로)
**Step 1: dataset_builder.py LOOKAHEAD 변경**
`src/dataset_builder.py` line 14:
```python
# 변경 전:
LOOKAHEAD = 24 # 15분봉 × 24 = 6시간 뷰
# 변경 후:
LOOKAHEAD = 60 # 1분봉 × 60 = 1시간 뷰
```
**Step 2: train_model.py LOOKAHEAD 변경**
`scripts/train_model.py` line 56:
```python
# 변경 전:
LOOKAHEAD = 24 # 15분봉 × 24 = 6시간 (dataset_builder.py와 동기화)
# 변경 후:
LOOKAHEAD = 60 # 1분봉 × 60 = 1시간 (dataset_builder.py와 동기화)
```
**Step 3: train_and_deploy.sh 수정 — 1분봉 파이프라인**
`scripts/train_and_deploy.sh`의 데이터 경로와 수집 파라미터를 1분봉으로 변경:
```bash
# line 32: 파일명 변경
PARQUET_FILE="data/combined_1m.parquet"
# line 46-50: --interval 1m으로 변경
python scripts/fetch_history.py \
--symbols XRPUSDT BTCUSDT ETHUSDT \
--interval 1m \
--days "$FETCH_DAYS" \
$UPSERT_FLAG \
--output "$PARQUET_FILE"
# line 57, 60: --data 경로 변경
python scripts/train_mlx_model.py --data data/combined_1m.parquet --decay "$DECAY"
# ...
python scripts/train_model.py --data data/combined_1m.parquet --decay "$DECAY"
# walk-forward 섹션도 동일하게 --data data/combined_1m.parquet로 변경
```
**Step 4: Commit**
```bash
git add src/dataset_builder.py scripts/train_model.py scripts/train_and_deploy.sh
git commit -m "feat: adjust LOOKAHEAD to 60 for 1m candles, update training pipeline"
```
---
## Task 8: 1분봉 데이터 수집
**Step 1: 1분봉 데이터 수집 (30일)**
Run:
```bash
python scripts/fetch_history.py \
--symbols XRPUSDT BTCUSDT ETHUSDT \
--interval 1m \
--days 30 \
--no-oi \
--no-upsert \
--output data/combined_1m.parquet
```
Expected: `data/combined_1m.parquet` 생성 (약 43,000행 × 15컬럼)
> Note: 테스트넷 학습용이므로 OI/펀딩비는 건너뜀 (--no-oi). 1분봉 30일 데이터면 약 43,200개 캔들.
---
## Task 9: ML 모델 학습
**Step 1: LightGBM 모델 학습**
Run:
```bash
python scripts/train_model.py --data data/combined_1m.parquet --decay 2.0
```
Expected: `models/lgbm_filter.pkl` 생성, AUC 출력
**Step 2: 학습 결과 확인**
학습 결과의 AUC가 0.50 이상인지 확인. 모델이 생성되었는지 확인:
Run: `ls -la models/lgbm_filter.pkl`
Expected: 파일 존재
---
## Task 10: 테스트넷 봇 실행
**Step 1: 최종 확인**
`.env`에 테스트넷 설정이 올바른지 확인:
- `BINANCE_TESTNET=true`
- `BINANCE_TESTNET_API_KEY` 설정됨
- `BINANCE_TESTNET_API_SECRET` 설정됨
- `LEVERAGE=125`
- `INTERVAL=1m`
**Step 2: 봇 실행**
Run: `python main.py`
Expected output:
```
봇 시작: XRPUSDT, 레버리지 125x, 테스트넷=True
```
봇이 정상 시작되면 1분봉 캔들을 수신하고 ML 필터를 통해 거래 신호를 처리한다.