/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Data.Market;
namespace QuantConnect.Indicators
{
///
/// Pivot Points (High/Low), also known as Bar Count Reversals, indicator.
/// https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/pivot-points-high-low
///
public class PivotPointsHighLow : IndicatorBase, IIndicatorWarmUpPeriodProvider
{
private readonly int _surroundingBarsCountForHighPoint;
private readonly int _surroundingBarsCountForLowPoint;
private readonly RollingWindow _windowHighs;
private readonly RollingWindow _windowLows;
// Stores information of that last N pivot points
private readonly RollingWindow _windowPivotPoints;
///
/// Event informs of new pivot point formed with new data update
///
public event EventHandler NewPivotPointFormed;
///
/// Gets a flag indicating when this indicator is ready and fully initialized
///
public override bool IsReady => _windowHighs.IsReady && _windowLows.IsReady;
///
/// Required period, in data points, for the indicator to be ready and fully initialized.
///
public int WarmUpPeriod { get; protected set; }
///
/// Creates a new instance of indicator with an equal high and low length
///
/// The length parameter here defines the number of surrounding bars that we compare against the current bar high and lows for the max/min
/// The number of last stored indicator values
public PivotPointsHighLow(int surroundingBarsCount, int lastStoredValues = 100)
: this($"PivotPointsHighLow({surroundingBarsCount})", surroundingBarsCount, surroundingBarsCount, lastStoredValues)
{ }
///
/// Creates a new instance of indicator
///
/// 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
/// 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
/// The number of last stored indicator values
public PivotPointsHighLow(int surroundingBarsCountForHighPoint, int surroundingBarsCountForLowPoint, int lastStoredValues = 100)
: this($"PivotPointsHighLow({surroundingBarsCountForHighPoint},{surroundingBarsCountForLowPoint})", surroundingBarsCountForHighPoint, surroundingBarsCountForLowPoint, lastStoredValues)
{ }
///
/// Creates a new instance of indicator
///
/// The name of an indicator
/// 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
/// 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
/// The number of last stored indicator values
public PivotPointsHighLow(string name, int surroundingBarsCountForHighPoint, int surroundingBarsCountForLowPoint, int lastStoredValues = 100)
: base(name)
{
_surroundingBarsCountForHighPoint = surroundingBarsCountForHighPoint;
_surroundingBarsCountForLowPoint = surroundingBarsCountForLowPoint;
_windowHighs = new RollingWindow(2 * surroundingBarsCountForHighPoint + 1);
_windowLows = new RollingWindow(2 * _surroundingBarsCountForLowPoint + 1);
_windowPivotPoints = new RollingWindow(lastStoredValues);
WarmUpPeriod = Math.Max(_windowHighs.Size, _windowLows.Size);
}
///
/// Computes the next value of this indicator from the given state
///
/// The input given to the indicator
/// A new value for this indicator
protected override decimal ComputeNextValue(IBaseDataBar input)
{
_windowHighs.Add(input);
_windowLows.Add(input);
PivotPoint highPoint = null, lowPoint = null;
if (_windowHighs.IsReady)
{
highPoint = FindNextHighPivotPoint(_windowHighs, _surroundingBarsCountForHighPoint);
}
if (_windowLows.IsReady)
{
lowPoint = FindNextLowPivotPoint(_windowLows, _surroundingBarsCountForLowPoint);
}
OnNewPivotPointFormed(highPoint);
OnNewPivotPointFormed(lowPoint);
return ConvertToComputedValue(highPoint, lowPoint);
}
///
/// Looks for the next low pivot point.
///
/// rolling window that tracks the lows
/// The midpoint index or surrounding bars count for lows
/// pivot point if found else null
protected virtual PivotPoint FindNextLowPivotPoint(RollingWindow windowLows, int midPointIndexOrSurroundingBarsCount)
{
var isLow = true;
var middlePoint = windowLows[midPointIndexOrSurroundingBarsCount];
for (var k = 0; k < windowLows.Size && isLow; k++)
{
if (k == midPointIndexOrSurroundingBarsCount)
{
continue;
}
isLow = windowLows[k].Low > middlePoint.Low;
}
PivotPoint low = null;
if (isLow)
{
low = new PivotPoint(PivotPointType.Low, middlePoint.Low, middlePoint.EndTime);
}
return low;
}
///
/// Looks for the next high pivot point.
///
/// rolling window that tracks the highs
/// The midpoint index or surrounding bars count for highs
/// pivot point if found else null
protected virtual PivotPoint FindNextHighPivotPoint(RollingWindow windowHighs, int midPointIndexOrSurroundingBarsCount)
{
var isHigh = true;
var middlePoint = windowHighs[midPointIndexOrSurroundingBarsCount];
for (var k = 0; k < windowHighs.Size && isHigh; k++)
{
// Skip the middle point
if (k == midPointIndexOrSurroundingBarsCount)
{
continue;
}
// Check if current high is below middle point high
isHigh = windowHighs[k].High < middlePoint.High;
}
PivotPoint high = null;
if (isHigh)
{
high = new PivotPoint(PivotPointType.High, middlePoint.High, middlePoint.EndTime);
}
return high;
}
///
/// Method for converting high and low pivot points to a decimal value.
///
/// new high point or null
/// new low point or null
/// a decimal value representing the values of high and low pivot points
protected virtual decimal ConvertToComputedValue(PivotPoint highPoint, PivotPoint lowPoint)
{
if (highPoint != null)
{
if (lowPoint != null)
{
// Can be the bar forms both high and low pivot points at the same time
return (decimal)PivotPointType.Both;
}
return (decimal)PivotPointType.High;
}
if (lowPoint != null)
{
return (decimal)PivotPointType.Low;
}
return (decimal)PivotPointType.None;
}
///
/// Resets this indicator to its initial state
///
public override void Reset()
{
_windowHighs.Reset();
_windowLows.Reset();
_windowPivotPoints.Reset();
base.Reset();
}
///
/// Get current high pivot points, in the order such that first element in collection is the nearest to the present date
///
/// An array of high pivot points.
/// Returned array can be empty if no points have been registered yet/
public PivotPoint[] GetHighPivotPointsArray()
{
return _windowPivotPoints.Where(p => p.PivotPointType == PivotPointType.High).ToArray();
}
///
/// Get current low pivot points, in the order such that first element in collection is the nearest to the present date
///
/// An array of low pivot points.
/// Returned array can be empty if no points have been registered yet/
public PivotPoint[] GetLowPivotPointsArray()
{
return _windowPivotPoints.Where(p => p.PivotPointType == PivotPointType.Low).ToArray();
}
///
/// Get all pivot points, in the order such that first element in collection is the nearest to the present date
///
/// An array of low and high pivot points. Ordered by time in descending order.
/// Returned array can be empty if no points have been registered yet/
public PivotPoint[] GetAllPivotPointsArray()
{
// Get all pivot points within rolling wind. collection as an array
return _windowPivotPoints.ToArray();
}
///
/// Invokes NewPivotPointFormed event
///
private void OnNewPivotPointFormed(PivotPoint pivotPoint)
{
if (pivotPoint != null)
{
_windowPivotPoints.Add(pivotPoint);
NewPivotPointFormed?.Invoke(this, new PivotPointsEventArgs(pivotPoint));
}
}
}
///
/// Represents the points identified by Pivot Point High/Low Indicator.
///
public class PivotPoint : BaseData
{
///
/// Represents pivot point type : High or Low
///
public PivotPointType PivotPointType { get; set; }
///
/// Peak value
///
public sealed override decimal Value { get; set; }
///
/// Creates a new instance of
///
public PivotPoint(PivotPointType type, decimal price, DateTime time)
{
PivotPointType = type;
Value = price;
Time = time;
}
}
///
/// Pivot point direction
///
public enum PivotPointType
{
///
/// Low pivot point (-1)
///
Low = -1,
///
/// No pivot point (0)
///
None = 0,
///
/// High pivot point (1)
///
High = 1,
///
/// Both high and low pivot point (2)
///
Both = 2
}
///
/// Event arguments class for the event
///
public class PivotPointsEventArgs : EventArgs
{
///
/// New pivot point
///
public PivotPoint PivotPoint { get; }
///
/// Creates a new instance of
///
///
public PivotPointsEventArgs(PivotPoint pivotPoint)
{
PivotPoint = pivotPoint;
}
}
}