/*
* 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);
}
}
}