Skip to content

Allow order book subscriptions to share a websocket connection#25

Open
Da-Teach wants to merge 1 commit into
JKorf:mainfrom
Da-Teach:fix/orderbook-connection-pooling
Open

Allow order book subscriptions to share a websocket connection#25
Da-Teach wants to merge 1 commit into
JKorf:mainfrom
Da-Teach:fix/orderbook-connection-pooling

Conversation

@Da-Teach

Copy link
Copy Markdown

Problem

WhiteBitSocketClientV4Api sets AllowTopicsOnTheSameConnection = false. Every order book subscription uses the same topic ("OrderBook"), so CryptoExchange.Net's connection-reuse rule (AllowTopicsOnTheSameConnection || !connection.Topics.Contains(topic)) excludes any connection that already holds a book subscription. As a result SocketSubscriptionsCombineTarget is effectively ignored for order books and the client opens one websocket per symbol.

Subscribing a large universe (hundreds of pairs) then sprawls to ~one connection per symbol, which exhausts the venue's per-IP connection limit — some subscriptions then silently never receive data.

Fix

Set AllowTopicsOnTheSameConnection = true. WhiteBit supports multiple subscriptions per connection (depth_subscribe is sent with multiple_subscriptions = true), so order book subscriptions can safely share a connection.

Verification

Subscribing 785 spot symbols (combine target 500, depth 20) drops from 777 sockets to ~40 once enabled, with no loss of data.

@JKorf

JKorf commented Jun 26, 2026

Copy link
Copy Markdown
Owner

Hi, the current situation is nog ideal, but setting AllowTopicsOnTheSameConnection to true will break the logic.
When making 2 seperate calls to subscribe a topic for 2 different markets the second call will override the first one, and the first one will no longer send data.

See https://docs.whitebit.com/websocket/market-streams/overview#managing-subscriptions.

To fix this there would need to be some kind of logic which keeps track what symbols are currently subscribed to a topic so a new subscribe/unsubscribe can send that full list again.

WhiteBitSocketClientV4Api set AllowTopicsOnTheSameConnection = false, forcing every
order book subscription (all share Topic "OrderBook") onto its own websocket. For a
large universe that sprawls to ~one socket per symbol and exhausts WhiteBit's per-IP
connection allowance, leaving books empty.

Separate per-symbol depth_subscribe calls sent with multiple_subscriptions = true
(which this client always does) coexist on a connection rather than replacing each
other — verified against the live API: 200 markets streaming on one socket, and
per-market depth_unsubscribe. Setting the flag true lets SocketSubscriptionsCombineTarget
pool them.

WhiteBit rate-limits depth_subscribe to ~200 requests per connection per burst, so the
default combine target is set to 150 to stay safely under that when a reconnect
re-subscribes a connection's whole set in one burst.
@Da-Teach Da-Teach force-pushed the fix/orderbook-connection-pooling branch from 890a7f8 to 913445f Compare June 27, 2026 14:12
@Da-Teach

Copy link
Copy Markdown
Author

Thanks for taking a look! I went and tested this against the live API, and I don't think the override you're describing happens for the subscriptions this client makes — but I'd genuinely like your read on whether I'm missing a case.

The key detail is the 4th parameter of depth_subscribe. WhiteBitBookSubscription.GetSubQuery already builds the request as [symbol, depth, "0", true] — that trailing true is multiple_subscriptions. With it set, separate per-market depth_subscribe calls coexist on the connection rather than replacing each other. The "a new subscription replaces the previous one" behaviour in the docs is the multiple_subscriptions = false path, which this client never takes.

Verified directly against wss://api.whitebit.com/ws:

  • BTC_USDT, ETH_USDT, LTC_USDT subscribed separately on one connection → all three keep streaming.
  • 200 markets subscribed separately on one connection → all 200 stream, 0 errors.
  • depth_unsubscribe [BTC_USDT] on that connection removes only BTC_USDT; ETH_USDT/LTC_USDT keep streaming.

So the per-symbol GetSubQuery/GetUnsubQuery already in place stay correct under pooling, and I don't think the "track the currently-subscribed symbols and resend the full list" logic is needed.

One real constraint I did hit: WhiteBit rate-limits depth_subscribe to ~200 per connection per burst ({"code": 7, "too many requests"} past that). Since a reconnect re-subscribes a connection's whole set in one burst, the combine target has to stay under that — so I've set the default SocketSubscriptionsCombineTarget to 150 here for headroom. Happy to dial that down if you'd rather ship a more conservative default.

I've updated this PR accordingly: the flag flip, the 150 default, and a comment documenting the above. Does that line up with your understanding, or is there a scenario where the override still kicks in that I haven't tested?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants