From 363234ac7c1e1f1d9c01d55d1dd42da31d2373b6 Mon Sep 17 00:00:00 2001 From: 21in7 Date: Sat, 14 Mar 2026 12:41:23 +0900 Subject: [PATCH] fix: add fallback position sync check to detect missed WebSocket closes The position monitor now checks Binance API every 5 minutes to verify the bot's internal state matches the actual position. If the bot thinks a position is open but Binance shows none, it syncs state and logs a SYNC close event. This prevents the bot from being stuck in a phantom position when User Data Stream misses a TP/SL fill event. Co-Authored-By: Claude Opus 4.6 --- src/bot.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/bot.py b/src/bot.py index 55d95f1..80638d7 100644 --- a/src/bot.py +++ b/src/bot.py @@ -306,9 +306,42 @@ class TradingBot: _MONITOR_INTERVAL = 300 # 5분 async def _position_monitor(self): - """포지션 보유 중일 때 5분마다 현재가·미실현 PnL을 로깅한다.""" + """포지션 보유 중일 때 5분마다 현재가·미실현 PnL을 로깅한다. + 또한 Binance API를 조회하여 WebSocket 이벤트 누락 시 청산을 감지한다.""" while True: await asyncio.sleep(self._MONITOR_INTERVAL) + + # ── 폴백: Binance API로 실제 포지션 상태 확인 ── + if self.current_trade_side is not None and not self._is_reentering: + try: + actual_pos = await self.exchange.get_position() + if actual_pos is None: + logger.warning( + f"[{self.symbol}] 포지션 불일치 감지: " + f"봇={self.current_trade_side}, 바이낸스=포지션 없음 — 상태 동기화" + ) + estimated_pnl = 0.0 + await self.risk.close_position(self.symbol, estimated_pnl) + self.notifier.notify_close( + symbol=self.symbol, + side=self.current_trade_side, + close_reason="SYNC", + exit_price=0.0, + estimated_pnl=0.0, + net_pnl=0.0, + diff=0.0, + ) + logger.info( + f"[{self.symbol}] 청산 감지(SYNC): exit=0.0000, " + f"rp=+0.0000, commission=0.0000, net_pnl=+0.0000" + ) + self.current_trade_side = None + self._entry_price = None + self._entry_quantity = None + continue + except Exception as e: + logger.debug(f"[{self.symbol}] 포지션 동기화 확인 실패 (무시): {e}") + if self.current_trade_side is None: continue price = self.stream.latest_price