195 lines
8.2 KiB
C#
195 lines
8.2 KiB
C#
/*
|
|
* 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
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public abstract class BaseTimelessConsolidator<T> : ConsolidatorBase
|
|
where T : IBaseData
|
|
{
|
|
/// <summary>
|
|
/// Extracts the value from a data instance to be formed into a <see cref="T"/>.
|
|
/// </summary>
|
|
protected Func<IBaseData, decimal> Selector { get; set; }
|
|
|
|
/// <summary>
|
|
/// Extracts the volume from a data instance. The default value is null which does
|
|
/// not aggregate volume per bar.
|
|
/// </summary>
|
|
protected Func<IBaseData, decimal> VolumeSelector { get; set; }
|
|
|
|
/// <summary>
|
|
/// Bar being created
|
|
/// </summary>
|
|
protected virtual T CurrentBar { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets a clone of the data being currently consolidated
|
|
/// </summary>
|
|
public abstract override IBaseData WorkingData { get; }
|
|
|
|
/// <summary>
|
|
/// Gets the type consumed by this consolidator
|
|
/// </summary>
|
|
public override Type InputType => typeof(IBaseData);
|
|
|
|
/// <summary>
|
|
/// Gets <see cref="T"/> which is the type emitted in the <see cref="IDataConsolidator.DataConsolidated"/> event.
|
|
/// </summary>
|
|
public override Type OutputType => typeof(T);
|
|
|
|
/// <summary>
|
|
/// Typed event handler that fires when a new piece of data is produced
|
|
/// </summary>
|
|
public new event EventHandler<T> DataConsolidated;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="BaseTimelessConsolidator{T}" /> class.
|
|
/// </summary>
|
|
/// <param name="selector">Extracts the value from a data instance to be formed into a new bar which inherits from <see cref="IBaseData"/>. The default
|
|
/// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
|
|
/// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
|
|
/// not aggregate volume per bar.</param>
|
|
protected BaseTimelessConsolidator(Func<IBaseData, decimal> selector = null, Func<IBaseData, decimal> volumeSelector = null)
|
|
{
|
|
Selector = selector ?? (x => x.Value);
|
|
VolumeSelector = volumeSelector ?? (x => x is TradeBar bar ? bar.Volume : (x is Tick tick ? tick.Quantity : 0));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="BaseTimelessConsolidator{T}" /> class.
|
|
/// </summary>
|
|
/// <param name="valueSelector">Extracts the value from a data instance to be formed into a new bar which inherits from <see cref="IBaseData"/>. The default
|
|
/// value is (x => x.Value) the <see cref="IBaseData.Value"/> property on <see cref="IBaseData"/></param>
|
|
/// <param name="volumeSelector">Extracts the volume from a data instance. The default value is null which does
|
|
/// not aggregate volume per bar.</param>
|
|
protected BaseTimelessConsolidator(PyObject valueSelector, PyObject volumeSelector = null)
|
|
: this (TryToConvertSelector(valueSelector, nameof(valueSelector)), TryToConvertSelector(volumeSelector, nameof(volumeSelector)))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to convert the given python selector to a C# one. If the conversion is not
|
|
/// possible it returns null
|
|
/// </summary>
|
|
/// <param name="selector">The python selector to be converted</param>
|
|
/// <param name="selectorName">The name of the selector to be used in case an exception is thrown</param>
|
|
/// <exception cref="ArgumentException">This exception will be thrown if it's not possible to convert the
|
|
/// given python selector to C#</exception>
|
|
private static Func<IBaseData, decimal> TryToConvertSelector(PyObject selector, string selectorName)
|
|
{
|
|
using (Py.GIL())
|
|
{
|
|
Func<IBaseData, decimal> resultSelector;
|
|
if (selector != null && !selector.IsNone())
|
|
{
|
|
if (!selector.TrySafeAs(out resultSelector))
|
|
{
|
|
throw new ArgumentException(
|
|
$"Unable to convert parameter {selectorName} to delegate type Func<IBaseData, decimal>");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
resultSelector = null;
|
|
}
|
|
|
|
return resultSelector;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates this consolidator with the specified data
|
|
/// </summary>
|
|
/// <param name="data">The new data for the consolidator</param>
|
|
public override 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the current RangeBar being created with the given data.
|
|
/// Additionally, if it's the case, it consolidates the current RangeBar
|
|
/// </summary>
|
|
/// <param name="time">Time of the given data</param>
|
|
/// <param name="currentValue">Value of the given data</param>
|
|
/// <param name="volume">Volume of the given data</param>
|
|
protected abstract void UpdateBar(DateTime time, decimal currentValue, decimal volume);
|
|
|
|
/// <summary>
|
|
/// Creates a new bar with the given data
|
|
/// </summary>
|
|
/// <param name="data">The new data for the bar</param>
|
|
/// <param name="currentValue">The new value for the bar</param>
|
|
/// <param name="volume">The new volume to the bar</param>
|
|
protected abstract void CreateNewBar(IBaseData data, decimal currentValue, decimal volume);
|
|
|
|
/// <summary>
|
|
/// Raises the strongly typed DataConsolidated event
|
|
/// </summary>
|
|
/// <param name="consolidated">The newly consolidated data</param>
|
|
protected override void FireDataConsolidated(IBaseData consolidated)
|
|
{
|
|
DataConsolidated?.Invoke(this, (T)consolidated);
|
|
}
|
|
|
|
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
|
/// <filterpriority>2</filterpriority>
|
|
public override void Dispose()
|
|
{
|
|
DataConsolidated = null;
|
|
base.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the consolidator
|
|
/// </summary>
|
|
public override void Reset()
|
|
{
|
|
CurrentBar = default(T);
|
|
base.Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scans this consolidator to see if it should emit a bar due to time passing
|
|
/// </summary>
|
|
/// <param name="currentLocalTime">The current time in the local time zone (same as <see cref="BaseData.Time"/>)</param>
|
|
public override void Scan(DateTime currentLocalTime)
|
|
{
|
|
}
|
|
}
|
|
}
|