/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using System; using System.Collections; using System.Collections.Generic; using QuantConnect.Data; using QuantConnect.Lean.Engine.Results; using QuantConnect.Logging; using QuantConnect.Securities; using QuantConnect.Securities.Interfaces; namespace QuantConnect.Lean.Engine.DataFeeds.Enumerators { /// /// Implements a wrapper around a base data enumerator to provide a final filtering step /// public class SubscriptionFilterEnumerator : IEnumerator { /// /// Fired when there's an error executing a user's data filter /// public event EventHandler DataFilterError; private readonly bool _liveMode; private readonly Security _security; private readonly DateTime _endTime; private readonly bool _extendedMarketHours; private readonly SecurityExchangeHours _exchangeHours; private readonly ISecurityDataFilter _dataFilter; private readonly IEnumerator _enumerator; /// /// Convenience method to wrap the enumerator and attach the data filter event to log and alery users of errors /// /// Result handler reference used to send errors /// The source enumerator to be wrapped /// The security who's data is being enumerated /// The end time of the subscription /// True if extended market hours are enabled /// True if live mode /// The security exchange hours instance to use /// A new instance of the class that has had it's /// event subscribed to to send errors to the result handler public static SubscriptionFilterEnumerator WrapForDataFeed(IResultHandler resultHandler, IEnumerator enumerator, Security security, DateTime endTime, bool extendedMarketHours, bool liveMode, SecurityExchangeHours securityExchangeHours) { var filter = new SubscriptionFilterEnumerator(enumerator, security, endTime, extendedMarketHours, liveMode, securityExchangeHours); filter.DataFilterError += (sender, exception) => { Log.Error(exception, "WrapForDataFeed"); resultHandler.RuntimeError("Runtime error applying data filter. Assuming filter pass: " + exception.Message, exception.StackTrace); }; return filter; } /// /// Initializes a new instance of the class /// /// The source enumerator to be wrapped /// The security containing an exchange and data filter /// The end time of the subscription /// True if extended market hours are enabled /// True if live mode /// The security exchange hours instance to use public SubscriptionFilterEnumerator(IEnumerator enumerator, Security security, DateTime endTime, bool extendedMarketHours, bool liveMode, SecurityExchangeHours securityExchangeHours) { _liveMode = liveMode; _enumerator = enumerator; _security = security; _endTime = endTime; _exchangeHours = securityExchangeHours; _dataFilter = _security.DataFilter; _extendedMarketHours = extendedMarketHours; } /// /// Gets the element in the collection at the current position of the enumerator. /// /// /// The element in the collection at the current position of the enumerator. /// public BaseData Current { get; private set; } /// /// Gets the current element in the collection. /// /// /// The current element in the collection. /// /// 2 object IEnumerator.Current { get { return Current; } } /// /// Advances the enumerator to the next element of the collection. /// /// /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. /// /// The collection was modified after the enumerator was created. 2 public bool MoveNext() { while (_enumerator.MoveNext()) { var current = _enumerator.Current; if (current != null) { try { // execute user data filters if (current.DataType != MarketDataType.Auxiliary && !_dataFilter.Filter(_security, current)) { continue; } } catch (Exception err) { OnDataFilterError(err); continue; } // verify that the bar is within the exchange's market hours if (current.DataType != MarketDataType.Auxiliary && !_exchangeHours.IsOpen(current.Time, current.EndTime, _extendedMarketHours)) { if (_liveMode && !current.IsFillForward) { // TODO: replace for setting security.RealTimePrice not to modify security cache data directly _security.SetMarketPrice(current); } continue; } // make sure we haven't passed the end if (current.Time > _endTime) { return false; } } Current = current; return true; } return false; } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// /// 2 public void Dispose() { _enumerator.Dispose(); } /// /// Sets the enumerator to its initial position, which is before the first element in the collection. /// /// The collection was modified after the enumerator was created. 2 public void Reset() { _enumerator.Reset(); } /// /// Event invocated for the event /// /// The exception that was thrown when trying to perform data filtering private void OnDataFilterError(Exception exception) { var handler = DataFilterError; if (handler != null) handler(this, exception); } } }