nautechsystems/nautilus_trader · error

cannot subscribe L1_MBP and L2_MBP for the same Binance Futu

Error message

cannot subscribe L1_MBP and L2_MBP for the same Binance Futures instrument

What it means

The adapter tracks L2_MBP subscriptions in `book_subscriptions` and L1_MBP refcounts in `l1_book_subscriptions`, and refuses to mix book types per instrument. This instance fires when an L1_MBP subscribe arrives while `book_subscriptions` already contains the same instrument_id (an active L2 subscription). The ambiguous state is rejected before any stream is opened.

Source

Thrown at crates/adapters/binance/src/futures/data.rs:1919

            "subscribe_instruments: Binance Futures instruments are fetched via HTTP on connect"
        );
        Ok(())
    }

    fn subscribe_instrument(&mut self, _cmd: SubscribeInstrument) -> anyhow::Result<()> {
        log::debug!(
            "subscribe_instrument: Binance Futures 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 Futures 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 Futures 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 Futures 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 Futures instrument"
        );

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Unsubscribe the existing L2_MBP book deltas for the instrument before subscribing L1_MBP
  2. Or keep the existing L2 subscription — depth-1 like behaviour can be derived from an L2 book
  3. Centralise book subscriptions per instrument in one component so conflicting types cannot race

Example fix

# before: L2 already active for instrument_id
actor.subscribe_order_book_deltas(instrument_id, book_type=BookType.L1_MBP, depth=None)  # fails

# after: unsubscribe L2 first, then subscribe L1
actor.unsubscribe_order_book_deltas(instrument_id)
actor.subscribe_order_book_deltas(instrument_id, book_type=BookType.L1_MBP, depth=None)
Defensive patterns

Strategy: validation

Validate before calling

active_book_types: dict = {}  # instrument_id -> 'L1' | 'L2', track what you subscribed

def subscribe_book_deltas_checked(actor, instrument_id, book_type, depth=None):
    current = active_book_types.get(instrument_id)
    if current and current != ('L1' if book_type == BookType.L1_MBP else 'L2'):
        raise ValueError(
            f'{instrument_id} already has an {current} subscription; unsubscribe it before switching book type'
        )
    actor.subscribe_order_book_deltas(instrument_id, book_type=book_type, depth=depth)
    active_book_types[instrument_id] = 'L1' if book_type == BookType.L1_MBP else 'L2'

Try / catch

try:
    actor.subscribe_order_book_deltas(instrument_id, book_type=BookType.L1_MBP, depth=None)
except Exception as e:
    if 'cannot subscribe L1_MBP and L2_MBP' in str(e):
        actor.unsubscribe_order_book_deltas(instrument_id)  # drop the L2 sub, then retry once
        actor.subscribe_order_book_deltas(instrument_id, book_type=BookType.L1_MBP, depth=None)
    else:
        raise

Prevention

When it happens

Trigger: Subscribing L1_MBP book deltas for an instrument that currently has an active L2_MBP book-deltas subscription (e.g. subscribe L2 depth=20 first, then L1 for the same instrument_id).

Common situations: A strategy module subscribing top-of-book while another module (or an indicator) already holds an L2 book for the same instrument; switching book types at runtime without unsubscribing; duplicated actors subscribing the same instrument with different configs.

Related errors


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