/*
* 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);
}
}
}