Lean  $LEAN_TAG$
PivotPointsHighLow.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 System.Linq;
18 using QuantConnect.Data;
20 
22 {
23  /// <summary>
24  /// Pivot Points (High/Low), also known as Bar Count Reversals, indicator.
25  /// https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/pivot-points-high-low
26  /// </summary>
28  {
29  private readonly int _surroundingBarsCountForHighPoint;
30  private readonly int _surroundingBarsCountForLowPoint;
31  private readonly RollingWindow<IBaseDataBar> _windowHighs;
32  private readonly RollingWindow<IBaseDataBar> _windowLows;
33  // Stores information of that last N pivot points
34  private readonly RollingWindow<PivotPoint> _windowPivotPoints;
35 
36  /// <summary>
37  /// Event informs of new pivot point formed with new data update
38  /// </summary>
39  public event EventHandler<PivotPointsEventArgs> NewPivotPointFormed;
40 
41  /// <summary>
42  /// Gets a flag indicating when this indicator is ready and fully initialized
43  /// </summary>
44  public override bool IsReady => _windowHighs.IsReady && _windowLows.IsReady;
45 
46  /// <summary>
47  /// Required period, in data points, for the indicator to be ready and fully initialized.
48  /// </summary>
49  public int WarmUpPeriod { get; protected set; }
50 
51  /// <summary>
52  /// Creates a new instance of <see cref="PivotPointsHighLow"/> indicator with an equal high and low length
53  /// </summary>
54  /// <param name="surroundingBarsCount">The length parameter here defines the number of surrounding bars that we compare against the current bar high and lows for the max/min </param>
55  /// <param name="lastStoredValues">The number of last stored indicator values</param>
56  public PivotPointsHighLow(int surroundingBarsCount, int lastStoredValues = 100)
57  : this($"PivotPointsHighLow({surroundingBarsCount})", surroundingBarsCount, surroundingBarsCount, lastStoredValues)
58  { }
59 
60  /// <summary>
61  /// Creates a new instance of <see cref="PivotPointsHighLow"/> indicator
62  /// </summary>
63  /// <param name="surroundingBarsCountForHighPoint">The number of surrounding bars whose high values should be less than the current bar's for the bar high to be marked as high pivot point</param>
64  /// <param name="surroundingBarsCountForLowPoint">The number of surrounding bars whose low values should be more than the current bar's for the bar low to be marked as low pivot point</param>
65  /// <param name="lastStoredValues">The number of last stored indicator values</param>
66  public PivotPointsHighLow(int surroundingBarsCountForHighPoint, int surroundingBarsCountForLowPoint, int lastStoredValues = 100)
67  : this($"PivotPointsHighLow({surroundingBarsCountForHighPoint},{surroundingBarsCountForLowPoint})", surroundingBarsCountForHighPoint, surroundingBarsCountForLowPoint, lastStoredValues)
68  { }
69 
70 
71  /// <summary>
72  /// Creates a new instance of <see cref="PivotPointsHighLow"/> indicator
73  /// </summary>
74  /// <param name="name">The name of an indicator</param>
75  /// <param name="surroundingBarsCountForHighPoint">The number of surrounding bars whose high values should be less than the current bar's for the bar high to be marked as high pivot point</param>
76  /// <param name="surroundingBarsCountForLowPoint">The number of surrounding bars whose low values should be more than the current bar's for the bar low to be marked as low pivot point</param>
77  /// <param name="lastStoredValues">The number of last stored indicator values</param>
78  public PivotPointsHighLow(string name, int surroundingBarsCountForHighPoint, int surroundingBarsCountForLowPoint, int lastStoredValues = 100)
79  : base(name)
80  {
81  _surroundingBarsCountForHighPoint = surroundingBarsCountForHighPoint;
82  _surroundingBarsCountForLowPoint = surroundingBarsCountForLowPoint;
83  _windowHighs = new RollingWindow<IBaseDataBar>(2 * surroundingBarsCountForHighPoint + 1);
84  _windowLows = new RollingWindow<IBaseDataBar>(2 * _surroundingBarsCountForLowPoint + 1);
85  _windowPivotPoints = new RollingWindow<PivotPoint>(lastStoredValues);
86  WarmUpPeriod = Math.Max(_windowHighs.Size, _windowLows.Size);
87  }
88 
89  /// <summary>
90  /// Computes the next value of this indicator from the given state
91  /// </summary>
92  /// <param name="input">The input given to the indicator</param>
93  /// <returns>A new value for this indicator</returns>
94  protected override decimal ComputeNextValue(IBaseDataBar input)
95  {
96  _windowHighs.Add(input);
97  _windowLows.Add(input);
98 
99  PivotPoint highPoint = null, lowPoint = null;
100 
101  if (_windowHighs.IsReady)
102  {
103  highPoint = FindNextHighPivotPoint(_windowHighs, _surroundingBarsCountForHighPoint);
104  }
105 
106  if (_windowLows.IsReady)
107  {
108  lowPoint = FindNextLowPivotPoint(_windowLows, _surroundingBarsCountForLowPoint);
109  }
110 
111  OnNewPivotPointFormed(highPoint);
112  OnNewPivotPointFormed(lowPoint);
113 
114  return ConvertToComputedValue(highPoint, lowPoint);
115  }
116 
117  /// <summary>
118  /// Looks for the next low pivot point.
119  /// </summary>
120  /// <param name="windowLows">rolling window that tracks the lows</param>
121  /// <param name="midPointIndexOrSurroundingBarsCount">The midpoint index or surrounding bars count for lows</param>
122  /// <returns>pivot point if found else null</returns>
123  protected virtual PivotPoint FindNextLowPivotPoint(RollingWindow<IBaseDataBar> windowLows, int midPointIndexOrSurroundingBarsCount)
124  {
125  var isLow = true;
126  var middlePoint = windowLows[midPointIndexOrSurroundingBarsCount];
127  for (var k = 0; k < windowLows.Size && isLow; k++)
128  {
129  if (k == midPointIndexOrSurroundingBarsCount)
130  {
131  continue;
132  }
133 
134  isLow = windowLows[k].Low > middlePoint.Low;
135  }
136 
137  PivotPoint low = null;
138  if (isLow)
139  {
140  low = new PivotPoint(PivotPointType.Low, middlePoint.Low, middlePoint.EndTime);
141  }
142 
143  return low;
144  }
145 
146  /// <summary>
147  /// Looks for the next high pivot point.
148  /// </summary>
149  /// <param name="windowHighs">rolling window that tracks the highs</param>
150  /// <param name="midPointIndexOrSurroundingBarsCount">The midpoint index or surrounding bars count for highs</param>
151  /// <returns>pivot point if found else null</returns>
152  protected virtual PivotPoint FindNextHighPivotPoint(RollingWindow<IBaseDataBar> windowHighs, int midPointIndexOrSurroundingBarsCount)
153  {
154  var isHigh = true;
155  var middlePoint = windowHighs[midPointIndexOrSurroundingBarsCount];
156  for (var k = 0; k < windowHighs.Size && isHigh; k++)
157  {
158  // Skip the middle point
159  if (k == midPointIndexOrSurroundingBarsCount)
160  {
161  continue;
162  }
163 
164  // Check if current high is below middle point high
165  isHigh = windowHighs[k].High < middlePoint.High;
166  }
167 
168  PivotPoint high = null;
169  if (isHigh)
170  {
171  high = new PivotPoint(PivotPointType.High, middlePoint.High, middlePoint.EndTime);
172  }
173 
174  return high;
175  }
176 
177  /// <summary>
178  /// Method for converting high and low pivot points to a decimal value.
179  /// </summary>
180  /// <param name="highPoint">new high point or null</param>
181  /// <param name="lowPoint">new low point or null</param>
182  /// <returns>a decimal value representing the values of high and low pivot points</returns>
183  protected virtual decimal ConvertToComputedValue(PivotPoint highPoint, PivotPoint lowPoint)
184  {
185  if (highPoint != null)
186  {
187  if (lowPoint != null)
188  {
189  // Can be the bar forms both high and low pivot points at the same time
190  return (decimal)PivotPointType.Both;
191  }
192  return (decimal)PivotPointType.High;
193  }
194 
195  if (lowPoint != null)
196  {
197  return (decimal)PivotPointType.Low;
198  }
199 
200  return (decimal)PivotPointType.None;
201  }
202 
203  /// <summary>
204  /// Resets this indicator to its initial state
205  /// </summary>
206  public override void Reset()
207  {
208  _windowHighs.Reset();
209  _windowLows.Reset();
210  _windowPivotPoints.Reset();
211  base.Reset();
212  }
213 
214  /// <summary>
215  /// Get current high pivot points, in the order such that first element in collection is the nearest to the present date
216  /// </summary>
217  /// <returns>An array of high pivot points.</returns>
218  /// <remarks>Returned array can be empty if no points have been registered yet/</remarks>
220  {
221  return _windowPivotPoints.Where(p => p.PivotPointType == PivotPointType.High).ToArray();
222  }
223 
224  /// <summary>
225  /// Get current low pivot points, in the order such that first element in collection is the nearest to the present date
226  /// </summary>
227  /// <returns>An array of low pivot points.</returns>
228  /// <remarks>Returned array can be empty if no points have been registered yet/</remarks>
230  {
231  return _windowPivotPoints.Where(p => p.PivotPointType == PivotPointType.Low).ToArray();
232  }
233 
234  /// <summary>
235  /// Get all pivot points, in the order such that first element in collection is the nearest to the present date
236  /// </summary>
237  /// <returns>An array of low and high pivot points. Ordered by time in descending order.</returns>
238  /// <remarks>Returned array can be empty if no points have been registered yet/</remarks>
240  {
241  // Get all pivot points within rolling wind. collection as an array
242  return _windowPivotPoints.ToArray();
243  }
244 
245  /// <summary>
246  /// Invokes NewPivotPointFormed event
247  /// </summary>
248  private void OnNewPivotPointFormed(PivotPoint pivotPoint)
249  {
250  if (pivotPoint != null)
251  {
252  _windowPivotPoints.Add(pivotPoint);
253  NewPivotPointFormed?.Invoke(this, new PivotPointsEventArgs(pivotPoint));
254  }
255  }
256  }
257 
258  /// <summary>
259  /// Represents the points identified by Pivot Point High/Low Indicator.
260  /// </summary>
261  public class PivotPoint : BaseData
262  {
263  /// <summary>
264  /// Represents pivot point type : High or Low
265  /// </summary>
266  public PivotPointType PivotPointType { get; set; }
267 
268  /// <summary>
269  /// Peak value
270  /// </summary>
271  public sealed override decimal Value { get; set; }
272 
273  /// <summary>
274  /// Creates a new instance of <see cref="PivotPoint"/>
275  /// </summary>
276  public PivotPoint(PivotPointType type, decimal price, DateTime time)
277  {
278  PivotPointType = type;
279  Value = price;
280  Time = time;
281  }
282  }
283 
284  /// <summary>
285  /// Pivot point direction
286  /// </summary>
287  public enum PivotPointType
288  {
289  /// <summary>
290  /// Low pivot point (-1)
291  /// </summary>
292  Low = -1,
293 
294  /// <summary>
295  /// No pivot point (0)
296  /// </summary>
297  None = 0,
298 
299  /// <summary>
300  /// High pivot point (1)
301  /// </summary>
302  High = 1,
303 
304  /// <summary>
305  /// Both high and low pivot point (2)
306  /// </summary>
307  Both = 2
308  }
309 
310  /// <summary>
311  /// Event arguments class for the <see cref="PivotPointsHighLow.NewPivotPointFormed"/> event
312  /// </summary>
313  public class PivotPointsEventArgs : EventArgs
314  {
315  /// <summary>
316  /// New pivot point
317  /// </summary>
318  public PivotPoint PivotPoint { get; }
319 
320  /// <summary>
321  /// Creates a new instance of <see cref="PivotPointsEventArgs"/>
322  /// </summary>
323  /// <param name="pivotPoint"></param>
324  public PivotPointsEventArgs(PivotPoint pivotPoint)
325  {
326  PivotPoint = pivotPoint;
327  }
328  }
329 }