Audits › Controls › Websocket and data reconnection
Connection and operation · control 7 of 21
Websocket and data reconnection: does your trading bot have it?
Does it reconnect on its own when the websocket or API drops, with bounded retries and without duplicating subscriptions? Does it back off on rate limits (HTTP 429)?
Why it matters
Frozen data means decisions on stale prices. Duplicated subscriptions mean duplicated signals.
How to check it in the code
- Search for reconnect, backoff, heartbeat, ping, on_close, 429.
- Check that retries are bounded and that a stale feed stops trading rather than trading on the last price.
- Check that resubscribing after a drop does not register handlers twice.
This is what the audit does for every bot in the list, on public code, without installing or running anything. Verdict values: present, partial, absent, not applicable, not verified. See the method.
Results across audited bots
Across 10 audited bots: 3 present, 3 partial, 0 absent, 0 not applicable, 4 not verified.
| Bot | Verdict | Evidence | Note |
|---|---|---|---|
| HKUDS/Vibe-Trading | Not verified | agent/src/market_data.py implements a multi-source REST fallback chain with bounded retries (max_fallback_attempts=5) for market data | No dedicated websocket module with explicit HTTP 429 backoff handling was located within budget; architecture may be pull/REST-based rather than streaming. |
| hummingbot/hummingbot | Partial | hummingbot/core/api_throttler/async_throttler.py (AsyncRequestContext.within_capacity, rate-limit waiting); hummingbot/core/web_assistant/connections/ws_connection.py (detects closed socket, raises ConnectionError, handles ping/pong) | HTTP rate-limit handling (429-style) exists at the throttler layer. The base WS connection class detects disconnects but does not itself reconnect; the retry/backoff loop is delegated to each connector's own data source, which was not individually audited (out of budget). |
| Drakkar-Software/OctoBot | Not verified | packages/trading/octobot_trading/exchanges/abstract_websocket_exchange.py (8521 bytes) exists as a dedicated websocket abstraction, confirmed via directory listing, but its reconnect/backoff/429-handling logic was not read due to time/budget. | Presence of a dedicated abstraction is a positive signal but not evidence of correct reconnect behavior. |
| freqtrade/freqtrade | Present | freqtrade/exchange/common.py (retrier / retrier_async decorators, API_RETRY_COUNT=4, calculate_backoff); freqtrade/freqtradebot.py (daily self._schedule.every().day.at('00:02').do(self.exchange.ws_connection_reset)) -- both at commit 3774521e7028fa666770e7c7a8da323af5c4cefa | Bounded retries (4 attempts) with exponential backoff on TemporaryError/DDosProtection for both REST and websocket (ccxt.pro) calls, plus a scheduled daily websocket reset. |
| jesse-ai/jesse | Not verified | jesse/config.py only has a logging flag 'env.logging.exchange_ws_reconnection': True (a log toggle, not an implementation). The websocket clients themselves are constructed in jesse/services/api.py via 'jh.get_config(f"app.live_drivers.{name}")', i.e. classes supplied by the closed-source jesse_live plugin. | Reconnection/backoff/429 handling logic is not present in this repository. |
| chrisleekr/binance-trading-bot | Present | apps/worker/src/boot/builders/market-data.ts | Single combined-stream WebSocket + REST fallback (fetchClosedKlines), with a Redis-backed per-IP weight governor (budget 6000/min = Binance's real spot REST ceiling, 80% utilisation headroom) that reserves the top 8 weight units for order placement/cancellation ahead of bulk-read crons, so a protective SELL is never rate-limited behind a discovery/technicals scan; the governor fails open for orders and closed for bulk reads if Redis is down. A "reconnect resync" is referenced as late-bound in the fleet builder but that specific file was not opened within budget. |
| alsk1992/CloddsBot | Not verified | ExecutionService interface exposes connectFillsWebSocket/disconnectFillsWebSocket/isFillsWebSocketConnected (referenced in src/trading/orchestrator.ts pass-through list), implying a fills websocket exists, but the reconnect/backoff and HTTP 429 handling logic was not located within this session's time and API-search budget. | Not found, not ruled out; needs a follow-up pass on src/execution/index.ts or the exchange adapter files. |
| Lumiwealth/lumibot | Partial | docs/FAST_ORDER_LIFECYCLE_GUIDE.md, section 'Schwab Profile': 'uses account-activity WebSocket messages as a wake-up signal... performs an active-order reconciliation after login or reconnect and retains a 30-second broad order-history poll only as a healing fallback' and 'Schwab 429 responses suppress more reads... for the server's Retry-After interval, or bounded exponential backoff with jitter when that header is absent.' | |
| YizhiSong/FriesTrader | Partial | No websocket exists in this architecture at all -- it is synchronous MCP tool calls inside two scheduled batch sessions (README 'How it works'), so continuous-connection reconnection does not apply. PHASE_B_TASK.md Step 6's only documented failure rule is generic: 'if a tool call fails, log the failure and skip that candidate.' | No explicit HTTP 429 / rate-limit backoff logic found; cadence.news_search_budget_per_cycle=30 self-limits call volume but is not a retry mechanism. Remainder of PHASE_B_TASK.md (chars 20000-35765) not read in this pass -- may contain more detail, marked no_verificado in spirit. |
| c9s/bbgo | Present | pkg/exchange/binance/stream.go (OnListenKeyExpired -> stream.Reconnect(), OnServerShutdownEvent -> stream.Reconnect(), handleDisconnect resets depth buffers) | Reconnection on listen-key/token expiry and server-initiated disconnects is implemented via a shared StandardStream abstraction; explicit HTTP 429 backoff handling was not directly located in the time available. |
If your bot does not have it
The watchdog enforces daily loss, drawdown, position size and heartbeat limits from outside the bot, with a read-only key, whatever the bot's own code does.