Lean  $LEAN_TAG$
MarketHourAwareConsolidator.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2024 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using NodaTime;
18 using QuantConnect.Util;
22 
24 {
25  /// <summary>
26  /// Consolidator for open markets bar only, extended hours bar are not consolidated.
27  /// </summary>
29  {
30  private readonly bool _dailyStrictEndTimeEnabled;
31  private readonly bool _extendedMarketHours;
32  private bool _useStrictEndTime;
33 
34  /// <summary>
35  /// The consolidation period requested
36  /// </summary>
37  protected TimeSpan Period { get; }
38 
39  /// <summary>
40  /// The consolidator instance
41  /// </summary>
42  protected IDataConsolidator Consolidator { get; }
43 
44  /// <summary>
45  /// The associated security exchange hours instance
46  /// </summary>
47  protected SecurityExchangeHours ExchangeHours { get; set; }
48 
49  /// <summary>
50  /// The associated data time zone
51  /// </summary>
52  protected DateTimeZone DataTimeZone { get; set; }
53 
54  /// <summary>
55  /// Gets the most recently consolidated piece of data. This will be null if this consolidator
56  /// has not produced any data yet.
57  /// </summary>
59 
60  /// <summary>
61  /// Gets the type consumed by this consolidator
62  /// </summary>
64 
65  /// <summary>
66  /// Gets a clone of the data being currently consolidated
67  /// </summary>
69 
70  /// <summary>
71  /// Gets the type produced by this consolidator
72  /// </summary>
74 
75  /// <summary>
76  /// Initializes a new instance of the <see cref="MarketHourAwareConsolidator"/> class.
77  /// </summary>
78  /// <param name="resolution">The resolution.</param>
79  /// <param name="dataType">The target data type</param>
80  /// <param name="tickType">The target tick type</param>
81  /// <param name="extendedMarketHours">True if extended market hours should be consolidated</param>
82  public MarketHourAwareConsolidator(bool dailyStrictEndTimeEnabled, Resolution resolution, Type dataType, TickType tickType, bool extendedMarketHours)
83  {
84  _dailyStrictEndTimeEnabled = dailyStrictEndTimeEnabled;
85  Period = resolution.ToTimeSpan();
86  _extendedMarketHours = extendedMarketHours;
87 
88  if (dataType == typeof(Tick))
89  {
90  if (tickType == TickType.Trade)
91  {
92  Consolidator = resolution == Resolution.Daily
94  : new TickConsolidator(Period);
95  }
96  else
97  {
98  Consolidator = resolution == Resolution.Daily
101  }
102  }
103  else if (dataType == typeof(TradeBar))
104  {
105  Consolidator = resolution == Resolution.Daily
108  }
109  else if (dataType == typeof(QuoteBar))
110  {
111  Consolidator = resolution == Resolution.Daily
114  }
115  else
116  {
117  throw new ArgumentNullException(nameof(dataType), $"{dataType.Name} not supported");
118  }
120  }
121 
122  /// <summary>
123  /// Event handler that fires when a new piece of data is produced
124  /// </summary>
126 
127  /// <summary>
128  /// Updates this consolidator with the specified data
129  /// </summary>
130  /// <param name="data">The new data for the consolidator</param>
131  public virtual void Update(IBaseData data)
132  {
133  Initialize(data);
134 
135  if (_extendedMarketHours || ExchangeHours.IsOpen(data.Time, false))
136  {
137  Consolidator.Update(data);
138  }
139  }
140 
141  /// <summary>
142  /// Scans this consolidator to see if it should emit a bar due to time passing
143  /// </summary>
144  /// <param name="currentLocalTime">The current time in the local time zone (same as <see cref="P:QuantConnect.Data.BaseData.Time" />)</param>
145  public void Scan(DateTime currentLocalTime)
146  {
147  Consolidator.Scan(currentLocalTime);
148  }
149 
150  /// <summary>
151  /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
152  /// </summary>
153  public void Dispose()
154  {
156  Consolidator.Dispose();
157  }
158 
159  /// <summary>
160  /// Perform late initialization based on the datas symbol
161  /// </summary>
162  protected void Initialize(IBaseData data)
163  {
164  if (ExchangeHours == null)
165  {
166  var symbol = data.Symbol;
167  var marketHoursDatabase = MarketHoursDatabase.FromDataFolder();
168  ExchangeHours = marketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
169  DataTimeZone = marketHoursDatabase.GetDataTimeZone(symbol.ID.Market, symbol, symbol.SecurityType);
170 
171  _useStrictEndTime = UseStrictEndTime(data.Symbol);
172  }
173  }
174 
175  /// <summary>
176  /// Determines a bar start time and period
177  /// </summary>
178  protected virtual CalendarInfo DailyStrictEndTime(DateTime dateTime)
179  {
180  if (!_useStrictEndTime)
181  {
182  return new (Period > Time.OneDay ? dateTime : dateTime.RoundDown(Period), Period);
183  }
184  return LeanData.GetDailyCalendar(dateTime, ExchangeHours, _extendedMarketHours);
185  }
186 
187  /// <summary>
188  /// Useful for testing
189  /// </summary>
190  protected virtual bool UseStrictEndTime(Symbol symbol)
191  {
192  return LeanData.UseStrictEndTime(_dailyStrictEndTimeEnabled, symbol, Period, ExchangeHours);
193  }
194 
195  /// <summary>
196  /// Will forward the underlying consolidated bar to consumers on this object
197  /// </summary>
198  protected virtual void ForwardConsolidatedBar(object sender, IBaseData consolidated)
199  {
200  DataConsolidated?.Invoke(this, consolidated);
201  }
202  }
203 }