Time Modeling
Periods
Start and End Time
Bars have a start and end time that represent the time period of which the bar aggregates data. LEAN passes the bar to your algorithm at the end time so that you don't receive the bar before it was actually available. Free online data providers commonly timestamp their bars to the start time of the bar and include the bar close price, making your research prone to look ahead bias.
Carefully consider the end-time of data to avoid one-off errors when you import the data into QC or compare indicator values across different platforms. Generally, bar timestamps can be represented as follows: bar.EndTime = bar.Time + bar.Periodbar.end_time = bar.time + bar.period.
Period Values
Bars aggregate data across a period of time into a single object. We make this easy for you by pre-aggregating billions of raw trade-ticks into TradeBar objects and quote-ticks into QuoteBar objects.
We don't know the close of an intraday bar until the start of the next bar, which can sometimes be confusing. For daily bars, the bar will include all the ticks from market open to market close, but LEAN will emit it to your algorithm at market close. As a result, if you analyze the Friday data and then place an order, LEAN sends the order to your brokerage on Friday after the market close.
When there are no ticks during a period, LEAN emits the previous bar. This is the default behavior for bar data and it's referred to as "filling the data forward". You can enable and disable this setting when you create the security subscription.
We provide bar data in second, minute, hour, and daily bar formats. To create other periods of bars, see Consolidating Data.
Daily Periods
LEAN emits daily bars at market close or midnight, depending on your DailyPreciseEndTimedaily_precise_end_time setting. To be notified when a security has finished trading for the day, add an OnEndOfDayon_end_of_day method to your algorithm. LEAN calls this method for each security every day.
# Perform end-of-day logic for a given symbol.
def on_end_of_day(self, symbol: Symbol) -> None:
pass // Perform end-of-day logic for a given symbol.
public override void OnEndOfDay(Symbol symbol)
{
}
Point Values
Point values have no period because they occur at a singular point in time. Examples of point data includes ticks, open interest, and news releases. Tick data represents a single trade or quote in the market. It's a discrete event with no period, so the Timetime and EndTimeend_time properties are the same. LEAN emits ticks as soon as they arrive and doesn't fill them forward.
Examples
The following examples demonstrate some common practices for period time modeling.
Example 1: Tick Signals & Daily Bars
This example shows using a tick/pointwise signal from the Smart Insider Intention dataset while trading Starbuck's stock with daily bars.
To liquidate the position after two trading days, you can count the number of TradeBar objects you get in the OnDataon_data method.
public class PeriodTimeModelingAlgorithm : QCAlgorithm
{
private Equity _equity;
private Symbol _smartInsiderIntention;
// Add a member to track the number of days until the position
// should be liquidated.
private int _holdDays = 0;
public override void Initialize()
{
SetStartDate(2024, 9, 1);
SetEndDate(2024, 12, 31);
// Add an Equity asset. We will enter with a market on open order
// and hold for 2 full days, so daily resolution is sufficient.
_equity = AddEquity("SBUX", Resolution.Daily);
// Add insider trade intention as the sentiment trading signal.
_smartInsiderIntention = AddData<SmartInsiderIntention>(_equity.Symbol).Symbol;
}
public override void OnData(Slice slice)
{
// Buy Starbuck's stock whenever we receive a buyback intention
// or transaction notification due to volatility.
if (slice.ContainsKey(_smartInsiderIntention))
{
SetHoldings(_equity.Symbol, 1);
// Hold the position for 2 days to fully digest the insider
// intention sentiment.
_holdDays = 2;
}
// Count down the holding day per each daily trade bar received.
if (_equity.Holdings.Invested && slice.Bars.ContainsKey(_equity.Symbol) && _holdDays-- <= 0)
{
// LLiquidate the position if 2 days were fully counted.
Liquidate(_equity.Symbol);
}
}
} class PeriodTimeModelingAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2024, 9, 1)
self.set_end_date(2024, 12, 31)
# Add an Equity asset. We will enter with a market on open order
# and hold for 2 full days, so daily resolution is sufficient.
self._equity = self.add_equity("SBUX", Resolution.DAILY)
# Add insider trade intention as the sentiment trading signal.
self._smart_insider_intention = self.add_data(
SmartInsiderIntention, self._equity.symbol
)
# Add a member to track the number of days until the position
# should be liquidated.
self._hold_days = 0
def on_data(self, slice: Slice):
# Buy Starbuck's stock whenever we receive a buyback intention
# or transaction notification due to volatility.
if self._smart_insider_intention.symbol in slice:
self.set_holdings(self._equity.symbol, 1)
# Hold the position for 2 days to fully digest the insider
# intention sentiment.
self._hold_days = 2
# Count down the holding day per each daily trade bar received.
if self._equity.holdings.invested and self._equity.symbol in slice.bars:
self._hold_days -= 1
# Liquidate the position if 2 days were fully counted.
if self._hold_days <= 0:
self.liquidate(self._equity.symbol)
