🚀 AI One-Click Generation of quantconnect Strategy Code

How to Use the on_warmup_finished Event to Prepare Your QuantConnect Algorithm

quantconnect | Published on: 2025/11/28
The following content is generated by EasyQuant .

Problem Description

How can I prepare my indicators before my trading algorithm starts running?

Solution

To prepare your indicators before the algorithm starts trading, you should use the Warm Up feature or manually seed indicators using History requests.

The most standard and efficient method in QuantConnect is using self.set_warm_up(). This tells the LEAN engine to feed historical data into your algorithm for a specified period before the start date, allowing indicators to calculate their initial values.

Key Concepts

  1. set_warm_up(period): Automatically pumps data into the algorithm before the start date.
  2. self.is_warming_up: A boolean flag used in on_data to prevent placing trades while the indicators are still priming.
  3. indicator.is_ready: A property to check if an individual indicator has received enough data to be valid.

Strategy Implementation

Below is a complete algorithm demonstrating how to warm up a 200-day Exponential Moving Average (EMA) so it is ready to use immediately on the first day of the backtest.

# region imports
from AlgorithmImports import *
# endregion

class IndicatorWarmupAlgorithm(QCAlgorithm):

    def initialize(self):
        self.set_start_date(2020, 1, 1)
        self.set_end_date(2023, 1, 1)
        self.set_cash(100000)

        # 1. Add Security
        self.symbol = self.add_equity("SPY", Resolution.DAILY).symbol

        # 2. Define Indicators
        # We use a 200-period EMA. Without warmup, this would take 200 days 
        # of the backtest to produce a valid value.
        self.fast_ema = self.ema(self.symbol, 50, Resolution.DAILY)
        self.slow_ema = self.ema(self.symbol, 200, Resolution.DAILY)

        # 3. Set Warm Up Period
        # We set the warmup to the longest lookback period required (200 days).
        # You can pass an integer (number of bars) or a timedelta.
        self.set_warm_up(200)

    def on_data(self, data: Slice):
        # 4. Check Warm Up Status
        # While the engine is feeding pre-start data, this flag is True.
        # We return here to ensure we don't trade on historical warmup data.
        if self.is_warming_up:
            return

        # 5. Double Check Indicator Readiness (Good Practice)
        if not self.slow_ema.is_ready or not self.fast_ema.is_ready:
            return

        # Trading Logic (Golden Cross)
        if self.fast_ema.current.value > self.slow_ema.current.value:
            if not self.portfolio.invested:
                self.set_holdings(self.symbol, 1.0)
                self.log(f"Buy at {self.time}. Fast: {self.fast_ema.current.value}, Slow: {self.slow_ema.current.value}")
        
        elif self.fast_ema.current.value < self.slow_ema.current.value:
            if self.portfolio.invested:
                self.liquidate(self.symbol)
                self.log(f"Sell at {self.time}. Fast: {self.fast_ema.current.value}, Slow: {self.slow_ema.current.value}")

Alternative: Manual History Seeding

If you do not want to use set_warm_up (which simulates the passage of time), you can manually download history in initialize and update the indicator instantly. This is faster for very long lookback periods.

    def initialize(self):
        # ... setup code ...
        self.slow_ema = self.EMA("SPY", 200)
        
        # Get 200 bars of history
        history = self.history(self.symbol, 200, Resolution.DAILY)
        
        # Iterate through history and update the indicator
        for time, row in history.loc[self.symbol].iterrows():
            # Manually update the indicator with historical close prices
            self.slow_ema.update(time, row["close"])
            
        # Now self.slow_ema.is_ready is True immediately

Q&A

Q: What happens if I don't use set_warm_up?
A: Your indicators will start with a value of 0 (or be unready). A 200-day moving average will require the algorithm to run for 200 days of simulation time before it produces its first valid data point, effectively wasting the first year of your backtest.

Q: Does set_warm_up execute trades?
A: No, provided you check if self.is_warming_up: return in your on_data method. If you omit this check, your logic might trigger trades based on the historical data feed, but these trades will be simulated as if they happened in the past (though usually, the engine prevents submitting orders during warmup).

Q: Can I use timedelta instead of bar count for warmup?
A: Yes. You can use self.set_warm_up(timedelta(days=30)) if you prefer time-based warming over bar-count warming. This is useful for high-resolution data (e.g., Minute) where calculating the exact number of bars is difficult.

Q: How do I know when the warmup is finished?
A: The self.is_warming_up flag will turn False. Additionally, you can define an on_warmup_finished(self) method in your algorithm, which LEAN will call exactly once when the warmup phase completes.