diff --git a/Algorithm/QCAlgorithm.Python.cs b/Algorithm/QCAlgorithm.Python.cs
index 19e453f729fb..e1e72d668bac 100644
--- a/Algorithm/QCAlgorithm.Python.cs
+++ b/Algorithm/QCAlgorithm.Python.cs
@@ -299,7 +299,7 @@ public Universe AddUniverse(PyObject pyObject)
return AddUniverse(pyObject, null, null);
}
// TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved
- else if(pyObject.TryConvert(out universe))
+ else if (pyObject.TryConvert(out universe))
{
return AddUniverse(universe);
}
@@ -569,6 +569,65 @@ public void AddUniverseOptions(PyObject universe, PyObject optionFilter)
}
}
+ ///
+ /// Creates a new using two indicators and a custom Python function as a handler.
+ ///
+ /// The name of the composite indicator.
+ /// The first indicator used in the composition.
+ /// The second indicator used in the composition.
+ /// A Python function that takes two indicator values and returns the computed result.
+ /// A new instance of .
+ ///
+ /// Thrown when the provided left or right indicator is not a valid QuantConnect Indicator object.
+ ///
+ [DocumentationAttribute(Universes)]
+ public CompositeIndicator CompositeIndicator(string name, PyObject left, PyObject right, PyObject handler)
+ {
+ var leftIndicator = GetIndicator(left);
+ var rightIndicator = GetIndicator(right);
+ if (leftIndicator == null)
+ {
+ throw new ArgumentException($"The left argument should be a QuantConnect Indicator object, {left} was provided.");
+ }
+ if (rightIndicator == null)
+ {
+ throw new ArgumentException($"The right argument should be a QuantConnect Indicator object, {right} was provided.");
+ }
+ CompositeIndicator.IndicatorComposer composer = (left, right) =>
+ {
+ using (Py.GIL())
+ {
+ dynamic result = handler.Invoke(left.Current.Value, right.Current.Value);
+ return new IndicatorResult(result);
+ }
+ };
+ return new CompositeIndicator(name, leftIndicator, rightIndicator, composer);
+ }
+
+ ///
+ /// Attempts to convert a Python object into a valid QuantConnect indicator.
+ ///
+ /// The Python object to convert.
+ ///
+ /// A valid instance if conversion is successful; otherwise, null.
+ ///
+ public IndicatorBase GetIndicator(PyObject pyObject)
+ {
+ if (pyObject.TryConvert(out IndicatorBase indicatorDataPoint))
+ {
+ return indicatorDataPoint;
+ }
+ if (pyObject.TryConvert(out IndicatorBase indicatorDataBar))
+ {
+ return indicatorDataBar;
+ }
+ if (pyObject.TryConvert(out IndicatorBase indicatorTradeBar))
+ {
+ return indicatorTradeBar;
+ }
+ return null;
+ }
+
///
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
/// from the consolidator.
@@ -1768,7 +1827,7 @@ private dynamic[] GetIndicatorArray(PyObject first, PyObject second = null, PyOb
{
using (Py.GIL())
{
- var array = new[] {first, second, third, fourth}
+ var array = new[] { first, second, third, fourth }
.Select(
x =>
{