nautechsystems/nautilus_trader · error · anyhow::Error

historical BinanceBar requests require EXTERNAL aggregation

Error message

historical BinanceBar requests require EXTERNAL aggregation

What it means

Historical BinanceBar (kline) requests are served by the Binance klines REST API, which only returns exchange-built candles. The requested BarType must therefore carry AggregationSource::External; a BarType with Internal aggregation (meant for client-side aggregation) is rejected by this check.

Source

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

                    }
                }
                Err(e) => log::error!("Instrument request failed: {e:?}"),
            }
        });

        Ok(())
    }

    fn request_data(&self, request: RequestCustomData) -> anyhow::Result<()> {
        if request.data_type.type_name() != "BinanceBar" {
            log::warn!(
                "Unsupported custom data request: {}",
                request.data_type.type_name()
            );
            return Ok(());
        }
        let bar_type = parse_binance_bar_type(&request.data_type)?;
        anyhow::ensure!(
            bar_type.aggregation_source() == AggregationSource::External,
            "historical BinanceBar requests require EXTERNAL aggregation"
        );
        anyhow::ensure!(
            bar_type.spec().price_type == PriceType::Last,
            "historical BinanceBar requests require LAST price type"
        );
        anyhow::ensure!(
            bar_type.spec().is_time_aggregated(),
            "historical BinanceBar requests require time aggregation"
        );
        let http = self.http_client.clone();
        let sender = self.data_sender.clone();
        let request_id = request.request_id;
        let client_id = request.client_id;
        let data_type = request.data_type;
        let start = request.start;
        let end = request.end;

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Build the request BarType with AggregationSource::External, e.g. 'BTCUSDT.BINANCE-1-MINUTE-LAST-EXTERNAL'
  2. Keep two BarTypes: External for historical/backfill requests, Internal for live client-side aggregation, and route requests accordingly

Example fix

# before
bar_type = BarType.from_str("BTCUSDT.BINANCE-1-MINUTE-LAST-INTERNAL")

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

Strategy: validation

Validate before calling

from nautilus_trader.model.data import BarType, AggregationSource

def is_external_bar_type(bar_type: BarType) -> bool:
    return bar_type.aggregation_source == AggregationSource.EXTERNAL

assert is_external_bar_type(bar_type), "BinanceBar history requires EXTERNAL aggregation"

Type guard

def is_external_bar_type(bar_type) -> bool:
    return bar_type.aggregation_source == AggregationSource.EXTERNAL

Prevention

When it happens

Trigger: request_data (RequestCustomData) for a DataType named 'BinanceBar' whose bar_type has aggregation_source != External, e.g. 'BTCUSDT.BINANCE-1-MINUTE-LAST-INTERNAL'.

Common situations: Reusing a BarType configured for internal aggregation (from a live internal aggregator) when issuing a historical backfill request; constructing bar types generically without pinning the aggregation source for history.

Related errors


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