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