Configuring Databento Historical Data for Lumibot Backtests
Summary
The document explains how to connect Databento historical market data to Lumibot backtests. It covers API-key setup, asset definitions, timeframes, date-range configuration, caching, and handling common retrieval errors. Examples include stocks, continuous futures, and options, with guidance to choose data intervals that match the strategy and to check that requested history is available. A multi-asset example illustrates calculating recent returns as a simple momentum signal, though it is presented as a sample workflow rather than a tested strategy.
For futures, the guide describes retrieving contract specifications, applying margin-based mark-to-market accounting, and resolving continuous symbols across expirations. It also lists data adjustments and cleaning features, but provides no independent validation of those claims or measured backtest comparisons. Results depend on API access, data coverage, costs, and the assumptions used by the backtesting engine. Continuous-contract histories and adjusted data can differ from the prices and fills a live strategy would encounter, so backtest results require separate scrutiny of roll handling, execution, and data quality.
Key ideas
- Lumibot can use Databento historical data for backtests across several asset classes and timeframes.
- Continuous futures symbols are presented as a way to handle contract rollovers in historical tests.
- The guide describes futures accounting that tracks margin and updates cash for unrealized profit and loss.
- Caching, batching requests, and selecting appropriate intervals can help manage retrieval performance and data costs.
- The document gives setup examples but does not independently validate data quality or live-trading realism.
Tags
Full text
# backtesting.databento
Databento Backtesting with LumiBot
**********************************
.. meta::
:description: Configure Databento historical data for LumiBot backtests across equities, futures, options, schemas, and multiple timeframes.
DataBento is a premium financial data provider that offers high-quality, clean market data for backtesting. Lumibot integrates with DataBento to provide reliable historical data for stocks, futures, options, and other instruments.
Overview
========
DataBento provides:
- **High-quality historical data** with minimal gaps or errors
- **Multiple timeframes** from tick-level to daily data
- **Extensive instrument coverage** including stocks, futures, and options
- **Clean data processing** with corporate action adjustments
- **API-based access** for automated data retrieval
.. tip::
If you want to test DataBento-backed or other supported Lumibot strategies without managing the full local setup, `BotSpot <https://botspot.trade/sales?showLogin=1&utm_source=documentation&utm_medium=databento&utm_campaign=lumibot&utm_content=hosted_backtesting_tip&prompt=I%20want%20to%20backtest%20a%20Lumibot%20strategy%20on%20BotSpot.%20Please%20help%20me%20set%20up%20hosted%20backtesting%2C%20compare%20strategy%20variants%2C%20and%20prepare%20paper%20or%20live%20deployment.>`_ can help create or revise the strategy, run hosted backtests where supported, compare variants in parallel, and inspect the resulting charts, logs, trades, and artifacts.
Setting Up DataBento
====================
1. **Get DataBento API Key**
Visit `DataBento <https://databento.com>`_ to sign up and get your API key.
2. **Install Dependencies**
DataBento support is included with Lumibot, but you may need to install additional dependencies:
.. code-block:: bash
pip install databento
3. **Configure API Key**
Set your DataBento API key in your environment or strategy:
.. code-block:: python
import os
os.environ['DATABENTO_API_KEY'] = 'your_api_key_here'
Or create a ``.env`` file:
.. code-block:: bash
DATABENTO_API_KEY=your_api_key_here
Basic Usage
===========
Here's how to use DataBento for backtesting:
.. code-block:: python
from lumibot.strategies import Strategy
from lumibot.entities import Asset
from lumibot.backtesting import DataBentoDataBacktesting
class MyStrategy(Strategy):
def initialize(self):
# Use continuous futures for clean backtesting
self.asset = Asset("MES", asset_type=Asset.AssetType.CONT_FUTURE)
def on_trading_iteration(self):
# Get historical data
bars = self.get_historical_prices(self.asset, 20, "minute")
if bars and not bars.df.empty:
# Your strategy logic here
pass
# Run backtest with DataBento
if __name__ == "__main__":
results = MyStrategy.backtest(
DataBentoDataBacktesting,
benchmark_asset=Asset("SPY", Asset.AssetType.STOCK)
)
Supported Assets
================
DataBento supports a wide range of instruments:
**Stocks**
.. code-block:: python
# Major stocks
aapl = Asset("AAPL", asset_type=Asset.AssetType.STOCK)
msft = Asset("MSFT", asset_type=Asset.AssetType.STOCK)
googl = Asset("GOOGL", asset_type=Asset.AssetType.STOCK)
**Futures**
.. code-block:: python
# Equity index futures (continuous)
es = Asset("ES", asset_type=Asset.AssetType.CONT_FUTURE) # S&P 500
nq = Asset("NQ", asset_type=Asset.AssetType.CONT_FUTURE) # NASDAQ 100
rty = Asset("RTY", asset_type=Asset.AssetType.CONT_FUTURE) # Russell 2000
# Micro futures
mes = Asset("MES", asset_type=Asset.AssetType.CONT_FUTURE) # Micro S&P 500
mnq = Asset("MNQ", asset_type=Asset.AssetType.CONT_FUTURE) # Micro NASDAQ 100
m2k = Asset("M2K", asset_type=Asset.AssetType.CONT_FUTURE) # Micro Russell 2000
# Commodity futures
cl = Asset("CL", asset_type=Asset.AssetType.CONT_FUTURE) # Crude Oil
gc = Asset("GC", asset_type=Asset.AssetType.CONT_FUTURE) # Gold
ng = Asset("NG", asset_type=Asset.AssetType.CONT_FUTURE) # Natural Gas
Futures-Specific Features
--------------------------
When backtesting futures with DataBento, Lumibot provides several specialized features:
**Automatic Multiplier Detection:**
Futures contract multipliers are automatically fetched from DataBento's definition schema:
.. code-block:: python
# MES multiplier is automatically detected as 5
mes = Asset("MES", asset_type=Asset.AssetType.CONT_FUTURE)
# When you trade MES:
# - 1 point move = $5 P&L per contract
# - 10 contracts at +2 points = +$100 total P&L
Lumibot fetches contract specifications from DataBento including:
- Contract multiplier (e.g., 5 for MES, 50 for ES)
- Tick size and value
- Contract unit of measure
- Settlement type
This information is cached to avoid repeated API calls.
**Mark-to-Market Accounting:**
DataBento backtests use mark-to-market accounting that matches real futures trading:
.. code-block:: python
# Example: Trading 1 MES contract
# Starting capital: $100,000
# BUY 1 MES @ $5,000
# - Initial margin deducted: ~$1,300
# - Cash: $98,700
# Price moves to $5,010 (up 10 points)
# - Mark-to-market: +10 points × $5 = +$50
# - Cash: $98,750 (includes unrealized P&L)
# SELL 1 MES @ $5,010
# - Margin released: +$1,300
# - Final P&L already in cash
# - Cash: $100,050
Key accounting features:
1. **Entry**: Initial margin is deducted from cash (not full notional value)
2. **During Trade**: Cash is updated every iteration with unrealized P&L changes
3. **Exit**: Margin is released and final P&L settlement applied
This ensures:
- Cash always shows available buying power
- Portfolio value = Cash (includes all unrealized P&L)
- Leverage tracking is accurate
- Results match real broker accounting
For more details on futures accounting, see the :doc:`futures` documentation.
**Symbol Resolution:**
DataBento automatically handles symbol resolution for continuous futures:
.. code-block:: python
# You specify the root symbol
mes = Asset("MES", asset_type=Asset.AssetType.CONT_FUTURE)
# DataBento resolves to actual contracts:
# - For Jan 2024: MESH4 (March 2024 expiry)
# - For Apr 2024: MESM4 (June 2024 expiry)
# - Seamless rollover handling
This makes backtesting across multiple years seamless without managing contract expirations.
**Options** (when supported)
.. code-block:: python
from datetime import date
# Stock options
aapl_call = Asset(
symbol="AAPL",
asset_type=Asset.AssetType.OPTION,
expiration=date(2025, 12, 19),
strike=150,
right="CALL"
)
Time Frames
===========
DataBento supports multiple timeframes:
.. code-block:: python
class DataStrategy(Strategy):
def on_trading_iteration(self):
# Different timeframes
minute_data = self.get_historical_prices(self.asset, 100, "minute")
hour_data = self.get_historical_prices(self.asset, 24, "hour")
daily_data = self.get_historical_prices(self.asset, 30, "day")
# Use the data for analysis
if minute_data and not minute_data.df.empty:
# High-frequency analysis
latest_price = minute_data.df['close'].iloc[-1]
Advanced Configuration
========================
You can configure DataBento backtesting with additional parameters:
.. code-block:: python
from datetime import datetime
from lumibot.backtesting import DataBentoDataBacktesting
# Custom backtest configuration
backtest_start = datetime(2024, 1, 1)
backtest_end = datetime(2024, 12, 31)
results = MyStrategy.backtest(
DataBentoDataBacktesting,
start=backtest_start,
end=backtest_end,
benchmark_asset=Asset("SPY", Asset.AssetType.STOCK),
show_plot=True,
show_tearsheet=True,
save_tearsheet=True
)
Data Quality Features
========================
DataBento provides several data quality features:
**Corporate Actions**
- Automatic dividend adjustments
- Stock split adjustments
- Merger and acquisition handling
**Data Cleaning**
- Outlier detection and removal
- Gap filling for missing data
- Timestamp normalization
**Market Hours**
- Proper market hour filtering
- Pre-market and after-hours data
- Holiday schedule handling
Caching
=======
Lumibot automatically caches DataBento data to improve performance:
.. code-block:: python
# Data is automatically cached locally
# Subsequent requests for the same data will be faster
bars = self.get_historical_prices(asset, 100, "minute")
Cache files are stored in the Lumibot cache directory and are automatically managed.
Best Practices
==============
1. **Use Continuous Futures**
For futures backtesting, always use continuous contracts for seamless data across expiration rollovers.
2. **Batch Data Requests**
Request larger chunks of data rather than making many small requests.
3. **Monitor API Limits**
DataBento has API rate limits. Avoid excessive requests in short time periods.
4. **Cache Management**
Let Lumibot handle caching automatically. Clear cache only when needed.
5. **Data Validation**
Always check that data is available before using it in your strategy.
Example: Multi-Asset Strategy
==============================
Here's a complete example using multiple assets with DataBento:
.. code-block:: python
from lumibot.strategies import Strategy
from lumibot.entities import Asset, Order
from lumibot.backtesting import DataBentoDataBacktesting
import pandas as pd
class MultiAssetStrategy(Strategy):
def initialize(self):
# Portfolio of futures contracts
self.assets = [
Asset("MES", asset_type=Asset.AssetType.CONT_FUTURE), # Micro S&P 500
Asset("MNQ", asset_type=Asset.AssetType.CONT_FUTURE), # Micro NASDAQ 100
Asset("M2K", asset_type=Asset.AssetType.CONT_FUTURE), # Micro Russell 2000
]
self.lookback_period = 20
def on_trading_iteration(self):
for asset in self.assets:
# Get data for each asset
bars = self.get_historical_prices(asset, self.lookback_period, "day")
if bars and len(bars.df) >= self.lookback_period:
# Calculate momentum
returns = bars.df['close'].pct_change().dropna()
momentum = returns.tail(5).mean() # 5-day average return
position = self.get_position(asset)
# Long momentum strategy
if momentum > 0.001: # Positive momentum threshold
if position is None or position.quantity <= 0:
order = self.create_order(asset, 1, "buy")
self.submit_order(order)
# Short momentum strategy
elif momentum < -0.001: # Negative momentum threshold
if position is None or position.quantity >= 0:
if position and position.quantity > 0:
# Close long first
close_order = self.create_order(asset, position.quantity, "sell")
self.submit_order(close_order)
# Then go short
order = self.create_order(asset, 1, "sell")
self.submit_order(order)
if __name__ == "__main__":
results = MultiAssetStrategy.backtest(
DataBentoDataBacktesting,
benchmark_asset=Asset("SPY", Asset.AssetType.STOCK)
)
Error Handling
==============
Handle common DataBento issues gracefully:
.. code-block:: python
class RobustStrategy(Strategy):
def on_trading_iteration(self):
try:
bars = self.get_historical_prices(self.asset, 20, "minute")
if bars is None or bars.df.empty:
self.log_message("No data available", color="yellow")
return
# Your strategy logic here
except Exception as e:
self.log_message(f"Data error: {e}", color="red")
return
Performance Optimization
===========================
Tips for optimizing DataBento performance:
1. **Minimize Data Requests**
Request data once and reuse it within the same iteration.
2. **Use Appropriate Timeframes**
Don't request minute data if you only need daily signals.
3. **Leverage Caching**
Repeated backtests will be faster due to automatic caching.
4. **Batch Processing**
Process multiple assets efficiently in loops.
Troubleshooting
==================
**Common Issues:**
1. **"No DataBento API key found"**
- Set the ``DATABENTO_API_KEY`` environment variable
- Check your .env file configuration
2. **"Rate limit exceeded"**
- Reduce the frequency of data requests
- Use longer timeframes when possible
- Add delays between requests if needed
3. **"No data available for symbol"**
- Verify the symbol is correct
- Check if DataBento supports the instrument
- Ensure the date range is valid
4. **"Connection timeout"**
- Check your internet connection
- Verify DataBento service status
- Retry the request
Cost Considerations
=====================
DataBento is a premium service with costs based on:
- **Data volume** (number of symbols and timeframes)
- **Historical depth** (how far back you request data)
- **API usage** (number of requests)
For cost-effective backtesting:
- Use continuous futures instead of multiple expiry contracts
- Request appropriate timeframes (don't use minute data for daily strategies)
- Leverage caching to avoid repeated requests
- Focus on the symbols you actually need
DataBento provides excellent value for professional strategy development due to its data quality and reliability.Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.