nautechsystems/nautilus_trader · error · anyhow::Error

Binance historical bars require LAST price type

Error message

Binance historical bars require LAST price type

What it means

Binance klines only record LAST-trade prices; there is no historical source for MID, BID or ASK price types. The adapter validates bar_type.spec().price_type on every historical bar request and rejects anything but PriceType::Last so the venue never receives an unsatisfiable query.

Source

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

    fn request_bars(&self, request: RequestBars) -> anyhow::Result<()> {
        let http = self.http_client.clone();
        let sender = self.data_sender.clone();
        let bar_type = request.bar_type;
        let start = request.start;
        let end = request.end;
        let limit = request.limit.map(|n| n.get() as u32);
        let request_id = request.request_id;
        let client_id = request.client_id.unwrap_or(self.client_id);
        let params = request.params;
        let clock = self.clock;
        let start_nanos = datetime_to_unix_nanos(start);
        let end_nanos = datetime_to_unix_nanos(end);
        anyhow::ensure!(
            bar_type.aggregation_source() == AggregationSource::External,
            "Binance historical bars require EXTERNAL aggregation"
        );
        anyhow::ensure!(
            bar_type.spec().price_type == PriceType::Last,
            "Binance historical bars require LAST price type"
        );
        anyhow::ensure!(
            bar_type.spec().is_time_aggregated(),
            "Binance historical bars require time aggregation"
        );

        get_runtime().spawn(async move {
            let result = http.request_bars(bar_type, start, end, limit).await;

            match result.context("failed to request bars from Binance") {
                Ok(bars) => {
                    let response = DataResponse::Bars(BarsResponse::new(
                        request_id,
                        client_id,
                        bar_type,
                        bars,

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Use -LAST- price type for all historical Binance bar requests
  2. If MID/BID/ASK bars are required, subscribe to QuoteTicks and aggregate locally with an INTERNAL bar type
  3. Pre-validate bar_type.spec().price_type == PriceType::Last before requesting

Example fix

// before
let bar_type = BarType::from_str("BTCUSDT_BINANCE-1-MINUTE-MID-EXTERNAL")?;

// after
let bar_type = BarType::from_str("BTCUSDT_BINANCE-1-MINUTE-LAST-EXTERNAL")?;
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_last_price(bar_type: &BarType) -> anyhow::Result<()> {
    anyhow::ensure!(
        bar_type.spec().price_type == PriceType::Last,
        "historical Binance bars need LAST price type, got {bar_type}"
    );
    Ok(())
}

Type guard

fn is_last_price_bar(bar_type: &BarType) -> bool {
    bar_type.spec().price_type == PriceType::Last
}

Prevention

When it happens

Trigger: Calling request_bars with a price type other than LAST, e.g. 'BTCUSDT_BINANCE-1-MINUTE-MID-EXTERNAL' or 'BTCUSDT_BINANCE-1-HOUR-BID-EXTERNAL'. The ensure! at spot/data.rs:2360 fires synchronously.

Common situations: Strategies that trade quote-derived bars (MID) ported from venues or data providers that do serve mid-price history; configs generated from analytics tooling that defaults to MID.

Related errors


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