nautechsystems/nautilus_trader · error · anyhow::Error
Binance historical bars require LAST price type
Error message
Binance historical bars require LAST price type
What it means
Binance klines only record LAST-trade prices; there is no historical source for MID, BID or ASK price types. The adapter validates bar_type.spec().price_type on every historical bar request and rejects anything but PriceType::Last so the venue never receives an unsatisfiable query.
Source
Thrown at crates/adapters/binance/src/spot/data.rs:2360
fn request_bars(&self, request: RequestBars) -> anyhow::Result<()> {
let http = self.http_client.clone();
let sender = self.data_sender.clone();
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,View on GitHub (pinned to a4b06ed870)
Solutions
- Use -LAST- price type for all historical Binance bar requests
- If MID/BID/ASK bars are required, subscribe to QuoteTicks and aggregate locally with an INTERNAL bar type
- Pre-validate bar_type.spec().price_type == PriceType::Last before requesting
Example fix
// before
let bar_type = BarType::from_str("BTCUSDT_BINANCE-1-MINUTE-MID-EXTERNAL")?;
// after
let bar_type = BarType::from_str("BTCUSDT_BINANCE-1-MINUTE-LAST-EXTERNAL")?; Defensive patterns
Strategy: validation
Validate before calling
fn ensure_last_price(bar_type: &BarType) -> anyhow::Result<()> {
anyhow::ensure!(
bar_type.spec().price_type == PriceType::Last,
"historical Binance bars need LAST price type, got {bar_type}"
);
Ok(())
} Type guard
fn is_last_price_bar(bar_type: &BarType) -> bool {
bar_type.spec().price_type == PriceType::Last
} Prevention
- Assume exchange bar history is last-price unless the venue documents otherwise
- Derive MID/BID/ASK bars from quote streams locally, never from history requests
- Add a startup assertion for every bar type the strategy will request historically
When it happens
Trigger: Calling request_bars with a price type other than LAST, e.g. 'BTCUSDT_BINANCE-1-MINUTE-MID-EXTERNAL' or 'BTCUSDT_BINANCE-1-HOUR-BID-EXTERNAL'. The ensure! at spot/data.rs:2360 fires synchronously.
Common situations: Strategies that trade quote-derived bars (MID) ported from venues or data providers that do serve mid-price history; configs generated from analytics tooling that defaults to MID.
Related errors
- historical BinanceBar requests require LAST price type
- historical BinanceBar requests require EXTERNAL aggregation
- historical BinanceBar requests require time aggregation
- Binance historical bars require EXTERNAL aggregation
- Binance historical bars require time aggregation
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/5723bc6e470c16e9.
Report an issue: GitHub.