{"record":{"id":"d74381e78a52d642","repo":"nautechsystems/nautilus_trader","slug":"binance-historical-bars-require-time-aggregation","errorCode":null,"errorMessage":"Binance historical bars require time aggregation","messagePattern":"Binance historical bars require time aggregation","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/binance/src/futures/data.rs","lineNumber":3010,"sourceCode":"        let bar_type = request.bar_type;\n        let start = request.start;\n        let end = request.end;\n        let limit = request.limit.map(|n| n.get() as u32);\n        let request_id = request.request_id;\n        let client_id = request.client_id.unwrap_or(self.client_id);\n        let params = request.params;\n        let clock = self.clock;\n        let start_nanos = datetime_to_unix_nanos(start);\n        let end_nanos = datetime_to_unix_nanos(end);\n        anyhow::ensure!(\n            bar_type.aggregation_source() == AggregationSource::External,\n            \"Binance historical bars require EXTERNAL aggregation\"\n        );\n        anyhow::ensure!(\n            bar_type.spec().price_type == PriceType::Last,\n            \"Binance historical bars require LAST price type\"\n        );\n        anyhow::ensure!(\n            bar_type.spec().is_time_aggregated(),\n            \"Binance historical bars require time aggregation\"\n        );\n\n        get_runtime().spawn(async move {\n            let result = http.request_bars(bar_type, start, end, limit).await;\n\n            match result.context(\"failed to request bars from Binance Futures\") {\n                Ok(bars) => {\n                    let response = DataResponse::Bars(BarsResponse::new(\n                        request_id,\n                        client_id,\n                        bar_type,\n                        bars,\n                        start_nanos,\n                        end_nanos,\n                        clock.get_time_ns(),\n                        params,","sourceCodeStart":2992,"sourceCodeEnd":3028,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/binance/src/futures/data.rs#L2992-L3028","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nlet bar_type = BarType::from_str(\"BTCUSDT-PERP.BINANCE-1-VOLUME-LAST-EXTERNAL\")?;\nclient.request_bars(request, bar_type, start, end, None).await?;\n\n// after\nlet bar_type = BarType::from_str(\"BTCUSDT-PERP.BINANCE-1-MINUTE-LAST-EXTERNAL\")?;\nclient.request_bars(request, bar_type, start, end, None).await?;","handlingStrategy":"validation","validationCode":"use nautilus_model::data::{AggregationSource, BarType};\nuse nautilus_model::enums::PriceType;\n\nfn is_binance_requestable_bar(bar_type: &BarType) -> bool {\n    bar_type.aggregation_source() == AggregationSource::External\n        && bar_type.spec().price_type == PriceType::Last\n        && bar_type.spec().is_time_aggregated()\n}\n\n// before request_bars:\nassert!(is_binance_requestable_bar(&bar_type), \"unsupported bar spec\");","typeGuard":"fn is_binance_requestable_bar(bar_type: &BarType) -> bool {\n    bar_type.aggregation_source() == AggregationSource::External\n        && bar_type.spec().price_type == PriceType::Last\n        && bar_type.spec().is_time_aggregated()\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["binance-futures","historical-data","bars","bar-type","rust"],"backgroundTag":"bar-type-validation","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}