nautechsystems/nautilus_trader · error · anyhow::Error

cannot subscribe L1_MBP and L2_MBP for the same Binance Spot

Error message

cannot subscribe L1_MBP and L2_MBP for the same Binance Spot instrument

What it means

The Spot adapter keeps L1 (bookTicker) and L2 (depth stream) book subscriptions in separate maps per instrument; subscribing top-of-book while an L2 book subscription already exists for the same instrument would produce conflicting/mixed book semantics, so the L1 request is rejected.

Source

Thrown at crates/adapters/binance/src/spot/data.rs:1820

    }

    fn subscribe_instruments(&mut self, _cmd: SubscribeInstruments) -> anyhow::Result<()> {
        log::debug!("subscribe_instruments: Binance instruments are fetched via HTTP on connect");
        Ok(())
    }

    fn subscribe_instrument(&mut self, _cmd: SubscribeInstrument) -> anyhow::Result<()> {
        log::debug!("subscribe_instrument: Binance instruments are fetched via HTTP on connect");
        Ok(())
    }

    fn subscribe_book_deltas(&mut self, cmd: SubscribeBookDeltas) -> anyhow::Result<()> {
        if cmd.book_type == BookType::L1_MBP {
            anyhow::ensure!(
                cmd.depth.is_none_or(|depth| depth.get() == 1),
                "Binance Spot L1_MBP supports depth 1 only"
            );
            anyhow::ensure!(
                !self.book_subscriptions.contains_key(&cmd.instrument_id),
                "cannot subscribe L1_MBP and L2_MBP for the same Binance Spot instrument"
            );
            self.l1_book_subscriptions.rcu(|subscriptions| {
                *subscriptions.entry(cmd.instrument_id).or_insert(0) += 1;
            });
            self.subscribe_top_of_book(cmd.instrument_id);
            return Ok(());
        }

        if cmd.book_type != BookType::L2_MBP {
            anyhow::bail!("Binance Spot supports L1_MBP and L2_MBP order book subscriptions");
        }
        anyhow::ensure!(
            !self.l1_book_subscriptions.contains_key(&cmd.instrument_id),
            "cannot subscribe L1_MBP and L2_MBP for the same Binance Spot instrument"
        );

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Unsubscribe the existing L2 book-deltas subscription for that instrument before adding the L1 subscription
  2. Standardize on one book type per instrument across all strategies; if both views are needed, derive the top of book from the L2 book instead of subscribing L1

Example fix

# before
client.subscribe_book_deltas(instrument_id, BookType.L2_MBP, depth=None)
client.subscribe_book_deltas(instrument_id, BookType.L1_MBP)  # errors: conflict

# after
client.unsubscribe_book_deltas(instrument_id)  # drop L2 first
client.subscribe_book_deltas(instrument_id, BookType.L1_MBP)
Defensive patterns

Strategy: validation

Validate before calling

# Track which book type is subscribed per instrument before issuing requests
active: dict[InstrumentId, BookType] = {}

def can_subscribe_l1(instrument_id) -> bool:
    return active.get(instrument_id) is not BookType.L2_MBP

assert can_subscribe_l1(instrument_id), "L2 already subscribed for this instrument"

Prevention

When it happens

Trigger: subscribe_book_deltas with L1_MBP for an instrument already present in book_subscriptions (an active L2 book subscription).

Common situations: One component of a stack subscribes L2 depth while another (e.g. a quoting or spread module) subscribes L1 for the same symbol; dynamic reconfiguration at runtime without unsubscribing first.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/93635d61befbbc42. Report an issue: GitHub.