/*
* 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 System.Globalization;
using QuantConnect.Data;
using Python.Runtime;
using QuantConnect.Util;
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 alway 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, TWeight weight, int period)
where T : IBaseData
where TWeight : IndicatorBase
{
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);
};
return numerator.Over(denominator);
}
///
/// 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)
where T : IBaseData
{
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)
where T : IBaseData
{
return new CompositeIndicator(left, right, (l, r) => l + r);
}
///
/// 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)
where T : IBaseData
{
return new CompositeIndicator(name, left, right, (l, r) => l + r);
}
///
/// 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)
where T : IBaseData
{
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)
where T : IBaseData
{
return new CompositeIndicator(left, right, (l, r) => l - r);
}
///
/// 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)
where T : IBaseData
{
return new CompositeIndicator(name, left, right, (l, r) => l - r);
}
///
/// 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)
where T : IBaseData
{
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)
where T : IBaseData
{
return new CompositeIndicator(left, right, (l, r) => r == 0m ? new IndicatorResult(0m, IndicatorStatus.MathError) : new IndicatorResult(l / r));
}
///
/// 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)
where T : IBaseData
{
return new CompositeIndicator(name, left, right, (l, r) => r == 0m ? new IndicatorResult(0m, IndicatorStatus.MathError) : new IndicatorResult(l / r));
}
///
/// 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)
where T : IBaseData
{
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)
where T : IBaseData
{
return new CompositeIndicator(left, right, (l, r) => l * r);
}
///
/// 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)
where T : IBaseData
{
return new CompositeIndicator(name, left, right, (l, r) => l * r);
}
/// 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 alway 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)
where T : IBaseData
{
decimal k = smoothingFactor.HasValue ? k = smoothingFactor.Value : 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 alway 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 alway 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)
where T : IBaseData
{
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 alway 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)
where T : IBaseData
{
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 alway send updates to second
/// The reference to the second indicator to allow for method chaining
public static object Of(PyObject second, PyObject first, bool waitForFirstToReady = true)
{
dynamic indicator1 = first.AsManagedObject((Type)first.GetPythonType().AsManagedObject(typeof(Type)));
dynamic indicator2 = second.AsManagedObject((Type)second.GetPythonType().AsManagedObject(typeof(Type)));
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
public static CompositeIndicator WeightedBy(PyObject value, PyObject weight, int period)
{
dynamic indicator1 = value.AsManagedObject((Type)value.GetPythonType().AsManagedObject(typeof(Type)));
dynamic indicator2 = weight.AsManagedObject((Type)weight.GetPythonType().AsManagedObject(typeof(Type)));
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 alway 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)
{
dynamic indicator = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 alway send updates
/// A reference to the Maximum indicator to allow for method chaining
public static Maximum MAX(PyObject left, int period, bool waitForFirstToReady = true)
{
dynamic indicator = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 alway send updates
/// A reference to the Minimum indicator to allow for method chaining
public static Minimum MIN(PyObject left, int period, bool waitForFirstToReady = true)
{
dynamic indicator = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 alway 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)
{
dynamic indicator = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 object Over(PyObject left, decimal constant)
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 object Over(PyObject left, PyObject right, string name = "")
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
dynamic indicatorRight = right.AsManagedObject((Type)right.GetPythonType().AsManagedObject(typeof(Type)));
if (name.IsNullOrEmpty())
{
return Over(indicatorLeft, indicatorRight);
}
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 object Minus(PyObject left, decimal constant)
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 object Minus(PyObject left, PyObject right, string name = "")
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
dynamic indicatorRight = right.AsManagedObject((Type)right.GetPythonType().AsManagedObject(typeof(Type)));
if (name.IsNullOrEmpty())
{
return Minus(indicatorLeft, indicatorRight);
}
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 object Times(PyObject left, decimal constant)
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 object Times(PyObject left, PyObject right, string name = "")
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
dynamic indicatorRight = right.AsManagedObject((Type)right.GetPythonType().AsManagedObject(typeof(Type)));
if (name.IsNullOrEmpty())
{
return Times(indicatorLeft, indicatorRight);
}
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 object Plus(PyObject left, decimal constant)
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
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 object Plus(PyObject left, PyObject right, string name = "")
{
dynamic indicatorLeft = left.AsManagedObject((Type)left.GetPythonType().AsManagedObject(typeof(Type)));
dynamic indicatorRight = right.AsManagedObject((Type)right.GetPythonType().AsManagedObject(typeof(Type)));
if (name.IsNullOrEmpty())
{
return Plus(indicatorLeft, indicatorRight);
}
return Plus(indicatorLeft, indicatorRight, name);
}
}
}