Skip to content

Commit 4dfb6eb

Browse files
committed
Initial solution
1 parent fabf119 commit 4dfb6eb

File tree

1 file changed

+61
-2
lines changed

1 file changed

+61
-2
lines changed

Algorithm/QCAlgorithm.Python.cs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ public Universe AddUniverse(PyObject pyObject)
299299
return AddUniverse(pyObject, null, null);
300300
}
301301
// TODO: to be removed when https://github.com/QuantConnect/pythonnet/issues/62 is solved
302-
else if(pyObject.TryConvert(out universe))
302+
else if (pyObject.TryConvert(out universe))
303303
{
304304
return AddUniverse(universe);
305305
}
@@ -569,6 +569,65 @@ public void AddUniverseOptions(PyObject universe, PyObject optionFilter)
569569
}
570570
}
571571

572+
/// <summary>
573+
/// Creates a new <see cref="CompositeIndicator"/> using two indicators and a custom Python function as a handler.
574+
/// </summary>
575+
/// <param name="name">The name of the composite indicator.</param>
576+
/// <param name="left">The first indicator used in the composition.</param>
577+
/// <param name="right">The second indicator used in the composition.</param>
578+
/// <param name="handler">A Python function that takes two indicator values and returns the computed result.</param>
579+
/// <returns>A new instance of <see cref="CompositeIndicator"/>.</returns>
580+
/// <exception cref="ArgumentException">
581+
/// Thrown when the provided left or right indicator is not a valid QuantConnect Indicator object.
582+
/// </exception>
583+
[DocumentationAttribute(Universes)]
584+
public CompositeIndicator CompositeIndicator(string name, PyObject left, PyObject right, PyObject handler)
585+
{
586+
var leftIndicator = GetIndicator(left);
587+
var rightIndicator = GetIndicator(right);
588+
if (leftIndicator == null)
589+
{
590+
throw new ArgumentException($"The left argument should be a QuantConnect Indicator object, {left} was provided.");
591+
}
592+
if (rightIndicator == null)
593+
{
594+
throw new ArgumentException($"The right argument should be a QuantConnect Indicator object, {right} was provided.");
595+
}
596+
CompositeIndicator.IndicatorComposer composer = (left, right) =>
597+
{
598+
using (Py.GIL())
599+
{
600+
dynamic result = handler.Invoke(left.Current.Value, right.Current.Value);
601+
return new IndicatorResult(result);
602+
}
603+
};
604+
return new CompositeIndicator(name, leftIndicator, rightIndicator, composer);
605+
}
606+
607+
/// <summary>
608+
/// Attempts to convert a Python object into a valid QuantConnect indicator.
609+
/// </summary>
610+
/// <param name="pyObject">The Python object to convert.</param>
611+
/// <returns>
612+
/// A valid <see cref="IndicatorBase"/> instance if conversion is successful; otherwise, <c>null</c>.
613+
/// </returns>
614+
public IndicatorBase GetIndicator(PyObject pyObject)
615+
{
616+
if (pyObject.TryConvert(out IndicatorBase<IndicatorDataPoint> indicatorDataPoint))
617+
{
618+
return indicatorDataPoint;
619+
}
620+
if (pyObject.TryConvert(out IndicatorBase<IBaseDataBar> indicatorDataBar))
621+
{
622+
return indicatorDataBar;
623+
}
624+
if (pyObject.TryConvert(out IndicatorBase<TradeBar> indicatorTradeBar))
625+
{
626+
return indicatorTradeBar;
627+
}
628+
return null;
629+
}
630+
572631
/// <summary>
573632
/// Registers the consolidator to receive automatic updates as well as configures the indicator to receive updates
574633
/// from the consolidator.
@@ -1768,7 +1827,7 @@ private dynamic[] GetIndicatorArray(PyObject first, PyObject second = null, PyOb
17681827
{
17691828
using (Py.GIL())
17701829
{
1771-
var array = new[] {first, second, third, fourth}
1830+
var array = new[] { first, second, third, fourth }
17721831
.Select(
17731832
x =>
17741833
{

0 commit comments

Comments
 (0)