1515
1616using System ;
1717using NUnit . Framework ;
18+ using Python . Runtime ;
1819using QuantConnect . Indicators ;
1920
2021namespace QuantConnect . Tests . Indicators
@@ -72,13 +73,14 @@ public void CallsDelegateCorrectly()
7273 }
7374
7475 [ Test ]
75- public virtual void ResetsProperly ( ) {
76+ public virtual void ResetsProperly ( )
77+ {
7678 var left = new Maximum ( "left" , 2 ) ;
7779 var right = new Minimum ( "right" , 2 ) ;
7880 var composite = CreateCompositeIndicator ( left , right , ( l , r ) => l . Current . Value + r . Current . Value ) ;
7981
8082 left . Update ( DateTime . Today , 1m ) ;
81- right . Update ( DateTime . Today , - 1m ) ;
83+ right . Update ( DateTime . Today , - 1m ) ;
8284
8385 left . Update ( DateTime . Today . AddDays ( 1 ) , - 1m ) ;
8486 right . Update ( DateTime . Today . AddDays ( 1 ) , 1m ) ;
@@ -94,6 +96,55 @@ public virtual void ResetsProperly() {
9496 Assert . AreEqual ( right . PeriodsSinceMinimum , 0 ) ;
9597 }
9698
99+ [ TestCase ( "sum" , 5 , 10 , 15 ) ]
100+ [ TestCase ( "min" , - 12 , 52 , - 12 ) ]
101+ public virtual void PythonCompositeIndicatorConstructorValidatesBehavior ( string operation , decimal leftValue , decimal rightValue , decimal expectedValue )
102+ {
103+ var left = new SimpleMovingAverage ( "SMA" , 10 ) ;
104+ var right = new SimpleMovingAverage ( "SMA" , 10 ) ;
105+ using ( Py . GIL ( ) )
106+ {
107+ var testModule = PyModule . FromString ( "testModule" ,
108+ @"
109+ from AlgorithmImports import *
110+ from QuantConnect.Indicators import *
111+
112+ def create_composite_indicator(left, right, operation):
113+ if operation == 'sum':
114+ def composer(l, r):
115+ return IndicatorResult(l.Current.Value + r.Current.Value)
116+ elif operation == 'min':
117+ def composer(l, r):
118+ return IndicatorResult(min(l.Current.Value, r.Current.Value))
119+ return CompositeIndicator(left, right, composer)
120+
121+ def update_indicators(left, right, value_left, value_right):
122+ left.Update(IndicatorDataPoint(DateTime.Now, value_left))
123+ right.Update(IndicatorDataPoint(DateTime.Now, value_right))
124+ " ) ;
125+
126+ var createCompositeIndicator = testModule . GetAttr ( "create_composite_indicator" ) ;
127+ var updateIndicators = testModule . GetAttr ( "update_indicators" ) ;
128+
129+ var leftPy = left . ToPython ( ) ;
130+ var rightPy = right . ToPython ( ) ;
131+
132+ // Create composite indicator using Python logic
133+ var composite = createCompositeIndicator . Invoke ( leftPy , rightPy , operation . ToPython ( ) ) ;
134+
135+ // Update the indicator with sample values (left, right)
136+ updateIndicators . Invoke ( leftPy , rightPy , leftValue . ToPython ( ) , rightValue . ToPython ( ) ) ;
137+
138+ // Verify composite indicator name and properties
139+ Assert . AreEqual ( $ "COMPOSE({ left . Name } ,{ right . Name } )", composite . GetAttr ( "Name" ) . ToString ( ) ) ;
140+ Assert . AreEqual ( left , composite . GetAttr ( "Left" ) . As < IndicatorBase > ( ) ) ;
141+ Assert . AreEqual ( right , composite . GetAttr ( "Right" ) . As < IndicatorBase > ( ) ) ;
142+
143+ // Validate the composite indicator computed value
144+ Assert . AreEqual ( expectedValue , composite . GetAttr ( "Current" ) . GetAttr ( "Value" ) . As < decimal > ( ) ) ;
145+ }
146+ }
147+
97148 protected virtual CompositeIndicator CreateCompositeIndicator ( IndicatorBase left , IndicatorBase right , QuantConnect . Indicators . CompositeIndicator . IndicatorComposer composer )
98149 {
99150 return new CompositeIndicator ( left , right , composer ) ;
0 commit comments