/*
* 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 QuantConnect.Data.Market;
namespace QuantConnect.Indicators
{
///
/// The Relative Vigor Index (RVI) compares the ratio of the closing price of a security to its trading range.
/// For illustration, let:
/// a = Close−Open
/// b = Close−Open of One Bar Prior to a
/// c = Close−Open of One Bar Prior to b
/// d = Close−Open of One Bar Prior to c
/// e = High−Low of Bar a
/// f = High−Low of Bar b
/// g = High−Low of Bar c
/// h = High−Low of Bar d
///
/// Then let (a+2*(b+c)+d)/6 be NUM and (e+2*(f+g)+h)/6 be DENOM.
/// RVI = SMA(NUM)/SMA(DENOM)
/// for a specified period.
///
/// https://www.investopedia.com/terms/r/relative_vigor_index.asp
///
public class RelativeVigorIndex : BarIndicator, IIndicatorWarmUpPeriodProvider
{
private readonly RollingWindow _previousInputs;
///
/// Gets the band of Closes for the RVI.
///
private IndicatorBase CloseBand { get; }
///
/// Gets the band of Ranges for the RVI.
///
private IndicatorBase RangeBand { get; }
///
/// A signal line which behaves like a slowed version of the RVI.
///
public RelativeVigorIndexSignal Signal { get; }
///
/// Gets a flag indicating when this indicator is ready and fully initialized
///
public override bool IsReady => CloseBand.IsReady && RangeBand.IsReady;
///
/// Required period, in data points, for the indicator to be ready and fully initialized.
///
public int WarmUpPeriod { get; }
///
/// Initializes a new instance of the (RVI) class.
///
/// The period for the RelativeVigorIndex.
/// The type of Moving Average to use
public RelativeVigorIndex(int period, MovingAverageType type)
: this($"RVI({period},{type})", period, type)
{
}
///
/// Initializes a new instance of the (RVI) class.
///
/// The name of this indicator.
/// The period for the RelativeVigorIndex.
/// The type of Moving Average to use
public RelativeVigorIndex(string name, int period, MovingAverageType type = MovingAverageType.Simple)
: base(name)
{
WarmUpPeriod = period + 3;
CloseBand = type.AsIndicator("_closingBand", period);
RangeBand = type.AsIndicator("_rangeBand", period);
Signal = new RelativeVigorIndexSignal($"{name}_S");
_previousInputs = new RollingWindow(3);
}
///
/// 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)
{
if (_previousInputs.IsReady)
{
var a = input.Close - input.Open;
var b = _previousInputs[0].Close - _previousInputs[0].Open;
var c = _previousInputs[1].Close - _previousInputs[1].Open;
var d = _previousInputs[2].Close - _previousInputs[2].Open;
var e = input.High - input.Low;
var f = _previousInputs[0].High - _previousInputs[0].Low;
var g = _previousInputs[1].High - _previousInputs[1].Low;
var h = _previousInputs[2].High - _previousInputs[2].Low;
CloseBand.Update(input.EndTime, (a + 2 * (b + c) + d) / 6);
RangeBand.Update(input.EndTime, (e + 2 * (f + g) + h) / 6);
if (CloseBand.IsReady && RangeBand.IsReady && RangeBand != 0m)
{
_previousInputs.Add(input);
var rvi = CloseBand / RangeBand;
Signal?.Update(input.EndTime, rvi); // Checks for null before updating.
return rvi;
}
}
_previousInputs.Add(input);
return 0m;
}
///
/// Resets this indicator to its initial state
///
public override void Reset()
{
base.Reset();
CloseBand.Reset();
RangeBand.Reset();
_previousInputs.Reset();
Signal?.Reset();
}
}
}