Skip to content

ield mein: best_5_buy_data and best_5_sell_data fields are swapped in SNAP_QUOTE (Mode 3) parsing #71

@myaifairtrading-cyber

Description

@myaifairtrading-cyber

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)

  1. 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.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions