Lean  $LEAN_TAG$
SubscriptionFilterEnumerator.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 System.Collections;
19 using System.Collections.Generic;
20 using QuantConnect.Data;
22 using QuantConnect.Logging;
25 
27 {
28  /// <summary>
29  /// Implements a wrapper around a base data enumerator to provide a final filtering step
30  /// </summary>
31  public class SubscriptionFilterEnumerator : IEnumerator<BaseData>
32  {
33  /// <summary>
34  /// Fired when there's an error executing a user's data filter
35  /// </summary>
36  public event EventHandler<Exception> DataFilterError;
37 
38  private readonly bool _liveMode;
39  private readonly Security _security;
40  private readonly DateTime _endTime;
41  private readonly bool _extendedMarketHours;
42  private readonly SecurityExchangeHours _exchangeHours;
43  private readonly ISecurityDataFilter _dataFilter;
44  private readonly IEnumerator<BaseData> _enumerator;
45 
46  /// <summary>
47  /// Convenience method to wrap the enumerator and attach the data filter event to log and alery users of errors
48  /// </summary>
49  /// <param name="resultHandler">Result handler reference used to send errors</param>
50  /// <param name="enumerator">The source enumerator to be wrapped</param>
51  /// <param name="security">The security who's data is being enumerated</param>
52  /// <param name="endTime">The end time of the subscription</param>
53  /// <param name="extendedMarketHours">True if extended market hours are enabled</param>
54  /// <param name="liveMode">True if live mode</param>
55  /// <param name="securityExchangeHours">The security exchange hours instance to use</param>
56  /// <returns>A new instance of the <see cref="SubscriptionFilterEnumerator"/> class that has had it's <see cref="DataFilterError"/>
57  /// event subscribed to to send errors to the result handler</returns>
58  public static SubscriptionFilterEnumerator WrapForDataFeed(IResultHandler resultHandler, IEnumerator<BaseData> enumerator, Security security, DateTime endTime, bool extendedMarketHours, bool liveMode,
59  SecurityExchangeHours securityExchangeHours)
60  {
61  var filter = new SubscriptionFilterEnumerator(enumerator, security, endTime, extendedMarketHours, liveMode, securityExchangeHours);
62  filter.DataFilterError += (sender, exception) =>
63  {
64  Log.Error(exception, "WrapForDataFeed");
65  resultHandler.RuntimeError("Runtime error applying data filter. Assuming filter pass: " + exception.Message, exception.StackTrace);
66  };
67  return filter;
68  }
69 
70  /// <summary>
71  /// Initializes a new instance of the <see cref="SubscriptionFilterEnumerator"/> class
72  /// </summary>
73  /// <param name="enumerator">The source enumerator to be wrapped</param>
74  /// <param name="security">The security containing an exchange and data filter</param>
75  /// <param name="endTime">The end time of the subscription</param>
76  /// <param name="extendedMarketHours">True if extended market hours are enabled</param>
77  /// <param name="liveMode">True if live mode</param>
78  /// <param name="securityExchangeHours">The security exchange hours instance to use</param>
79  public SubscriptionFilterEnumerator(IEnumerator<BaseData> enumerator, Security security, DateTime endTime, bool extendedMarketHours, bool liveMode, SecurityExchangeHours securityExchangeHours)
80  {
81  _liveMode = liveMode;
82  _enumerator = enumerator;
83  _security = security;
84  _endTime = endTime;
85  _exchangeHours = securityExchangeHours;
86  _dataFilter = _security.DataFilter;
87  _extendedMarketHours = extendedMarketHours;
88  }
89 
90  /// <summary>
91  /// Gets the element in the collection at the current position of the enumerator.
92  /// </summary>
93  /// <returns>
94  /// The element in the collection at the current position of the enumerator.
95  /// </returns>
96  public BaseData Current
97  {
98  get;
99  private set;
100  }
101 
102  /// <summary>
103  /// Gets the current element in the collection.
104  /// </summary>
105  /// <returns>
106  /// The current element in the collection.
107  /// </returns>
108  /// <filterpriority>2</filterpriority>
109  object IEnumerator.Current
110  {
111  get { return Current; }
112  }
113 
114  /// <summary>
115  /// Advances the enumerator to the next element of the collection.
116  /// </summary>
117  /// <returns>
118  /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
119  /// </returns>
120  /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception><filterpriority>2</filterpriority>
121  public bool MoveNext()
122  {
123  while (_enumerator.MoveNext())
124  {
125  var current = _enumerator.Current;
126  if (current != null)
127  {
128  try
129  {
130  // execute user data filters
131  if (current.DataType != MarketDataType.Auxiliary && !_dataFilter.Filter(_security, current))
132  {
133  continue;
134  }
135  }
136  catch (Exception err)
137  {
138  OnDataFilterError(err);
139  continue;
140  }
141 
142  // verify that the bar is within the exchange's market hours
143  if (current.DataType != MarketDataType.Auxiliary && !_exchangeHours.IsOpen(current.Time, current.EndTime, _extendedMarketHours))
144  {
145  if (_liveMode && !current.IsFillForward)
146  {
147  // TODO: replace for setting security.RealTimePrice not to modify security cache data directly
148  _security.SetMarketPrice(current);
149  }
150  continue;
151  }
152 
153  // make sure we haven't passed the end
154  if (current.Time > _endTime)
155  {
156  return false;
157  }
158  }
159 
160  Current = current;
161  return true;
162  }
163 
164  return false;
165  }
166 
167  /// <summary>
168  /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
169  /// </summary>
170  /// <filterpriority>2</filterpriority>
171  public void Dispose()
172  {
173  _enumerator.Dispose();
174  }
175 
176  /// <summary>
177  /// Sets the enumerator to its initial position, which is before the first element in the collection.
178  /// </summary>
179  /// <exception cref="T:System.InvalidOperationException">The collection was modified after the enumerator was created. </exception><filterpriority>2</filterpriority>
180  public void Reset()
181  {
182  _enumerator.Reset();
183  }
184 
185  /// <summary>
186  /// Event invocated for the <see cref="DataFilterError"/> event
187  /// </summary>
188  /// <param name="exception">The exception that was thrown when trying to perform data filtering</param>
189  private void OnDataFilterError(Exception exception)
190  {
191  var handler = DataFilterError;
192  if (handler != null) handler(this, exception);
193  }
194  }
195 }