Writing Algorithms

Scheduled Events

Introduction

Scheduled Events let you trigger code to run at specific times of day, regardless of your algorithm's data subscriptions. It's easier and more reliable to execute time-based events with Scheduled Events than checking the current algorithm time in the OnData event handler.

Create Scheduled Events

To create a Scheduled Event, call the Schedule.On method. The method expects a DateRules object, a TimeRules object, and a function to execute. The following examples demonstrate some common Scheduled Events.

Schedule Events Before Market Open

You may want to train a model or fetch historical data before the market opens. The following example demonstrates how to set a Scheduled Event for 10 minutes before the market opens.

Schedule.On(DateRules.EveryDay("SPY"),
            TimeRules.AfterMarketOpen("SPY", -10),
            BeforeMarketOpen);
self.Schedule.On(self.DateRules.EveryDay("SPY"),
                 self.TimeRules.AfterMarketOpen("SPY", -10),
                 self.BeforeMarketOpen)

Schedule Events on the Last Trading Day of the Week

You may want to rebalance your portfolio on the last trading day of each week and factor in market holidays. The following example demonstrates how to set a Scheduled Event for the last trading day of each week 30 minutes before the market closes.

Schedule.On(DateRules.WeekEnd("SPY"),
            TimeRules.BeforeMarketClose("SPY", 30),
            Rebalance);
self.Schedule.On(self.DateRules.WeekEnd("SPY"),
                 self.TimeRules.BeforeMarketClose("SPY", 30),
                 self.Rebalance)

Schedule Events on Regular Intervals Throughout the Trading Day

You may want to perform some action on a regular interval through each trading day. The following example demonstrates how to set a Scheduled Event for every 30 minutes through the trading day for SPY.

Schedule.On(DateRules.EveryDay("SPY"),
            TimeRules.Every(TimeSpan.FromMinutes(30)),
            SomeAction);
self.Schedule.On(self.DateRules.EveryDay("SPY"),
                 self.TimeRules.Every(timedelta(minutes=30)),
                 self.SomeAction)

Date Rules

The following table describes the supported DateRules:

MemberDescription
self.DateRules.On(year: int, month: int, day: int) DateRules.On(int year, int month, int day)Trigger an event on a specific date in the algorithm time zone.
self.DateRules.EveryDay()Trigger an event every day.
self.DateRules.EveryDay(symbol: Symbol) DateRules.EveryDay(Symbol symbol)Trigger an event every day a specific symbol is trading.
self.DateRules.Every(days: list[DayOfWeek]) DateRules.Every(params DayOfWeek[] days)Trigger an event on specific days throughout the week in the algorithm time zone. To view the DayOfWeek enum members, see DayOfWeek Enum in the .NET documentation.
self.DateRules.MonthStart(daysOffset: int = 0) DateRules.MonthStart(int daysOffset = 0)Trigger an event on the first day of each month in the algorithm time zone plus an offset.
self.DateRules.MonthStart(symbol: Symbol, daysOffset: int = 0) DateRules.MonthStart(Symbol symbol, int daysOffset = 0)Trigger an event on the first tradable date of each month for a specific symbol plus an offset.
self.DateRules.MonthEnd(daysOffset: int = 0) DateRules.MonthEnd(int daysOffset = 0)Trigger an event on the last day of each month in the algorithm time zone minus an offset.
self.DateRules.MonthEnd(symbol: Symbol, daysOffset: int = 0) DateRules.MonthEnd(Symbol symbol, int daysOffset = 0)Trigger an event on the last tradable date of each month for a specific symbol minus an offset.
self.DateRules.WeekStart(daysOffset: int = 0) DateRules.WeekStart(int daysOffset = 0)Trigger an event on the first day of each week in the algorithm time zone plus an offset.
self.DateRules.WeekStart(symbol: Symbol, daysOffset: int = 0) DateRules.WeekStart(Symbol symbol, int daysOffset = 0)Trigger an event on the first tradable date of each week for a specific symbol plus an offset.
self.DateRules.WeekEnd(daysOffset: int = 0) DateRules.WeekEnd(int daysOffset = 0)Trigger an event on the last day of each week in the algorithm time zone minus an offset.
self.DateRules.WeekEnd(symbol: Symbol, daysOffset: int = 0) DateRules.WeekEnd(Symbol symbol, int daysOffset = 0)Trigger an event on the last tradable date of each week for a specific symbol minus an offset.
self.DateRules.TodayTrigger an event once today in the time zone of the algorithm.
self.DateRules.TomorrowTrigger an event once tomorrow in the time zone of the algorithm.

