Lean  $LEAN_TAG$
ConnorsRelativeStrengthIndex.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 
20 {
21  /// <summary>
22  /// Represents the Connors Relative Strength Index (CRSI), a combination of
23  /// the traditional Relative Strength Index (RSI), a Streak RSI (SRSI), and
24  /// Percent Rank.
25  /// This index is designed to provide a more robust measure of market strength
26  /// by combining momentum, streak behavior, and price change.
27  /// </summary>
29  {
30  /// <summary>
31  /// Computes the traditional Relative Strength Index (RSI).
32  /// </summary>
33  private readonly RelativeStrengthIndex _rsi;
34 
35  /// <summary>
36  /// Computes the RSI based on consecutive price streaks (SRSI).
37  /// </summary>
38  private readonly RelativeStrengthIndex _srsi;
39 
40  /// <summary>
41  /// Stores recent price change ratios for calculating the Percent Rank.
42  /// </summary>
43  private readonly RollingWindow<decimal> _priceChangeRatios;
44 
45  /// <summary>
46  /// Tracks the current trend streak (positive or negative) of price movements.
47  /// </summary>
48  private int _trendStreak;
49 
50  /// <summary>
51  /// Stores the previous input data point.
52  /// </summary>
53  private IndicatorDataPoint _previousInput;
54 
55  /// <summary>
56  /// Initializes a new instance of the <see cref="ConnorsRelativeStrengthIndex"/> class.
57  /// </summary>
58  /// <param name="name">The name of the indicator instance.</param>
59  /// <param name="rsiPeriod">The period for the RSI calculation.</param>
60  /// <param name="rsiPeriodStreak">The period for the Streak RSI calculation.</param>
61  /// <param name="lookBackPeriod">The period for calculating the Percent Rank.</param>
62  public ConnorsRelativeStrengthIndex(string name, int rsiPeriod, int rsiPeriodStreak, int lookBackPeriod) : base(name)
63  {
64  _rsi = new RelativeStrengthIndex(rsiPeriod);
65  _srsi = new RelativeStrengthIndex(rsiPeriodStreak);
66  _priceChangeRatios = new RollingWindow<decimal>(lookBackPeriod);
67  _trendStreak = 0;
68  WarmUpPeriod = Math.Max(lookBackPeriod, Math.Max(_rsi.WarmUpPeriod, _srsi.WarmUpPeriod));
69  }
70 
71  /// <summary>
72  /// Initializes a new instance of the ConnorsRelativeStrengthIndex with specified RSI, Streak RSI,
73  /// and lookBack periods, using a default name format based on the provided parameters.
74  /// </summary>
75  public ConnorsRelativeStrengthIndex(int rsiPeriod, int rsiPeriodStreak, int rocPeriod)
76  : this($"CRSI({rsiPeriod},{rsiPeriodStreak},{rocPeriod})", rsiPeriod, rsiPeriodStreak, rocPeriod)
77  {
78  }
79 
80  /// <summary>
81  /// Gets a value indicating whether the indicator is ready for use.
82  /// The indicator is ready when all its components (RSI, SRSI, and PriceChangeRatios) are ready.
83  /// </summary>
84  public override bool IsReady => _rsi.IsReady && _srsi.IsReady && _priceChangeRatios.IsReady;
85 
86  /// <summary>
87  /// Gets the warm-up period required for the indicator to be ready.
88  /// This is the maximum period of all components (RSI, SRSI, and PriceChangeRatios).
89  /// </summary>
90  public int WarmUpPeriod { get; }
91 
92  /// <summary>
93  /// Computes the next value for the Connors Relative Strength Index (CRSI) based on the latest input data point.
94  /// The CRSI is calculated as the average of the traditional RSI, Streak RSI, and Percent Rank.
95  /// </summary>
96  /// <param name="input">The current input data point (typically the price data for the current period).</param>
97  /// <returns>The computed CRSI value, which combines the RSI, Streak RSI, and Percent Rank into a single value.
98  /// Returns zero if the indicator is not yet ready.</returns>
99  protected override decimal ComputeNextValue(IndicatorDataPoint input)
100  {
101  // RSI
102  _rsi.Update(input);
103 
104  ComputeTrendStreak(input);
105  _srsi.Update(new IndicatorDataPoint(input.EndTime, _trendStreak));
106 
107  if (_previousInput == null || _previousInput.Value == 0)
108  {
109  _previousInput = input;
110  _priceChangeRatios.Add(0m);
111  return decimal.Zero;
112  }
113 
114  // PercentRank
115  var relativeMagnitude = 0m;
116  var priceChangeRatio = (input.Value - _previousInput.Value) / _previousInput.Value;
117 
118  // Calculate PercentRank using only the previous values (exclude the current priceChangeRatio)
119  if (_priceChangeRatios.IsReady)
120  {
121  relativeMagnitude = 100m * _priceChangeRatios.Count(x => x < priceChangeRatio) / _priceChangeRatios.Count;
122  }
123 
124  // Add the current priceChangeRatio to the rolling window for future calculations
125  _priceChangeRatios.Add(priceChangeRatio);
126 
127  _previousInput = input;
128 
129  // CRSI
130  if (IsReady)
131  {
132  // Calculate the CRSI only if all components are ready
133  return (_rsi.Current.Value + _srsi.Current.Value + relativeMagnitude) / 3;
134  }
135 
136  // If not ready, return 0
137  return decimal.Zero;
138  }
139 
140  /// <summary>
141  /// Updates the trend streak based on the price change direction between the current and previous input.
142  /// Resets the streak if the direction changes, otherwise increments or decrements it.
143  /// </summary>
144  /// <param name="input">The current input data point with price information.</param>
145  private void ComputeTrendStreak(IndicatorDataPoint input)
146  {
147  if (_previousInput == null)
148  {
149  return;
150  }
151  var change = input.Value - _previousInput.Value;
152  // If the price changes direction (up to down or down to up), reset the trend streak
153  if ((_trendStreak > 0 && change < 0) || (_trendStreak < 0 && change > 0))
154  {
155  _trendStreak = 0;
156  }
157  // Increment or decrement the trend streak based on price change direction
158  if (change > 0)
159  {
160  _trendStreak++;
161  }
162  else if (change < 0)
163  {
164  _trendStreak--;
165  }
166  }
167 
168  /// <summary>
169  /// Resets the indicator to its initial state. This clears all internal data and resets
170  /// the RSI, Streak RSI, and PriceChangeRatios, as well as the trend streak counter.
171  /// </summary>
172  public override void Reset()
173  {
174  _rsi.Reset();
175  _srsi.Reset();
176  _priceChangeRatios.Reset();
177  _trendStreak = 0;
178  base.Reset();
179  }
180  }
181 }