/* * 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; namespace QuantConnect.Indicators { /// /// Represents the Connors Relative Strength Index (CRSI), a combination of /// the traditional Relative Strength Index (RSI), a Streak RSI (SRSI), and /// Percent Rank. /// This index is designed to provide a more robust measure of market strength /// by combining momentum, streak behavior, and price change. /// public class ConnorsRelativeStrengthIndex : Indicator, IIndicatorWarmUpPeriodProvider { /// /// Computes the traditional Relative Strength Index (RSI). /// private readonly RelativeStrengthIndex _rsi; /// /// Computes the RSI based on consecutive price streaks (SRSI). /// private readonly RelativeStrengthIndex _srsi; /// /// Stores recent price change ratios for calculating the Percent Rank. /// private readonly RollingWindow _priceChangeRatios; /// /// Tracks the current trend streak (positive or negative) of price movements. /// private int _trendStreak; /// /// Stores the previous input data point. /// private IndicatorDataPoint _previousInput; /// /// Initializes a new instance of the class. /// /// The name of the indicator instance. /// The period for the RSI calculation. /// The period for the Streak RSI calculation. /// The period for calculating the Percent Rank. public ConnorsRelativeStrengthIndex(string name, int rsiPeriod, int rsiPeriodStreak, int lookBackPeriod) : base(name) { _rsi = new RelativeStrengthIndex(rsiPeriod); _srsi = new RelativeStrengthIndex(rsiPeriodStreak); _priceChangeRatios = new RollingWindow(lookBackPeriod); _trendStreak = 0; WarmUpPeriod = Math.Max(lookBackPeriod, Math.Max(_rsi.WarmUpPeriod, _srsi.WarmUpPeriod)); } /// /// Initializes a new instance of the ConnorsRelativeStrengthIndex with specified RSI, Streak RSI, /// and lookBack periods, using a default name format based on the provided parameters. /// public ConnorsRelativeStrengthIndex(int rsiPeriod, int rsiPeriodStreak, int rocPeriod) : this($"CRSI({rsiPeriod},{rsiPeriodStreak},{rocPeriod})", rsiPeriod, rsiPeriodStreak, rocPeriod) { } /// /// Gets a value indicating whether the indicator is ready for use. /// The indicator is ready when all its components (RSI, SRSI, and PriceChangeRatios) are ready. /// public override bool IsReady => _rsi.IsReady && _srsi.IsReady && _priceChangeRatios.IsReady; /// /// Gets the warm-up period required for the indicator to be ready. /// This is the maximum period of all components (RSI, SRSI, and PriceChangeRatios). /// public int WarmUpPeriod { get; } /// /// Computes the next value for the Connors Relative Strength Index (CRSI) based on the latest input data point. /// The CRSI is calculated as the average of the traditional RSI, Streak RSI, and Percent Rank. /// /// The current input data point (typically the price data for the current period). /// The computed CRSI value, which combines the RSI, Streak RSI, and Percent Rank into a single value. /// Returns zero if the indicator is not yet ready. protected override decimal ComputeNextValue(IndicatorDataPoint input) { // RSI _rsi.Update(input); ComputeTrendStreak(input); _srsi.Update(new IndicatorDataPoint(input.EndTime, _trendStreak)); if (_previousInput == null || _previousInput.Value == 0) { _previousInput = input; _priceChangeRatios.Add(0m); return decimal.Zero; } // PercentRank var relativeMagnitude = 0m; var priceChangeRatio = (input.Value - _previousInput.Value) / _previousInput.Value; // Calculate PercentRank using only the previous values (exclude the current priceChangeRatio) if (_priceChangeRatios.IsReady) { relativeMagnitude = 100m * _priceChangeRatios.Count(x => x < priceChangeRatio) / _priceChangeRatios.Count; } // Add the current priceChangeRatio to the rolling window for future calculations _priceChangeRatios.Add(priceChangeRatio); _previousInput = input; // CRSI if (IsReady) { // Calculate the CRSI only if all components are ready return (_rsi.Current.Value + _srsi.Current.Value + relativeMagnitude) / 3; } // If not ready, return 0 return decimal.Zero; } /// /// Updates the trend streak based on the price change direction between the current and previous input. /// Resets the streak if the direction changes, otherwise increments or decrements it. /// /// The current input data point with price information. private void ComputeTrendStreak(IndicatorDataPoint input) { if (_previousInput == null) { return; } var change = input.Value - _previousInput.Value; // If the price changes direction (up to down or down to up), reset the trend streak if ((_trendStreak > 0 && change < 0) || (_trendStreak < 0 && change > 0)) { _trendStreak = 0; } // Increment or decrement the trend streak based on price change direction if (change > 0) { _trendStreak++; } else if (change < 0) { _trendStreak--; } } /// /// Resets the indicator to its initial state. This clears all internal data and resets /// the RSI, Streak RSI, and PriceChangeRatios, as well as the trend streak counter. /// public override void Reset() { _rsi.Reset(); _srsi.Reset(); _priceChangeRatios.Reset(); _trendStreak = 0; base.Reset(); } } }