Lean  $LEAN_TAG$
LiveFillForwardEnumerator.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 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 
17 using System;
18 using NodaTime;
19 using QuantConnect.Data;
20 using QuantConnect.Util;
22 using System.Collections.Generic;
23 
25 {
26  /// <summary>
27  /// An implementation of the <see cref="FillForwardEnumerator"/> that uses an <see cref="ITimeProvider"/>
28  /// to determine if a fill forward bar needs to be emitted
29  /// </summary>
31  {
32  private readonly TimeSpan _dataResolution;
33  private readonly TimeSpan _underlyingTimeout;
34  private readonly ITimeProvider _timeProvider;
35 
36  /// <summary>
37  /// Initializes a new instance of the <see cref="LiveFillForwardEnumerator"/> class that accepts
38  /// a reference to the fill forward resolution, useful if the fill forward resolution is dynamic
39  /// and changing as the enumeration progresses
40  /// </summary>
41  /// <param name="timeProvider">The source of time used to gauage when this enumerator should emit extra bars when
42  /// null data is returned from the source enumerator</param>
43  /// <param name="enumerator">The source enumerator to be filled forward</param>
44  /// <param name="exchange">The exchange used to determine when to insert fill forward data</param>
45  /// <param name="fillForwardResolution">The resolution we'd like to receive data on</param>
46  /// <param name="isExtendedMarketHours">True to use the exchange's extended market hours, false to use the regular market hours</param>
47  /// <param name="subscriptionEndTime">The end time of the subscription, once passing this date the enumerator will stop</param>
48  /// <param name="dataResolution">The source enumerator's data resolution</param>
49  /// <param name="dataTimeZone">Time zone of the underlying source data</param>
50  /// <param name="dailyStrictEndTimeEnabled">True if daily strict end times are enabled</param>
51  /// <param name="dataType">The configuration data type this enumerator is for</param>
52  public LiveFillForwardEnumerator(ITimeProvider timeProvider, IEnumerator<BaseData> enumerator, SecurityExchange exchange, IReadOnlyRef<TimeSpan> fillForwardResolution,
53  bool isExtendedMarketHours, DateTime subscriptionEndTime, Resolution dataResolution, DateTimeZone dataTimeZone, bool dailyStrictEndTimeEnabled,
54  Type dataType = null)
55  : base(enumerator, exchange, fillForwardResolution, isExtendedMarketHours, subscriptionEndTime, dataResolution.ToTimeSpan(), dataTimeZone,
56  dailyStrictEndTimeEnabled, dataType)
57  {
58  _timeProvider = timeProvider;
59  _dataResolution = dataResolution.ToTimeSpan();
60  _underlyingTimeout = GetMaximumDataTimeout(dataResolution);
61  }
62 
63  /// <summary>
64  /// Determines whether or not fill forward is required, and if true, will produce the new fill forward data
65  /// </summary>
66  /// <param name="fillForwardResolution"></param>
67  /// <param name="previous">The last piece of data emitted by this enumerator</param>
68  /// <param name="next">The next piece of data on the source enumerator, this may be null</param>
69  /// <param name="fillForward">When this function returns true, this will have a non-null value, null when the function returns false</param>
70  /// <returns>True when a new fill forward piece of data was produced and should be emitted by this enumerator</returns>
71  protected override bool RequiresFillForwardData(TimeSpan fillForwardResolution, BaseData previous, BaseData next, out BaseData fillForward)
72  {
73  if (base.RequiresFillForwardData(fillForwardResolution, previous, next, out fillForward))
74  {
75  var underlyingTimeout = TimeSpan.Zero;
76  if (fillForwardResolution >= _dataResolution)
77  {
78  // we enforece the underlying FF timeout when the FF resolution matches it or is bigger, not the other way round, for example:
79  // this is a daily enumerator and FF resolution is second, we are expected to emit a bar every second, we can't wait until the timeout each time
80  underlyingTimeout = _underlyingTimeout;
81  }
82 
83  var nextEndTimeUtc = (fillForward.EndTime + underlyingTimeout).ConvertToUtc(Exchange.TimeZone);
84  if (next != null || nextEndTimeUtc <= _timeProvider.GetUtcNow())
85  {
86  // we FF if next is here but in the future or next has not come yet and we've wait enough time
87  return true;
88  }
89  }
90  return false;
91  }
92 
93  /// <summary>
94  /// Helper method to know how much we should wait before fill forwarding a bar in live trading
95  /// </summary>
96  /// <remarks>This allows us to create bars taking into account the market auction close and open official prices. Also it will
97  /// allow data providers which might have some delay on creating the bars on their end, to be consumed correctly, when available, by Lean</remarks>
98  public static TimeSpan GetMaximumDataTimeout(Resolution resolution)
99  {
100  switch (resolution)
101  {
102  case Resolution.Tick:
103  return TimeSpan.Zero;
104  case Resolution.Second:
105  return TimeSpan.FromSeconds(0.9);
106  case Resolution.Minute:
107  return TimeSpan.FromMinutes(0.9);
108  case Resolution.Hour:
109  return TimeSpan.FromMinutes(10);
110  case Resolution.Daily:
111  return TimeSpan.FromMinutes(10);
112  default:
113  throw new ArgumentOutOfRangeException(nameof(resolution), resolution, null);
114  }
115  }
116  }
117 }