nautechsystems/nautilus_trader · error · anyhow::Error
Python on_historical_quotes failed: {e}
Error message
Python on_historical_quotes failed: {e} What it means
Raised when the strategy's Python `on_historical_quotes` handler throws while processing historical QuoteTicks. The Rust bridge clones the quote slice, dispatches via PyO3, and wraps any error with this anyhow message. The underlying failure is an unhandled exception in the user's Python handler.
Source
Thrown at crates/trading/src/python/strategy.rs:1234
};
self.dispatch_on_historical_data(py_data)
.map_err(|e| anyhow::anyhow!("Python on_historical_data failed: {e}"))
})
}
fn on_historical_book_deltas(&mut self, deltas: &[OrderBookDelta]) -> anyhow::Result<()> {
self.dispatch_on_historical_book_deltas(deltas.to_vec())
.map_err(|e| anyhow::anyhow!("Python on_historical_book_deltas failed: {e}"))
}
fn on_historical_book_depth(&mut self, depths: &[OrderBookDepth10]) -> anyhow::Result<()> {
self.dispatch_on_historical_book_depth(depths.to_vec())
.map_err(|e| anyhow::anyhow!("Python on_historical_book_depth failed: {e}"))
}
fn on_historical_quotes(&mut self, quotes: &[QuoteTick]) -> anyhow::Result<()> {
self.dispatch_on_historical_quotes(quotes.to_vec())
.map_err(|e| anyhow::anyhow!("Python on_historical_quotes failed: {e}"))
}
fn on_historical_trades(&mut self, trades: &[TradeTick]) -> anyhow::Result<()> {
self.dispatch_on_historical_trades(trades.to_vec())
.map_err(|e| anyhow::anyhow!("Python on_historical_trades failed: {e}"))
}
fn on_historical_funding_rates(
&mut self,
funding_rates: &[FundingRateUpdate],
) -> anyhow::Result<()> {
self.dispatch_on_historical_funding_rates(funding_rates.to_vec())
.map_err(|e| anyhow::anyhow!("Python on_historical_funding_rates failed: {e}"))
}
fn on_historical_bars(&mut self, bars: &[Bar]) -> anyhow::Result<()> {
self.dispatch_on_historical_bars(bars.to_vec())
.map_err(|e| anyhow::anyhow!("Python on_historical_bars failed: {e}"))View on GitHub (pinned to 18893faf8b)
Solutions
- Read the Python traceback in `e` and fix `on_historical_quotes`
- Guard against empty quote lists and zero/None bid-ask values before computing
- Verify signature `on_historical_quotes(self, quotes: list[QuoteTick])`
- Test warm-up with an empty-quote scenario
Example fix
// before (Python)
def on_historical_quotes(self, quotes):
spread = quotes[-1].ask_price - quotes[-1].bid_price # IndexError when empty
// after
def on_historical_quotes(self, quotes):
if not quotes:
return
spread = quotes[-1].ask_price - quotes[-1].bid_price Defensive patterns
Strategy: validation
Validate before calling
def quotes_ok(quotes):
return bool(quotes) and all(q.ask_price > 0 and q.bid_price > 0 for q in quotes) Type guard
def quotes_usable(quotes):
return quotes is not None and len(quotes) > 0 and quotes[-1].ask_price is not None and quotes[-1].bid_price is not None Try / catch
try:
strategy.on_historical_quotes(quotes)
except Exception as e:
strategy.log.error(f"on_historical_quotes failed: {e}", exc_info=True) Prevention
- Return early on empty quote lists
- Guard zero/negative bid-ask before spread math
- Warm-up code must tolerate insufficient history windows
- Match handler to data type — don't wire bar logic to quote callbacks
When it happens
Trigger: Historical quote data loads (backtest warm-up or data request) and `on_historical_quotes(self, quotes)` raises — e.g. computing spreads on empty lists, dividing by zero bid/ask, or wrong attribute usage.
Common situations: Handlers computing rolling stats during warm-up with empty windows; division by zero when ask is 0/None; handlers written for bars mistakenly wired to quotes.
Related errors
- Python on_historical_data failed: {e}
- Python on_historical_trades failed: {e}
- Python on_historical_book_deltas failed: {e}
- Python on_historical_book_depth failed: {e}
- Python on_historical_quotes failed: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/9f2047f8002a6848.
Report an issue: GitHub.