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

11 KiB
Raw Blame History

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이면 테스트넷 키를 사용하도록 변경:

@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

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__을 수정:

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

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.pyKlineStream.start() (line 78)을 수정:

    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.pyMultiSymbolStream.start() (line 185)을 수정:

    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

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 파라미터 추가

    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

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을 수정:

        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 전달:

    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

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

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:

# 변경 전:
LOOKAHEAD    = 24   # 15분봉 × 24 = 6시간 뷰

# 변경 후:
LOOKAHEAD    = 60   # 1분봉 × 60 = 1시간 뷰

Step 2: train_model.py LOOKAHEAD 변경

scripts/train_model.py line 56:

# 변경 전:
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분봉으로 변경:

# 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

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:

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:

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 필터를 통해 거래 신호를 처리한다.