Time Rules

The following table describes the supported TimeRules:

MemberDescription
self.TimeRules.At(hour: int, minute: int, second: int = 0) TimeRules.At(int hour, int minute, int second = 0)Trigger an event at a specific time of day (e.g. 13:10) in the algorithm time zone.
self.TimeRules.AfterMarketOpen(symbol: Symbol, minutesAfterOpen: float = 0, extendedMarketOpen: bool = False) TimeRules.AfterMarketOpen(Symbol symbol, double minutesAfterOpen = 0, bool extendedMarketOpen = false)Trigger an event a few minutes after market open for a specific symbol (default is 0). This rule doesn't work for Crypto securities or custom data.
self.TimeRules.BeforeMarketClose(symbol: Symbol, minutesBeforeClose: float = 0, extendedMarketOpen: bool = False) TimeRules.BeforeMarketClose(Symbol symbol, double minutesBeforeClose = 0, bool extendedMarketOpen = false)Trigger an event a few minutes before market close for a specific symbol (default is 0). This rule doesn't work for Crypto securities or custom data.
self.TimeRules.Every(interval: timedelta) TimeRules.Every(TimeSpan interval)Trigger an event every period interval starting at midnight in the algorithm time zone.
self.TimeRules.NowTrigger an event at the current time of day.
self.TimeRules.MidnightTrigger an event at midnight in the algorithm time zone.
self.TimeRules.NoonTrigger an event at noon in the algorithm time zone.

Remove Scheduled Events

If you no longer need a Scheduled Event in your algorithm, remove it so your algorithm doesn't execute unnecessary functions. To remove a Scheduled Event, save a reference to the Scheduled Event when you create it and then call the Remove method to remove it.

// Create a Scheduled Event
var scheduledEvent = Schedule.On(DateRules.EveryDay("SPY"),
                                 TimeRules.AfterMarketOpen("SPY", 10),
                                 tenMinutesAfterOpen);

// Remove the Scheduled Event
Schedule.Remove(scheduledEvent);
# Create a Scheduled Event
scheduled_event = self.Schedule.On(self.DateRules.EveryDay("SPY"),
                                   self.TimeRules.AfterMarketOpen("SPY", 10),       
                                   self.ten_minutes_after_open)

# Remove the Scheduled Event
self.Schedule.Remove(scheduled_event)

Common Errors

Common errors with Scheduled Events include stale fills and execution timeouts.

Stale Fills

A common error is to subscribe to daily resolution data and set a Scheduled Event intraday to place trades. If you trade intraday with daily data, you get stale fills.

Another common error is to set a Scheduled Event to trade immediately after the market open on illiquid securities. Illiquid securities can have no trades for the first few minutes after the market open. If you trade during this time, you get stale fills.

Execution Timeouts

If your Scheduled Event takes longer than 10 minutes to execute, your algorithm will timeout. To increase the amount of time that your Scheduled Event can run, replace your Scheduled Event with a training session.

Execution Sequence

The algorithm manager calls events in the following order:

  1. Scheduled Events
  2. Consolidation event handlers
  3. OnData event handler

This event flow is important to note. For instance, if your consolidation handlers or OnData event handler appends data to a RollingWindow and you use that RollingWindow in your Scheduled Event, when the Scheduled Event executes, the RollingWindow won't contain the most recent data.

Live Trading Considerations

In live trading, Scheduled Events execute in a parallel thread based on a real-time clock. If you set a Scheduled Event to fire at 10:00 AM, it executes at exactly 10:00 AM. In backtesting, Scheduled Events are part of the main algorithm manager loop, so they may not execute exactly when you set them. For example, if your algorithm subscribes to minute resolution US Equity data with regular trading hours and you set a Scheduled Event to occur at 2:00 AM, your Scheduled Event will execute at 9:31 AM when the next bar is fed into your algorithm.

The difference between live trading and backtesting is important to note because it can affect your algorithm's behavior. There are two common scenarios to consider.

Execution Timing and Backtest Timeouts

Take the following scenario:

  • You set Scheduled Events for 2:00 AM, 3:00 AM, and 4:00 AM each day.
  • Each Scheduled Event takes eight minutes to execute.
  • Your algorithm only subscribes to US Equity securities without extended market hours (9:30 AM - 4:00 PM).

