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