Lean  $LEAN_TAG$
IndicatorExtensions.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 // ReSharper disable InconsistentNaming
17 using System;
18 using System.Globalization;
19 using QuantConnect.Data;
20 using Python.Runtime;
21 using QuantConnect.Util;
22 
24 {
25  /// <summary>
26  /// Provides extension methods for Indicator
27  /// </summary>
28  public static class IndicatorExtensions
29  {
30  /// <summary>
31  /// Updates the state of this indicator with the given value and returns true
32  /// if this indicator is ready, false otherwise
33  /// </summary>
34  /// <param name="indicator">The indicator to be updated</param>
35  /// <param name="time">The time associated with the value</param>
36  /// <param name="value">The value to use to update this indicator</param>
37  /// <returns>True if this indicator is ready, false otherwise</returns>
38  public static bool Update(this IndicatorBase<IndicatorDataPoint> indicator, DateTime time, decimal value)
39  {
40  return indicator.Update(new IndicatorDataPoint(time, value));
41  }
42 
43  /// <summary>
44  /// Configures the second indicator to receive automatic updates from the first by attaching an event handler
45  /// to first.DataConsolidated
46  /// </summary>
47  /// <param name="second">The indicator that receives data from the first</param>
48  /// <param name="first">The indicator that sends data via DataConsolidated even to the second</param>
49  /// <param name="waitForFirstToReady">True to only send updates to the second if first.IsReady returns true, false to always send updates to second</param>
50  /// <returns>The reference to the second indicator to allow for method chaining</returns>
51  public static T Of<T>(this T second, IIndicator first, bool waitForFirstToReady = true)
52  where T : IIndicator
53  {
54  first.Updated += (sender, consolidated) =>
55  {
56  // only send the data along if we're ready
57  if (!waitForFirstToReady || first.IsReady)
58  {
59  second.Update(consolidated);
60  }
61  };
62 
63  return second;
64  }
65 
66  /// <summary>
67  /// Creates a new CompositeIndicator such that the result will be average of a first indicator weighted by a second one
68  /// </summary>
69  /// <param name="value">Indicator that will be averaged</param>
70  /// <param name="weight">Indicator that provides the average weights</param>
71  /// <param name="period">Average period</param>
72  /// <returns>Indicator that results of the average of first by weights given by second</returns>
73  public static CompositeIndicator WeightedBy<T, TWeight>(this IndicatorBase<T> value, TWeight weight, int period)
74  where T : IBaseData
75  where TWeight : IndicatorBase<IndicatorDataPoint>
76  {
77  var x = new WindowIdentity(period);
78  var y = new WindowIdentity(period);
79  var numerator = new Sum("Sum_xy", period);
80  var denominator = new Sum("Sum_y", period);
81 
82  value.Updated += (sender, consolidated) =>
83  {
84  x.Update(consolidated);
85  if (x.Samples == y.Samples)
86  {
87  numerator.Update(consolidated.Time, consolidated.Value * y.Current.Value);
88  }
89  };
90 
91  weight.Updated += (sender, consolidated) =>
92  {
93  y.Update(consolidated);
94  if (x.Samples == y.Samples)
95  {
96  numerator.Update(consolidated.Time, consolidated.Value * x.Current.Value);
97  }
98  denominator.Update(consolidated);
99  };
100 
101  var resetCompositeIndicator = new ResetCompositeIndicator(numerator, denominator, GetOverIndicatorComposer(), () => {
102  x.Reset();
103  y.Reset();
104  });
105 
106  return resetCompositeIndicator;
107  }
108 
109  /// <summary>
110  /// Creates a new CompositeIndicator such that the result will be the sum of the left and the constant
111  /// </summary>
112  /// <remarks>
113  /// value = left + constant
114  /// </remarks>
115  /// <param name="left">The left indicator</param>
116  /// <param name="constant">The addend</param>
117  /// <returns>The sum of the left and right indicators</returns>
118  public static CompositeIndicator Plus(this IndicatorBase left, decimal constant)
119  {
120  var constantIndicator = new ConstantIndicator<IBaseData>(constant.ToString(CultureInfo.InvariantCulture), constant);
121  return left.Plus(constantIndicator);
122  }
123 
124  /// <summary>
125  /// Creates a new CompositeIndicator such that the result will be the sum of the left and right
126  /// </summary>
127  /// <remarks>
128  /// value = left + right
129  /// </remarks>
130  /// <param name="left">The left indicator</param>
131  /// <param name="right">The right indicator</param>
132  /// <returns>The sum of the left and right indicators</returns>
133  public static CompositeIndicator Plus(this IndicatorBase left, IndicatorBase right)
134  {
135  return new (left, right, (l, r) => l.Current.Value + r.Current.Value);
136  }
137 
138  /// <summary>
139  /// Creates a new CompositeIndicator such that the result will be the sum of the left and right
140  /// </summary>
141  /// <remarks>
142  /// value = left + right
143  /// </remarks>
144  /// <param name="left">The left indicator</param>
145  /// <param name="right">The right indicator</param>
146  /// <param name="name">The name of this indicator</param>
147  /// <returns>The sum of the left and right indicators</returns>
148  public static CompositeIndicator Plus(this IndicatorBase left, IndicatorBase right, string name)
149  {
150  return new (name, left, right, (l, r) => l.Current.Value + r.Current.Value);
151  }
152 
153  /// <summary>
154  /// Creates a new CompositeIndicator such that the result will be the difference of the left and constant
155  /// </summary>
156  /// <remarks>
157  /// value = left - constant
158  /// </remarks>
159  /// <param name="left">The left indicator</param>
160  /// <param name="constant">The subtrahend</param>
161  /// <returns>The difference of the left and right indicators</returns>
162  public static CompositeIndicator Minus(this IndicatorBase left, decimal constant)
163  {
164  var constantIndicator = new ConstantIndicator<IBaseData>(constant.ToString(CultureInfo.InvariantCulture), constant);
165  return left.Minus(constantIndicator);
166  }
167 
168  /// <summary>
169  /// Creates a new CompositeIndicator such that the result will be the difference of the left and right
170  /// </summary>
171  /// <remarks>
172  /// value = left - right
173  /// </remarks>
174  /// <param name="left">The left indicator</param>
175  /// <param name="right">The right indicator</param>
176  /// <returns>The difference of the left and right indicators</returns>
177  public static CompositeIndicator Minus(this IndicatorBase left, IndicatorBase right)
178  {
179  return new (left, right, (l, r) => l.Current.Value - r.Current.Value);
180  }
181 
182  /// <summary>
183  /// Creates a new CompositeIndicator such that the result will be the difference of the left and right
184  /// </summary>
185  /// <remarks>
186  /// value = left - right
187  /// </remarks>
188  /// <param name="left">The left indicator</param>
189  /// <param name="right">The right indicator</param>
190  /// <param name="name">The name of this indicator</param>
191  /// <returns>The difference of the left and right indicators</returns>
192  public static CompositeIndicator Minus(this IndicatorBase left, IndicatorBase right, string name)
193  {
194  return new (name, left, right, (l, r) => l.Current.Value - r.Current.Value);
195  }
196 
197  /// <summary>
198  /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the constant
199  /// </summary>
200  /// <remarks>
201  /// value = left/constant
202  /// </remarks>
203  /// <param name="left">The left indicator</param>
204  /// <param name="constant">The constant value denominator</param>
205  /// <returns>The ratio of the left to the right indicator</returns>
206  public static CompositeIndicator Over(this IndicatorBase left, decimal constant)
207  {
208  var constantIndicator = new ConstantIndicator<IndicatorDataPoint>(constant.ToString(CultureInfo.InvariantCulture), constant);
209  return left.Over(constantIndicator);
210  }
211 
212  /// <summary>
213  /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the right
214  /// </summary>
215  /// <remarks>
216  /// value = left/right
217  /// </remarks>
218  /// <param name="left">The left indicator</param>
219  /// <param name="right">The right indicator</param>
220  /// <returns>The ratio of the left to the right indicator</returns>
221  public static CompositeIndicator Over(this IndicatorBase left, IndicatorBase right)
222  {
223  return new (left, right, GetOverIndicatorComposer());
224  }
225 
226  /// <summary>
227  /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the right
228  /// </summary>
229  /// <remarks>
230  /// value = left/right
231  /// </remarks>
232  /// <param name="left">The left indicator</param>
233  /// <param name="right">The right indicator</param>
234  /// <param name="name">The name of this indicator</param>
235  /// <returns>The ratio of the left to the right indicator</returns>
236  public static CompositeIndicator Over(this IndicatorBase left, IndicatorBase right, string name)
237  {
238  return new (name, left, right, GetOverIndicatorComposer());
239  }
240 
241  /// <summary>
242  /// Creates a new CompositeIndicator such that the result will be the product of the left and the constant
243  /// </summary>
244  /// <remarks>
245  /// value = left*constant
246  /// </remarks>
247  /// <param name="left">The left indicator</param>
248  /// <param name="constant">The constant value to multiple by</param>
249  /// <returns>The product of the left to the right indicators</returns>
250  public static CompositeIndicator Times(this IndicatorBase left, decimal constant)
251  {
252  var constantIndicator = new ConstantIndicator<IndicatorDataPoint>(constant.ToString(CultureInfo.InvariantCulture), constant);
253  return left.Times(constantIndicator);
254  }
255 
256  /// <summary>
257  /// Creates a new CompositeIndicator such that the result will be the product of the left to the right
258  /// </summary>
259  /// <remarks>
260  /// value = left*right
261  /// </remarks>
262  /// <param name="left">The left indicator</param>
263  /// <param name="right">The right indicator</param>
264  /// <returns>The product of the left to the right indicators</returns>
265  public static CompositeIndicator Times(this IndicatorBase left, IndicatorBase right)
266  {
267  return new (left, right, (l, r) => l.Current.Value * r.Current.Value);
268  }
269 
270  /// <summary>
271  /// Creates a new CompositeIndicator such that the result will be the product of the left to the right
272  /// </summary>
273  /// <remarks>
274  /// value = left*right
275  /// </remarks>
276  /// <param name="left">The left indicator</param>
277  /// <param name="right">The right indicator</param>
278  /// <param name="name">The name of this indicator</param>
279  /// <returns>The product of the left to the right indicators</returns>
280  public static CompositeIndicator Times(this IndicatorBase left, IndicatorBase right, string name)
281  {
282  return new (name, left, right, (l, r) => l.Current.Value * r.Current.Value);
283  }
284 
285  /// <summary>Creates a new ExponentialMovingAverage indicator with the specified period and smoothingFactor from the left indicator
286  /// </summary>
287  /// <param name="left">The ExponentialMovingAverage indicator will be created using the data from left</param>
288  /// <param name="period">The period of the ExponentialMovingAverage indicators</param>
289  /// <param name="smoothingFactor">The percentage of data from the previous value to be carried into the next value</param>
290  /// <param name="waitForFirstToReady">True to only send updates to the second if left.IsReady returns true, false to always send updates</param>
291  /// <returns>A reference to the ExponentialMovingAverage indicator to allow for method chaining</returns>
292  public static ExponentialMovingAverage EMA<T>(this IndicatorBase<T> left, int period, decimal? smoothingFactor = null, bool waitForFirstToReady = true)
293  where T : IBaseData
294  {
295  var k = smoothingFactor ?? ExponentialMovingAverage.SmoothingFactorDefault(period);
296  return new ExponentialMovingAverage($"EMA{period}_Of_{left.Name}", period, k).Of(left, waitForFirstToReady);
297  }
298 
299  /// <summary>Creates a new Maximum indicator with the specified period from the left indicator
300  /// </summary>
301  /// <param name="left">The Maximum indicator will be created using the data from left</param>
302  /// <param name="period">The period of the Maximum indicator</param>
303  /// <param name="waitForFirstToReady">True to only send updates to the second if left.IsReady returns true, false to always send updates</param>
304  /// <returns>A reference to the Maximum indicator to allow for method chaining</returns>
305  public static Maximum MAX(this IIndicator left, int period, bool waitForFirstToReady = true)
306  {
307  return new Maximum($"MAX{period}_Of_{left.Name}", period).Of(left, waitForFirstToReady);
308  }
309 
310  /// <summary>Creates a new Minimum indicator with the specified period from the left indicator
311  /// </summary>
312  /// <param name="left">The Minimum indicator will be created using the data from left</param>
313  /// <param name="period">The period of the Minimum indicator</param>
314  /// <param name="waitForFirstToReady">True to only send updates to the second if left.IsReady returns true, false to always send updates</param>
315  /// <returns>A reference to the Minimum indicator to allow for method chaining</returns>
316  public static Minimum MIN<T>(this IndicatorBase<T> left, int period, bool waitForFirstToReady = true)
317  where T : IBaseData
318  {
319  return new Minimum($"MIN{period}_Of_{left.Name}", period).Of(left, waitForFirstToReady);
320  }
321 
322  /// <summary>Initializes a new instance of the SimpleMovingAverage class with the specified name and period from the left indicator
323  /// </summary>
324  /// <param name="left">The SimpleMovingAverage indicator will be created using the data from left</param>
325  /// <param name="period">The period of the SMA</param>
326  /// <param name="waitForFirstToReady">True to only send updates to the second if first.IsReady returns true, false to always send updates to second</param>
327  /// <returns>The reference to the SimpleMovingAverage indicator to allow for method chaining</returns>
328  public static SimpleMovingAverage SMA<T>(this IndicatorBase<T> left, int period, bool waitForFirstToReady = true)
329  where T : IBaseData
330  {
331  return new SimpleMovingAverage($"SMA{period}_Of_{left.Name}", period).Of(left, waitForFirstToReady);
332  }
333 
334 
335 
336  /// The methods overloads bellow are due to python.net not being able to correctly solve generic methods overload
337 
338  /// <summary>
339  /// Configures the second indicator to receive automatic updates from the first by attaching an event handler
340  /// to first.DataConsolidated
341  /// </summary>
342  /// <param name="second">The indicator that receives data from the first</param>
343  /// <param name="first">The indicator that sends data via DataConsolidated even to the second</param>
344  /// <param name="waitForFirstToReady">True to only send updates to the second if first.IsReady returns true, false to always send updates to second</param>
345  /// <returns>The reference to the second indicator to allow for method chaining</returns>
346  public static object Of(PyObject second, PyObject first, bool waitForFirstToReady = true)
347  {
348  dynamic indicator1 = GetIndicatorAsManagedObject(first);
349  dynamic indicator2 = GetIndicatorAsManagedObject(second);
350  return Of(indicator2, indicator1, waitForFirstToReady);
351  }
352 
353  /// <summary>
354  /// Creates a new CompositeIndicator such that the result will be average of a first indicator weighted by a second one
355  /// </summary>
356  /// <param name="value">Indicator that will be averaged</param>
357  /// <param name="weight">Indicator that provides the average weights</param>
358  /// <param name="period">Average period</param>
359  /// <returns>Indicator that results of the average of first by weights given by second</returns>
360  // ReSharper disable once UnusedMember.Global
361  public static CompositeIndicator WeightedBy(PyObject value, PyObject weight, int period)
362  {
363  dynamic indicator1 = GetIndicatorAsManagedObject(value);
364  dynamic indicator2 = GetIndicatorAsManagedObject(weight);
365  return WeightedBy(indicator1, indicator2, period);
366  }
367 
368  /// <summary>
369  /// Creates a new ExponentialMovingAverage indicator with the specified period and smoothingFactor from the left indicator
370  /// </summary>
371  /// <param name="left">The ExponentialMovingAverage indicator will be created using the data from left</param>
372  /// <param name="period">The period of the ExponentialMovingAverage indicators</param>
373  /// <param name="smoothingFactor">The percentage of data from the previous value to be carried into the next value</param>
374  /// <param name="waitForFirstToReady">True to only send updates to the second if left.IsReady returns true, false to always send updates</param>
375  /// <returns>A reference to the ExponentialMovingAverage indicator to allow for method chaining</returns>
376  public static ExponentialMovingAverage EMA(PyObject left, int period, decimal? smoothingFactor = null, bool waitForFirstToReady = true)
377  {
378  dynamic indicator = GetIndicatorAsManagedObject(left);
379  return EMA(indicator, period, smoothingFactor, waitForFirstToReady);
380  }
381 
382  /// <summary>
383  /// Creates a new Maximum indicator with the specified period from the left indicator
384  /// </summary>
385  /// <param name="left">The Maximum indicator will be created using the data from left</param>
386  /// <param name="period">The period of the Maximum indicator</param>
387  /// <param name="waitForFirstToReady">True to only send updates to the second if left.IsReady returns true, false to always send updates</param>
388  /// <returns>A reference to the Maximum indicator to allow for method chaining</returns>
389  public static Maximum MAX(PyObject left, int period, bool waitForFirstToReady = true)
390  {
391  dynamic indicator = GetIndicatorAsManagedObject(left);
392  return MAX(indicator, period, waitForFirstToReady);
393  }
394 
395  /// <summary>
396  /// Creates a new Minimum indicator with the specified period from the left indicator
397  /// </summary>
398  /// <param name="left">The Minimum indicator will be created using the data from left</param>
399  /// <param name="period">The period of the Minimum indicator</param>
400  /// <param name="waitForFirstToReady">True to only send updates to the second if left.IsReady returns true, false to always send updates</param>
401  /// <returns>A reference to the Minimum indicator to allow for method chaining</returns>
402  public static Minimum MIN(PyObject left, int period, bool waitForFirstToReady = true)
403  {
404  dynamic indicator = GetIndicatorAsManagedObject(left);
405  return MIN(indicator, period, waitForFirstToReady);
406  }
407 
408  /// <summary>
409  /// Initializes a new instance of the SimpleMovingAverage class with the specified name and period from the left indicator
410  /// </summary>
411  /// <param name="left">The SimpleMovingAverage indicator will be created using the data from left</param>
412  /// <param name="period">The period of the SMA</param>
413  /// <param name="waitForFirstToReady">True to only send updates to the second if first.IsReady returns true, false to always send updates to second</param>
414  /// <returns>The reference to the SimpleMovingAverage indicator to allow for method chaining</returns>
415  public static SimpleMovingAverage SMA(PyObject left, int period, bool waitForFirstToReady = true)
416  {
417  dynamic indicator = GetIndicatorAsManagedObject(left);
418  return SMA(indicator, period, waitForFirstToReady);
419  }
420 
421  /// <summary>
422  /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the constant
423  /// </summary>
424  /// <remarks>
425  /// value = left/constant
426  /// </remarks>
427  /// <param name="left">The left indicator</param>
428  /// <param name="constant">The constant value denominator</param>
429  /// <returns>The ratio of the left to the right indicator</returns>
430  public static object Over(PyObject left, decimal constant)
431  {
432  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
433  return Over(indicatorLeft, constant);
434  }
435 
436  /// <summary>
437  /// Creates a new CompositeIndicator such that the result will be the ratio of the left to the right
438  /// </summary>
439  /// <remarks>
440  /// value = left/right
441  /// </remarks>
442  /// <param name="left">The left indicator</param>
443  /// <param name="right">The right indicator</param>
444  /// <param name="name">The name of this indicator</param>
445  /// <returns>The ratio of the left to the right indicator</returns>
446  public static object Over(PyObject left, PyObject right, string name = "")
447  {
448  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
449  dynamic indicatorRight = GetIndicatorAsManagedObject(right);
450  if (name.IsNullOrEmpty())
451  {
452  return Over(indicatorLeft, indicatorRight);
453  }
454  return Over(indicatorLeft, indicatorRight, name);
455  }
456 
457  /// <summary>
458  /// Creates a new CompositeIndicator such that the result will be the difference of the left and constant
459  /// </summary>
460  /// <remarks>
461  /// value = left - constant
462  /// </remarks>
463  /// <param name="left">The left indicator</param>
464  /// <param name="constant">The subtrahend</param>
465  /// <returns>The difference of the left and right indicators</returns>
466  public static object Minus(PyObject left, decimal constant)
467  {
468  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
469  return Minus(indicatorLeft, constant);
470  }
471 
472  /// <summary>
473  /// Creates a new CompositeIndicator such that the result will be the difference of the left and right
474  /// </summary>
475  /// <remarks>
476  /// value = left - right
477  /// </remarks>
478  /// <param name="left">The left indicator</param>
479  /// <param name="right">The right indicator</param>
480  /// <param name="name">The name of this indicator</param>
481  /// <returns>The difference of the left and right indicators</returns>
482  public static object Minus(PyObject left, PyObject right, string name = "")
483  {
484  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
485  dynamic indicatorRight = GetIndicatorAsManagedObject(right);
486  if (name.IsNullOrEmpty())
487  {
488  return Minus(indicatorLeft, indicatorRight);
489  }
490  return Minus(indicatorLeft, indicatorRight, name);
491  }
492 
493  /// <summary>
494  /// Creates a new CompositeIndicator such that the result will be the product of the left and the constant
495  /// </summary>
496  /// <remarks>
497  /// value = left*constant
498  /// </remarks>
499  /// <param name="left">The left indicator</param>
500  /// <param name="constant">The constant value to multiple by</param>
501  /// <returns>The product of the left to the right indicators</returns>
502  public static object Times(PyObject left, decimal constant)
503  {
504  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
505  return Times(indicatorLeft, constant);
506  }
507 
508  /// <summary>
509  /// Creates a new CompositeIndicator such that the result will be the product of the left to the right
510  /// </summary>
511  /// <remarks>
512  /// value = left*right
513  /// </remarks>
514  /// <param name="left">The left indicator</param>
515  /// <param name="right">The right indicator</param>
516  /// <param name="name">The name of this indicator</param>
517  /// <returns>The product of the left to the right indicators</returns>
518  public static object Times(PyObject left, PyObject right, string name = "")
519  {
520  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
521  dynamic indicatorRight = GetIndicatorAsManagedObject(right);
522  if (name.IsNullOrEmpty())
523  {
524  return Times(indicatorLeft, indicatorRight);
525  }
526  return Times(indicatorLeft, indicatorRight, name);
527  }
528 
529  /// <summary>
530  /// Creates a new CompositeIndicator such that the result will be the sum of the left and the constant
531  /// </summary>
532  /// <remarks>
533  /// value = left + constant
534  /// </remarks>
535  /// <param name="left">The left indicator</param>
536  /// <param name="constant">The addend</param>
537  /// <returns>The sum of the left and right indicators</returns>
538  public static object Plus(PyObject left, decimal constant)
539  {
540  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
541  return Plus(indicatorLeft, constant);
542  }
543 
544  /// <summary>
545  /// Creates a new CompositeIndicator such that the result will be the sum of the left and right
546  /// </summary>
547  /// <remarks>
548  /// value = left + right
549  /// </remarks>
550  /// <param name="left">The left indicator</param>
551  /// <param name="right">The right indicator</param>
552  /// <param name="name">The name of this indicator</param>
553  /// <returns>The sum of the left and right indicators</returns>
554  public static object Plus(PyObject left, PyObject right, string name = "")
555  {
556  dynamic indicatorLeft = GetIndicatorAsManagedObject(left);
557  dynamic indicatorRight = GetIndicatorAsManagedObject(right);
558  if (name.IsNullOrEmpty())
559  {
560  return Plus(indicatorLeft, indicatorRight);
561  }
562  return Plus(indicatorLeft, indicatorRight, name);
563  }
564 
565  private static dynamic GetIndicatorAsManagedObject(PyObject indicator)
566  {
567  if (indicator.TryConvert(out PythonIndicator pythonIndicator, true))
568  {
569  pythonIndicator.SetIndicator(indicator);
570  return pythonIndicator;
571  }
572 
573  return indicator.SafeAsManagedObject();
574  }
575 
576  /// <summary>
577  /// Gets the IndicatorComposer for a CompositeIndicator whose result is the ratio of the left to the right
578  /// </summary>
579  /// <returns>The IndicatorComposer for a CompositeIndicator whose result is the ratio of the left to the right</returns>
580  private static CompositeIndicator.IndicatorComposer GetOverIndicatorComposer()
581  {
582  return (l, r) => r.Current.Value == 0m ? new IndicatorResult(0m, IndicatorStatus.MathError) : new IndicatorResult(l.Current.Value / r.Current.Value);
583  }
584  }
585 }