Time Modeling
Timeslices
Introduction
The core technology behind QuantConnect algorithmic trading is an event-based, streaming analysis system called LEAN. LEAN attempts to model the stream of time as accurately as possible, presenting data ("events") to your algorithms in the order it arrives, as you would experience in reality.
All QuantConnect algorithms have this time-stream baked in as the primary event handler, OnData
. The Slice
object this method receives represents all of the data at a moment of time, a time-slice. No matter what data you request, you receive it in the order created according to simulated algorithm time. By only letting your algorithm see the present and past moments, we can prevent the most common quantitative-analysis error, look-ahead bias.
Time Frontier
If you request data for multiple securities and multiple resolutions, it can create a situation where one of your data subscriptions is ready to emit, but another subscription with a longer period is still be constructing its bar. Furthermore, if you request multiple datasets that have different time zones, your algorithm will receive the daily bars of each dataset at midnight in the respective time zone. To coordinate the data in these situations, we use the EndTime
of each data point to signal when LEAN should transmit it to your algorithm.

Once your algorithm reaches the EndTime
of a data point, LEAN sends the data to your OnData
method. For bar data, this is the beginning of the next period. To avoid look-ahead bias, you can only access data from before this Time Frontier. The Time
property of your algorithm is always equal to the Time Frontier.
Get Time Slices
To get the current Slice
object, define an OnData
method or use the CurrentSlice
property of your algorithm. The Slice
contains all the data for a given moment in time. The TradeBars
and QuoteBars
properties are Symbol
/string indexed dictionaries. The Ticks
property is a list of ticks for that moment of time, indexed by the Symbol
. To check which data formats are available for each asset class, see the Data Formats page in the Asset Classes chapter.
The Slice
object gives you the following ways to access your data:
- Indexing the
Slice
, which returns a dynamic object of your type. - Indexing the static properties, which returns the type you specify.
-
Calling the
Get<T>()
helper method.
def OnData(self, slice: Slice) -> None: data = slice[self.symbol]
public override void OnData(Slice slice) { var data = slice[_symbol]; }
With minute and second resolution data, the dynamic type is TradeBar
for Equities and QuoteBar
for other asset classes.
def OnData(self, slice: Slice) -> None: trade_bar = slice.Bars[self.symbol] quote_bar = slice.QuoteBars[self.symbol]
public override void OnData(Slice slice) { var tradeBars = slice.Bars[_symbol]; var quoteBars = slice.QuoteBars[_symbol]; }
public override void OnData(Slice slice) { var tradeBar = slice.Get<TradeBar>(_symbol); var quoteBar = slice.Get<QuoteBar>(_symbol); var ticks = slice.Get<Ticks>(_symbol); }
Strongly typed access gives you compile-time safety, but dynamic type access can sometimes simplify coding. We recommend static types since they are easier to debug.
Check if the Slice
contains the data you're looking for before you index it. If there is little trading, or you are in the same time loop as when you added the security, it may not have any data. Even if you enabled fill-forward for a security subscription, you should
check if the data exists in the dictionary before you try to index it. To check if the Slice
contains for a security, call the ContainsKey
method. Note: if the Slice
object doesn't contain any market data but it contains auxiliary data, the slice.ContainsKey(symbol)
method can return true while slice[symbol]
returns None
null
.
def OnData(self, slice: Slice) -> None: if slice.ContainsKey(self.symbol) and slice[self.symbol] is not None: data = slice[self.symbol]
public override void OnData(Slice slice) { if (slice.ContainsKey(_symbol)) { var data = slice[_symbol]; } }