Lean  $LEAN_TAG$
CompositeIndicator.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 using System;
17 
19 {
20  /// <summary>
21  /// This indicator is capable of wiring up two separate indicators into a single indicator
22  /// such that the output of each will be sent to a user specified function.
23  /// </summary>
24  /// <remarks>
25  /// This type is initialized such that there is no need to call the Update function. This indicator
26  /// will have its values automatically updated each time a new piece of data is received from both
27  /// the left and right indicators.
28  /// </remarks>
29  public class CompositeIndicator : IndicatorBase<IndicatorDataPoint>
30  {
31  /// <summary>
32  /// Delegate type used to compose the output of two indicators into a new value.
33  /// </summary>
34  /// <remarks>
35  /// A simple example would be to compute the difference between the two indicators (such as with MACD)
36  /// (left, right) => left - right
37  /// </remarks>
38  /// <param name="left">The left indicator</param>
39  /// <param name="right">The right indicator</param>
40  /// <returns>And indicator result representing the composition of the two indicators</returns>
41  public delegate IndicatorResult IndicatorComposer(IndicatorBase left, IndicatorBase right);
42 
43  /// <summary>function used to compose the individual indicators</summary>
44  private readonly IndicatorComposer _composer;
45 
46  /// <summary>
47  /// Gets the 'left' indicator for the delegate
48  /// </summary>
49  public IndicatorBase Left { get; private set; }
50 
51  /// <summary>
52  /// Gets the 'right' indicator for the delegate
53  /// </summary>
54  public IndicatorBase Right { get; private set; }
55 
56  /// <summary>
57  /// Gets a flag indicating when this indicator is ready and fully initialized
58  /// </summary>
59  public override bool IsReady
60  {
61  get { return Left.IsReady && Right.IsReady; }
62  }
63 
64  /// <summary>
65  /// Resets this indicator to its initial state
66  /// </summary>
67  public override void Reset() {
68  Left.Reset();
69  Right.Reset();
70  base.Reset();
71  }
72 
73  /// <summary>
74  /// Creates a new CompositeIndicator capable of taking the output from the left and right indicators
75  /// and producing a new value via the composer delegate specified
76  /// </summary>
77  /// <param name="name">The name of this indicator</param>
78  /// <param name="left">The left indicator for the 'composer'</param>
79  /// <param name="right">The right indicator for the 'composer'</param>
80  /// <param name="composer">Function used to compose the left and right indicators</param>
81  public CompositeIndicator(string name, IndicatorBase left, IndicatorBase right, IndicatorComposer composer)
82  : base(name)
83  {
84  _composer = composer;
85  Left = left;
86  Right = right;
87  ConfigureEventHandlers();
88  }
89 
90  /// <summary>
91  /// Creates a new CompositeIndicator capable of taking the output from the left and right indicators
92  /// and producing a new value via the composer delegate specified
93  /// </summary>
94  /// <param name="left">The left indicator for the 'composer'</param>
95  /// <param name="right">The right indicator for the 'composer'</param>
96  /// <param name="composer">Function used to compose the left and right indicators</param>
98  : this($"COMPOSE({left.Name},{right.Name})", left, right, composer)
99  { }
100 
101  /// <summary>
102  /// Computes the next value of this indicator from the given state
103  /// and returns an instance of the <see cref="IndicatorResult"/> class
104  /// </summary>
105  /// <param name="input">The input given to the indicator</param>
106  /// <returns>An IndicatorResult object including the status of the indicator</returns>
108  {
109  return _composer.Invoke(Left, Right);
110  }
111 
112  /// <summary>
113  /// Computes the next value of this indicator from the given state
114  /// </summary>
115  /// <remarks>
116  /// Since this class overrides <see cref="ValidateAndComputeNextValue"/>, this method is a no-op
117  /// </remarks>
118  /// <param name="_">The input given to the indicator</param>
119  /// <returns>A new value for this indicator</returns>
120  protected override decimal ComputeNextValue(IndicatorDataPoint _)
121  {
122  // this should never actually be invoked
123  return _composer.Invoke(Left, Right).Value;
124  }
125 
126  /// <summary>
127  /// Configures the event handlers for Left.Updated and Right.Updated to update this instance when
128  /// they both have new data.
129  /// </summary>
130  private void ConfigureEventHandlers()
131  {
132  // if either of these are constants then there's no reason
133  bool leftIsConstant = Left.GetType().IsSubclassOfGeneric(typeof (ConstantIndicator<>));
134  bool rightIsConstant = Right.GetType().IsSubclassOfGeneric(typeof (ConstantIndicator<>));
135 
136  // wire up the Updated events such that when we get a new piece of data from both left and right
137  // we'll call update on this indicator. It's important to note that the CompositeIndicator only uses
138  // the timestamp that gets passed into the Update function, his compuation is soley a function
139  // of the left and right indicator via '_composer'
140 
141  IndicatorDataPoint newLeftData = null;
142  IndicatorDataPoint newRightData = null;
143  Left.Updated += (sender, updated) =>
144  {
145  newLeftData = updated;
146 
147  // if we have left and right data (or if right is a constant) then we need to update
148  if (newRightData != null || rightIsConstant)
149  {
150  var dataPoint = new IndicatorDataPoint { Time = MaxTime(updated) };
151  Update(dataPoint);
152  // reset these to null after each update
153  newLeftData = null;
154  newRightData = null;
155  }
156  };
157 
158  Right.Updated += (sender, updated) =>
159  {
160  newRightData = updated;
161 
162  // if we have left and right data (or if left is a constant) then we need to update
163  if (newLeftData != null || leftIsConstant)
164  {
165  var dataPoint = new IndicatorDataPoint { Time = MaxTime(updated) };
166  Update(dataPoint);
167  // reset these to null after each update
168  newLeftData = null;
169  newRightData = null;
170  }
171  };
172  }
173 
174  private DateTime MaxTime(IndicatorDataPoint updated)
175  {
176  return new DateTime(Math.Max(updated.Time.Ticks, Math.Max(Right.Current.Time.Ticks, Left.Current.Time.Ticks)));
177  }
178  }
179 }