From 684c8a32b983b5406b0a99f3f64cbd449704b9b7 Mon Sep 17 00:00:00 2001 From: 21in7 Date: Mon, 2 Mar 2026 02:03:50 +0900 Subject: [PATCH] feat: add Algo Order API support and update ML feature handling - Introduced support for Algo Order API, allowing automatic sending of STOP_MARKET and TAKE_PROFIT_MARKET orders. - Updated README.md to include new features related to Algo Order API and real-time handling of ML features. - Enhanced ML feature processing to fill missing OI and funding rate values with zeros for consistency in training data. - Added new training log entries for the lgbm model with updated metrics. --- README.md | 3 ++- models/training_log.json | 12 ++++++++++++ src/exchange.py | 40 ++++++++++++++++++++++++++++++++++++++++ src/ml_features.py | 4 ++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e40994d..7a2ac38 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ Binance Futures 자동매매 봇. 복합 기술 지표와 ML 필터(LightGBM / M - **ML 필터 (ONNX 우선 / LightGBM 폴백)**: 기술 지표 신호를 한 번 더 검증하여 오진입 차단. 우선순위: ONNX > LightGBM > 폴백(항상 허용) - **모델 핫리로드**: 캔들마다 모델 파일 mtime을 감지해 변경 시 자동 리로드 (봇 재시작 불필요) - **멀티심볼 스트림**: XRP/BTC/ETH 3개 심볼을 단일 Combined WebSocket으로 수신, BTC·ETH 상관관계 피처 활용 -- **23개 ML 피처**: XRP 기술 지표 13개 + BTC/ETH 수익률·상대강도 8개 + OI 변화율·펀딩비 2개 +- **23개 ML 피처**: XRP 기술 지표 13개 + BTC/ETH 수익률·상대강도 8개 + OI 변화율·펀딩비 2개 (실시간 미수집 항목은 0으로 채움) - **ATR 기반 손절/익절**: 변동성에 따라 동적으로 SL/TP 계산 (1.5× / 3.0× ATR) +- **Algo Order API 지원**: 계정 설정에 따라 STOP_MARKET/TAKE_PROFIT_MARKET 주문을 `/fapi/v1/algoOrder` 엔드포인트로 자동 전송 (오류 코드 -4120 대응) - **동적 증거금 비율**: 잔고 증가에 따라 선형 감소 (최대 50% → 최소 20%) - **반대 시그널 재진입**: 보유 포지션과 반대 신호 발생 시 즉시 청산 후 ML 필터 통과 시 반대 방향 재진입 - **리스크 관리**: 트레이드당 리스크 비율, 최대 포지션 수, 일일 손실 한도(5%) 제어 diff --git a/models/training_log.json b/models/training_log.json index 8d55ddc..c63698b 100644 --- a/models/training_log.json +++ b/models/training_log.json @@ -264,5 +264,17 @@ "features": 23, "time_weight_decay": 2.0, "model_path": "models/lgbm_filter.pkl" + }, + { + "date": "2026-03-02T02:00:45.931227", + "backend": "lgbm", + "auc": 0.5752, + "best_threshold": 0.6307, + "best_precision": 0.471, + "best_recall": 0.229, + "samples": 533, + "features": 23, + "time_weight_decay": 2.0, + "model_path": "models/lgbm_filter.pkl" } ] \ No newline at end of file diff --git a/src/exchange.py b/src/exchange.py index 888d25a..b0ba178 100644 --- a/src/exchange.py +++ b/src/exchange.py @@ -45,6 +45,8 @@ class BinanceFuturesClient: return float(b["balance"]) return 0.0 + _ALGO_ORDER_TYPES = {"STOP_MARKET", "TAKE_PROFIT_MARKET", "STOP", "TAKE_PROFIT", "TRAILING_STOP_MARKET"} + async def place_order( self, side: str, @@ -55,6 +57,16 @@ class BinanceFuturesClient: reduce_only: bool = False, ) -> dict: loop = asyncio.get_event_loop() + + if order_type in self._ALGO_ORDER_TYPES: + return await self._place_algo_order( + side=side, + quantity=quantity, + order_type=order_type, + stop_price=stop_price, + reduce_only=reduce_only, + ) + params = dict( symbol=self.config.symbol, side=side, @@ -75,6 +87,34 @@ class BinanceFuturesClient: logger.error(f"주문 실패: {e}") raise + async def _place_algo_order( + self, + side: str, + quantity: float, + order_type: str, + stop_price: float = None, + reduce_only: bool = False, + ) -> dict: + """STOP_MARKET / TAKE_PROFIT_MARKET 등 Algo Order API(/fapi/v1/algoOrder)로 전송.""" + loop = asyncio.get_event_loop() + params = dict( + symbol=self.config.symbol, + side=side, + algoType="CONDITIONAL", + type=order_type, + quantity=quantity, + reduceOnly="true" if reduce_only else "false", + ) + if stop_price: + params["triggerPrice"] = stop_price + try: + return await loop.run_in_executor( + None, lambda: self.client.futures_create_algo_order(**params) + ) + except BinanceAPIException as e: + logger.error(f"Algo 주문 실패: {e}") + raise + async def get_position(self) -> dict | None: loop = asyncio.get_event_loop() positions = await loop.run_in_executor( diff --git a/src/ml_features.py b/src/ml_features.py index 125b45a..86ba00a 100644 --- a/src/ml_features.py +++ b/src/ml_features.py @@ -127,4 +127,8 @@ def build_features( "xrp_eth_rs": float(_calc_rs(ret_1, eth_ret_1)), }) + # 실시간에서는 OI/펀딩비를 수집하지 않으므로 0으로 채워 학습 피처(23개)와 일치시킨다 + base.setdefault("oi_change", 0.0) + base.setdefault("funding_rate", 0.0) + return pd.Series(base)