/* * 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 Python.Runtime; using System; using QuantConnect.Data.Market; namespace QuantConnect.Data.Consolidators { /// /// Represents a timeless consolidator which depends on the given values. This consolidator /// is meant to consolidate data into bars that do not depend on time, e.g., RangeBar's. /// public abstract class BaseTimelessConsolidator : IDataConsolidator where T : IBaseData { /// /// Extracts the value from a data instance to be formed into a . /// protected Func Selector { get; set; } /// /// Extracts the volume from a data instance. The default value is null which does /// not aggregate volume per bar. /// protected Func VolumeSelector { get; set; } /// /// Event handler type for the IDataConsolidator.DataConsolidated event /// protected DataConsolidatedHandler DataConsolidatedHandler { get; set; } /// /// Bar being created /// protected virtual T CurrentBar { get; set; } /// /// Gets the most recently consolidated piece of data. This will be null if this consolidator /// has not produced any data yet. /// public IBaseData Consolidated { get; protected set; } /// /// Gets a clone of the data being currently consolidated /// public abstract IBaseData WorkingData { get; } /// /// Gets the type consumed by this consolidator /// public Type InputType => typeof(IBaseData); /// /// Gets which is the type emitted in the event. /// public virtual Type OutputType => typeof(T); /// /// Event handler that fires when a new piece of data is produced /// public event EventHandler DataConsolidated; /// /// Event handler that fires when a new piece of data is produced /// event DataConsolidatedHandler IDataConsolidator.DataConsolidated { add { DataConsolidatedHandler += value; } remove { DataConsolidatedHandler -= value; } } /// /// Initializes a new instance of the class. /// /// Extracts the value from a data instance to be formed into a new bar which inherits from . The default /// value is (x => x.Value) the property on /// Extracts the volume from a data instance. The default value is null which does /// not aggregate volume per bar. protected BaseTimelessConsolidator(Func selector = null, Func volumeSelector = null) { Selector = selector ?? (x => x.Value); VolumeSelector = volumeSelector ?? (x => x is TradeBar bar ? bar.Volume : (x is Tick tick ? tick.Quantity : 0)); } /// /// Initializes a new instance of the class. /// /// Extracts the value from a data instance to be formed into a new bar which inherits from . The default /// value is (x => x.Value) the property on /// Extracts the volume from a data instance. The default value is null which does /// not aggregate volume per bar. protected BaseTimelessConsolidator(PyObject valueSelector, PyObject volumeSelector = null) : this (TryToConvertSelector(valueSelector, nameof(valueSelector)), TryToConvertSelector(volumeSelector, nameof(volumeSelector))) { } /// /// Tries to convert the given python selector to a C# one. If the conversion is not /// possible it returns null /// /// The python selector to be converted /// The name of the selector to be used in case an exception is thrown /// This exception will be thrown if it's not possible to convert the /// given python selector to C# private static Func TryToConvertSelector(PyObject selector, string selectorName) { using (Py.GIL()) { Func resultSelector; if (selector != null && !selector.IsNone()) { if (!selector.TryConvertToDelegate(out resultSelector)) { throw new ArgumentException( $"Unable to convert parameter {selectorName} to delegate type Func"); } } else { resultSelector = null; } return resultSelector; } } /// /// Updates this consolidator with the specified data /// /// The new data for the consolidator public void Update(IBaseData data) { var currentValue = Selector(data); var volume = VolumeSelector(data); // If we're already in a bar then update it if (CurrentBar != null) { UpdateBar(data.Time, currentValue, volume); } // The state of the CurrentBar could have changed after UpdateBar(), // then we might need to create a new bar if (CurrentBar == null) { CreateNewBar(data, currentValue, volume); } } /// /// Updates the current RangeBar being created with the given data. /// Additionally, if it's the case, it consolidates the current RangeBar /// /// Time of the given data /// Value of the given data /// Volume of the given data protected abstract void UpdateBar(DateTime time, decimal currentValue, decimal volume); /// /// Creates a new bar with the given data /// /// The new data for the bar /// The new value for the bar /// The new volume to the bar protected abstract void CreateNewBar(IBaseData data, decimal currentValue, decimal volume); /// /// Event invocator for the DataConsolidated event. This should be invoked /// by derived classes when they have consolidated a new piece of data. /// /// The newly consolidated data protected void OnDataConsolidated(T consolidated) { DataConsolidated?.Invoke(this, consolidated); DataConsolidatedHandler?.Invoke(this, consolidated); Consolidated = consolidated; } /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// 2 public virtual void Dispose() { DataConsolidated = null; DataConsolidatedHandler = null; } /// /// Resets the consolidator /// public virtual void Reset() { Consolidated = null; CurrentBar = default(T); } /// /// Scans this consolidator to see if it should emit a bar due to time passing /// /// The current time in the local time zone (same as ) public void Scan(DateTime currentLocalTime) { } } }