Key Concepts
Event Handlers
Data Events
public override void OnData(Slice slice) { slice.TryGetValue(_symbol, out var myData); } public override void OnSplits(Splits splits) { splits.TryGetValue(_symbol, out var split); } public override void OnDividends(Dividends dividends) { dividends.TryGetValue(_symbol, out var dividend); } public override void OnSymbolChangedEvents(SymbolChangedEvents symbolChangedEvents) { symbolChangedEvents.TryGetValue(_symbol, out var symbolChangedEvent); } public override void OnDelistings(Delistings delistings) { delistings.TryGetValue(_symbol, out var delisting); }
def on_data(self, slice: Slice) -> None: my_data = slice.get(self._symbol) def on_splits(self, splits: Splits) -> None: split = splits.get(self._symbol) def on_dividends(self, dividends: Dividends) -> None: dividend = dividends.get(self._symbol) def on_symbol_changed_events(self, symbol_changed_events: SymbolChangedEvents) -> None: symbol_changed_event = symbol_changed_events.get(self._symbol]) def on_delistings(self, delistings: OnDelistings) -> None: delisting = delistings.get(self._symbol)
The OnData
on_data
method is the primary event handler for receiving financial data events to your algorithm. It is triggered sequentially at the point in time the data is available; in backtesting and live. For daily data this means the event is processed at midnight in backtesting or 6am the following day in live trading. In this method, 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.
In backtesting, if your algorithm takes a long time to process a slice, the following slice objects queue up and the next event triggers when your algorithm finishes processing the current slice. In live trading, if your algorithm takes longer to process the current slice than the time period that the slice spans, the next event triggers when your algorithm finishes processing the current slice, but the slice of the following event is the most recent slice. For example, say your algorithm consumes second resolution Crypto data but it takes your algorithm 3.5 seconds to process each slice. In backtesting, you'll get every slice. In live trading, if you deploy at 12:00:00 AM Coordinated Universal Time (UTC), you'll get the first slice at 12:00:01 AM UTC (spanning 12:00:00 AM UTC to 12:00:01 AM UTC) and you'll get the second slice at 12:00:04.5 AM UTC (roughly spanning 12:00:03 AM UTC to 12:00:04 AM UTC).
The OnSplits
on_splits
, OnDividends
on_dividends
, OnSymbolChangedEvents
on_symbol_changed_events
, and OnDelistings
on_delistings
event handlers provide data for their respective types in an isolated way. However, all of the data for these corporate actions is also available in the Slice
in OnData
on_data
.
To perform intensive computation before the market opens, use a Scheduled Event or the Traintrain method.
For more information on the Slice
object and OnData event, see Handling Data.
Securities Changed Event
public override void OnSecuritiesChanged(SecurityChanges changes) { foreach (var security in changes.AddedSecurities) { Debug($"{Time}: Added {security.Symbol}"); } foreach (var security in changes.RemovedSecurities) { Debug($"{Time}: Removed {security.Symbol}"); if (security.Invested) { Liquidate(security.Symbol, "Removed from Universe"); } } }
def on_securities_changed(self, changes: SecurityChanges) -> None: for security in changes.added_securities:: self.debug(f"{self.time}: Added {security.symbol}") for security in changes.removed_securities: self.debug(f"{self.time}: Removed {security.symbol}") if security.invested: self.liquidate(security.symbol, "Removed from Universe")
The OnSecuritiesChanged
on_securities_changed
event notifies the algorithm when assets are added or removed from the universe. This can be due to changes in the Universe constituents, or an explicit call to the RemoveSecurityremove_security method.
The event is triggered immediately when the asset is removed from the universe; however the data feed for the asset may remain active if the algorithm has open orders.
For more information, see how to use Security Changed Events.
Order Events
public override void OnOrderEvent(OrderEvent orderEvent) { var order = Transactions.GetOrderById(orderEvent.OrderId); if (orderEvent.Status == OrderStatus.Filled) { Debug($"{Time}: {order.Type}: {orderEvent}"); } } public override void OnAssignmentOrderEvent(OrderEvent assignmentEvent) { Log(assignmentEvent.ToString()); }
def on_order_event(self, order_event: OrderEvent) -> None: order = self.transactions.get_order_by_id(order_event.order_id) if order_event.status == OrderStatus.FILLED: self.debug(f"{self.time}: {order.type}: {order_event}") def on_assignment_order_event(self, assignment_event: OrderEvent) -> None: self.log(str(assignment_event))
The OnOrderEvent
on_order_event
method notifies the algorithm of new orders, and changes in the order status such as fill events and cancelations. For options assignment events there is a dedicated event handler OnAssignmentOrderEvent
on_assignment_order_event
.
In backtesting order events are triggered synchronously after the main data events. In live trading, order events are asynchronously as they occur. To avoid infinite loops, we recommend not to place orders in the OnOrderEvent.
For more information, see how to use Order Events.
Brokerage Events
public override void OnBrokerageMessage(BrokerageMessageEvent message) { if (message.Type== BrokerageMessageType.Reconnect) { Log($"{Time}: {message.Type}: Message: {message.Message}"); } } public override void OnBrokerageDisconnect() { Log($"Brokerage disconnection detected at {Time}."); } public override void OnBrokerageReconnect() { Log($"Brokerage reconnected at {Time}."); }
def on_brokerage_message(self, message: BrokerageMessageEvent) -> None: if message.type == BrokerageMessageType.RECONNECT: self.log(f"{self.time}: {message.type}: Message: {message.message}") def on_brokerage_disconnect(self) -> None: self.log(f"Brokerage disconnection detected at {self.time}") def on_brokerage_reconnect(self) -> None: self.log(f"Brokerage reconnected at {self.time}")
The OnBrokerageDisconnect
on_brokerage_disconnect
and OnBrokerageReconnect
on_brokerage_reconnect
event handlers pass information to the algorithm about the brokerage connection status. This can be helpful for considering when to place a trade when a brokerage API is unreliable or under maintenance. The OnBrokerageMessage
on_brokerage_message
event handler provides information from the brokerages, including the disconnect and reconnect messages. Message content varies with each brokerage.
Brokerage events are triggered asynchronously in live trading, and are not created in backtesting.
Margin Call Events
public override void OnMarginCall(Listrequests) { foreach (var orderRequest in requests) { } } public override void OnMarginCallWarning() { Log("Margin call warning, 5% margin remaining"); }
def on_margin_call(self, requests): -> List[SubmitOrderRequest]: # Modify order requests to choose what to liquidate. return requests def on_margin_call_warning(self) -> None: self.log("Margin call warning, 5% margin remaining")
The OnMarginCallWarning
on_margin_call_warning
and OnMarginCall
on_margin_call
event handlers provide information and control over how to reduce algorithm leverage when performance is poor.
The OnMarginCallWarning
on_margin_call_warning
method is called when there is less than 5% margin remaining, this gives you an opportunity to reduce leverage before an official margin call is performed by the brokerage. The OnMarginCall
on_margin_call
event handler is passed a list of SubmitOrderRequest
objects which will be executed on exiting the method.
Margin events are called before the data events are processed.
The following demonstration demonstration processes suggested orders and modifies the qualities to liquidate more than necessary and prevent a second margin call. For more information, see how to handle Margin Calls.
Warmup Finished Event
public override void OnWarmupFinished() { // Done warming up }
def on_warmup_finished(self) -> None: pass # Done warming up
The OnWarmupFinished
on_warmup_finished
event handler is triggered after initialization and warm up is complete. This event signals that the initialization of the algorithm is complete.
For more information, see how to use Warm Up to prepare your algorithm.
End Of Algorithm Events
def on_end_of_algorithm(self) -> None: self.debug("Algorithm done")
public override void OnEndOfAlgorithm() { Debug("Algorithm done"); }
The OnEndOfAlgorithm
on_end_of_algorithm
event handler is when the algorithm has finished executing as its final step. This is helpful for performing final analysis, or saving algorithm state.
Event Flow
When you deploy an algorithm, LEAN first calls the Initializeinitialize method. In live mode, the engine loads your holdings and open orders from your brokerage account to add data subscriptions and populate the Securities
securities
, Portfolio
portfolio
, and Transactions
transactions
objects. LEAN receives the data from the subscriptions, synchronizes the data to create a timeslice, and then performs the following steps. Your algorithm can spend up to 10 minutes on each timeslice unless you call the Traintrain method.
- If it's a backtest, check if there are Scheduled Events in the past that didn't fire because there was no data between the previous slice and the current slice. LEAN automatically creates a Scheduled Event to call the
OnEndOfDay
on_end_of_day
method at the end of each day. - Update the algorithm time.
- Update the
CurrentSlice
current_slice
. - Pass the
SymbolChangedEvents
to theOnSymbolChangedEvents
on_symbol_changed_events
method. - Cancel all open orders for securities that changed their ticker.
- Add a
Security
object to theSecurities
securities
collection for each new security in the universe. - Update the
Security
objects with the latest data. - Update the
Cash
objects in the CashBook with the latest data. - Process fill models for non-market orders.
- Submit market on open orders to liquidate Equity Option contracts if the underlying Equity has a split warning.
- Process margin calls.
- If it's time to settle unsettled cash, perform settlement.
- Call the OnSecuritiesChangedon_securities_changed method with the latest security changes.
- Apply dividends to the portfolio.
- For securities that have a split warning, update their portfolio holdings and adjust their open orders to account for the split.
- Update consolidators with the latest data.
- Pass custom data to the
OnData
on_data
method. - Pass the
Dividends
to theOnDividends
on_dividends
method. - Pass the
Splits
to theOnSplits
on_splits
method. - Pass the
Delistings
to theOnDelistings
on_delistings
method. - Pass the
Slice
to theOnData
on_data
method. - Perform universe selection.
- Pass the
Slice
to theUpdate
update
method of each Alpha model. - Pass the
Insight
objects from the Alpha model to the Portfolio Construction model. - Pass the
PortfolioTarget
objects from the Portfolio Construction model to each Risk Management model. - Pass the risk-adjusted
PortfolioTarget
objects from the Risk Management models to the Execution model.
In live mode, Scheduled Events occur in a separate thread from the algorithm manager, so they run at the correct time.
When your algorithm stops executing, LEAN calls the OnEndOfAlgorithm
on_end_of_algorithm
method.