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

The standard historical-bars request path only serves LAST-price bars: after the EXTERNAL aggregation check it enforces `PriceType::Last`. Binance klines are last-trade candles, so MID/BID/ASK bar types are rejected before the HTTP fetch is spawned.

Source

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

    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 Futures") {
                Ok(bars) => {
                    let response = DataResponse::Bars(BarsResponse::new(
                        request_id,
                        client_id,
                        bar_type,
                        bars,

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Request a LAST bar type, e.g. `BTCUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL`
  2. For quote-price bars, collect book/quote data and aggregate locally instead of requesting Binance history

Example fix

# before
bar_type = BarType.from_str('BTCUSDT-PERP.BINANCE-1-MINUTE-MID-EXTERNAL')

# after
bar_type = BarType.from_str('BTCUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL')
Defensive patterns

Strategy: validation

Validate before calling

from nautilus_trader.model.enums import PriceType

def is_requestable_binance_bar(bar_type) -> bool:
    return bar_type.spec.price_type == PriceType.LAST

if not is_requestable_binance_bar(request_bar_type):
    raise ValueError(f'{request_bar_type} is {request_bar_type.spec.price_type}; Binance kline history is LAST only')

Type guard

def is_last_price_bar(bar_type) -> bool:
    return bar_type.spec.price_type == PriceType.LAST

Try / catch

try:
    actor.request_bars(bar_type, start=start_ts, end=end_ts)
except Exception as e:
    if 'require LAST price type' in str(e):
        raise ValueError('Request the -LAST- bar type for Binance history; build quote-price bars locally from book data') from e
    raise

Prevention

When it happens

Trigger: `request_bars` with a BarType such as `BTCUSDT-PERP.BINANCE-1-MINUTE-MID-EXTERNAL`, `-BID-EXTERNAL`, or `-ASK-EXTERNAL`. (Price type is checked after aggregation source, so an INTERNAL MID bar fails with the aggregation error first.)

Common situations: Strategies standardised on MID bars across venues; generic backfill code requesting whatever bar types a config lists; assuming quote-price klines exist on Binance.

Related errors


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