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  private bool _loggedForwardOnlyIndicatorError;
236 
237  /// <summary>the most recent input that was given to this indicator</summary>
238  private Dictionary<SecurityIdentifier, T> _previousInput = new Dictionary<SecurityIdentifier, T>();
239 
240  /// <summary>
241  /// Initializes a new instance of the Indicator class using the specified name.
242  /// </summary>
243  /// <param name="name">The name of this indicator</param>
244  protected IndicatorBase(string name)
245  : base(name)
246  { }
247 
248  /// <summary>
249  /// Updates the state of this indicator with the given value and returns true
250  /// if this indicator is ready, false otherwise
251  /// </summary>
252  /// <param name="input">The value to use to update this indicator</param>
253  /// <returns>True if this indicator is ready, false otherwise</returns>
254  public override bool Update(IBaseData input)
255  {
256  T _previousSymbolInput = default(T);
257  if (_previousInput.TryGetValue(input.Symbol.ID, out _previousSymbolInput) && input.EndTime < _previousSymbolInput.EndTime)
258  {
259  if (!_loggedForwardOnlyIndicatorError)
260  {
261  _loggedForwardOnlyIndicatorError = true;
262  // if we receive a time in the past, log once and return
263  Log.Error($"IndicatorBase.Update(): This is a forward only indicator: {Name} Input: {input.EndTime:u} Previous: {_previousSymbolInput.EndTime:u}. It will not be updated with this input.");
264  }
265  return IsReady;
266  }
267  if (!ReferenceEquals(input, _previousSymbolInput))
268  {
269  // compute a new value and update our previous time
270  Samples++;
271 
272  if (!(input is T))
273  {
274  if (typeof(T) == typeof(IndicatorDataPoint))
275  {
276  input = new IndicatorDataPoint(input.EndTime, input.Value);
277  }
278  else
279  {
280  throw new ArgumentException($"IndicatorBase.Update() 'input' expected to be of type {typeof(T)} but is of type {input.GetType()}");
281  }
282  }
283  _previousInput[input.Symbol.ID] = (T)input;
284 
285  var nextResult = ValidateAndComputeNextValue((T)input);
286  if (nextResult.Status == IndicatorStatus.Success)
287  {
288  Current = new IndicatorDataPoint(input.EndTime, nextResult.Value);
289 
290  // let others know we've produced a new data point
292  }
293  }
294  return IsReady;
295  }
296 
297  /// <summary>
298  /// Updates the state of this indicator with the given value and returns true
299  /// if this indicator is ready, false otherwise
300  /// </summary>
301  /// <param name="time">The time associated with the value</param>
302  /// <param name="value">The value to use to update this indicator</param>
303  /// <returns>True if this indicator is ready, false otherwise</returns>
304  public bool Update(DateTime time, decimal value)
305  {
306  if (typeof(T) == typeof(IndicatorDataPoint))
307  {
308  return Update((T)(object)new IndicatorDataPoint(time, value));
309  }
310 
311  var suggestions = new List<string>
312  {
313  "Update(TradeBar)",
314  "Update(QuoteBar)"
315  };
316 
317  if (typeof(T) == typeof(IBaseData))
318  {
319  suggestions.Add("Update(Tick)");
320  }
321 
322  throw new NotSupportedException($"{GetType().Name} does not support the `Update(DateTime, decimal)` method. Use one of the following methods instead: {string.Join(", ", suggestions)}");
323  }
324 
325  /// <summary>
326  /// Resets this indicator to its initial state
327  /// </summary>
328  public override void Reset()
329  {
330  Samples = 0;
331  _previousInput.Clear();
332  Window.Reset();
333  Current = new IndicatorDataPoint(DateTime.MinValue, default(decimal));
334  }
335 
336  /// <summary>
337  /// Computes the next value of this indicator from the given state
338  /// </summary>
339  /// <param name="input">The input given to the indicator</param>
340  /// <returns>A new value for this indicator</returns>
341  protected abstract decimal ComputeNextValue(T input);
342 
343  /// <summary>
344  /// Computes the next value of this indicator from the given state
345  /// and returns an instance of the <see cref="IndicatorResult"/> class
346  /// </summary>
347  /// <param name="input">The input given to the indicator</param>
348  /// <returns>An IndicatorResult object including the status of the indicator</returns>
349  protected virtual IndicatorResult ValidateAndComputeNextValue(T input)
350  {
351  // default implementation always returns IndicatorStatus.Success
352  return new IndicatorResult(ComputeNextValue(input));
353  }
354 
355  /// <summary>
356  /// Determines whether the specified object is equal to the current object.
357  /// </summary>
358  /// <returns>
359  /// true if the specified object is equal to the current object; otherwise, false.
360  /// </returns>
361  /// <param name="obj">The object to compare with the current object. </param>
362  public override bool Equals(object obj)
363  {
364  // this implementation acts as a liason to prevent inconsistency between the operators
365  // == and != against primitive types. the core impl for equals between two indicators
366  // is still reference equality, however, when comparing value types (floats/int, ect..)
367  // we'll use value type semantics on Current.Value
368  // because of this, we shouldn't need to override GetHashCode as well since we're still
369  // solely relying on reference semantics (think hashset/dictionary impls)
370 
371  if (ReferenceEquals(obj, null)) return false;
372  var type = obj.GetType();
373 
374  while (type != null && type != typeof(object))
375  {
376  var cur = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
377  if (typeof(IndicatorBase<>) == cur)
378  {
379  return ReferenceEquals(this, obj);
380  }
381  type = type.BaseType;
382  }
383 
384  try
385  {
386  // the obj is not an indicator, so let's check for value types, try converting to decimal
387  var converted = obj.ConvertInvariant<decimal>();
388  return Current.Value == converted;
389  }
390  catch (InvalidCastException)
391  {
392  // conversion failed, return false
393  return false;
394  }
395  }
396 
397  /// <summary>
398  /// Get Hash Code for this Object
399  /// </summary>
400  /// <returns>Integer Hash Code</returns>
401  public override int GetHashCode()
402  {
403  return base.GetHashCode();
404  }
405  }
406 }