nautechsystems/nautilus_trader · error · anyhow::Error

Binance historical bars require time aggregation

Error message

Binance historical bars require time aggregation

What it means

The standard historical-bars path (request_bars on the Spot data client) enforces the same constraint as the custom BinanceBar path: Binance klines exist only for time-based intervals, so BarSpecification::is_time_aggregated() must be true. Threshold aggregations (TICK, VOLUME, VALUE and imbalance variants) are rejected up front.

Source

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

        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,
                        start_nanos,
                        end_nanos,
                        clock.get_time_ns(),
                        params,

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Switch to a time aggregation (SECOND/MINUTE/HOUR/DAY/WEEK/MONTH), e.g. 'BTCUSDT_BINANCE-1-MINUTE-LAST-EXTERNAL'
  2. Aggregate threshold bars locally from TradeTicks/QuoteTicks with an INTERNAL bar type
  3. Validate bar_type.spec().is_time_aggregated() before the request

Example fix

# before (Python strategy)
bar_type = BarType.from_str("BTCUSDT_BINANCE-1000-VOLUME-LAST-EXTERNAL")
self.request_bars(BarHistoryData(bar_type))

# after
bar_type = BarType.from_str("BTCUSDT_BINANCE-1-MINUTE-LAST-EXTERNAL")
self.request_bars(BarHistoryData(bar_type))
Defensive patterns

Strategy: validation

Validate before calling

fn validate_binance_bar_request(bar_type: &BarType) -> anyhow::Result<()> {
    anyhow::ensure!(bar_type.aggregation_source() == AggregationSource::External);
    anyhow::ensure!(bar_type.spec().price_type == PriceType::Last);
    anyhow::ensure!(bar_type.spec().is_time_aggregated());
    Ok(())
}

Type guard

fn is_requestable_binance_bar(bar_type: &BarType) -> bool {
    bar_type.aggregation_source() == AggregationSource::External
        && bar_type.spec().price_type == PriceType::Last
        && bar_type.spec().is_time_aggregated()
}

Prevention

When it happens

Trigger: Calling request_bars with a non-time aggregation, e.g. 'BTCUSDT_BINANCE-1000-VOLUME-LAST-EXTERNAL' or 'BTCUSDT_BINANCE-50-TICK-LAST-EXTERNAL'. The ensure! at spot/data.rs:2364 fails before the request task spawns.

Common situations: Strategies requesting volume-bar or tick-bar history; bar specs shared between a backtest (which can aggregate anything) and live Binance execution; copy-pasted bar type strings from other adapters.

Related errors


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