nautechsystems/nautilus_trader · error

Binance Futures does not support {a:?} aggregation

Error message

Binance Futures does not support {a:?} aggregation

What it means

Thrown inside request_binance_bars when the BarAggregation falls into the catch-all arm — i.e. anything other than Second/Minute/Hour/Day/Week/Month, with {a:?} naming the variant (Tick, Volume, Value, or future variants). These aggregation kinds are NautilusTrader-internal constructs with no Binance kline interval equivalent (e.g. 'Binance Futures does not support Tick aggregation'), so the client refuses to translate them.

Source

Thrown at crates/adapters/binance/src/futures/http/client.rs:2991

        limit: Option<u32>,
    ) -> anyhow::Result<Vec<BinanceBar>> {
        anyhow::ensure!(
            bar_type.aggregation_source() == AggregationSource::External,
            "Only EXTERNAL aggregation is supported"
        );

        let spec = bar_type.spec();
        let step = spec.step.get();
        let interval = match spec.aggregation {
            BarAggregation::Second => {
                anyhow::bail!("Binance Futures does not support second-level kline intervals")
            }
            BarAggregation::Minute => format!("{step}m"),
            BarAggregation::Hour => format!("{step}h"),
            BarAggregation::Day => format!("{step}d"),
            BarAggregation::Week => format!("{step}w"),
            BarAggregation::Month => format!("{step}M"),
            a => anyhow::bail!("Binance Futures does not support {a:?} aggregation"),
        };

        let instrument_id = bar_type.instrument_id();
        let (symbol, price_precision, size_precision) =
            self.cached_precisions_by_id(instrument_id)?;

        let params = BinanceKlinesParams {
            symbol,
            interval,
            start_time: start.map(|dt| dt.as_millisecond()),
            end_time: end.map(|dt| dt.as_millisecond()),
            limit,
        };

        let klines = self.inner.klines(&params).await?;
        let now = self.clock.get_time_ns();

        let mut result = Vec::with_capacity(klines.len());

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Use a time-based aggregation (Minute/Hour/Day/Week/Month) for venue klines
  2. For Tick/Volume/Value bars, download historical trades (request_agg_trades within 24h, or klines as an approximation) and run the internal aggregator over them
  3. Skip-and-log non-time aggregations in generic loaders rather than erroring the batch

Example fix

// before
let bar_type: BarType = "BTCUSDT.BINANCE_PERP-100-VOLUME-LAST-EXTERNAL".parse()?;
let bars = client.request_binance_bars(bar_type, start, end, None).await?;

// after
let trades = client.request_agg_trades(instrument_id, None, None, None).await?;
// aggregate volume bars locally from trades
Defensive patterns

Strategy: type-guard

Validate before calling

match bar_type.spec().aggregation {
    BarAggregation::Minute
    | BarAggregation::Hour
    | BarAggregation::Day
    | BarAggregation::Week
    | BarAggregation::Month => { /* safe to request */ }
    other => log::warn!("unsupported kline aggregation {other:?}; skipping"),
}

Type guard

fn is_supported_kline_aggregation(bar_type: &BarType) -> bool {
    matches!(
        bar_type.spec().aggregation,
        BarAggregation::Minute
            | BarAggregation::Hour
            | BarAggregation::Day
            | BarAggregation::Week
            | BarAggregation::Month
    )
}

Try / catch

Skip-and-log: catch the bail, inspect {a:?} in the message, and continue with remaining bar types instead of failing the whole backfill.

Prevention

When it happens

Trigger: Requesting venue history for volume-aggregated bars ('...-100-VOLUME-MID-EXTERNAL'), tick bars, or value bars; generic request builders that iterate all BarAggregation variants; strategies migrating from an adapter/venue that exposes volume klines natively.

Common situations: Volume/tick-bar trading strategies attempting a historical warm-up from the venue; bar-type strings with the aggregation field misparsed (e.g. step value landing in the aggregation slot); shared data-request code that assumes every aggregation maps to an interval.

Related errors


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