/* * 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. */ // ReSharper disable InconsistentNaming using System; using Python.Runtime; using QuantConnect.Data; using System.Globalization; namespace QuantConnect.Indicators { /// /// Provides extension methods for Indicator /// public static class IndicatorExtensions { /// /// Updates the state of this indicator with the given value and returns true /// if this indicator is ready, false otherwise /// /// The indicator to be updated /// The time associated with the value /// The value to use to update this indicator /// True if this indicator is ready, false otherwise public static bool Update(this IndicatorBase indicator, DateTime time, decimal value) { return indicator.Update(new IndicatorDataPoint(time, value)); } /// /// Configures the second indicator to receive automatic updates from the first by attaching an event handler /// to first.DataConsolidated /// /// The indicator that receives data from the first /// The indicator that sends data via DataConsolidated even to the second /// True to only send updates to the second if first.IsReady returns true, false to always send updates to second /// The reference to the second indicator to allow for method chaining public static T Of(this T second, IIndicator first, bool waitForFirstToReady = true) where T : IIndicator { first.Updated += (sender, consolidated) => { // only send the data along if we're ready if (!waitForFirstToReady || first.IsReady) { second.Update(consolidated); } }; return second; } /// /// Creates a new CompositeIndicator such that the result will be average of a first indicator weighted by a second one /// /// Indicator that will be averaged /// Indicator that provides the average weights /// Average period /// Indicator that results of the average of first by weights given by second public static CompositeIndicator WeightedBy(this IndicatorBase value, IndicatorBase weight, int period) { var x = new WindowIdentity(period); var y = new WindowIdentity(period); var numerator = new Sum("Sum_xy", period); var denominator = new Sum("Sum_y", period); value.Updated += (sender, consolidated) => { x.Update(consolidated); if (x.Samples == y.Samples) { numerator.Update(consolidated.Time, consolidated.Value * y.Current.Value); } }; weight.Updated += (sender, consolidated) => { y.Update(consolidated); if (x.Samples == y.Samples) { numerator.Update(consolidated.Time, consolidated.Value * x.Current.Value); } denominator.Update(consolidated); }; var resetCompositeIndicator = new ResetCompositeIndicator(numerator, denominator, GetOverIndicatorComposer(), () => { x.Reset(); y.Reset(); }); return resetCompositeIndicator; } /// /// Creates a new CompositeIndicator such that the result will be the sum of the left and the constant /// /// /// value = left + constant /// /// The left indicator /// The addend /// The sum of the left and right indicators public static CompositeIndicator Plus(this IndicatorBase left, decimal constant) { var constantIndicator = new ConstantIndicator(constant.ToString(CultureInfo.InvariantCulture), constant); return left.Plus(constantIndicator); } /// /// Creates a new CompositeIndicator such that the result will be the sum of the left and right /// /// /// value = left + right /// /// The left indicator /// The right indicator /// The sum of the left and right indicators public static CompositeIndicator Plus(this IndicatorBase left, IndicatorBase right) { return new(left, right, (l, r) => l.Current.Value + r.Current.Value); } /// /// Creates a new CompositeIndicator such that the result will be the sum of the left and right /// /// /// value = left + right /// /// The left indicator /// The right indicator /// The name of this indicator /// The sum of the left and right indicators public static CompositeIndicator Plus(this IndicatorBase left, IndicatorBase right, string name) { return new(name, left, right, (l, r) => l.Current.Value + r.Current.Value); } /// /// Creates a new CompositeIndicator such that the result will be the difference of the left and constant /// /// /// value = left - constant /// /// The left indicator /// The subtrahend /// The difference of the left and right indicators public static CompositeIndicator Minus(this IndicatorBase left, decimal constant) { var constantIndicator = new ConstantIndicator(constant.ToString(CultureInfo.InvariantCulture), constant); return left.Minus(constantIndicator); } /// /// Creates a new CompositeIndicator such that the result will be the difference of the left and right /// /// /// value = left - right /// /// The left indicator /// The right indicator /// The difference of the left and right indicators public static CompositeIndicator Minus(this IndicatorBase left, IndicatorBase right) { return new(left, right, (l, r) => l.Current.Value - r.Current.Value); } /// /// Creates a new CompositeIndicator such that the result will be the difference of the left and right /// /// /// value = left - right /// /// The left indicator /// The right indicator /// The name of this indicator /// The difference of the left and right indicators public static CompositeIndicator Minus(this IndicatorBase left, IndicatorBase right, string name) { return new(name, left, right, (l, r) => l.Current.Value - r.Current.Value); } /// /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the constant /// /// /// value = left/constant /// /// The left indicator /// The constant value denominator /// The ratio of the left to the right indicator public static CompositeIndicator Over(this IndicatorBase left, decimal constant) { var constantIndicator = new ConstantIndicator(constant.ToString(CultureInfo.InvariantCulture), constant); return left.Over(constantIndicator); } /// /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the right /// /// /// value = left/right /// /// The left indicator /// The right indicator /// The ratio of the left to the right indicator public static CompositeIndicator Over(this IndicatorBase left, IndicatorBase right) { return new(left, right, GetOverIndicatorComposer()); } /// /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the right /// /// /// value = left/right /// /// The left indicator /// The right indicator /// The name of this indicator /// The ratio of the left to the right indicator public static CompositeIndicator Over(this IndicatorBase left, IndicatorBase right, string name) { return new(name, left, right, GetOverIndicatorComposer()); } /// /// Creates a new CompositeIndicator such that the result will be the product of the left and the constant /// /// /// value = left*constant /// /// The left indicator /// The constant value to multiple by /// The product of the left to the right indicators public static CompositeIndicator Times(this IndicatorBase left, decimal constant) { var constantIndicator = new ConstantIndicator(constant.ToString(CultureInfo.InvariantCulture), constant); return left.Times(constantIndicator); } /// /// Creates a new CompositeIndicator such that the result will be the product of the left to the right /// /// /// value = left*right /// /// The left indicator /// The right indicator /// The product of the left to the right indicators public static CompositeIndicator Times(this IndicatorBase left, IndicatorBase right) { return new(left, right, (l, r) => l.Current.Value * r.Current.Value); } /// /// Creates a new CompositeIndicator such that the result will be the product of the left to the right /// /// /// value = left*right /// /// The left indicator /// The right indicator /// The name of this indicator /// The product of the left to the right indicators public static CompositeIndicator Times(this IndicatorBase left, IndicatorBase right, string name) { return new(name, left, right, (l, r) => l.Current.Value * r.Current.Value); } /// Creates a new ExponentialMovingAverage indicator with the specified period and smoothingFactor from the left indicator /// /// The ExponentialMovingAverage indicator will be created using the data from left /// The period of the ExponentialMovingAverage indicators /// The percentage of data from the previous value to be carried into the next value /// True to only send updates to the second if left.IsReady returns true, false to always send updates /// A reference to the ExponentialMovingAverage indicator to allow for method chaining public static ExponentialMovingAverage EMA(this IndicatorBase left, int period, decimal? smoothingFactor = null, bool waitForFirstToReady = true) { var k = smoothingFactor ?? ExponentialMovingAverage.SmoothingFactorDefault(period); return new ExponentialMovingAverage($"EMA{period}_Of_{left.Name}", period, k).Of(left, waitForFirstToReady); } /// Creates a new Maximum indicator with the specified period from the left indicator /// /// The Maximum indicator will be created using the data from left /// The period of the Maximum indicator /// True to only send updates to the second if left.IsReady returns true, false to always send updates /// A reference to the Maximum indicator to allow for method chaining public static Maximum MAX(this IIndicator left, int period, bool waitForFirstToReady = true) { return new Maximum($"MAX{period}_Of_{left.Name}", period).Of(left, waitForFirstToReady); } /// Creates a new Minimum indicator with the specified period from the left indicator /// /// The Minimum indicator will be created using the data from left /// The period of the Minimum indicator /// True to only send updates to the second if left.IsReady returns true, false to always send updates /// A reference to the Minimum indicator to allow for method chaining public static Minimum MIN(this IndicatorBase left, int period, bool waitForFirstToReady = true) { return new Minimum($"MIN{period}_Of_{left.Name}", period).Of(left, waitForFirstToReady); } /// Initializes a new instance of the SimpleMovingAverage class with the specified name and period from the left indicator /// /// The SimpleMovingAverage indicator will be created using the data from left /// The period of the SMA /// True to only send updates to the second if first.IsReady returns true, false to always send updates to second /// The reference to the SimpleMovingAverage indicator to allow for method chaining public static SimpleMovingAverage SMA(this IndicatorBase left, int period, bool waitForFirstToReady = true) { return new SimpleMovingAverage($"SMA{period}_Of_{left.Name}", period).Of(left, waitForFirstToReady); } /// The methods overloads bellow are due to python.net not being able to correctly solve generic methods overload /// /// Configures the second indicator to receive automatic updates from the first by attaching an event handler /// to first.DataConsolidated /// /// The indicator that receives data from the first /// The indicator that sends data via DataConsolidated even to the second /// True to only send updates to the second if first.IsReady returns true, false to always send updates to second /// The reference to the second indicator to allow for method chaining public static IndicatorBase Of(PyObject second, PyObject first, bool waitForFirstToReady = true) { var indicator1 = GetIndicatorAsManagedObject(first); var indicator2 = GetIndicatorAsManagedObject(second); return Of(indicator2, indicator1, waitForFirstToReady); } /// /// Creates a new CompositeIndicator such that the result will be average of a first indicator weighted by a second one /// /// Indicator that will be averaged /// Indicator that provides the average weights /// Average period /// Indicator that results of the average of first by weights given by second // ReSharper disable once UnusedMember.Global public static CompositeIndicator WeightedBy(PyObject value, PyObject weight, int period) { var indicator1 = GetIndicatorAsManagedObject(value); var indicator2 = GetIndicatorAsManagedObject(weight); return WeightedBy(indicator1, indicator2, period); } /// /// Creates a new ExponentialMovingAverage indicator with the specified period and smoothingFactor from the left indicator /// /// The ExponentialMovingAverage indicator will be created using the data from left /// The period of the ExponentialMovingAverage indicators /// The percentage of data from the previous value to be carried into the next value /// True to only send updates to the second if left.IsReady returns true, false to always send updates /// A reference to the ExponentialMovingAverage indicator to allow for method chaining public static ExponentialMovingAverage EMA(PyObject left, int period, decimal? smoothingFactor = null, bool waitForFirstToReady = true) { var indicator = GetIndicatorAsManagedObject(left); return EMA(indicator, period, smoothingFactor, waitForFirstToReady); } /// /// Creates a new Maximum indicator with the specified period from the left indicator /// /// The Maximum indicator will be created using the data from left /// The period of the Maximum indicator /// True to only send updates to the second if left.IsReady returns true, false to always send updates /// A reference to the Maximum indicator to allow for method chaining public static Maximum MAX(PyObject left, int period, bool waitForFirstToReady = true) { var indicator = GetIndicatorAsManagedObject(left); return MAX(indicator, period, waitForFirstToReady); } /// /// Creates a new Minimum indicator with the specified period from the left indicator /// /// The Minimum indicator will be created using the data from left /// The period of the Minimum indicator /// True to only send updates to the second if left.IsReady returns true, false to always send updates /// A reference to the Minimum indicator to allow for method chaining public static Minimum MIN(PyObject left, int period, bool waitForFirstToReady = true) { var indicator = GetIndicatorAsManagedObject(left); return MIN(indicator, period, waitForFirstToReady); } /// /// Initializes a new instance of the SimpleMovingAverage class with the specified name and period from the left indicator /// /// The SimpleMovingAverage indicator will be created using the data from left /// The period of the SMA /// True to only send updates to the second if first.IsReady returns true, false to always send updates to second /// The reference to the SimpleMovingAverage indicator to allow for method chaining public static SimpleMovingAverage SMA(PyObject left, int period, bool waitForFirstToReady = true) { var indicator = GetIndicatorAsManagedObject(left); return SMA(indicator, period, waitForFirstToReady); } /// /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the constant /// /// /// value = left/constant /// /// The left indicator /// The constant value denominator /// The ratio of the left to the right indicator public static CompositeIndicator Over(PyObject left, decimal constant) { var indicatorLeft = GetIndicatorAsManagedObject(left); return Over(indicatorLeft, constant); } /// /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the right /// /// /// value = left/right /// /// The left indicator /// The right indicator /// The name of this indicator /// The ratio of the left to the right indicator public static CompositeIndicator Over(PyObject left, PyObject right, string name = null) { var indicatorLeft = GetIndicatorAsManagedObject(left); var indicatorRight = GetIndicatorAsManagedObject(right); return Over(indicatorLeft, indicatorRight, name); } /// /// Creates a new CompositeIndicator such that the result will be the difference of the left and constant /// /// /// value = left - constant /// /// The left indicator /// The subtrahend /// The difference of the left and right indicators public static CompositeIndicator Minus(PyObject left, decimal constant) { var indicatorLeft = GetIndicatorAsManagedObject(left); return Minus(indicatorLeft, constant); } /// /// Creates a new CompositeIndicator such that the result will be the difference of the left and right /// /// /// value = left - right /// /// The left indicator /// The right indicator /// The name of this indicator /// The difference of the left and right indicators public static CompositeIndicator Minus(PyObject left, PyObject right, string name = null) { var indicatorLeft = GetIndicatorAsManagedObject(left); var indicatorRight = GetIndicatorAsManagedObject(right); return Minus(indicatorLeft, indicatorRight, name); } /// /// Creates a new CompositeIndicator such that the result will be the product of the left and the constant /// /// /// value = left*constant /// /// The left indicator /// The constant value to multiple by /// The product of the left to the right indicators public static CompositeIndicator Times(PyObject left, decimal constant) { var indicatorLeft = GetIndicatorAsManagedObject(left); return Times(indicatorLeft, constant); } /// /// Creates a new CompositeIndicator such that the result will be the product of the left to the right /// /// /// value = left*right /// /// The left indicator /// The right indicator /// The name of this indicator /// The product of the left to the right indicators public static CompositeIndicator Times(PyObject left, PyObject right, string name = null) { var indicatorLeft = GetIndicatorAsManagedObject(left); var indicatorRight = GetIndicatorAsManagedObject(right); return Times(indicatorLeft, indicatorRight, name); } /// /// Creates a new CompositeIndicator such that the result will be the sum of the left and the constant /// /// /// value = left + constant /// /// The left indicator /// The addend /// The sum of the left and right indicators public static CompositeIndicator Plus(PyObject left, decimal constant) { var indicatorLeft = GetIndicatorAsManagedObject(left); return Plus(indicatorLeft, constant); } /// /// Creates a new CompositeIndicator such that the result will be the sum of the left and right /// /// /// value = left + right /// /// The left indicator /// The right indicator /// The name of this indicator /// The sum of the left and right indicators public static CompositeIndicator Plus(PyObject left, PyObject right, string name = null) { var indicatorLeft = GetIndicatorAsManagedObject(left); var indicatorRight = GetIndicatorAsManagedObject(right); return Plus(indicatorLeft, indicatorRight, name); } internal static IndicatorBase GetIndicatorAsManagedObject(this PyObject indicator) { if (indicator.TryConvert(out PythonIndicator pythonIndicator, true)) { pythonIndicator.SetIndicator(indicator); return pythonIndicator; } return indicator.SafeAsManagedObject(); } /// /// Gets the IndicatorComposer for a CompositeIndicator whose result is the ratio of the left to the right /// /// The IndicatorComposer for a CompositeIndicator whose result is the ratio of the left to the right private static CompositeIndicator.IndicatorComposer GetOverIndicatorComposer() { return (l, r) => r.Current.Value == 0m ? new IndicatorResult(0m, IndicatorStatus.MathError) : new IndicatorResult(l.Current.Value / r.Current.Value); } } }