-
-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement Connor's Relative Strength Index(CRSI) Indicator (#8419)
* Add Connors Relative Strength Index(CRSI) Indicator - Implemented CRSI combining RSI, Streak RSI, and ROC - Added unit tests for CRSI calculation to ensure correctness and reliability. - Inclued the spy_crsi.csv file for testing purposes and validation of CRSI logic. * Refactor CRSI indicator - Updated the CRSI logic to use Percent Change of Daily Returns instead of ROC. - Modified the ConnorsRelativeStrengthIndex class: - Updated ComputeNextValue to calculate daily returns and relative magnitude - Improved documentation and comments * Refactor CRSI Indicator: - Refactored the method that updated the trend streak based on price changes. - Created a new method ComputeTrendStreak. - Added check for null or 0 value in _previousInput. * Fix comments from code review - Add _ to priceChangeRatios - Move _srsi.Update() to the top to avoid duplication
- Loading branch information
Showing
5 changed files
with
830 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
/* | ||
* 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 | ||
{ | ||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
public class ConnorsRelativeStrengthIndex : Indicator, IIndicatorWarmUpPeriodProvider | ||
{ | ||
/// <summary> | ||
/// Computes the traditional Relative Strength Index (RSI). | ||
/// </summary> | ||
private readonly RelativeStrengthIndex _rsi; | ||
|
||
/// <summary> | ||
/// Computes the RSI based on consecutive price streaks (SRSI). | ||
/// </summary> | ||
private readonly RelativeStrengthIndex _srsi; | ||
|
||
/// <summary> | ||
/// Stores recent price change ratios for calculating the Percent Rank. | ||
/// </summary> | ||
private readonly RollingWindow<decimal> _priceChangeRatios; | ||
|
||
/// <summary> | ||
/// Tracks the current trend streak (positive or negative) of price movements. | ||
/// </summary> | ||
private int _trendStreak; | ||
|
||
/// <summary> | ||
/// Stores the previous input data point. | ||
/// </summary> | ||
private IndicatorDataPoint _previousInput; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ConnorsRelativeStrengthIndex"/> class. | ||
/// </summary> | ||
/// <param name="name">The name of the indicator instance.</param> | ||
/// <param name="rsiPeriod">The period for the RSI calculation.</param> | ||
/// <param name="rsiPeriodStreak">The period for the Streak RSI calculation.</param> | ||
/// <param name="lookBackPeriod">The period for calculating the Percent Rank.</param> | ||
public ConnorsRelativeStrengthIndex(string name, int rsiPeriod, int rsiPeriodStreak, int lookBackPeriod) : base(name) | ||
{ | ||
_rsi = new RelativeStrengthIndex(rsiPeriod); | ||
_srsi = new RelativeStrengthIndex(rsiPeriodStreak); | ||
_priceChangeRatios = new RollingWindow<decimal>(lookBackPeriod); | ||
_trendStreak = 0; | ||
WarmUpPeriod = Math.Max(rsiPeriod, Math.Max(rsiPeriodStreak, lookBackPeriod)); | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
public ConnorsRelativeStrengthIndex(int rsiPeriod, int rsiPeriodStreak, int rocPeriod) | ||
: this($"CRSI({rsiPeriod},{rsiPeriodStreak},{rocPeriod})", rsiPeriod, rsiPeriodStreak, rocPeriod) | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
public override bool IsReady => _rsi.IsReady && _srsi.IsReady && _priceChangeRatios.IsReady; | ||
|
||
/// <summary> | ||
/// Gets the warm-up period required for the indicator to be ready. | ||
/// This is the maximum period of all components (RSI, SRSI, and PriceChangeRatios). | ||
/// </summary> | ||
public int WarmUpPeriod { get; } | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
/// <param name="input">The current input data point (typically the price data for the current period).</param> | ||
/// <returns>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.</returns> | ||
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; | ||
if (_priceChangeRatios.IsReady) | ||
{ | ||
// Calculate the percentage of previous change ratios that are smaller than the current price change ratio | ||
relativeMagnitude = 100m * _priceChangeRatios.Count(x => x < priceChangeRatio) / _priceChangeRatios.Count; | ||
} | ||
_previousInput = input; | ||
|
||
//CRSI | ||
if (IsReady) | ||
{ | ||
// Add the priceChangeRatio after checking if IsReady is true or false, preventing premature returns | ||
_priceChangeRatios.Add(priceChangeRatio); | ||
return (_rsi.Current.Value + _srsi.Current.Value + relativeMagnitude) / 3; | ||
} | ||
// CRSI is not ready yet, so we store the price change ratio in the rolling window and return zero | ||
_priceChangeRatios.Add(priceChangeRatio); | ||
return decimal.Zero; | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
/// <param name="input">The current input data point with price information.</param> | ||
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--; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
public override void Reset() | ||
{ | ||
_rsi.Reset(); | ||
_srsi.Reset(); | ||
_priceChangeRatios.Reset(); | ||
_trendStreak = 0; | ||
base.Reset(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* 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 NUnit.Framework; | ||
using QuantConnect.Indicators; | ||
|
||
namespace QuantConnect.Tests.Indicators | ||
{ | ||
[TestFixture] | ||
public class ConnorsRelativeStrengthIndexTests : CommonIndicatorTests<IndicatorDataPoint> | ||
{ | ||
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator() | ||
{ | ||
return new ConnorsRelativeStrengthIndex(3, 2, 100); | ||
} | ||
protected override string TestFileName => "spy_crsi.csv"; | ||
|
||
protected override string TestColumnName => "crsi"; | ||
|
||
[Test] | ||
public void DoesNotThrowDivisionByZero() | ||
{ | ||
var crsi = new ConnorsRelativeStrengthIndex(2, 2, 2); | ||
for (var i = 0; i < 10; i++) | ||
{ | ||
Assert.DoesNotThrow(() => crsi.Update(DateTime.UtcNow, 0m)); | ||
} | ||
} | ||
|
||
[Test] | ||
public void IsReadyAfterPeriodUpdates() | ||
{ | ||
var rsiPeriod = 2; | ||
var rsiPeriodStreak = 3; | ||
var lookBackPeriod = 4; | ||
var crsi = new ConnorsRelativeStrengthIndex(rsiPeriod, rsiPeriodStreak, lookBackPeriod); | ||
int minInputValues = Math.Max(rsiPeriod, Math.Max(rsiPeriodStreak, lookBackPeriod)); | ||
for (int i = 0; i < minInputValues; i++) | ||
{ | ||
Assert.IsFalse(crsi.IsReady); | ||
crsi.Update(DateTime.Now, i + 1); | ||
} | ||
Assert.IsTrue(crsi.IsReady); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.