Lean  $LEAN_TAG$
RelativeVigorIndex.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 
17 
19 {
20  /// <summary>
21  /// The Relative Vigor Index (RVI) compares the ratio of the closing price of a security to its trading range.
22  /// For illustration, let:
23  /// <para>a = Close−Open</para>
24  /// <para>b = Close−Open of One Bar Prior to a</para>
25  /// <para>c = Close−Open of One Bar Prior to b</para>
26  /// <para>d = Close−Open of One Bar Prior to c</para>
27  /// <para>e = High−Low of Bar a</para>
28  /// <para>f = High−Low of Bar b</para>
29  /// <para>g = High−Low of Bar c</para>
30  /// <para>h = High−Low of Bar d</para>
31  ///
32  /// Then let (a+2*(b+c)+d)/6 be NUM and (e+2*(f+g)+h)/6 be DENOM.
33  /// <para>RVI = SMA(NUM)/SMA(DENOM)</para>
34  /// for a specified period.
35  ///
36  /// https://www.investopedia.com/terms/r/relative_vigor_index.asp
37  /// </summary>
39  {
40  private readonly RollingWindow<IBaseDataBar> _previousInputs;
41 
42  /// <summary>
43  /// Gets the band of Closes for the RVI.
44  /// </summary>
45  private IndicatorBase<IndicatorDataPoint> CloseBand { get; }
46 
47  /// <summary>
48  /// Gets the band of Ranges for the RVI.
49  /// </summary>
50  private IndicatorBase<IndicatorDataPoint> RangeBand { get; }
51 
52  /// <summary>
53  /// A signal line which behaves like a slowed version of the RVI.
54  /// </summary>
56 
57  /// <summary>
58  /// Gets a flag indicating when this indicator is ready and fully initialized
59  /// </summary>
60  public override bool IsReady => CloseBand.IsReady && RangeBand.IsReady;
61 
62  /// <summary>
63  /// Required period, in data points, for the indicator to be ready and fully initialized.
64  /// </summary>
65  public int WarmUpPeriod { get; }
66 
67  /// <summary>
68  /// Initializes a new instance of the <see cref="RelativeVigorIndex"/> (RVI) class.
69  /// </summary>
70  /// <param name="period">The period for the RelativeVigorIndex.</param>
71  /// <param name="type">The type of Moving Average to use</param>
72  public RelativeVigorIndex(int period, MovingAverageType type)
73  : this($"RVI({period},{type})", period, type)
74  {
75  }
76 
77  /// <summary>
78  /// Initializes a new instance of the <see cref="RelativeVigorIndex"/> (RVI) class.
79  /// </summary>
80  /// <param name="name">The name of this indicator.</param>
81  /// <param name="period">The period for the RelativeVigorIndex.</param>
82  /// <param name="type">The type of Moving Average to use</param>
83  public RelativeVigorIndex(string name, int period, MovingAverageType type = MovingAverageType.Simple)
84  : base(name)
85  {
86  WarmUpPeriod = period + 3;
87  CloseBand = type.AsIndicator("_closingBand", period);
88  RangeBand = type.AsIndicator("_rangeBand", period);
89  Signal = new RelativeVigorIndexSignal($"{name}_S");
90  _previousInputs = new RollingWindow<IBaseDataBar>(3);
91  }
92 
93  /// <summary>
94  /// Computes the next value of this indicator from the given state
95  /// </summary>
96  /// <param name="input">The input given to the indicator</param>
97  /// <returns>A new value for this indicator</returns>
98  protected override decimal ComputeNextValue(IBaseDataBar input)
99  {
100  if (_previousInputs.IsReady)
101  {
102  var a = input.Close - input.Open;
103  var b = _previousInputs[0].Close - _previousInputs[0].Open;
104  var c = _previousInputs[1].Close - _previousInputs[1].Open;
105  var d = _previousInputs[2].Close - _previousInputs[2].Open;
106  var e = input.High - input.Low;
107  var f = _previousInputs[0].High - _previousInputs[0].Low;
108  var g = _previousInputs[1].High - _previousInputs[1].Low;
109  var h = _previousInputs[2].High - _previousInputs[2].Low;
110  CloseBand.Update(input.Time, (a + 2 * (b + c) + d) / 6);
111  RangeBand.Update(input.Time, (e + 2 * (f + g) + h) / 6);
112 
113  if (CloseBand.IsReady && RangeBand.IsReady && RangeBand != 0m)
114  {
115  _previousInputs.Add(input);
116  var rvi = CloseBand / RangeBand;
117  Signal?.Update(input.Time, rvi); // Checks for null before updating.
118  return rvi;
119  }
120  }
121 
122  _previousInputs.Add(input);
123  return 0m;
124  }
125 
126  /// <summary>
127  /// Resets this indicator to its initial state
128  /// </summary>
129  public override void Reset()
130  {
131  base.Reset();
132  CloseBand.Reset();
133  RangeBand.Reset();
134  _previousInputs.Reset();
135  Signal?.Reset();
136  }
137  }
138 }