nautechsystems/nautilus_trader · error · anyhow::Error
Binance historical bars require time aggregation
Error message
Binance historical bars require time aggregation
What it means
Thrown by BinanceFuturesDataClient::request_bars when the requested BarType's aggregation method is not time-based. Binance's klines endpoint can only serve time-aggregated (Millisecond/Second/Minute/Hour/Day/Week/Month/Year) LAST-price bars with AggregationSource::External, so tick, volume, or internally aggregated bars cannot be backfilled from the venue. This is the third of three anyhow::ensure! guards validating the bar spec before the HTTP request is spawned.
Source
Thrown at crates/adapters/binance/src/futures/data.rs:3010
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,
start_nanos,
end_nanos,
clock.get_time_ns(),
params,View on GitHub (pinned to a4b06ed870)
Solutions
- Use a time-based aggregation in the BarType, e.g. -1-MINUTE-LAST-, -1-HOUR-LAST-, -1-DAY-LAST-
- Set AggregationSource::External (e.g. BarType::standard) so the venue supplies the bars rather than the engine aggregating them
- If you need volume/tick bars, subscribe to trades/quotes and aggregate locally with an internal bar builder instead of calling request_bars
- Verify the two sibling guards on the same path also pass: price_type == Last and aggregation_source == External
Example fix
// before
let bar_type = BarType::from_str("BTCUSDT-PERP.BINANCE-1-VOLUME-LAST-EXTERNAL")?;
client.request_bars(request, bar_type, start, end, None).await?;
// after
let bar_type = BarType::from_str("BTCUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL")?;
client.request_bars(request, bar_type, start, end, None).await?; Defensive patterns
Strategy: validation
Validate before calling
use nautilus_model::data::{AggregationSource, BarType};
use nautilus_model::enums::PriceType;
fn is_binance_requestable_bar(bar_type: &BarType) -> bool {
bar_type.aggregation_source() == AggregationSource::External
&& bar_type.spec().price_type == PriceType::Last
&& bar_type.spec().is_time_aggregated()
}
// before request_bars:
assert!(is_binance_requestable_bar(&bar_type), "unsupported bar spec"); Type guard
fn is_binance_requestable_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
- Standardize on BarType::standard()/from_str with time-based aggregation for Binance backfills
- Keep a single constant list of BarTypes per venue in strategy config so unsupported specs are caught at config load
- Remember all three guards: External source, Last price type, time aggregation
When it happens
Trigger: Calling request_bars (from a strategy or the data engine) with a BarType whose aggregation is Tick/Volume/VolumeDollar/Index/Custom, e.g. BTCUSDT-PERP.BINANCE-1-VOLUME-LAST-EXTERNAL, or any spec where bar_type.spec().is_time_aggregated() (crates/model/src/data/bar.rs:531) returns false.
Common situations: Strategies written around internal aggregation (e.g. -1-MINUTE-LAST-INTERNAL) pointed at the Binance Futures adapter for historical backfill; hand-built BarType strings with wrong aggregation tokens; porting configs from venues that serve volume bars.
Related errors
- Unsupported second interval: {step}s
- Unsupported minute interval: {step}m
- Unsupported hour interval: {step}h
- Unsupported day interval: {step}d
- Unsupported week interval: {step}w
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/d74381e78a52d642.
Report an issue: GitHub.