Problem Description
Solution
Based on the QuantConnect LEAN documentation, the reason your on_data method is being called without new trade data is due to the Fill Forward mechanism.
The Fill Forward Mechanism
By default, QuantConnect enables "Fill Forward" for all asset subscriptions. This ensures that your algorithm receives a continuous stream of data points, even if no trades occurred during a specific time resolution (e.g., a specific minute or hour).
- Default Behavior: If there is no trading activity for a specific resolution period, LEAN copies the last known closing price and creates a new bar for the current time step. This triggers the
on_dataevent. - Purpose: This feature is designed to prevent your algorithm from stalling during illiquid periods. It ensures that logic dependent on time (such as scheduled events, stop-loss checks, or portfolio rebalancing) continues to execute even if the market is quiet.
How to Handle Fill Forward Data
You can manage this behavior in two ways: checking the data within on_data or disabling the feature entirely.
1. Check for Fill Forward in on_data
The data bars inside the Slice object have a property called is_fill_forward. You can check this property to ignore bars that are not generated by actual market activity.
def on_data(self, slice: Slice) -> None:
bar = slice.bars.get("SPY")
# Check if the bar exists and if it is real data
if bar and not bar.is_fill_forward:
self.log(f"New trade occurred: {bar.close}")
else:
# This is a fill-forward bar (no new trade)
pass
2. Disable Fill Forward
If your strategy strictly requires only real trade data, you can disable this feature when you subscribe to the asset using add_equity, add_crypto, or add_forex.
def initialize(self) -> None:
# Set fill_data_forward to False to only trigger OnData when actual trades occur
self.add_equity("SPY", resolution=Resolution.MINUTE, fill_data_forward=False)
Additional Context: Data Events
According to the documentation on Event Handlers:
"In this method [on_data], all data for a given moment of time is grouped in a single event, including custom data types. This data is passed with the Slice object. When fill-forward is enabled for your asset, the OnData event handler will be called regularly even if there was no new data. This is the default behavior."
Q&A
Q: Does disabling Fill Forward affect Scheduled Events?
A: No. Scheduled Events (e.g., self.schedule.on(...)) run on a separate thread in live trading and are calculated independently in backtesting. They will still fire at the designated time regardless of data flow.
Q: How does Fill Forward affect indicators?
A: If you use automatic indicators (registered via helper methods like self.ema()), they are updated with fill-forward data by default. If you disable fill forward, indicators will only update when a real trade occurs, which may affect the timing of your signals.
Q: Is Fill Forward active in Live Trading?
A: Yes, the behavior is consistent across both Backtesting and Live Trading to ensure your strategy logic remains deterministic.