TITLE: best_5_buy_data and best_5_sell_data fields are swapped in SNAP_QUOTE (Mode 3) parsing
BODY:
Bug Description
In smartWebSocketV2.py, the _parse_binary_data method incorrectly swaps
the best_5_buy_data and best_5_sell_data keys after parsing them. The
internal parsing function correctly identifies buy vs sell levels using the
flag field (0 = buy, 1 = sell), but the final assignment swaps them:
best_5_buy_and_sell_data = self._parse_best_5_buy_and_sell_data(binary_data[147:347])
parsed_data["best_5_buy_data"] = best_5_buy_and_sell_data["best_5_sell_data"] # ← swapped
parsed_data["best_5_sell_data"] = best_5_buy_and_sell_data["best_5_buy_data"] # ← swapped
This means any application consuming parsed_data["best_5_buy_data"]
directly is actually receiving ASK-side (sell) levels, and
parsed_data["best_5_sell_data"] is actually receiving BID-side (buy) levels.
Why this is confirmed (not a misunderstanding of field naming)
- The internal
_parse_best_5_buy_and_sell_data method correctly buckets
levels by the flag field: flag == 0 appends to best_5_buy_data,
flag == 1 appends to best_5_sell_data. This bucketing is correct
per the SNAP_QUOTE packet spec.
- Immediately after this correct bucketing, the caller assigns the
result keys in reverse — parsed_data["best_5_buy_data"] is set from
the function's best_5_sell_data output, and vice versa.
- We confirmed this in production by comparing parsed values against the
actual order book for liquid NFO futures contracts during market hours:
prices coming out under the best_5_buy_data key were consistently
HIGHER than prices under best_5_sell_data — which is only possible if
the labels are inverted, since asks must be >= bids in a valid book.
Impact
Any consumer of this library that uses best_5_buy_data/best_5_sell_data
directly for spread calculation, order book imbalance, liquidity checks, or
order flow imbalance (OFI) computation will get inverted results — e.g. an
imbalance signal that should indicate buying pressure will read as selling
pressure, and vice versa. This is a silent correctness bug, not a crash,
so it's easy to miss without explicitly cross-checking price ordering.
Suggested Fix
Remove the swap in _parse_binary_data — assign directly without crossing
the keys:
parsed_data["best_5_buy_data"] = best_5_buy_and_sell_data["best_5_buy_data"]
parsed_data["best_5_sell_data"] = best_5_buy_and_sell_data["best_5_sell_data"]
Happy to provide a sample tick payload (with account-identifying details
redacted) demonstrating the price-ordering inversion if useful for
verification.
Note on backward compatibility
Since this bug may have existed for a while, some downstream applications
may have already written their own compensation logic (reading
best_5_sell_data as the true buy side and vice versa). Fixing this in
the library would silently break that compensation for existing users.
Recommend either:
(a) fixing it with a clear changelog/version bump and migration note, or
(b) adding a new correctly-labeled field set (e.g. bid_5_data/ask_5_data)
alongside the existing (buggy) ones, deprecating the old keys gradually.
TITLE: best_5_buy_data and best_5_sell_data fields are swapped in SNAP_QUOTE (Mode 3) parsing
BODY:
Bug Description
In
smartWebSocketV2.py, the_parse_binary_datamethod incorrectly swapsthe
best_5_buy_dataandbest_5_sell_datakeys after parsing them. Theinternal parsing function correctly identifies buy vs sell levels using the
flagfield (0 = buy, 1 = sell), but the final assignment swaps them:This means any application consuming
parsed_data["best_5_buy_data"]directly is actually receiving ASK-side (sell) levels, and
parsed_data["best_5_sell_data"]is actually receiving BID-side (buy) levels.Why this is confirmed (not a misunderstanding of field naming)
_parse_best_5_buy_and_sell_datamethod correctly bucketslevels by the
flagfield:flag == 0appends tobest_5_buy_data,flag == 1appends tobest_5_sell_data. This bucketing is correctper the SNAP_QUOTE packet spec.
result keys in reverse —
parsed_data["best_5_buy_data"]is set fromthe function's
best_5_sell_dataoutput, and vice versa.actual order book for liquid NFO futures contracts during market hours:
prices coming out under the
best_5_buy_datakey were consistentlyHIGHER than prices under
best_5_sell_data— which is only possible ifthe labels are inverted, since asks must be >= bids in a valid book.
Impact
Any consumer of this library that uses
best_5_buy_data/best_5_sell_datadirectly for spread calculation, order book imbalance, liquidity checks, or
order flow imbalance (OFI) computation will get inverted results — e.g. an
imbalance signal that should indicate buying pressure will read as selling
pressure, and vice versa. This is a silent correctness bug, not a crash,
so it's easy to miss without explicitly cross-checking price ordering.
Suggested Fix
Remove the swap in
_parse_binary_data— assign directly without crossingthe keys:
Happy to provide a sample tick payload (with account-identifying details
redacted) demonstrating the price-ordering inversion if useful for
verification.
Note on backward compatibility
Since this bug may have existed for a while, some downstream applications
may have already written their own compensation logic (reading
best_5_sell_dataas the true buy side and vice versa). Fixing this inthe library would silently break that compensation for existing users.
Recommend either:
(a) fixing it with a clear changelog/version bump and migration note, or
(b) adding a new correctly-labeled field set (e.g.
bid_5_data/ask_5_data)alongside the existing (buggy) ones, deprecating the old keys gradually.