In this scenario, the Scheduled Events each fire at the correct time and execute without error in live trading. In backtesting, all of the Scheduled Events execute at 9:31 AM when your algorithm receives the first bar of the trading day. Since all the Scheduled Events take eight minutes to execute, the algorithm tries to execute all the Scheduled Events but reaches the 10-minute timeout and the backtest stops execution.

Live Data Delays

In backtests, your algorithm receives data at perfect timing. If you request minute resolution data, your algorithm receives the bars at the top of each minute. In live trading, bars have a slight delay, so you may receive them milliseconds after the top of each minute. Take the following scenario:

  • You subscribe to minute resolution data
  • You set a Scheduled Event for 10:00 AM
  • The Scheduled Event checks the current asset price

In live trading, the Scheduled Event executes at exactly 10:00 AM but your algorithm may receive the 9:59-10:00 AM bar at 10:00:00.01 AM. Therefore, when you check the price in the Scheduled Event, the price from the 9:58-9:59 AM bar is the latest price. In backtesting, the Scheduled Event gets the price from the 9:59-10:00 AM bar since your algorithm receives the bar at perfect timing.

Examples

// Schedule an event to fire at a specific date/time
Schedule.On(DateRules.On(2013, 10, 7),
            TimeRules.At(13, 0),
            () => Log($"SpecificTime: Fired at : {Time}"));

// Schedule an event to fire every trading day for a security
// The time rule here tells it to fire 10 minutes after SPY's market open
Schedule.On(DateRules.EveryDay("SPY"),
            TimeRules.AfterMarketOpen("SPY", 10),
            () => Log($"EveryDay.SPY 10 min after open: Fired at: {Time}"));

// Schedule an event to fire every trading day for a security
// The time rule here tells it to fire 10 minutes before SPY's market close
Schedule.On(DateRules.EveryDay("SPY"),
            TimeRules.BeforeMarketClose("SPY", 10),
            () => Log($"EveryDay.SPY 10 min before close: Fired at: {Time}"));

// Schedule an event to fire on certain days of the week
Schedule.On(DateRules.Every(DayOfWeek.Monday, DayOfWeek.Friday),
            TimeRules.At(12, 0),
            () => Log($"Mon/Fri at 12pm: Fired at: {Time}"));

// Schedule an event to fire once today at when this method is called (now)
Schedule.On(DateRules.Today,
            TimeRules.Now,
            () => Log($"Now: Fired at: {Time}"));

// Schedule an event to fire once tomorrow at midnight
Schedule.On(DateRules.Tomorrow,
            TimeRules.Midnight,
            () => Log($"Tomorrow at midnight: Fired at: {Time}"));

// Schedule an event to fire once today at noon
Schedule.On(DateRules.Today,
            TimeRules.Noon,
            () => Log($"Today at noon: Fired at: {Time}"));

// The scheduling methods return the ScheduledEvent object which can be used 
// for other things here I set the event up to check the portfolio value every
// 10 minutes, and liquidate if we have too many losses
Schedule.On(DateRules.EveryDay(), 
            TimeRules.Every(TimeSpan.FromMinutes(10)),
            LiquidateUnrealizedLosses);
            
// Schedule an event to fire at the beginning of the month, the symbol is optional. 
// if specified, it will fire the first trading day for that symbol of the month,
// if not specified it will fire on the first day of the month
Schedule.On(DateRules.MonthStart("SPY"),
            TimeRules.AfterMarketOpen("SPY"),
            RebalancingCode);

// Schedule an event to fire at the end of the month, the symbol is optional. 
// if specified, it will fire the last trading day for that symbol of the month,
// if not specified it will fire on the first day of the month
Schedule.On(DateRules.MonthEnd("SPY"),
            TimeRules.BeforeMarketClose("SPY"),
            RebalancingCode);

// Schedule an event to fire at the beginning of the week, the symbol is optional. 
// if specified, it will fire the first trading day for that symbol of the week,
// if not specified it will fire on the first day of the week
Schedule.On(DateRules.WeekStart("SPY"),
            TimeRules.AfterMarketOpen("SPY", 5),
            RebalancingCode);

// Schedule an event to fire at the end of the week, the symbol is optional. 
// if specified, it will fire the last trading day for that symbol of the week,
// if not specified it will fire on the first day of the week
Schedule.On(DateRules.WeekEnd("SPY"),
            TimeRules.BeforeMarketClose("SPY", 5),
            RebalancingCode);


