fix: critical production issues — WebSocket reconnect, ghost positions, ONNX NaN
- fix(data_stream): add reconnect loop to MultiSymbolStream matching UserDataStream pattern Prevents bot-wide crash on WebSocket disconnect (#3 Critical) - fix(data_stream): increase buffer_size 200→300 and preload 200→300 Ensures z-score window (288) has sufficient data (#5 Important) - fix(bot): sync risk manager when Binance has no position but local state does Prevents ghost entries in open_positions blocking future trades (#1 Critical) - fix(ml_filter): add np.nan_to_num for ONNX input to handle NaN features Prevents all signals being blocked during initial ~2h warmup (#2 Critical) - fix(bot): replace _close_handled_by_sync with current_trade_side==None guard Eliminates race window in SYNC PnL double recording (#4 Important) - feat(bot): add _ensure_sl_tp_orders in _recover_position Detects and re-places missing SL/TP orders on bot restart (#6 Important) - feat(exchange): add get_open_orders method for SL/TP verification Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,10 @@ from loguru import logger
|
||||
_MIN_CANDLES_FOR_SIGNAL = 100
|
||||
|
||||
# 초기 구동 시 REST API로 가져올 과거 캔들 수.
|
||||
# 15분봉 200개 = 50시간치 — EMA50(12.5h) 대비 4배 여유.
|
||||
_PRELOAD_LIMIT = 200
|
||||
# z-score 윈도우(288) + EMA50(50) 안정화 여유분. 15분봉 300개 = 75시간.
|
||||
_PRELOAD_LIMIT = 300
|
||||
|
||||
_RECONNECT_DELAY = 5 # WebSocket 재연결 대기 초
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +107,7 @@ class MultiSymbolStream:
|
||||
self,
|
||||
symbols: list[str],
|
||||
interval: str = "15m",
|
||||
buffer_size: int = 200,
|
||||
buffer_size: int = 300,
|
||||
on_candle: Callable = None,
|
||||
):
|
||||
self.symbols = [s.lower() for s in symbols]
|
||||
@@ -199,9 +201,34 @@ class MultiSymbolStream:
|
||||
]
|
||||
logger.info(f"Combined WebSocket 시작: {streams}")
|
||||
try:
|
||||
async with bm.futures_multiplex_socket(streams) as stream:
|
||||
while True:
|
||||
msg = await stream.recv()
|
||||
await self.handle_message(msg)
|
||||
await self._run_loop(bm, streams)
|
||||
finally:
|
||||
await client.close_connection()
|
||||
|
||||
async def _run_loop(self, bm: BinanceSocketManager, streams: list[str]) -> None:
|
||||
"""WebSocket 연결 → 재연결 무한 루프."""
|
||||
while True:
|
||||
try:
|
||||
async with bm.futures_multiplex_socket(streams) as stream:
|
||||
logger.info("Kline WebSocket 연결 완료")
|
||||
while True:
|
||||
msg = await stream.recv()
|
||||
|
||||
if isinstance(msg, dict) and msg.get("e") == "error":
|
||||
logger.warning(
|
||||
f"Kline WebSocket 에러 수신: {msg.get('m', msg)} — 재연결"
|
||||
)
|
||||
break
|
||||
|
||||
await self.handle_message(msg)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
logger.info("Kline WebSocket 정상 종료")
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
f"Kline WebSocket 끊김: {e} — "
|
||||
f"{_RECONNECT_DELAY}초 후 재연결"
|
||||
)
|
||||
await asyncio.sleep(_RECONNECT_DELAY)
|
||||
|
||||
Reference in New Issue
Block a user