Lean  $LEAN_TAG$
IndicatorBase.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 using QuantConnect.Data;
18 using System.Diagnostics;
19 using QuantConnect.Logging;
20 using System.Collections.Generic;
22 using System.Collections;
23 
25 {
26  /// <summary>
27  /// Abstract Indicator base, meant to contain non-generic fields of indicator base to support non-typed inputs
28  /// </summary>
29  public abstract partial class IndicatorBase : IIndicator, IEnumerable<IndicatorDataPoint>
30  {
31  /// <summary>
32  /// The data consolidators associated with this indicator if any
33  /// </summary>
34  /// <remarks>These references allow us to unregister an indicator from getting future data updates through it's consolidators.
35  /// We need multiple consolitadors because some indicators consume data from multiple different symbols</remarks>
36  public ISet<IDataConsolidator> Consolidators { get; } = new HashSet<IDataConsolidator>();
37 
38  /// <summary>
39  /// Gets the current state of this indicator. If the state has not been updated
40  /// then the time on the value will equal DateTime.MinValue.
41  /// </summary>
42  public IndicatorDataPoint Current
43  {
44  get
45  {
46  return Window[0];
47  }
48  protected set
49  {
50  Window.Add(value);
51  }
52  }
53 
54  /// <summary>
55  /// Gets the previous state of this indicator. If the state has not been updated
56  /// then the time on the value will equal DateTime.MinValue.
57  /// </summary>
58  public IndicatorDataPoint Previous
59  {
60  get
61  {
62  return Window.Count > 1 ? Window[1] : new IndicatorDataPoint(DateTime.MinValue, 0);
63  }
64  }
65 
66  /// <summary>
67  /// Gets a name for this indicator
68  /// </summary>
69  public string Name { get; protected set; }
70 
71  /// <summary>
72  /// Gets the number of samples processed by this indicator
73  /// </summary>
74  public long Samples { get; internal set; }
75 
76  /// <summary>
77  /// Gets a flag indicating when this indicator is ready and fully initialized
78  /// </summary>
79  public abstract bool IsReady { get; }
80 
81  /// <summary>
82  /// Event handler that fires after this indicator is updated
83  /// </summary>
85 
86  /// <summary>
87  /// A rolling window keeping a history of the indicator values of a given period
88  /// </summary>
90 
91  /// <summary>
92  /// Resets this indicator to its initial state
93  /// </summary>
94  public abstract void Reset();
95 
96  /// <summary>
97  /// Initializes a new instance of the Indicator class.
98  /// </summary>
99  protected IndicatorBase()
100  {
102  Current = new IndicatorDataPoint(DateTime.MinValue, 0m);
103  }
104 
105  /// <summary>
106  /// Initializes a new instance of the Indicator class using the specified name.
107  /// </summary>
108  /// <param name="name">The name of this indicator</param>
109  protected IndicatorBase(string name)
110  : this()
111  {
112  Name = name;
113  }
114 
115  /// <summary>
116  /// Event invocator for the Updated event
117  /// </summary>
118  /// <param name="consolidated">This is the new piece of data produced by this indicator</param>
119  protected virtual void OnUpdated(IndicatorDataPoint consolidated)
120  {
121  Updated?.Invoke(this, consolidated);
122  }
123 
124  /// <summary>
125  /// Updates the state of this indicator with the given value and returns true
126  /// if this indicator is ready, false otherwise
127  /// </summary>
128  /// <param name="input">The value to use to update this indicator</param>
129  /// <returns>True if this indicator is ready, false otherwise</returns>
130  public abstract bool Update(IBaseData input);
131 
132  /// <summary>
133  /// Indexes the history windows, where index 0 is the most recent indicator value.
134  /// If index is greater or equal than the current count, it returns null.
135  /// If the index is greater or equal than the window size, it returns null and resizes the windows to i + 1.
136  /// </summary>
137  /// <param name="i">The index</param>
138  /// <returns>the ith most recent indicator value</returns>
139  public IndicatorDataPoint this[int i]
140  {
141  get
142  {
143  return Window[i];
144  }
145  }
146 
147  /// <summary>
148  /// Returns an enumerator that iterates through the history window.
149  /// </summary>
150  /// <returns>
151  /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the history window.
152  /// </returns>
153  /// <filterpriority>1</filterpriority>
154  public IEnumerator<IndicatorDataPoint> GetEnumerator()
155  {
156  return Window.GetEnumerator();
157  }
158 
159  /// <summary>
160  /// Returns an enumerator that iterates through the history window.
161  /// </summary>
162  /// <returns>
163  /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the history window.
164  /// </returns>
165  /// <filterpriority>2</filterpriority>
166  IEnumerator IEnumerable.GetEnumerator()
167  {
168  return GetEnumerator();
169  }
170 
171  /// <summary>
172  /// ToString Overload for Indicator Base
173  /// </summary>
174  /// <returns>String representation of the indicator</returns>
175  public override string ToString()
176  {
177  return Current.Value.ToStringInvariant("#######0.0####");
178  }
179 
180  /// <summary>
181  /// Provides a more detailed string of this indicator in the form of {Name} - {Value}
182  /// </summary>
183  /// <returns>A detailed string of this indicator's current state</returns>
184  public string ToDetailedString()
185  {
186  return $"{Name} - {this}";
187  }
188 
189  /// <summary>
190  /// Compares the current object with another object of the same type.
191  /// </summary>
192  /// <returns>
193  /// A value that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other"/> parameter.Zero This object is equal to <paramref name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
194  /// </returns>
195  /// <param name="other">An object to compare with this object.</param>
196  public int CompareTo(IIndicator other)
197  {
198  if (ReferenceEquals(other, null))
199  {
200  // everything is greater than null via MSDN
201  return 1;
202  }
203 
204  return Current.CompareTo(other.Current);
205  }
206 
207  /// <summary>
208  /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
209  /// </summary>
210  /// <returns>
211  /// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj"/> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj"/>. Greater than zero This instance follows <paramref name="obj"/> in the sort order.
212  /// </returns>
213  /// <param name="obj">An object to compare with this instance. </param><exception cref="T:System.ArgumentException"><paramref name="obj"/> is not the same type as this instance. </exception><filterpriority>2</filterpriority>
214  public int CompareTo(object obj)
215  {
216  var other = obj as IndicatorBase;
217  if (other == null)
218  {
219  throw new ArgumentException("Object must be of type " + GetType().GetBetterTypeName());
220  }
221 
222  return CompareTo(other);
223  }
224 
225  }
226 
227  /// <summary>
228  /// Provides a base type for all indicators
229  /// </summary>
230  /// <typeparam name="T">The type of data input into this indicator</typeparam>
231  [DebuggerDisplay("{ToDetailedString()}")]
232  public abstract class IndicatorBase<T> : IndicatorBase
233  where T : IBaseData
234  {
235 
236  /// <summary>the most recent input that was given to this indicator</summary>
237  private Dictionary<SecurityIdentifier, T> _previousInput = new Dictionary<SecurityIdentifier, T>();
238 
239  /// <summary>
240  /// Initializes a new instance of the Indicator class using the specified name.
241  /// </summary>
242  /// <param name="name">The name of this indicator</param>
243  protected IndicatorBase(string name)
244  : base(name)
245  {}
246 
247  /// <summary>
248  /// Updates the state of this indicator with the given value and returns true
249  /// if this indicator is ready, false otherwise
250  /// </summary>
251  /// <param name="input">The value to use to update this indicator</param>
252  /// <returns>True if this indicator is ready, false otherwise</returns>
253  public override bool Update(IBaseData input)
254  {
255  T _previousSymbolInput = default(T);
256  if (_previousInput.TryGetValue(input.Symbol.ID, out _previousSymbolInput) && input.EndTime < _previousSymbolInput.EndTime)
257  {
258  // if we receive a time in the past, log and return
259  Log.Error($"This is a forward only indicator: {Name} Input: {input.EndTime:u} Previous: {_previousSymbolInput.EndTime:u}. It will not be updated with this input.");
260  return IsReady;
261  }
262  if (!ReferenceEquals(input, _previousSymbolInput))
263  {
264  // compute a new value and update our previous time
265  Samples++;
266 
267  if (!(input is T))
268  {
269  throw new ArgumentException($"IndicatorBase.Update() 'input' expected to be of type {typeof(T)} but is of type {input.GetType()}");
270  }
271  _previousInput[input.Symbol.ID] = (T)input;
272 
273  var nextResult = ValidateAndComputeNextValue((T)input);
274  if (nextResult.Status == IndicatorStatus.Success)
275  {
276  Current = new IndicatorDataPoint(input.EndTime, nextResult.Value);
277 
278  // let others know we've produced a new data point
280  }
281  }
282  return IsReady;
283  }
284 
285  /// <summary>
286  /// Updates the state of this indicator with the given value and returns true
287  /// if this indicator is ready, false otherwise
288  /// </summary>
289  /// <param name="time">The time associated with the value</param>
290  /// <param name="value">The value to use to update this indicator</param>
291  /// <returns>True if this indicator is ready, false otherwise</returns>
292  public bool Update(DateTime time, decimal value)
293  {
294  if (typeof(T) == typeof(IndicatorDataPoint))
295  {
296  return Update((T)(object)new IndicatorDataPoint(time, value));
297  }
298 
299  var suggestions = new List<string>
300  {
301  "Update(TradeBar)",
302  "Update(QuoteBar)"
303  };
304 
305  if (typeof(T) == typeof(IBaseData))
306  {
307  suggestions.Add("Update(Tick)");
308  }
309 
310  throw new NotSupportedException($"{GetType().Name} does not support the `Update(DateTime, decimal)` method. Use one of the following methods instead: {string.Join(", ", suggestions)}");
311  }
312 
313  /// <summary>
314  /// Resets this indicator to its initial state
315  /// </summary>
316  public override void Reset()
317  {
318  Samples = 0;
319  _previousInput.Clear();
320  Window.Reset();
321  Current = new IndicatorDataPoint(DateTime.MinValue, default(decimal));
322  }
323 
324  /// <summary>
325  /// Computes the next value of this indicator from the given state
326  /// </summary>
327  /// <param name="input">The input given to the indicator</param>
328  /// <returns>A new value for this indicator</returns>
329  protected abstract decimal ComputeNextValue(T input);
330 
331  /// <summary>
332  /// Computes the next value of this indicator from the given state
333  /// and returns an instance of the <see cref="IndicatorResult"/> class
334  /// </summary>
335  /// <param name="input">The input given to the indicator</param>
336  /// <returns>An IndicatorResult object including the status of the indicator</returns>
337  protected virtual IndicatorResult ValidateAndComputeNextValue(T input)
338  {
339  // default implementation always returns IndicatorStatus.Success
340  return new IndicatorResult(ComputeNextValue(input));
341  }
342 
343  /// <summary>
344  /// Determines whether the specified object is equal to the current object.
345  /// </summary>
346  /// <returns>
347  /// true if the specified object is equal to the current object; otherwise, false.
348  /// </returns>
349  /// <param name="obj">The object to compare with the current object. </param>
350  public override bool Equals(object obj)
351  {
352  // this implementation acts as a liason to prevent inconsistency between the operators
353  // == and != against primitive types. the core impl for equals between two indicators
354  // is still reference equality, however, when comparing value types (floats/int, ect..)
355  // we'll use value type semantics on Current.Value
356  // because of this, we shouldn't need to override GetHashCode as well since we're still
357  // solely relying on reference semantics (think hashset/dictionary impls)
358 
359  if (ReferenceEquals(obj, null)) return false;
360  var type = obj.GetType();
361 
362  while (type != null && type != typeof(object))
363  {
364  var cur = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
365  if (typeof(IndicatorBase<>) == cur)
366  {
367  return ReferenceEquals(this, obj);
368  }
369  type = type.BaseType;
370  }
371 
372  try
373  {
374  // the obj is not an indicator, so let's check for value types, try converting to decimal
375  var converted = obj.ConvertInvariant<decimal>();
376  return Current.Value == converted;
377  }
378  catch (InvalidCastException)
379  {
380  // conversion failed, return false
381  return false;
382  }
383  }
384 
385  /// <summary>
386  /// Get Hash Code for this Object
387  /// </summary>
388  /// <returns>Integer Hash Code</returns>
389  public override int GetHashCode()
390  {
391  return base.GetHashCode();
392  }
393  }
394 }