/*
* 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.Concurrent;
using System.Collections.Generic;
using System.Threading;
using QuantConnect.Algorithm;
using QuantConnect.AlgorithmFactory.Python.Wrappers;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Interfaces;
using QuantConnect.Lean.Engine.Results;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Lean.Engine.RealTime
{
///
/// Base class for the real time handler
/// and implementations
///
public abstract class BaseRealTimeHandler : IEventSchedule
{
private int _scheduledEventUniqueId;
// For performance only add OnEndOfDay Symbol scheduled events if the method is implemented.
// When there are many securities it adds a significant overhead
private bool _implementsOnEndOfDaySymbol;
///
/// Keep track of this event so we can remove it when we need to update it
///
private ScheduledEvent _algorithmOnEndOfDay;
///
/// Keep a separate track of these scheduled events so we can remove them
/// if the security gets removed
///
private readonly ConcurrentDictionary _securityOnEndOfDay
= new ConcurrentDictionary();
///
/// The scheduled events container
///
/// Initialize this immediately since the Initialize method gets
/// called after IAlgorithm.Initialize, so we want to be ready to accept
/// events as soon as possible
protected readonly ConcurrentDictionary ScheduledEvents
= new ConcurrentDictionary();
///
/// The algorithm instance
///
protected IAlgorithm Algorithm;
///
/// The result handler instance
///
protected IResultHandler ResultHandler;
///
/// Adds the specified event to the schedule
///
/// The event to be scheduled, including the date/times
/// the event fires and the callback
public abstract void Add(ScheduledEvent scheduledEvent);
///
/// Removes the specified event from the schedule
///
/// The event to be removed
public abstract void Remove(ScheduledEvent scheduledEvent);
///
/// Initializes the real time handler for the specified algorithm and job.
/// Adds EndOfDayEvents
///
protected void Setup(DateTime start, DateTime end, Language language, DateTime? currentUtcTime = null)
{
AddAlgorithmEndOfDayEvent(start, end, currentUtcTime);
if (language == Language.CSharp)
{
var method = Algorithm.GetType().GetMethod("OnEndOfDay", new[] { typeof(Symbol) });
var method2 = Algorithm.GetType().GetMethod("OnEndOfDay", new[] { typeof(string) });
if (method != null && method.DeclaringType != typeof(QCAlgorithm)
|| method2 != null && method2.DeclaringType != typeof(QCAlgorithm))
{
_implementsOnEndOfDaySymbol = true;
}
}
else if (language == Language.Python)
{
var wrapper = Algorithm as AlgorithmPythonWrapper;
_implementsOnEndOfDaySymbol = wrapper.IsOnEndOfDayImplemented;
}
else
{
throw new ArgumentException(nameof(language));
}
}
///
/// Gets a new scheduled event unique id
///
/// This value is used to order scheduled events in a deterministic way
protected int GetScheduledEventUniqueId()
{
return Interlocked.Increment(ref _scheduledEventUniqueId);
}
///
/// Creates a new that will fire before market close by the specified time
///
/// The date to start the events
/// The date to end the events
/// Specifies the current time in UTC, before which,
/// no events will be scheduled. Specify null to skip this filter.
[Obsolete("This method is deprecated. It will add ScheduledEvents for the deprecated IAlgorithm.OnEndOfDay()")]
protected void AddAlgorithmEndOfDayEvent(DateTime start, DateTime end, DateTime? currentUtcTime = null)
{
if (_algorithmOnEndOfDay != null)
{
// if we already set it once we remove the previous and
// add a new one, we don't want to keep both
Remove(_algorithmOnEndOfDay);
}
// add end of day events for each tradeable day
_algorithmOnEndOfDay = ScheduledEventFactory.EveryAlgorithmEndOfDay(
Algorithm,
ResultHandler,
start,
end,
ScheduledEvent.AlgorithmEndOfDayDelta,
currentUtcTime);
Add(_algorithmOnEndOfDay);
}
///
/// Creates a new that will fire before market
/// close by the specified time for each provided securities.
///
/// The securities for which we want to add the OnEndOfDay event
/// The date to start the events
/// The date to end the events
/// Specifies the current time in UTC, before which,
/// no events will be scheduled. Specify null to skip this filter.
protected void AddSecurityDependentEndOfDayEvents(
IEnumerable securities,
DateTime start,
DateTime end,
DateTime? currentUtcTime = null)
{
if (_implementsOnEndOfDaySymbol)
{
// add end of trading day events for each security
foreach (var security in securities)
{
if (!security.IsInternalFeed())
{
var scheduledEvent = ScheduledEventFactory.EverySecurityEndOfDay(
Algorithm, ResultHandler, security, start, end, ScheduledEvent.SecurityEndOfDayDelta, currentUtcTime);
// we keep separate track so we can remove it later
_securityOnEndOfDay[security.Symbol] = scheduledEvent;
// assumes security.Exchange has been updated with today's hours via RefreshMarketHoursToday
Add(scheduledEvent);
}
}
}
}
///
/// Event fired each time that we add/remove securities from the data feed
///
public void OnSecuritiesChanged(SecurityChanges changes)
{
if (changes != SecurityChanges.None)
{
if (_implementsOnEndOfDaySymbol)
{
AddSecurityDependentEndOfDayEvents(changes.AddedSecurities,
Algorithm.UtcTime,
Algorithm.EndDate,
Algorithm.UtcTime);
foreach (var security in changes.RemovedSecurities)
{
ScheduledEvent scheduledEvent;
if (_securityOnEndOfDay.TryRemove(security.Symbol, out scheduledEvent))
{
// we remove the schedule events of the securities that were removed
Remove(scheduledEvent);
}
}
}
// we re add the algorithm end of day event because it depends on the securities
// tradable dates
AddAlgorithmEndOfDayEvent(Algorithm.UtcTime, Algorithm.EndDate, Algorithm.UtcTime);
}
}
}
}