Writing Algorithms
Scheduled Events
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
:
Member | Description |
---|---|
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.Today | Trigger an event once today in the time zone of the algorithm. |
self. DateRules.Tomorrow | Trigger an event once tomorrow in the time zone of the algorithm. |
Time Rules
The following table describes the supported TimeRules
:
Member | Description |
---|---|
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.Now | Trigger an event at the current time of day. |
self. TimeRules.Midnight | Trigger an event at midnight in the algorithm time zone. |
self. TimeRules.Noon | Trigger 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:
- Scheduled Events
- Consolidation event handlers
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