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 <noreply@anthropic.com>
This commit is contained in:
21in7
2026-03-14 12:41:23 +09:00
parent de27f85e6d
commit 363234ac7c

View File

@@ -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