// The following methods are not defined in Initialize:
private void LiquidateUnrealizedLosses()
{
    // if we have over 1000 dollars in unrealized losses, liquidate
    if (Portfolio.TotalUnrealizedProfit < -1000)
    {
        Log($"Liquidated due to unrealized losses at: {Time}");
        Liquidate();
    }
}

private void RebalancingCode()
{
    // Good spot for rebalancing code?
}            
# Schedule an event to fire at a specific date/time
self.Schedule.On(self.DateRules.On(2013, 10, 7), 
                 self.TimeRules.At(13, 0), 
                 lambda: self.Log(f"SpecificTime: Fired at : {self.Time}"))

# Schedule an event to fire every trading day for a security the
# time rule here tells it to fire 10 minutes after SPY's market open
self.Schedule.On(self.DateRules.EveryDay("SPY"), 
                 self.TimeRules.AfterMarketOpen("SPY", 10),
                 lambda: self.Log(f"EveryDay.SPY 10 min after open: Fired at: {self.Time}"))

# Schedule an event to fire every trading day for a security the
# time rule here tells it to fire 10 minutes before SPY's market close
self.Schedule.On(self.DateRules.EveryDay("SPY"),
                 self.TimeRules.BeforeMarketClose("SPY", 10),
                 lambda: self.Log(f"EveryDay.SPY 10 min before close: Fired at: {self.Time}"))

# Schedule an event to fire on certain days of the week
self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Friday),
                 self.TimeRules.At(12, 0),
                 lambda: self.Log(f"Mon/Fri at 12pm: Fired at: {self.Time}"))

# Schedule an event to fire once today at when this method is called (now)
self.Schedule.On(self.DateRules.Today,
                 self.TimeRules.Now,
                 lambda: self.Log(f"Now: Fired at: {self.Time}"))

# Schedule an event to fire once tomorrow at midnight
self.Schedule.On(self.DateRules.Tomorrow,
                 self.TimeRules.Midnight,
                 lambda: self.Log(f"Tomorrow at midnight: Fired at: {self.Time}"))

# Schedule an event to fire once today at noon
self.Schedule.On(self.DateRules.Today,
                 self.TimeRules.Noon,
                 lambda: self.Log(f"Today at noon: Fired at: {self.Time}"))

# the scheduling methods return the ScheduledEvent object which can be used 
# for other things here I set the event up to check the portfolio value every
# 10 minutes, and liquidate if we have too many losses
self.Schedule.On(self.DateRules.EveryDay(), 
                 self.TimeRules.Every(timedelta(minutes=10)),
                 self.LiquidateUnrealizedLosses)

# Schedule an event to fire at the beginning of the month, the symbol is optional. 
# if specified, it will fire the first trading day for that symbol of the month,
# if not specified it will fire on the first day of the month
self.Schedule.On(self.DateRules.MonthStart("SPY"),
                 self.TimeRules.AfterMarketOpen("SPY"),
                 self.RebalancingCode)

# Schedule an event to fire at the end of the month, the symbol is optional. 
# if specified, it will fire the last trading day for that symbol of the month,
# if not specified it will fire on the first day of the month
self.Schedule.On(self.DateRules.MonthEnd("SPY"),
                 self.TimeRules.BeforeMarketClose("SPY"),
                 self.RebalancingCode)

# Schedule an event to fire at the beginning of the week, the symbol is optional. 
# if specified, it will fire the first trading day for that symbol of the week,
# if not specified it will fire on the first day of the week
self.Schedule.On(self.DateRules.WeekStart("SPY"),
                 self.TimeRules.AfterMarketOpen("SPY", 5),
                 self.RebalancingCode)

# Schedule an event to fire at the end of the week, the symbol is optional.
# if specified, it will fire the last trading day for that symbol of the week,
# if not specified it will fire on the first day of the week
self.Schedule.On(self.DateRules.WeekEnd("SPY"),
                 self.TimeRules.BeforeMarketClose("SPY", 5),
                 self.RebalancingCode)


# The following methods are not defined in Initialize:
def LiquidateUnrealizedLosses(self) -> None:
    ''' if we have over 1000 dollars in unrealized losses, liquidate'''
    if self.Portfolio.TotalUnrealizedProfit < -1000:
        self.Log(f"Liquidated due to unrealized losses at: {self.Time}")
        self.Liquidate()

def RebalancingCode(self) -> None:
    ''' Good spot for rebalancing code?'''
    pass

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: