chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Type capable of consolidating trade bars from any base data instance
|
||||
/// </summary>
|
||||
public class BaseDataConsolidator : TradeBarConsolidatorBase<BaseData>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new TickConsolidator for the desired resolution
|
||||
/// </summary>
|
||||
/// <param name="resolution">The resolution desired</param>
|
||||
/// <returns>A consolidator that produces data on the resolution interval</returns>
|
||||
public static BaseDataConsolidator FromResolution(Resolution resolution)
|
||||
{
|
||||
return new BaseDataConsolidator(resolution.ToTimeSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the period
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public BaseDataConsolidator(TimeSpan period)
|
||||
: base(period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
public BaseDataConsolidator(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public BaseDataConsolidator(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseDataConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
public BaseDataConsolidator(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BaseDataConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="pyfuncobj">Func that defines the start time of a consolidated data</param>
|
||||
public BaseDataConsolidator(PyObject pyfuncobj)
|
||||
: base(pyfuncobj)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new trade bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref TradeBar workingBar, BaseData data)
|
||||
{
|
||||
if (workingBar == null)
|
||||
{
|
||||
workingBar = new TradeBar
|
||||
{
|
||||
Symbol = data.Symbol,
|
||||
Time = GetRoundedBarTime(data.Time),
|
||||
Close = data.Value,
|
||||
High = data.Value,
|
||||
Low = data.Value,
|
||||
Open = data.Value,
|
||||
DataType = data.DataType,
|
||||
Value = data.Value
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
//Aggregate the working bar
|
||||
workingBar.Close = data.Value;
|
||||
if (data.Value < workingBar.Low) workingBar.Low = data.Value;
|
||||
if (data.Value > workingBar.High) workingBar.High = data.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class that provides <see cref="Func{DateTime,CalendarInfo}"/> used to define consolidation calendar
|
||||
/// </summary>
|
||||
public static class Calendar
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the start of week (previous Monday) of given date/time
|
||||
/// </summary>
|
||||
public static Func<DateTime, CalendarInfo> Weekly
|
||||
{
|
||||
get
|
||||
{
|
||||
return dt =>
|
||||
{
|
||||
var start = Expiry.EndOfWeek(dt).AddDays(-7);
|
||||
return new CalendarInfo(start, TimeSpan.FromDays(7));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the start of month (1st of the current month) of given date/time
|
||||
/// </summary>
|
||||
public static Func<DateTime, CalendarInfo> Monthly
|
||||
{
|
||||
get
|
||||
{
|
||||
return dt =>
|
||||
{
|
||||
var start = dt.AddDays(1 - dt.Day).Date;
|
||||
var end = Expiry.EndOfMonth(dt);
|
||||
return new CalendarInfo(start, end - start);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the start of quarter (1st of the starting month of current quarter) of given date/time
|
||||
/// </summary>
|
||||
public static Func<DateTime, CalendarInfo> Quarterly
|
||||
{
|
||||
get
|
||||
{
|
||||
return dt =>
|
||||
{
|
||||
var nthQuarter = (dt.Month - 1) / 3;
|
||||
var firstMonthOfQuarter = nthQuarter * 3 + 1;
|
||||
var start = new DateTime(dt.Year, firstMonthOfQuarter, 1);
|
||||
var end = Expiry.EndOfQuarter(dt);
|
||||
return new CalendarInfo(start, end - start);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the start of year (1st of the current year) of given date/time
|
||||
/// </summary>
|
||||
public static Func<DateTime, CalendarInfo> Yearly
|
||||
{
|
||||
get
|
||||
{
|
||||
return dt =>
|
||||
{
|
||||
var start = dt.AddDays(1 - dt.DayOfYear).Date;
|
||||
var end = Expiry.EndOfYear(dt);
|
||||
return new CalendarInfo(start, end - start);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calendar Info for storing information related to the start and period of a consolidator
|
||||
/// </summary>
|
||||
public readonly struct CalendarInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Calendar Start
|
||||
/// </summary>
|
||||
public DateTime Start { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Consolidation Period
|
||||
/// </summary>
|
||||
public TimeSpan Period { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Calendar End
|
||||
/// </summary>
|
||||
public readonly DateTime End => Start + Period;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for CalendarInfo; used for consolidation calendar
|
||||
/// </summary>
|
||||
/// <param name="start">Calendar Start</param>
|
||||
/// <param name="period">Consolidation Period</param>
|
||||
public CalendarInfo(DateTime start, TimeSpan period)
|
||||
{
|
||||
Start = start;
|
||||
Period = period;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string containing the Calendar start and the consolidation period
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Start} {Period}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the given object is equal to this object, this is, the Calendar start
|
||||
/// and consolidation period is the same for both
|
||||
/// </summary>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is not CalendarInfo other)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Start == other.Start && Period == other.Period;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the hash code for this object as an integer
|
||||
/// </summary>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = Start.GetHashCode();
|
||||
return (hashCode * 397) ^ Period.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the given object is equal to this object, this is, the Calendar start
|
||||
/// and consolidation period is the same for both
|
||||
/// </summary>
|
||||
public static bool operator ==(CalendarInfo left, CalendarInfo right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the given object is equal to this object, this is, the Calendar start
|
||||
/// and consolidation period is the same for both
|
||||
/// </summary>
|
||||
public static bool operator !=(CalendarInfo left, CalendarInfo right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Calendar Type Class; now obsolete routes functions to <see cref="Calendar"/>
|
||||
/// </summary>
|
||||
[Obsolete("CalendarType is obsolete, please use Calendar instead")]
|
||||
public static class CalendarType
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the start of week (previous Monday) of given date/time
|
||||
/// </summary>
|
||||
public static Func<DateTime, CalendarInfo> Weekly => Calendar.Weekly;
|
||||
|
||||
/// <summary>
|
||||
/// Computes the start of month (1st of the current month) of given date/time
|
||||
/// </summary>
|
||||
public static Func<DateTime, CalendarInfo> Monthly => Calendar.Monthly;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// This consolidator can transform a stream of <see cref="IBaseData"/> instances into a stream of <see cref="RangeBar"/>.
|
||||
/// The difference between this consolidator and <see cref="RangeConsolidator"/>, is that this last one creates intermediate/
|
||||
/// phantom RangeBar's (RangeBar's with zero volume) if the price rises up or falls down by above/below two times the range
|
||||
/// size. Therefore, <see cref="RangeConsolidator"/> leaves no space between two adyacent RangeBar's since it always start
|
||||
/// a new RangeBar one range above the last RangeBar's High value or one range below the last RangeBar's Low value, where
|
||||
/// one range equals to one minimum price change.
|
||||
/// </summary>
|
||||
public class ClassicRangeConsolidator : RangeConsolidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassicRangeConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="range">The Range interval sets the range in which the price moves, which in turn initiates the formation of a new bar.
|
||||
/// One range equals to one minimum price change, where this last value is defined depending of the RangeBar's symbol</param>
|
||||
/// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RangeBar"/>. 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, except if the input is a TradeBar.</param>
|
||||
public ClassicRangeConsolidator(
|
||||
int range,
|
||||
Func<IBaseData, decimal> selector = null,
|
||||
Func<IBaseData, decimal> volumeSelector = null)
|
||||
: base(range, selector, volumeSelector)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RangeConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="range">The Range interval sets the range in which the price moves, which in turn initiates the formation of a new bar.
|
||||
/// One range equals to one minimum price change, where this last value is defined depending of the RangeBar's symbol</param>
|
||||
/// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RangeBar"/>. 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>
|
||||
public ClassicRangeConsolidator(int range,
|
||||
PyObject selector,
|
||||
PyObject volumeSelector = null)
|
||||
: base(range, selector, volumeSelector)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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 override void UpdateBar(DateTime time, decimal currentValue, decimal volume)
|
||||
{
|
||||
CurrentBar.Update(time, currentValue, volume);
|
||||
|
||||
if (CurrentBar.IsClosed)
|
||||
{
|
||||
OnDataConsolidated(CurrentBar);
|
||||
CurrentBar = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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 Python.Runtime;
|
||||
using QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// This consolidator can transform a stream of <see cref="IBaseData"/> instances into a stream of <see cref="RenkoBar"/>
|
||||
/// </summary>
|
||||
public class ClassicRenkoConsolidator : BaseTimelessConsolidator<RenkoBar>
|
||||
{
|
||||
private decimal _barSize;
|
||||
private bool _evenBars;
|
||||
private decimal? _lastCloseValue;
|
||||
|
||||
/// <summary>
|
||||
/// Bar being created
|
||||
/// </summary>
|
||||
protected override RenkoBar CurrentBar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the kind of the bar
|
||||
/// </summary>
|
||||
public RenkoType Type => RenkoType.Classic;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData => CurrentBar?.Clone();
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="RenkoBar"/> which is the type emitted in the <see cref="IDataConsolidator.DataConsolidated"/> event.
|
||||
/// </summary>
|
||||
public override Type OutputType => typeof(RenkoBar);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// The value selector will by default select <see cref="IBaseData.Value"/>
|
||||
/// The volume selector will by default select zero.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
/// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
|
||||
public ClassicRenkoConsolidator(decimal barSize, bool evenBars = true)
|
||||
: base()
|
||||
{
|
||||
EpsilonCheck(barSize);
|
||||
_barSize = barSize;
|
||||
_evenBars = evenBars;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The size of each bar in units of the value produced by <paramref name="selector"/></param>
|
||||
/// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RenkoBar"/>. 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>
|
||||
/// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
|
||||
public ClassicRenkoConsolidator(
|
||||
decimal barSize,
|
||||
Func<IBaseData, decimal> selector,
|
||||
Func<IBaseData, decimal> volumeSelector = null,
|
||||
bool evenBars = true)
|
||||
: base(selector, volumeSelector)
|
||||
{
|
||||
EpsilonCheck(barSize);
|
||||
_barSize = barSize;
|
||||
_evenBars = evenBars;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
/// <param name="type">The RenkoType of the bar</param>
|
||||
[Obsolete("Please use the new RenkoConsolidator if RenkoType is not Classic")]
|
||||
public ClassicRenkoConsolidator(decimal barSize, RenkoType type)
|
||||
: this(barSize, true)
|
||||
{
|
||||
if (type != RenkoType.Classic)
|
||||
{
|
||||
throw new ArgumentException("Please use the new RenkoConsolidator type if RenkoType is not Classic");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The size of each bar in units of the value produced by <paramref name="selector"/></param>
|
||||
/// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RenkoBar"/>. 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>
|
||||
/// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
|
||||
public ClassicRenkoConsolidator(decimal barSize,
|
||||
PyObject selector,
|
||||
PyObject volumeSelector = null,
|
||||
bool evenBars = true)
|
||||
: base(selector, volumeSelector)
|
||||
{
|
||||
EpsilonCheck(barSize);
|
||||
_barSize = barSize;
|
||||
_evenBars = evenBars;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the ClassicRenkoConsolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
_lastCloseValue = null;
|
||||
}
|
||||
|
||||
/// <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 override void UpdateBar(DateTime time, decimal currentValue, decimal volume)
|
||||
{
|
||||
CurrentBar.Update(time, currentValue, volume);
|
||||
|
||||
if (CurrentBar.IsClosed)
|
||||
{
|
||||
_lastCloseValue = CurrentBar.Close;
|
||||
OnDataConsolidated(CurrentBar);
|
||||
CurrentBar = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 override void CreateNewBar(IBaseData data, decimal currentValue, decimal volume)
|
||||
{
|
||||
var open = _lastCloseValue ?? currentValue;
|
||||
if (_evenBars && !_lastCloseValue.HasValue)
|
||||
{
|
||||
open = Math.Ceiling(open / _barSize) * _barSize;
|
||||
}
|
||||
|
||||
CurrentBar = new RenkoBar(data.Symbol, data.Time, _barSize, open, volume);
|
||||
}
|
||||
|
||||
private static void EpsilonCheck(decimal barSize)
|
||||
{
|
||||
if (barSize < Extensions.GetDecimalEpsilon())
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(barSize),
|
||||
"RenkoConsolidator bar size must be positve and greater than 1e-28");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a type safe wrapper on the RenkoConsolidator class. This just allows us to define our selector functions with the real type they'll be receiving
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput"></typeparam>
|
||||
public class ClassicRenkoConsolidator<TInput> : ClassicRenkoConsolidator
|
||||
where TInput : IBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassicRenkoConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The size of each bar in units of the value produced by <paramref name="selector"/></param>
|
||||
/// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RenkoBar"/>. 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>
|
||||
/// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
|
||||
public ClassicRenkoConsolidator(
|
||||
decimal barSize,
|
||||
Func<TInput, decimal> selector,
|
||||
Func<TInput, decimal> volumeSelector = null,
|
||||
bool evenBars = true
|
||||
)
|
||||
: base(barSize, x => selector((TInput) x),
|
||||
volumeSelector == null ? (Func<IBaseData, decimal>) null : x => volumeSelector((TInput) x), evenBars)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// The value selector will by default select <see cref="IBaseData.Value"/>
|
||||
/// The volume selector will by default select zero.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
/// <param name="evenBars">When true bar open/close will be a multiple of the barSize</param>
|
||||
public ClassicRenkoConsolidator(decimal barSize, bool evenBars = true)
|
||||
: this(barSize, x => x.Value, x => 0, evenBars)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ClassicRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// The value selector will by default select <see cref="IBaseData.Value"/>
|
||||
/// The volume selector will by default select zero.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
/// <param name="type">The RenkoType of the bar</param>
|
||||
[Obsolete("Please use the WickedRenkoConsolidator if RenkoType is not Classic")]
|
||||
public ClassicRenkoConsolidator(decimal barSize, RenkoType type)
|
||||
: base(barSize, type)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Type safe shim method.
|
||||
/// </remarks>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public void Update(TInput data)
|
||||
{
|
||||
base.Update(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 QuantConnect.Indicators;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base implementation for consolidators, including a built-in rolling window
|
||||
/// that stores the history of consolidated bars.
|
||||
/// </summary>
|
||||
public abstract class ConsolidatorBase : WindowBase<IBaseData>, IDataConsolidator
|
||||
{
|
||||
private IBaseData _consolidated;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the most recently consolidated piece of data. This will be null if this consolidator
|
||||
/// has not produced any data yet.
|
||||
/// </summary>
|
||||
public IBaseData Consolidated
|
||||
{
|
||||
get
|
||||
{
|
||||
return _consolidated;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
_consolidated = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public abstract IBaseData WorkingData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type consumed by this consolidator
|
||||
/// </summary>
|
||||
public abstract Type InputType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type produced by this consolidator
|
||||
/// </summary>
|
||||
public abstract Type OutputType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data
|
||||
/// </summary>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public abstract void Update(IBaseData data);
|
||||
|
||||
/// <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 abstract void Scan(DateTime currentLocalTime);
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that fires when a new piece of data is produced. This is the single subscription
|
||||
/// point, shared by the <see cref="IDataConsolidator"/> interface and by derived consolidators whose
|
||||
/// output is a base data bar, so subscribing and unsubscribing always target the same handler list.
|
||||
/// </summary>
|
||||
public event DataConsolidatedHandler DataConsolidated;
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the DataConsolidated event. Populates the rolling window, raises the
|
||||
/// strongly typed and interface events, and finally updates the <see cref="Consolidated"/> property.
|
||||
/// </summary>
|
||||
protected virtual void OnDataConsolidated(IBaseData consolidated)
|
||||
{
|
||||
// populate the rolling window before firing any event so that, inside a DataConsolidated
|
||||
// handler, consolidator[0] is the bar that was just produced. Skip null bars, an out of order
|
||||
// data point can produce a null bar in count mode, so we never push null nor wipe the history
|
||||
if (consolidated != null)
|
||||
{
|
||||
Current = consolidated;
|
||||
}
|
||||
|
||||
// let derived consolidators raise their strongly typed DataConsolidated event
|
||||
FireDataConsolidated(consolidated);
|
||||
|
||||
DataConsolidated?.Invoke(this, consolidated);
|
||||
|
||||
// assign the Consolidated property after the event handlers are fired,
|
||||
// this allows the event handlers to look at the new consolidated data
|
||||
// and the previous consolidated data at the same time without extra bookkeeping
|
||||
Consolidated = consolidated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the strongly typed DataConsolidated event exposed by derived consolidators that produce a
|
||||
/// more specific bar type. Invoked after the rolling window is populated and before the shared event
|
||||
/// so every handler sees the same window. Consolidators whose output is a base data bar do not need
|
||||
/// to override this, the shared <see cref="DataConsolidated"/> event already carries their bar.
|
||||
/// </summary>
|
||||
/// <param name="consolidated">The newly consolidated data</param>
|
||||
protected virtual void FireDataConsolidated(IBaseData consolidated)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
DataConsolidated = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets this consolidator, clearing consolidated data and the rolling window.
|
||||
/// </summary>
|
||||
public virtual void Reset()
|
||||
{
|
||||
Consolidated = null;
|
||||
ResetWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a type that consumes BaseData instances and fires an event with consolidated
|
||||
/// and/or aggregated data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput">The type consumed by the consolidator</typeparam>
|
||||
public abstract class DataConsolidator<TInput> : ConsolidatorBase
|
||||
where TInput : IBaseData
|
||||
{
|
||||
/// <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)
|
||||
{
|
||||
if (!(data is TInput))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(data),
|
||||
$"Received type of {data.GetType().Name} but expected {typeof(TInput).Name}"
|
||||
);
|
||||
}
|
||||
Update((TInput)data);
|
||||
}
|
||||
|
||||
/// <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 abstract override void Scan(DateTime currentLocalTime);
|
||||
|
||||
/// <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(TInput);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type produced by this consolidator
|
||||
/// </summary>
|
||||
public abstract override Type OutputType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data. This method is
|
||||
/// responsible for raising the DataConsolidated event
|
||||
/// </summary>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public abstract void Update(TInput data);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Data;
|
||||
|
||||
namespace Common.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// This consolidator transforms a stream of <see cref="BaseData"/> instances into a stream of <see cref="RenkoBar"/>
|
||||
/// with a constant dollar volume for each bar.
|
||||
/// </summary>
|
||||
public class DollarVolumeRenkoConsolidator : VolumeRenkoConsolidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DollarVolumeRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant dollar volume size of each bar</param>
|
||||
public DollarVolumeRenkoConsolidator(decimal barSize)
|
||||
: base(barSize)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts raw volume into dollar volume by multiplying it with the trade price.
|
||||
/// </summary>
|
||||
/// <param name="volume">The raw trade volume</param>
|
||||
/// <param name="price">The trade price</param>
|
||||
/// <returns>The dollar volume</returns>
|
||||
protected override decimal AdjustVolume(decimal volume, decimal price)
|
||||
{
|
||||
return volume * price;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// A data csolidator that can make trade bars from DynamicData derived types. This is useful for
|
||||
/// aggregating Quandl and other highly flexible dynamic custom data types.
|
||||
/// </summary>
|
||||
public class DynamicDataConsolidator : TradeBarConsolidatorBase<DynamicData>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the period.
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public DynamicDataConsolidator(TimeSpan period)
|
||||
: base(period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data.
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
|
||||
public DynamicDataConsolidator(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first.
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public DynamicDataConsolidator(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first.
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
public DynamicDataConsolidator(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new trade bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref TradeBar workingBar, DynamicData data)
|
||||
{
|
||||
// grab the properties, if they don't exist just use the .Value property
|
||||
var open = GetNamedPropertyOrValueProperty(data, "Open");
|
||||
var high = GetNamedPropertyOrValueProperty(data, "High");
|
||||
var low = GetNamedPropertyOrValueProperty(data, "Low");
|
||||
var close = GetNamedPropertyOrValueProperty(data, "Close");
|
||||
|
||||
// if we have volume, use it, otherwise just use zero
|
||||
var volume = data.HasProperty("Volume")
|
||||
? data.GetProperty("Volume").ConvertInvariant<long>()
|
||||
: 0L;
|
||||
|
||||
if (workingBar == null)
|
||||
{
|
||||
workingBar = new TradeBar
|
||||
{
|
||||
Symbol = data.Symbol,
|
||||
Time = GetRoundedBarTime(data),
|
||||
Open = open,
|
||||
High = high,
|
||||
Low = low,
|
||||
Close = close,
|
||||
Volume = volume
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
//Aggregate the working bar
|
||||
workingBar.Close = close;
|
||||
workingBar.Volume += volume;
|
||||
if (low < workingBar.Low) workingBar.Low = low;
|
||||
if (high > workingBar.High) workingBar.High = high;
|
||||
}
|
||||
}
|
||||
|
||||
private static decimal GetNamedPropertyOrValueProperty(DynamicData data, string propertyName)
|
||||
{
|
||||
if (!data.HasProperty(propertyName))
|
||||
{
|
||||
return data.Value;
|
||||
}
|
||||
return data.GetProperty(propertyName).ConvertInvariant<decimal>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an implementation of <see cref="IDataConsolidator"/> that preserve the input
|
||||
/// data unmodified. The input data is filtering by the specified predicate function
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of data</typeparam>
|
||||
public class FilteredIdentityDataConsolidator<T> : IdentityDataConsolidator<T>
|
||||
where T : IBaseData
|
||||
{
|
||||
private readonly Func<T, bool> _predicate;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FilteredIdentityDataConsolidator{T}"/> class
|
||||
/// </summary>
|
||||
/// <param name="predicate">The predicate function, returning true to accept data and false to reject data</param>
|
||||
public FilteredIdentityDataConsolidator(Func<T, bool> predicate)
|
||||
{
|
||||
this._predicate = predicate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data
|
||||
/// </summary>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public override void Update(T data)
|
||||
{
|
||||
// only permit data that passes our predicate function to be passed through
|
||||
if (_predicate(data))
|
||||
{
|
||||
base.Update(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides factory methods for creating instances of <see cref="FilteredIdentityDataConsolidator{T}"/>
|
||||
/// </summary>
|
||||
public static class FilteredIdentityDataConsolidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="FilteredIdentityDataConsolidator{T}"/> that filters ticks
|
||||
/// based on the specified <see cref="TickType"/>
|
||||
/// </summary>
|
||||
/// <param name="tickType">The tick type of data to accept</param>
|
||||
/// <returns>A new <see cref="FilteredIdentityDataConsolidator{T}"/> that filters based on the provided tick type</returns>
|
||||
public static FilteredIdentityDataConsolidator<Tick> ForTickType(TickType tickType)
|
||||
{
|
||||
return new FilteredIdentityDataConsolidator<Tick>(tick => tick.TickType == tickType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Event handler type for the IDataConsolidator.DataConsolidated event
|
||||
/// </summary>
|
||||
/// <param name="sender">The consolidator that fired the event</param>
|
||||
/// <param name="consolidated">The consolidated piece of data</param>
|
||||
public delegate void DataConsolidatedHandler(object sender, IBaseData consolidated);
|
||||
|
||||
/// <summary>
|
||||
/// Represents a type capable of taking BaseData updates and firing events containing new
|
||||
/// 'consolidated' data. These types can be used to produce larger bars, or even be used to
|
||||
/// transform the data before being sent to another component. The most common usage of these
|
||||
/// types is with indicators.
|
||||
/// </summary>
|
||||
public interface IDataConsolidator : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the most recently consolidated piece of data. This will be null if this consolidator
|
||||
/// has not produced any data yet.
|
||||
/// </summary>
|
||||
IBaseData Consolidated { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
IBaseData WorkingData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type consumed by this consolidator
|
||||
/// </summary>
|
||||
Type InputType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type produced by this consolidator
|
||||
/// </summary>
|
||||
Type OutputType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data
|
||||
/// </summary>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
void Update(IBaseData data);
|
||||
|
||||
/// <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>
|
||||
void Scan(DateTime currentLocalTime);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that fires when a new piece of data is produced
|
||||
/// </summary>
|
||||
event DataConsolidatedHandler DataConsolidated;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the simplest DataConsolidator implementation, one that is defined
|
||||
/// by a straight pass through of the data. No projection or aggregation is performed.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of data</typeparam>
|
||||
public class IdentityDataConsolidator<T> : DataConsolidator<T>
|
||||
where T : IBaseData
|
||||
{
|
||||
private static readonly bool IsTick = typeof(T) == typeof(Tick);
|
||||
|
||||
private T _last;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the timestamp of the last processed data item.
|
||||
/// </summary>
|
||||
private DateTime _lastTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData
|
||||
{
|
||||
get { return _last == null ? null : _last.Clone(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type produced by this consolidator
|
||||
/// </summary>
|
||||
public override Type OutputType
|
||||
{
|
||||
get { return typeof(T); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data
|
||||
/// </summary>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public override void Update(T data)
|
||||
{
|
||||
if (IsTick || _last == null || data.EndTime != _lastTime)
|
||||
{
|
||||
OnDataConsolidated(data);
|
||||
_last = data;
|
||||
_lastTime = data.EndTime;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
_last = default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2024 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 NodaTime;
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Consolidator for open markets bar only, extended hours bar are not consolidated.
|
||||
/// </summary>
|
||||
public class MarketHourAwareConsolidator : ConsolidatorBase
|
||||
{
|
||||
private readonly bool _dailyStrictEndTimeEnabled;
|
||||
private readonly bool _extendedMarketHours;
|
||||
private bool _useStrictEndTime;
|
||||
|
||||
/// <summary>
|
||||
/// The consolidation period requested
|
||||
/// </summary>
|
||||
protected TimeSpan Period { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The consolidator instance
|
||||
/// </summary>
|
||||
protected IDataConsolidator Consolidator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The associated security exchange hours instance
|
||||
/// </summary>
|
||||
protected SecurityExchangeHours ExchangeHours { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The associated data time zone
|
||||
/// </summary>
|
||||
protected DateTimeZone DataTimeZone { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type consumed by this consolidator
|
||||
/// </summary>
|
||||
public override Type InputType => Consolidator.InputType;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData => Consolidator.WorkingData;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type produced by this consolidator
|
||||
/// </summary>
|
||||
public override Type OutputType => Consolidator.OutputType;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MarketHourAwareConsolidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="resolution">The resolution.</param>
|
||||
/// <param name="dataType">The target data type</param>
|
||||
/// <param name="tickType">The target tick type</param>
|
||||
/// <param name="extendedMarketHours">True if extended market hours should be consolidated</param>
|
||||
public MarketHourAwareConsolidator(bool dailyStrictEndTimeEnabled, Resolution resolution, Type dataType, TickType tickType, bool extendedMarketHours)
|
||||
{
|
||||
_dailyStrictEndTimeEnabled = dailyStrictEndTimeEnabled;
|
||||
Period = resolution.ToTimeSpan();
|
||||
_extendedMarketHours = extendedMarketHours;
|
||||
|
||||
Consolidator = CreateConsolidator(resolution, dataType, tickType);
|
||||
Consolidator.DataConsolidated += ForwardConsolidatedBar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MarketHourAwareConsolidator"/> class for an arbitrary period.
|
||||
/// Intraday periods are anchored to the market open without extending past the close.
|
||||
/// </summary>
|
||||
/// <param name="dailyStrictEndTimeEnabled">True if daily strict end times should be enabled</param>
|
||||
/// <param name="period">The consolidation period</param>
|
||||
/// <param name="dataType">The target data type</param>
|
||||
/// <param name="tickType">The target tick type</param>
|
||||
/// <param name="extendedMarketHours">True if extended market hours should be consolidated</param>
|
||||
public MarketHourAwareConsolidator(bool dailyStrictEndTimeEnabled, TimeSpan period, Type dataType, TickType tickType, bool extendedMarketHours)
|
||||
{
|
||||
_dailyStrictEndTimeEnabled = dailyStrictEndTimeEnabled;
|
||||
Period = period;
|
||||
_extendedMarketHours = extendedMarketHours;
|
||||
|
||||
// when the period exactly matches a standard resolution, reuse the resolution based consolidation so its
|
||||
// well-tested behavior is preserved; only arbitrary periods need the market-open anchored intraday calendar
|
||||
var resolution = period.ToHigherResolutionEquivalent(false);
|
||||
if (resolution.ToTimeSpan() == period)
|
||||
{
|
||||
Consolidator = CreateConsolidator(resolution, dataType, tickType);
|
||||
}
|
||||
else
|
||||
{
|
||||
Func<DateTime, CalendarInfo> calendar = period < Time.OneDay ? IntradayCalendar : DailyStrictEndTime;
|
||||
Consolidator = CreateConsolidator(calendar, dataType, tickType);
|
||||
}
|
||||
Consolidator.DataConsolidated += ForwardConsolidatedBar;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the inner consolidator that produces the requested <paramref name="dataType"/> output.
|
||||
/// </summary>
|
||||
protected virtual IDataConsolidator CreateConsolidator(Resolution resolution, Type dataType, TickType tickType)
|
||||
{
|
||||
if (dataType == typeof(Tick))
|
||||
{
|
||||
if (tickType == TickType.Trade)
|
||||
{
|
||||
return resolution == Resolution.Daily
|
||||
? new TickConsolidator(DailyStrictEndTime)
|
||||
: new TickConsolidator(Period);
|
||||
}
|
||||
return resolution == Resolution.Daily
|
||||
? new TickQuoteBarConsolidator(DailyStrictEndTime)
|
||||
: new TickQuoteBarConsolidator(Period);
|
||||
}
|
||||
if (dataType == typeof(TradeBar))
|
||||
{
|
||||
return resolution == Resolution.Daily
|
||||
? new TradeBarConsolidator(DailyStrictEndTime)
|
||||
: new TradeBarConsolidator(Period);
|
||||
}
|
||||
if (dataType == typeof(QuoteBar))
|
||||
{
|
||||
return resolution == Resolution.Daily
|
||||
? new QuoteBarConsolidator(DailyStrictEndTime)
|
||||
: new QuoteBarConsolidator(Period);
|
||||
}
|
||||
throw new ArgumentNullException(nameof(dataType), $"{dataType.Name} not supported");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the underlying calendar based consolidator for the given data type, used for arbitrary periods
|
||||
/// </summary>
|
||||
protected virtual IDataConsolidator CreateConsolidator(Func<DateTime, CalendarInfo> calendar, Type dataType, TickType tickType)
|
||||
{
|
||||
if (dataType == typeof(Tick))
|
||||
{
|
||||
return tickType == TickType.Trade
|
||||
? new TickConsolidator(calendar)
|
||||
: new TickQuoteBarConsolidator(calendar);
|
||||
}
|
||||
if (dataType == typeof(TradeBar))
|
||||
{
|
||||
return new TradeBarConsolidator(calendar);
|
||||
}
|
||||
if (dataType == typeof(QuoteBar))
|
||||
{
|
||||
return new QuoteBarConsolidator(calendar);
|
||||
}
|
||||
throw new ArgumentNullException(nameof(dataType), $"{dataType.Name} not supported");
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
Initialize(data);
|
||||
|
||||
// US equity hour data from the database starts at 9am but the exchange opens at 9:30am. Thus, we need to handle
|
||||
// this case specifically to avoid skipping the first hourly bar. To avoid this, we assert the period is daily,
|
||||
// the data resolution is hour and the exchange opens at any point in time over the data.Time to data.EndTime interval
|
||||
if (_extendedMarketHours ||
|
||||
ExchangeHours.IsOpen(data.Time, false) ||
|
||||
(Period == Time.OneDay && (data.EndTime - data.Time >= Time.OneHour) && ExchangeHours.IsOpen(data.Time, data.EndTime, false)))
|
||||
{
|
||||
Consolidator.Update(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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="P:QuantConnect.Data.BaseData.Time" />)</param>
|
||||
public override void Scan(DateTime currentLocalTime)
|
||||
{
|
||||
Consolidator.Scan(currentLocalTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
||||
/// </summary>
|
||||
public override void Dispose()
|
||||
{
|
||||
Consolidator.DataConsolidated -= ForwardConsolidatedBar;
|
||||
Consolidator.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
_useStrictEndTime = false;
|
||||
ExchangeHours = null;
|
||||
DataTimeZone = null;
|
||||
Consolidator.Reset();
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform late initialization based on the datas symbol
|
||||
/// </summary>
|
||||
protected void Initialize(IBaseData data)
|
||||
{
|
||||
if (ExchangeHours == null)
|
||||
{
|
||||
var symbol = data.Symbol;
|
||||
var marketHoursDatabase = MarketHoursDatabase.FromDataFolder();
|
||||
ExchangeHours = marketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType);
|
||||
DataTimeZone = marketHoursDatabase.GetDataTimeZone(symbol.ID.Market, symbol, symbol.SecurityType);
|
||||
|
||||
_useStrictEndTime = UseStrictEndTime(data.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a bar start time and period
|
||||
/// </summary>
|
||||
protected virtual CalendarInfo DailyStrictEndTime(DateTime dateTime)
|
||||
{
|
||||
// strict end times describe a single daily bar, so periods larger than a day fall back to standard period consolidation
|
||||
if (!_useStrictEndTime || Period > Time.OneDay)
|
||||
{
|
||||
return new(Period > Time.OneDay ? dateTime : dateTime.RoundDown(Period), Period);
|
||||
}
|
||||
return LeanData.GetDailyCalendar(dateTime, ExchangeHours, _extendedMarketHours);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines a bar start time and period for intraday consolidation, anchored to the market open
|
||||
/// without extending past the market close so a bar never spans across closed market hours
|
||||
/// </summary>
|
||||
protected virtual CalendarInfo IntradayCalendar(DateTime dateTime)
|
||||
{
|
||||
if (ExchangeHours == null || ExchangeHours.IsMarketAlwaysOpen)
|
||||
{
|
||||
return new(dateTime.RoundDown(Period), Period);
|
||||
}
|
||||
return LeanData.GetIntradayCalendar(dateTime, Period, ExchangeHours, _extendedMarketHours);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Useful for testing
|
||||
/// </summary>
|
||||
protected virtual bool UseStrictEndTime(Symbol symbol)
|
||||
{
|
||||
return LeanData.UseStrictEndTime(_dailyStrictEndTimeEnabled, symbol, Period, ExchangeHours);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will forward the underlying consolidated bar to consumers on this object.
|
||||
/// This wrapper keeps its own rolling window in addition to the inner consolidator's window.
|
||||
/// </summary>
|
||||
protected virtual void ForwardConsolidatedBar(object sender, IBaseData consolidated)
|
||||
{
|
||||
OnDataConsolidated(consolidated);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Type capable of consolidating open interest
|
||||
/// </summary>
|
||||
public class OpenInterestConsolidator : PeriodCountConsolidatorBase<Tick, OpenInterest>
|
||||
{
|
||||
private bool _hourOrDailyConsolidation;
|
||||
// Keep track of the last input to detect hour or date change
|
||||
private Tick _lastInput;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new OpenInterestConsolidator for the desired resolution
|
||||
/// </summary>
|
||||
/// <param name="resolution">The resolution desired</param>
|
||||
/// <returns>A consolidator that produces data on the resolution interval</returns>
|
||||
public static OpenInterestConsolidator FromResolution(Resolution resolution)
|
||||
{
|
||||
return new OpenInterestConsolidator(resolution.ToTimeSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'OpenInterest' representing the period
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
/// <param name="startTime">Optionally the bar start time anchor to use</param>
|
||||
public OpenInterestConsolidator(TimeSpan period, TimeSpan? startTime = null)
|
||||
: base(period, startTime)
|
||||
{
|
||||
_hourOrDailyConsolidation = period >= Time.OneHour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'OpenInterest' representing the last count pieces of data
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
public OpenInterestConsolidator(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'OpenInterest' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public OpenInterestConsolidator(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'OpenInterest'
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
public OpenInterestConsolidator(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'OpenInterest'
|
||||
/// </summary>
|
||||
/// <param name="pyfuncobj">Python function object that defines the start time of a consolidated data</param>
|
||||
public OpenInterestConsolidator(PyObject pyfuncobj)
|
||||
: base(pyfuncobj)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not the specified data should be processed
|
||||
/// </summary>
|
||||
/// <param name="data">The data to check</param>
|
||||
/// <returns>True if the consolidator should process this data, false otherwise</returns>
|
||||
protected override bool ShouldProcess(Tick data)
|
||||
{
|
||||
return data.TickType == TickType.OpenInterest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new OI bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref OpenInterest workingBar, Tick data)
|
||||
{
|
||||
if (workingBar == null)
|
||||
{
|
||||
workingBar = new OpenInterest
|
||||
{
|
||||
Symbol = data.Symbol,
|
||||
Time = _hourOrDailyConsolidation ? data.EndTime : GetRoundedBarTime(data),
|
||||
Value = data.Value
|
||||
};
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update the working bar
|
||||
workingBar.Value = data.Value;
|
||||
|
||||
// If we are consolidating hourly or daily, we need to update the time of the working bar
|
||||
// for the end time to match the last data point time
|
||||
if (_hourOrDailyConsolidation)
|
||||
{
|
||||
workingBar.Time = data.EndTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data. This method is
|
||||
/// responsible for raising the DataConsolidated event.
|
||||
/// It will check for date or hour change and force consolidation if needed.
|
||||
/// </summary>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public override void Update(Tick data)
|
||||
{
|
||||
if (_lastInput != null &&
|
||||
_hourOrDailyConsolidation &&
|
||||
// Detect hour or date change
|
||||
((Period == Time.OneHour && data.EndTime.Hour != _lastInput.EndTime.Hour) ||
|
||||
(Period == Time.OneDay && data.EndTime.Date != _lastInput.EndTime.Date)))
|
||||
{
|
||||
// Date or hour change, force consolidation, no need to wait for the whole period to pass.
|
||||
// Force consolidation by scanning at a time after the end of the period
|
||||
Scan(_lastInput.EndTime.Add(Period.Value + Time.OneSecond));
|
||||
}
|
||||
|
||||
base.Update(data);
|
||||
_lastInput = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
* 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 Python.Runtime;
|
||||
using QuantConnect.Util;
|
||||
using QuantConnect.Data.Market;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a base class for consolidators that emit data based on the passing of a period of time
|
||||
/// or after seeing a max count of data points.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The input type of the consolidator</typeparam>
|
||||
/// <typeparam name="TConsolidated">The output type of the consolidator</typeparam>
|
||||
public abstract class PeriodCountConsolidatorBase<T, TConsolidated> : DataConsolidator<T>
|
||||
where T : IBaseData
|
||||
where TConsolidated : BaseData
|
||||
{
|
||||
// The SecurityIdentifier that we are consolidating for.
|
||||
private SecurityIdentifier _securityIdentifier;
|
||||
private bool _securityIdentifierIsSet;
|
||||
//The number of data updates between creating new bars.
|
||||
private int? _maxCount;
|
||||
//
|
||||
private IPeriodSpecification _periodSpecification;
|
||||
//The minimum timespan between creating new bars.
|
||||
private TimeSpan? _period;
|
||||
//The number of pieces of data we've accumulated since our last emit
|
||||
private int _currentCount;
|
||||
//The working bar used for aggregating the data
|
||||
protected TConsolidated _workingBar;
|
||||
//The last time we emitted a consolidated bar
|
||||
private DateTime? _lastEmit;
|
||||
private bool _validateTimeSpan;
|
||||
|
||||
private PeriodCountConsolidatorBase(IPeriodSpecification periodSpecification)
|
||||
{
|
||||
_periodSpecification = periodSpecification;
|
||||
_period = _periodSpecification.Period;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new <typeparamref name="TConsolidated"/> instance representing the period
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
/// <param name="startTime">Optionally the bar start time anchor to use</param>
|
||||
protected PeriodCountConsolidatorBase(TimeSpan period, TimeSpan? startTime = null)
|
||||
: this(new TimeSpanPeriodSpecification(period, startTime))
|
||||
{
|
||||
_period = _periodSpecification.Period;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new <typeparamref name="TConsolidated"/> instance representing the last count pieces of data
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
|
||||
protected PeriodCountConsolidatorBase(int maxCount)
|
||||
: this(new BarCountPeriodSpecification())
|
||||
{
|
||||
_maxCount = maxCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new <typeparamref name="TConsolidated"/> instance representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
protected PeriodCountConsolidatorBase(int maxCount, TimeSpan period)
|
||||
: this(new MixedModePeriodSpecification(period))
|
||||
{
|
||||
_maxCount = maxCount;
|
||||
_period = _periodSpecification.Period;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new <typeparamref name="TConsolidated"/> instance representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
protected PeriodCountConsolidatorBase(Func<DateTime, CalendarInfo> func)
|
||||
: this(new FuncPeriodSpecification(func))
|
||||
{
|
||||
_period = Time.OneSecond;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new <typeparamref name="TConsolidated"/> instance representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="pyObject">Python object that defines either a function object that defines the start time of a consolidated data or a timespan</param>
|
||||
protected PeriodCountConsolidatorBase(PyObject pyObject)
|
||||
: this(GetPeriodSpecificationFromPyObject(pyObject))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type produced by this consolidator
|
||||
/// </summary>
|
||||
public override Type OutputType => typeof(TConsolidated);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData => _workingBar?.Clone();
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that fires when a new piece of data is produced. We define this as a 'new'
|
||||
/// event so we can expose it as a <typeparamref name="TConsolidated"/> instead of a <see cref="BaseData"/> instance
|
||||
/// </summary>
|
||||
public new event EventHandler<TConsolidated> DataConsolidated;
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data. This method is
|
||||
/// responsible for raising the DataConsolidated event
|
||||
/// In time span mode, the bar range is closed on the left and open on the right: [T, T+TimeSpan).
|
||||
/// For example, if time span is 1 minute, we have [10:00, 10:01): so data at 10:01 is not
|
||||
/// included in the bar starting at 10:00.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when multiple symbols are being consolidated.</exception>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public override void Update(T data)
|
||||
{
|
||||
if (!_securityIdentifierIsSet)
|
||||
{
|
||||
_securityIdentifierIsSet = true;
|
||||
_securityIdentifier = data.Symbol.ID;
|
||||
}
|
||||
else if (!data.Symbol.ID.Equals(_securityIdentifier))
|
||||
{
|
||||
throw new InvalidOperationException($"Consolidators can only be used with a single symbol. The previous consolidated SecurityIdentifier ({_securityIdentifier}) is not the same as in the current data ({data.Symbol.ID}).");
|
||||
}
|
||||
|
||||
if (!ShouldProcess(data))
|
||||
{
|
||||
// first allow the base class a chance to filter out data it doesn't want
|
||||
// before we start incrementing counts and what not
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_validateTimeSpan && _period.HasValue && _periodSpecification is TimeSpanPeriodSpecification)
|
||||
{
|
||||
// only do this check once
|
||||
_validateTimeSpan = true;
|
||||
var dataLength = data.EndTime - data.Time;
|
||||
if (dataLength > _period)
|
||||
{
|
||||
throw new ArgumentException($"For Symbol {data.Symbol} can not consolidate bars of period: {_period}, using data of the same or higher period: {data.EndTime - data.Time}");
|
||||
}
|
||||
}
|
||||
|
||||
//Decide to fire the event
|
||||
var fireDataConsolidated = false;
|
||||
|
||||
// decide to aggregate data before or after firing OnDataConsolidated event
|
||||
// always aggregate before firing in counting mode
|
||||
bool aggregateBeforeFire = _maxCount.HasValue;
|
||||
|
||||
if (_maxCount.HasValue)
|
||||
{
|
||||
// we're in count mode
|
||||
_currentCount++;
|
||||
if (_currentCount >= _maxCount.Value)
|
||||
{
|
||||
_currentCount = 0;
|
||||
fireDataConsolidated = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_lastEmit.HasValue)
|
||||
{
|
||||
// initialize this value for period computations
|
||||
_lastEmit = IsTimeBased ? DateTime.MinValue : data.Time;
|
||||
}
|
||||
|
||||
if (_period.HasValue)
|
||||
{
|
||||
// we're in time span mode and initialized
|
||||
if (_workingBar != null && data.Time - _workingBar.Time >= _period.Value && GetRoundedBarTime(data) > _lastEmit)
|
||||
{
|
||||
fireDataConsolidated = true;
|
||||
}
|
||||
|
||||
// special case: always aggregate before event trigger when TimeSpan is zero
|
||||
if (_period.Value == TimeSpan.Zero)
|
||||
{
|
||||
fireDataConsolidated = true;
|
||||
aggregateBeforeFire = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aggregateBeforeFire)
|
||||
{
|
||||
if (data.Time >= _lastEmit)
|
||||
{
|
||||
AggregateBar(ref _workingBar, data);
|
||||
|
||||
if (_maxCount.HasValue)
|
||||
{
|
||||
// When using count-based consolidation, set EndTime to the last input's EndTime
|
||||
_workingBar.EndTime = data.EndTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Fire the event
|
||||
if (fireDataConsolidated)
|
||||
{
|
||||
var workingTradeBar = _workingBar as TradeBar;
|
||||
if (workingTradeBar != null)
|
||||
{
|
||||
// we kind of are cheating here...
|
||||
if (_period.HasValue)
|
||||
{
|
||||
workingTradeBar.Period = _period.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Set _lastEmit first because OnDataConsolidated will set _workingBar to null
|
||||
_lastEmit = IsTimeBased && _workingBar != null ? _workingBar.EndTime : data.Time;
|
||||
OnDataConsolidated(_workingBar);
|
||||
}
|
||||
|
||||
if (!aggregateBeforeFire)
|
||||
{
|
||||
if (data.Time >= _lastEmit)
|
||||
{
|
||||
AggregateBar(ref _workingBar, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
if (_workingBar != null && _period.HasValue && _period.Value != TimeSpan.Zero
|
||||
&& currentLocalTime - _workingBar.Time >= _period.Value && GetRoundedBarTime(currentLocalTime) > _lastEmit)
|
||||
{
|
||||
_lastEmit = _workingBar.EndTime;
|
||||
OnDataConsolidated(_workingBar);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
_securityIdentifier = null;
|
||||
_securityIdentifierIsSet = false;
|
||||
_currentCount = 0;
|
||||
_workingBar = null;
|
||||
_lastEmit = null;
|
||||
_validateTimeSpan = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this consolidator is time-based, false otherwise
|
||||
/// </summary>
|
||||
protected bool IsTimeBased => !_maxCount.HasValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the time period for this consolidator
|
||||
/// </summary>
|
||||
protected TimeSpan? Period => _period;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not the specified data should be processed
|
||||
/// </summary>
|
||||
/// <param name="data">The data to check</param>
|
||||
/// <returns>True if the consolidator should process this data, false otherwise</returns>
|
||||
protected virtual bool ShouldProcess(T data) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new consolidated bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected abstract void AggregateBar(ref TConsolidated workingBar, T data);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a rounded-down bar time. Called by AggregateBar in derived classes.
|
||||
/// </summary>
|
||||
/// <param name="time">The bar time to be rounded down</param>
|
||||
/// <returns>The rounded bar time</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected DateTime GetRoundedBarTime(DateTime time)
|
||||
{
|
||||
var startTime = _periodSpecification.GetRoundedBarTime(time);
|
||||
|
||||
// In the case of a new bar, define the period defined at opening time
|
||||
if (_workingBar == null)
|
||||
{
|
||||
_period = _periodSpecification.Period;
|
||||
}
|
||||
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a rounded-down bar start time. Called by AggregateBar in derived classes.
|
||||
/// </summary>
|
||||
/// <param name="inputData">The input data point</param>
|
||||
/// <returns>The rounded bar start time</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
protected DateTime GetRoundedBarTime(IBaseData inputData)
|
||||
{
|
||||
var potentialStartTime = GetRoundedBarTime(inputData.Time);
|
||||
if (_period.HasValue && potentialStartTime + _period < inputData.EndTime)
|
||||
{
|
||||
// US equity hour bars from the database starts at 9am but the exchange opens at 9:30am. Thus, the method
|
||||
// GetRoundedBarTime(inputData.Time) returns the market open of the previous day, which is not consistent
|
||||
// with the given end time. For that reason we need to handle this case specifically, by calling
|
||||
// GetRoundedBarTime(inputData.EndTime) as it will return our expected start time: 9:30am
|
||||
if (inputData.EndTime - inputData.Time == Time.OneHour && potentialStartTime.Date < inputData.Time.Date)
|
||||
{
|
||||
potentialStartTime = GetRoundedBarTime(inputData.EndTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
// whops! the end time we were giving is beyond our potential end time, so let's use the giving bars star time instead
|
||||
potentialStartTime = inputData.Time;
|
||||
}
|
||||
}
|
||||
|
||||
return potentialStartTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the <see cref="DataConsolidated"/> event
|
||||
/// </summary>
|
||||
/// <param name="e">The consolidated data</param>
|
||||
protected virtual void OnDataConsolidated(TConsolidated e)
|
||||
{
|
||||
base.OnDataConsolidated(e);
|
||||
DataConsolidated?.Invoke(this, e);
|
||||
|
||||
ResetWorkingBar();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the working bar
|
||||
/// </summary>
|
||||
protected virtual void ResetWorkingBar()
|
||||
{
|
||||
_workingBar = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the period specification from the PyObject that can either represent a function object that defines the start time of a consolidated data or a timespan.
|
||||
/// </summary>
|
||||
/// <param name="pyObject">Python object that defines either a function object that defines the start time of a consolidated data or a timespan</param>
|
||||
/// <returns>IPeriodSpecification that represents the PyObject</returns>
|
||||
private static IPeriodSpecification GetPeriodSpecificationFromPyObject(PyObject pyObject)
|
||||
{
|
||||
Func<DateTime, CalendarInfo> expiryFunc;
|
||||
if (pyObject.TrySafeAs(out expiryFunc))
|
||||
{
|
||||
return new FuncPeriodSpecification(expiryFunc);
|
||||
}
|
||||
|
||||
using (Py.GIL())
|
||||
{
|
||||
return new TimeSpanPeriodSpecification(pyObject.As<TimeSpan>());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distinguishes between the different ways a consolidated data start time can be specified
|
||||
/// </summary>
|
||||
private interface IPeriodSpecification
|
||||
{
|
||||
TimeSpan? Period { get; }
|
||||
DateTime GetRoundedBarTime(DateTime time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User defined the bars period using a counter
|
||||
/// </summary>
|
||||
private class BarCountPeriodSpecification : IPeriodSpecification
|
||||
{
|
||||
public TimeSpan? Period { get; } = null;
|
||||
|
||||
public DateTime GetRoundedBarTime(DateTime time) => time;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User defined the bars period using a counter and a period (mixed mode)
|
||||
/// </summary>
|
||||
private class MixedModePeriodSpecification : IPeriodSpecification
|
||||
{
|
||||
public TimeSpan? Period { get; }
|
||||
|
||||
public MixedModePeriodSpecification(TimeSpan period)
|
||||
{
|
||||
Period = period;
|
||||
}
|
||||
|
||||
public DateTime GetRoundedBarTime(DateTime time) => time;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// User defined the bars period using a time span
|
||||
/// </summary>
|
||||
private class TimeSpanPeriodSpecification : IPeriodSpecification
|
||||
{
|
||||
public TimeSpan? StartTime { get; }
|
||||
public TimeSpan? Period { get; }
|
||||
|
||||
public TimeSpanPeriodSpecification(TimeSpan period, TimeSpan? startTime = null)
|
||||
{
|
||||
Period = period;
|
||||
StartTime = startTime;
|
||||
}
|
||||
|
||||
public DateTime GetRoundedBarTime(DateTime time)
|
||||
{
|
||||
if (StartTime.HasValue)
|
||||
{
|
||||
return LeanData.GetConsolidatorStartTime(Period.Value, StartTime.Value, time);
|
||||
}
|
||||
return Period.Value > Time.OneDay
|
||||
? time // #4915 For periods larger than a day, don't use a rounding schedule.
|
||||
: time.RoundDown(Period.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special case for bars where the open time is defined by a function.
|
||||
/// We assert on construction that the function returns a date time in the past or equal to the given time instant.
|
||||
/// </summary>
|
||||
private class FuncPeriodSpecification : IPeriodSpecification
|
||||
{
|
||||
private static readonly DateTime _verificationDate = new DateTime(2022, 01, 03, 10, 10, 10);
|
||||
public TimeSpan? Period { get; private set; }
|
||||
|
||||
public readonly Func<DateTime, CalendarInfo> _calendarInfoFunc;
|
||||
|
||||
public FuncPeriodSpecification(Func<DateTime, CalendarInfo> expiryFunc)
|
||||
{
|
||||
if (expiryFunc(_verificationDate).Start > _verificationDate)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(FuncPeriodSpecification)}: Please use a function that computes the start of the bar associated with the given date time. Should never return a time later than the one passed in.");
|
||||
}
|
||||
_calendarInfoFunc = expiryFunc;
|
||||
}
|
||||
|
||||
public DateTime GetRoundedBarTime(DateTime time)
|
||||
{
|
||||
var calendarInfo = _calendarInfoFunc(time);
|
||||
Period = calendarInfo.Period;
|
||||
return calendarInfo.Start;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 Python.Runtime;
|
||||
using QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Consolidates QuoteBars into larger QuoteBars
|
||||
/// </summary>
|
||||
public class QuoteBarConsolidator : PeriodCountConsolidatorBase<QuoteBar, QuoteBar>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
/// <param name="startTime">Optionally the bar start time anchor to use</param>
|
||||
public QuoteBarConsolidator(TimeSpan period, TimeSpan? startTime = null)
|
||||
: base(period, startTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
public QuoteBarConsolidator(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="QuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public QuoteBarConsolidator(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'QuoteBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
public QuoteBarConsolidator(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'QuoteBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="pyfuncobj">Python function object that defines the start time of a consolidated data</param>
|
||||
public QuoteBarConsolidator(PyObject pyfuncobj)
|
||||
: base(pyfuncobj)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new consolidated bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref QuoteBar workingBar, QuoteBar data)
|
||||
{
|
||||
var bid = data.Bid;
|
||||
var ask = data.Ask;
|
||||
|
||||
if (workingBar == null)
|
||||
{
|
||||
workingBar = new QuoteBar(GetRoundedBarTime(data), data.Symbol, null, 0, null, 0, IsTimeBased && Period.HasValue ? Period : data.Period);
|
||||
|
||||
// open ask and bid should match previous close ask and bid
|
||||
if (Consolidated != null)
|
||||
{
|
||||
// note that we will only fill forward previous close ask and bid when a new data point comes in and we generate a new working bar which is not a fill forward bar
|
||||
var previous = Consolidated as QuoteBar;
|
||||
workingBar.Update(0, previous.Bid?.Close ?? 0, previous.Ask?.Close ?? 0, 0, previous.LastBidSize, previous.LastAskSize);
|
||||
}
|
||||
}
|
||||
|
||||
// update the bid and ask
|
||||
if (bid != null)
|
||||
{
|
||||
workingBar.LastBidSize = data.LastBidSize;
|
||||
if (workingBar.Bid == null)
|
||||
{
|
||||
workingBar.Bid = new Bar(bid.Open, bid.High, bid.Low, bid.Close);
|
||||
}
|
||||
else
|
||||
{
|
||||
workingBar.Bid.Close = bid.Close;
|
||||
if (workingBar.Bid.High < bid.High) workingBar.Bid.High = bid.High;
|
||||
if (workingBar.Bid.Low > bid.Low) workingBar.Bid.Low = bid.Low;
|
||||
}
|
||||
}
|
||||
if (ask != null)
|
||||
{
|
||||
workingBar.LastAskSize = data.LastAskSize;
|
||||
if (workingBar.Ask == null)
|
||||
{
|
||||
workingBar.Ask = new Bar(ask.Open, ask.High, ask.Low, ask.Close);
|
||||
}
|
||||
else
|
||||
{
|
||||
workingBar.Ask.Close = ask.Close;
|
||||
if (workingBar.Ask.High < ask.High) workingBar.Ask.High = ask.High;
|
||||
if (workingBar.Ask.Low > ask.Low) workingBar.Ask.Low = ask.Low;
|
||||
}
|
||||
}
|
||||
|
||||
workingBar.Value = data.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using QuantConnect.Securities;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// This consolidator can transform a stream of <see cref="IBaseData"/> instances into a stream of <see cref="RangeBar"/>
|
||||
/// </summary>
|
||||
public class RangeConsolidator : BaseTimelessConsolidator<RangeBar>
|
||||
{
|
||||
private bool _firstTick;
|
||||
private decimal _minimumPriceVariation;
|
||||
|
||||
/// <summary>
|
||||
/// Symbol properties database to use to get the minimum price variation of certain symbol
|
||||
/// </summary>
|
||||
private static SymbolPropertiesDatabase _symbolPropertiesDatabase = SymbolPropertiesDatabase.FromDataFolder();
|
||||
|
||||
/// <summary>
|
||||
/// Bar being created
|
||||
/// </summary>
|
||||
protected override RangeBar CurrentBar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Range for each RangeBar, this is, the difference between the High and Low for each
|
||||
/// RangeBar
|
||||
/// </summary>
|
||||
public decimal RangeSize { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of MinimumPriceVariation units
|
||||
/// </summary>
|
||||
public int Range { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="RangeBar"/> which is the type emitted in the <see cref="IDataConsolidator.DataConsolidated"/> event.
|
||||
/// </summary>
|
||||
public override Type OutputType => typeof(RangeBar);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData => CurrentBar?.Clone();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RangeConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="range">The Range interval sets the range in which the price moves, which in turn initiates the formation of a new bar.
|
||||
/// One range equals to one minimum price change, where this last value is defined depending of the RangeBar's symbol</param>
|
||||
/// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RangeBar"/>. 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, except if the input is a TradeBar.</param>
|
||||
public RangeConsolidator(
|
||||
int range,
|
||||
Func<IBaseData, decimal> selector = null,
|
||||
Func<IBaseData, decimal> volumeSelector = null)
|
||||
: base(selector, volumeSelector)
|
||||
{
|
||||
Range = range;
|
||||
_firstTick = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RangeConsolidator" /> class.
|
||||
/// </summary>
|
||||
/// <param name="range">The Range interval sets the range in which the price moves, which in turn initiates the formation of a new bar.
|
||||
/// One range equals to one minimum price change, where this last value is defined depending of the RangeBar's symbol</param>
|
||||
/// <param name="selector">Extracts the value from a data instance to be formed into a <see cref="RangeBar"/>. 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>
|
||||
public RangeConsolidator(int range,
|
||||
PyObject selector,
|
||||
PyObject volumeSelector = null)
|
||||
: base(selector, volumeSelector)
|
||||
{
|
||||
Range = range;
|
||||
_firstTick = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
_firstTick = true;
|
||||
_minimumPriceVariation = 0m;
|
||||
RangeSize = 0m;
|
||||
}
|
||||
|
||||
/// <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 override void UpdateBar(DateTime time, decimal currentValue, decimal volume)
|
||||
{
|
||||
bool isRising = default;
|
||||
if (currentValue > CurrentBar.High)
|
||||
{
|
||||
isRising = true;
|
||||
}
|
||||
else if (currentValue < CurrentBar.Low)
|
||||
{
|
||||
isRising = false;
|
||||
}
|
||||
|
||||
CurrentBar.Update(time, currentValue, volume);
|
||||
while (CurrentBar.IsClosed)
|
||||
{
|
||||
OnDataConsolidated(CurrentBar);
|
||||
CurrentBar = new RangeBar(CurrentBar.Symbol, CurrentBar.EndTime, RangeSize, isRising ? CurrentBar.High + _minimumPriceVariation : CurrentBar.Low - _minimumPriceVariation);
|
||||
CurrentBar.Update(time, currentValue, Math.Abs(CurrentBar.Low - currentValue) > RangeSize ? 0 : volume); // Intermediate/phantom RangeBar's have zero 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 for the bar</param>
|
||||
protected override void CreateNewBar(IBaseData data, decimal currentValue, decimal volume)
|
||||
{
|
||||
var open = currentValue;
|
||||
|
||||
if (_firstTick)
|
||||
{
|
||||
_minimumPriceVariation = _symbolPropertiesDatabase.GetSymbolProperties(data.Symbol.ID.Market, data.Symbol, data.Symbol.ID.SecurityType, "USD").MinimumPriceVariation;
|
||||
RangeSize = _minimumPriceVariation * Range;
|
||||
open = Math.Ceiling(open / RangeSize) * RangeSize;
|
||||
_firstTick = false;
|
||||
}
|
||||
|
||||
CurrentBar = new RangeBar(data.Symbol, data.Time, RangeSize, open, volume: volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// This consolidator can transform a stream of <see cref="BaseData"/> instances into a stream of <see cref="RenkoBar"/>
|
||||
/// with Renko type <see cref="RenkoType.Wicked"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This implementation replaced the original implementation that was shown to have inaccuracies in its representation
|
||||
/// of Renko charts. The original implementation has been moved to <see cref="ClassicRenkoConsolidator"/>.</remarks>
|
||||
public class RenkoConsolidator : ConsolidatorBase
|
||||
{
|
||||
private bool _firstTick = true;
|
||||
private RenkoBar _lastWicko;
|
||||
private RenkoBar _currentBar;
|
||||
|
||||
/// <summary>
|
||||
/// Time of consolidated close.
|
||||
/// </summary>
|
||||
/// <remarks>Protected for testing</remarks>
|
||||
protected DateTime CloseOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value of consolidated close.
|
||||
/// </summary>
|
||||
/// <remarks>Protected for testing</remarks>
|
||||
protected decimal CloseRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value of consolidated high.
|
||||
/// </summary>
|
||||
/// <remarks>Protected for testing</remarks>
|
||||
protected decimal HighRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value of consolidated low.
|
||||
/// </summary>
|
||||
/// <remarks>Protected for testing</remarks>
|
||||
protected decimal LowRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time of consolidated open.
|
||||
/// </summary>
|
||||
/// <remarks>Protected for testing</remarks>
|
||||
protected DateTime OpenOn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Value of consolidate open.
|
||||
/// </summary>
|
||||
/// <remarks>Protected for testing</remarks>
|
||||
protected decimal OpenRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the consolidated bar.
|
||||
/// </summary>
|
||||
/// <remarks>Protected for testing</remarks>
|
||||
protected decimal BarSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the kind of the bar
|
||||
/// </summary>
|
||||
public RenkoType Type => RenkoType.Wicked;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData => _currentBar?.Clone();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type consumed by this consolidator
|
||||
/// </summary>
|
||||
public override Type InputType => typeof(IBaseData);
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="RenkoBar"/> which is the type emitted in the <see cref="IDataConsolidator.DataConsolidated"/> event.
|
||||
/// </summary>
|
||||
public override Type OutputType => typeof(RenkoBar);
|
||||
|
||||
/// <summary>
|
||||
/// Typed event handler that fires when a new piece of data is produced
|
||||
/// </summary>
|
||||
public new event EventHandler<RenkoBar> DataConsolidated;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
public RenkoConsolidator(decimal barSize)
|
||||
{
|
||||
if (barSize <= 0)
|
||||
{
|
||||
throw new ArgumentException("Renko consolidator BarSize must be strictly greater than zero");
|
||||
}
|
||||
|
||||
BarSize = barSize;
|
||||
}
|
||||
|
||||
/// <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 rate = data.Price;
|
||||
|
||||
if (_firstTick)
|
||||
{
|
||||
_firstTick = false;
|
||||
|
||||
// Round our first rate to the same length as BarSize
|
||||
rate = GetClosestMultiple(rate, BarSize);
|
||||
|
||||
OpenOn = data.Time;
|
||||
CloseOn = data.Time;
|
||||
OpenRate = rate;
|
||||
HighRate = rate;
|
||||
LowRate = rate;
|
||||
CloseRate = rate;
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseOn = data.Time;
|
||||
|
||||
if (rate > HighRate)
|
||||
{
|
||||
HighRate = rate;
|
||||
}
|
||||
|
||||
if (rate < LowRate)
|
||||
{
|
||||
LowRate = rate;
|
||||
}
|
||||
|
||||
CloseRate = rate;
|
||||
|
||||
if (CloseRate > OpenRate)
|
||||
{
|
||||
if (_lastWicko == null || _lastWicko.Direction == BarDirection.Rising)
|
||||
{
|
||||
Rising(data);
|
||||
return;
|
||||
}
|
||||
|
||||
var limit = _lastWicko.Open + BarSize;
|
||||
|
||||
if (CloseRate > limit)
|
||||
{
|
||||
var wicko = new RenkoBar(data.Symbol, OpenOn, CloseOn, BarSize, _lastWicko.Open, limit,
|
||||
LowRate, limit);
|
||||
|
||||
_lastWicko = wicko;
|
||||
|
||||
OnDataConsolidated(wicko);
|
||||
|
||||
OpenOn = CloseOn;
|
||||
OpenRate = limit;
|
||||
LowRate = limit;
|
||||
|
||||
Rising(data);
|
||||
}
|
||||
}
|
||||
else if (CloseRate < OpenRate)
|
||||
{
|
||||
if (_lastWicko == null || _lastWicko.Direction == BarDirection.Falling)
|
||||
{
|
||||
Falling(data);
|
||||
return;
|
||||
}
|
||||
|
||||
var limit = _lastWicko.Open - BarSize;
|
||||
|
||||
if (CloseRate < limit)
|
||||
{
|
||||
var wicko = new RenkoBar(data.Symbol, OpenOn, CloseOn, BarSize, _lastWicko.Open, HighRate,
|
||||
limit, limit);
|
||||
|
||||
_lastWicko = wicko;
|
||||
|
||||
OnDataConsolidated(wicko);
|
||||
|
||||
OpenOn = CloseOn;
|
||||
OpenRate = limit;
|
||||
HighRate = limit;
|
||||
|
||||
Falling(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
_firstTick = true;
|
||||
_lastWicko = null;
|
||||
_currentBar = null;
|
||||
CloseOn = default;
|
||||
CloseRate = default;
|
||||
HighRate = default;
|
||||
LowRate = default;
|
||||
OpenOn = default;
|
||||
OpenRate = default;
|
||||
base.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raises the strongly typed DataConsolidated event
|
||||
/// </summary>
|
||||
/// <param name="consolidated">The newly consolidated data</param>
|
||||
protected override void FireDataConsolidated(IBaseData consolidated)
|
||||
{
|
||||
var bar = (RenkoBar)consolidated;
|
||||
// fire the typed event before updating the current bar so handlers reading
|
||||
// WorkingData still see the previous bar, as they did before the rolling window
|
||||
DataConsolidated?.Invoke(this, bar);
|
||||
_currentBar = bar;
|
||||
}
|
||||
|
||||
private void Rising(IBaseData data)
|
||||
{
|
||||
decimal limit;
|
||||
|
||||
while (CloseRate > (limit = OpenRate + BarSize))
|
||||
{
|
||||
var wicko = new RenkoBar(data.Symbol, OpenOn, CloseOn, BarSize, OpenRate, limit, LowRate, limit);
|
||||
|
||||
_lastWicko = wicko;
|
||||
|
||||
OnDataConsolidated(wicko);
|
||||
|
||||
OpenOn = CloseOn;
|
||||
OpenRate = limit;
|
||||
LowRate = limit;
|
||||
}
|
||||
}
|
||||
|
||||
private void Falling(IBaseData data)
|
||||
{
|
||||
decimal limit;
|
||||
|
||||
while (CloseRate < (limit = OpenRate - BarSize))
|
||||
{
|
||||
var wicko = new RenkoBar(data.Symbol, OpenOn, CloseOn, BarSize, OpenRate, HighRate, limit, limit);
|
||||
|
||||
_lastWicko = wicko;
|
||||
|
||||
OnDataConsolidated(wicko);
|
||||
|
||||
OpenOn = CloseOn;
|
||||
OpenRate = limit;
|
||||
HighRate = limit;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the closest BarSize-Multiple to the price.
|
||||
/// </summary>
|
||||
/// <remarks>Based on: The Art of Computer Programming, Vol I, pag 39. Donald E. Knuth</remarks>
|
||||
/// <param name="price">Price to be rounded to the closest BarSize-Multiple</param>
|
||||
/// <param name="barSize">The size of the Renko bar</param>
|
||||
/// <returns>The closest BarSize-Multiple to the price</returns>
|
||||
public static decimal GetClosestMultiple(decimal price, decimal barSize)
|
||||
{
|
||||
if (barSize <= 0)
|
||||
{
|
||||
throw new ArgumentException("BarSize must be strictly greater than zero");
|
||||
}
|
||||
|
||||
var modulus = price - barSize * Math.Floor(price / barSize);
|
||||
var round = Math.Round(modulus / barSize);
|
||||
return barSize * (Math.Floor(price / barSize) + round);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides a type safe wrapper on the RenkoConsolidator class. This just allows us to define our selector functions with the real type they'll be receiving
|
||||
/// </summary>
|
||||
/// <typeparam name="TInput"></typeparam>
|
||||
public class RenkoConsolidator<TInput> : RenkoConsolidator
|
||||
where TInput : IBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
public RenkoConsolidator(decimal barSize)
|
||||
: base(barSize)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Type safe shim method.
|
||||
/// </remarks>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public void Update(TInput data)
|
||||
{
|
||||
base.Update(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This consolidator can transform a stream of <see cref="BaseData"/> instances into a stream of <see cref="RenkoBar"/>
|
||||
/// with Renko type <see cref="RenkoType.Wicked"/>.
|
||||
/// /// </summary>
|
||||
/// <remarks>For backwards compatibility now that WickedRenkoConsolidators -> RenkoConsolidator</remarks>
|
||||
public class WickedRenkoConsolidator : RenkoConsolidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
public WickedRenkoConsolidator(decimal barSize)
|
||||
: base(barSize)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This consolidator can transform a stream of <see cref="BaseData"/> instances into a stream of <see cref="RenkoBar"/>
|
||||
/// with Renko type <see cref="RenkoType.Wicked"/>.
|
||||
/// Provides a type safe wrapper on the WickedRenkoConsolidator class. This just allows us to define our selector functions with the real type they'll be receiving
|
||||
/// /// </summary>
|
||||
/// <remarks>For backwards compatibility now that WickedRenkoConsolidators -> RenkoConsolidator</remarks>
|
||||
public class WickedRenkoConsolidator<T> : RenkoConsolidator<T>
|
||||
where T : IBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant value size of each bar</param>
|
||||
public WickedRenkoConsolidator(decimal barSize)
|
||||
: base(barSize)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// This consolidator wires up the events on its First and Second consolidators
|
||||
/// such that data flows from the First to Second consolidator. It's output comes
|
||||
/// from the Second.
|
||||
/// </summary>
|
||||
public class SequentialConsolidator : ConsolidatorBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the first consolidator to receive data
|
||||
/// </summary>
|
||||
public IDataConsolidator First
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the second consolidator that ends up receiving data produced
|
||||
/// by the first
|
||||
/// </summary>
|
||||
public IDataConsolidator Second
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData
|
||||
{
|
||||
get { return Second.WorkingData; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type consumed by this consolidator
|
||||
/// </summary>
|
||||
public override Type InputType
|
||||
{
|
||||
get { return First.InputType; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type produced by this consolidator
|
||||
/// </summary>
|
||||
public override Type OutputType
|
||||
{
|
||||
get { return Second.OutputType; }
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
First.Update(data);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
First.Scan(currentLocalTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new consolidator that will pump date through the first, and then the output
|
||||
/// of the first into the second. This enables 'wrapping' or 'composing' of consolidators
|
||||
/// </summary>
|
||||
/// <param name="first">The first consolidator to receive data</param>
|
||||
/// <param name="second">The consolidator to receive first's output</param>
|
||||
public SequentialConsolidator(IDataConsolidator first, IDataConsolidator second)
|
||||
{
|
||||
if (!second.InputType.IsAssignableFrom(first.OutputType))
|
||||
{
|
||||
throw new ArgumentException("first.OutputType must equal second.OutputType!");
|
||||
}
|
||||
First = first;
|
||||
Second = second;
|
||||
|
||||
// wire up the second one to get data from the first
|
||||
first.DataConsolidated += (sender, consolidated) => second.Update(consolidated);
|
||||
|
||||
// wire up the second one's events to also fire this consolidator's event so consumers
|
||||
// can attach. This wrapper also keeps its own window
|
||||
second.DataConsolidated += (sender, consolidated) => OnDataConsolidated(consolidated);
|
||||
}
|
||||
|
||||
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
|
||||
/// <filterpriority>2</filterpriority>
|
||||
public override void Dispose()
|
||||
{
|
||||
First.Dispose();
|
||||
Second.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
First.Reset();
|
||||
Second.Reset();
|
||||
base.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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 QuantConnect;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace Common.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Consolidates intraday market data into a single daily <see cref="SessionBar"/> (OHLCV + OpenInterest).
|
||||
/// </summary>
|
||||
public class SessionConsolidator : PeriodCountConsolidatorBase<BaseData, SessionBar>
|
||||
{
|
||||
private readonly SecurityExchangeHours _exchangeHours;
|
||||
private readonly TickType _sourceTickType;
|
||||
private readonly Symbol _symbol;
|
||||
private bool _initialized;
|
||||
internal SessionBar WorkingInstance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_workingBar == null)
|
||||
{
|
||||
InitializeWorkingBar();
|
||||
}
|
||||
return _workingBar;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SessionConsolidator"/> class.
|
||||
/// </summary>
|
||||
/// <param name="exchangeHours">The exchange hours</param>
|
||||
/// <param name="sourceTickType">Type of the source tick</param>
|
||||
/// <param name="symbol">The symbol</param>
|
||||
public SessionConsolidator(SecurityExchangeHours exchangeHours, TickType sourceTickType, Symbol symbol) : base(Time.OneDay)
|
||||
{
|
||||
_symbol = symbol;
|
||||
_exchangeHours = exchangeHours;
|
||||
_sourceTickType = sourceTickType;
|
||||
InitializeWorkingBar();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new trade bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref SessionBar workingBar, BaseData data)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
if (workingBar.Time == DateTime.MaxValue || data.Time.Date > workingBar.Time.Date)
|
||||
{
|
||||
workingBar.Time = data.Time.Date;
|
||||
}
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
// Handle open interest
|
||||
if (data.DataType == MarketDataType.Tick && data is Tick oiTick && oiTick.TickType == TickType.OpenInterest)
|
||||
{
|
||||
// Update the working session bar with the latest open interest
|
||||
workingBar.OpenInterest = oiTick.Value;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_exchangeHours.IsOpen(data.Time, data.EndTime, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the working session bar
|
||||
workingBar.Update(data, Consolidated);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the current local time and triggers Scan() if a new day is detected.
|
||||
/// </summary>
|
||||
/// <param name="currentLocalTime">The current local time.</param>
|
||||
public void ValidateAndScan(DateTime currentLocalTime)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentLocalTime.Date != WorkingInstance.Time.Date)
|
||||
{
|
||||
Scan(currentLocalTime);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that fires when a new piece of data is produced
|
||||
/// </summary>
|
||||
//public event DataConsolidatedHandler DataConsolidated;
|
||||
|
||||
protected override void OnDataConsolidated(SessionBar e)
|
||||
{
|
||||
_workingBar = null;
|
||||
base.OnDataConsolidated(e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the working bar
|
||||
/// </summary>
|
||||
protected override void ResetWorkingBar()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
InitializeWorkingBar();
|
||||
}
|
||||
|
||||
private void InitializeWorkingBar()
|
||||
{
|
||||
var time = DateTime.MaxValue;
|
||||
if (Consolidated != null)
|
||||
{
|
||||
time = _exchangeHours.GetNextTradingDay(Consolidated.Time).Date;
|
||||
}
|
||||
_workingBar = new SessionBar(_sourceTickType)
|
||||
{
|
||||
Time = time,
|
||||
Symbol = _symbol
|
||||
};
|
||||
_initialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// A data consolidator that can make bigger bars from ticks over a given
|
||||
/// time span or a count of pieces of data.
|
||||
/// </summary>
|
||||
public class TickConsolidator : TradeBarConsolidatorBase<Tick>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the period
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public TickConsolidator(TimeSpan period)
|
||||
: base(period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
public TickConsolidator(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public TickConsolidator(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TickQuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
public TickConsolidator(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="pyfuncobj">Python function object that defines the start time of a consolidated data</param>
|
||||
public TickConsolidator(PyObject pyfuncobj)
|
||||
: base(pyfuncobj)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not the specified data should be processed
|
||||
/// </summary>
|
||||
/// <param name="data">The data to check</param>
|
||||
/// <returns>True if the consolidator should process this data, false otherwise</returns>
|
||||
protected override bool ShouldProcess(Tick data)
|
||||
{
|
||||
return data.TickType == TickType.Trade;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref TradeBar workingBar, Tick data)
|
||||
{
|
||||
if (workingBar == null)
|
||||
{
|
||||
workingBar = new TradeBar(GetRoundedBarTime(data),
|
||||
data.Symbol,
|
||||
data.Value,
|
||||
data.Value,
|
||||
data.Value,
|
||||
data.Value,
|
||||
data.Quantity,
|
||||
Period);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Aggregate the working bar
|
||||
workingBar.Close = data.Value;
|
||||
workingBar.Volume += data.Quantity;
|
||||
if (data.Value < workingBar.Low) workingBar.Low = data.Value;
|
||||
if (data.Value > workingBar.High) workingBar.High = data.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// Consolidates ticks into quote bars. This consolidator ignores trade ticks
|
||||
/// </summary>
|
||||
public class TickQuoteBarConsolidator : PeriodCountConsolidatorBase<Tick, QuoteBar>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TickQuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
/// <param name="startTime">Optionally the bar start time anchor to use</param>
|
||||
public TickQuoteBarConsolidator(TimeSpan period, TimeSpan? startTime = null)
|
||||
: base(period, startTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TickQuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
public TickQuoteBarConsolidator(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TickQuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public TickQuoteBarConsolidator(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TickQuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
public TickQuoteBarConsolidator(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TickQuoteBarConsolidator"/> class
|
||||
/// </summary>
|
||||
/// <param name="pyfuncobj">Python function object that defines the start time of a consolidated data</param>
|
||||
public TickQuoteBarConsolidator(PyObject pyfuncobj)
|
||||
: base(pyfuncobj)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not the specified data should be processed
|
||||
/// </summary>
|
||||
/// <param name="data">The data to check</param>
|
||||
/// <returns>True if the consolidator should process this data, false otherwise</returns>
|
||||
protected override bool ShouldProcess(Tick data)
|
||||
{
|
||||
return data.TickType == TickType.Quote;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new consolidated bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref QuoteBar workingBar, Tick data)
|
||||
{
|
||||
if (workingBar == null)
|
||||
{
|
||||
workingBar = new QuoteBar(GetRoundedBarTime(data), data.Symbol, null, decimal.Zero, null, decimal.Zero, Period);
|
||||
|
||||
// open ask and bid should match previous close ask and bid
|
||||
if (Consolidated != null)
|
||||
{
|
||||
// note that we will only fill forward previous close ask and bid when a new data point comes in and we generate a new working bar which is not a fill forward bar
|
||||
var previous = Consolidated as QuoteBar;
|
||||
workingBar.Update(decimal.Zero, previous.Bid?.Close ?? decimal.Zero, previous.Ask?.Close ?? decimal.Zero, decimal.Zero, previous.LastBidSize, previous.LastAskSize);
|
||||
}
|
||||
}
|
||||
|
||||
// update the bid and ask
|
||||
workingBar.Update(decimal.Zero, data.BidPrice, data.AskPrice, decimal.Zero, data.BidSize, data.AskSize);
|
||||
if (!Period.HasValue) workingBar.EndTime = GetRoundedBarTime(data.EndTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// A data consolidator that can make bigger bars from smaller ones over a given
|
||||
/// time span or a count of pieces of data.
|
||||
///
|
||||
/// Use this consolidator to turn data of a lower resolution into data of a higher resolution,
|
||||
/// for example, if you subscribe to minute data but want to have a 15 minute bar.
|
||||
/// </summary>
|
||||
public class TradeBarConsolidator : TradeBarConsolidatorBase<TradeBar>
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new TradeBarConsolidator for the desired resolution
|
||||
/// </summary>
|
||||
/// <param name="resolution">The resolution desired</param>
|
||||
/// <returns>A consolidator that produces data on the resolution interval</returns>
|
||||
public static TradeBarConsolidator FromResolution(Resolution resolution)
|
||||
{
|
||||
return new TradeBarConsolidator(resolution.ToTimeSpan());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the period
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
/// <param name="startTime">Optionally the bar start time anchor to use</param>
|
||||
public TradeBarConsolidator(TimeSpan period, TimeSpan? startTime = null)
|
||||
: base(period, startTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
public TradeBarConsolidator(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emitting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
public TradeBarConsolidator(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
public TradeBarConsolidator(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="pyfuncobj">Python function object that defines the start time of a consolidated data</param>
|
||||
public TradeBarConsolidator(PyObject pyfuncobj)
|
||||
: base(pyfuncobj)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggregates the new 'data' into the 'workingBar'. The 'workingBar' will be
|
||||
/// null following the event firing
|
||||
/// </summary>
|
||||
/// <param name="workingBar">The bar we're building, null if the event was just fired and we're starting a new trade bar</param>
|
||||
/// <param name="data">The new data</param>
|
||||
protected override void AggregateBar(ref TradeBar workingBar, TradeBar data)
|
||||
{
|
||||
if (workingBar == null)
|
||||
{
|
||||
workingBar = new TradeBar
|
||||
{
|
||||
Time = GetRoundedBarTime(data),
|
||||
Symbol = data.Symbol,
|
||||
Open = data.Open,
|
||||
High = data.High,
|
||||
Low = data.Low,
|
||||
Close = data.Close,
|
||||
Volume = data.Volume,
|
||||
DataType = MarketDataType.TradeBar,
|
||||
Period = IsTimeBased && Period.HasValue ? (TimeSpan)Period : data.Period
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
//Aggregate the working bar
|
||||
workingBar.Close = data.Close;
|
||||
workingBar.Volume += data.Volume;
|
||||
if (data.Low < workingBar.Low) workingBar.Low = data.Low;
|
||||
if (data.High > workingBar.High) workingBar.High = data.High;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
using Python.Runtime;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// A data consolidator that can make bigger bars from any base data
|
||||
///
|
||||
/// This type acts as the base for other consolidators that produce bars on a given time step or for a count of data.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The input type into the consolidator's Update method</typeparam>
|
||||
public abstract class TradeBarConsolidatorBase<T> : PeriodCountConsolidatorBase<T, TradeBar>
|
||||
where T : IBaseData
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the period
|
||||
/// </summary>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
/// <param name="startTime">Optionally the bar start time anchor to use</param>
|
||||
protected TradeBarConsolidatorBase(TimeSpan period, TimeSpan? startTime = null)
|
||||
: base(period, startTime)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
|
||||
protected TradeBarConsolidatorBase(int maxCount)
|
||||
: base(maxCount)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="maxCount">The number of pieces to accept before emiting a consolidated bar</param>
|
||||
/// <param name="period">The minimum span of time before emitting a consolidated bar</param>
|
||||
protected TradeBarConsolidatorBase(int maxCount, TimeSpan period)
|
||||
: base(maxCount, period)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="func">Func that defines the start time of a consolidated data</param>
|
||||
protected TradeBarConsolidatorBase(Func<DateTime, CalendarInfo> func)
|
||||
: base(func)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a consolidator to produce a new 'TradeBar' representing the last count pieces of data or the period, whichever comes first
|
||||
/// </summary>
|
||||
/// <param name="pyfuncobj">Python function object that defines the start time of a consolidated data</param>
|
||||
protected TradeBarConsolidatorBase(PyObject pyfuncobj)
|
||||
: base(pyfuncobj)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the current 'workingBar'.
|
||||
/// </summary>
|
||||
public TradeBar WorkingBar => (TradeBar) WorkingData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Data.Consolidators
|
||||
{
|
||||
/// <summary>
|
||||
/// This consolidator can transform a stream of <see cref="BaseData"/> instances into a stream of <see cref="RenkoBar"/>
|
||||
/// with a constant volume for each bar.
|
||||
/// </summary>
|
||||
public class VolumeRenkoConsolidator : DataConsolidator<BaseData>
|
||||
{
|
||||
private VolumeRenkoBar _currentBar;
|
||||
private decimal _barSize;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a clone of the data being currently consolidated
|
||||
/// </summary>
|
||||
public override IBaseData WorkingData => _currentBar;
|
||||
|
||||
/// <summary>
|
||||
/// Gets <see cref="VolumeRenkoBar"/> which is the type emitted in the <see cref="IDataConsolidator.DataConsolidated"/> event.
|
||||
/// </summary>
|
||||
public override Type OutputType => typeof(VolumeRenkoBar);
|
||||
|
||||
/// <summary>
|
||||
/// Event handler that fires when a new piece of data is produced
|
||||
/// </summary>
|
||||
public new event EventHandler<VolumeRenkoBar> DataConsolidated;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VolumeRenkoConsolidator"/> class using the specified <paramref name="barSize"/>.
|
||||
/// </summary>
|
||||
/// <param name="barSize">The constant volume size of each bar</param>
|
||||
public VolumeRenkoConsolidator(decimal barSize)
|
||||
{
|
||||
_barSize = barSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this consolidator with the specified data
|
||||
/// </summary>
|
||||
/// <param name="data">The new data for the consolidator</param>
|
||||
public override void Update(BaseData data)
|
||||
{
|
||||
var close = data.Price;
|
||||
var dataType = data.GetType();
|
||||
|
||||
decimal volume;
|
||||
decimal open;
|
||||
decimal high;
|
||||
decimal low;
|
||||
|
||||
if (dataType == typeof(TradeBar))
|
||||
{
|
||||
var tradeBar = (TradeBar)data;
|
||||
volume = tradeBar.Volume;
|
||||
open = tradeBar.Open;
|
||||
high = tradeBar.High;
|
||||
low = tradeBar.Low;
|
||||
}
|
||||
else if (dataType == typeof(Tick))
|
||||
{
|
||||
var tick = (Tick)data;
|
||||
// Only include actual trade information
|
||||
if (tick.TickType != TickType.Trade)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
volume = tick.Quantity;
|
||||
open = close;
|
||||
high = close;
|
||||
low = close;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"{GetType().Name} must be used with TradeBar or Tick data.");
|
||||
}
|
||||
|
||||
var adjustedVolume = AdjustVolume(volume, close);
|
||||
|
||||
if (_currentBar == null)
|
||||
{
|
||||
_currentBar = new VolumeRenkoBar(data.Symbol, data.Time, data.EndTime, _barSize, open, high, low, close, 0);
|
||||
}
|
||||
var volumeLeftOver = _currentBar.Update(data.EndTime, high, low, close, adjustedVolume);
|
||||
while (volumeLeftOver >= 0)
|
||||
{
|
||||
OnDataConsolidated(_currentBar);
|
||||
_currentBar = _currentBar.Rollover();
|
||||
volumeLeftOver = _currentBar.Update(data.EndTime, high, low, close, volumeLeftOver);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the raw volume without any adjustment.
|
||||
/// </summary>
|
||||
/// <param name="volume">The volume</param>
|
||||
/// <param name="price">The price</param>
|
||||
/// <returns>The unmodified volume</returns>
|
||||
protected virtual decimal AdjustVolume(decimal volume, decimal price)
|
||||
{
|
||||
return volume;
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the consolidator
|
||||
/// </summary>
|
||||
public override void Reset()
|
||||
{
|
||||
base.Reset();
|
||||
_currentBar = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event invocator for the DataConsolidated event. This should be invoked
|
||||
/// by derived classes when they have consolidated a new piece of data.
|
||||
/// </summary>
|
||||
/// <param name="consolidated">The newly consolidated data</param>
|
||||
protected void OnDataConsolidated(VolumeRenkoBar consolidated)
|
||||
{
|
||||
base.OnDataConsolidated(consolidated);
|
||||
DataConsolidated?.Invoke(this, consolidated);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user