Skip to content

Commit

Permalink
Implement Connor's Relative Strength Index(CRSI) Indicator (#8419)
Browse files Browse the repository at this point in the history
* 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
JosueNina authored Nov 25, 2024
1 parent 7eea365 commit 469d960
Show file tree
Hide file tree
Showing 5 changed files with 830 additions and 0 deletions.
21 changes: 21 additions & 0 deletions Algorithm/QCAlgorithm.Indicators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,27 @@ public ChandeMomentumOscillator CMO(Symbol symbol, int period, Resolution? resol
return chandeMomentumOscillator;
}

/// <summary>
/// Creates a new Connors Relative Strength Index (CRSI) indicator, which combines the traditional Relative Strength Index (RSI),
/// Streak RSI (SRSI), and Percent Rank to provide a more robust measure of market strength.
/// This indicator oscillates based on momentum, streak behavior, and price change over the specified periods.
/// </summary>
/// <param name="symbol">The symbol whose CRSI is to be calculated.</param>
/// <param name="rsiPeriod">The period for the traditional RSI calculation.</param>
/// <param name="rsiPeriodStreak">The period for the Streak RSI calculation (SRSI).</param>
/// <param name="lookBackPeriod">The look-back period for calculating the Percent Rank.</param>
/// <param name="resolution">The resolution of the data (optional).</param>
/// <param name="selector">Function to select a value from the BaseData to input into the indicator. Defaults to using the 'Value' property of BaseData if null.</param>
/// <returns>The Connors Relative Strength Index (CRSI) for the specified symbol and periods.</returns>
[DocumentationAttribute(Indicators)]
public ConnorsRelativeStrengthIndex CRSI(Symbol symbol, int rsiPeriod, int rsiPeriodStreak, int lookBackPeriod, Resolution? resolution = null, Func<IBaseData, decimal> selector = null)
{
var name = CreateIndicatorName(symbol, $"CRSI({rsiPeriod},{rsiPeriodStreak},{lookBackPeriod})", resolution);
var connorsRelativeStrengthIndex = new ConnorsRelativeStrengthIndex(name, rsiPeriod, rsiPeriodStreak, lookBackPeriod);
InitializeIndicator(connorsRelativeStrengthIndex, resolution, selector, symbol);
return connorsRelativeStrengthIndex;
}

///<summary>
/// Creates a new DeMarker Indicator (DEM), an oscillator-type indicator measuring changes in terms of an asset's
/// High and Low tradebar values.
Expand Down
177 changes: 177 additions & 0 deletions Indicators/ConnorsRelativeStrengthIndex.cs
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();
}
}
}
60 changes: 60 additions & 0 deletions Tests/Indicators/ConnorsRelativeStrengthIndexTests.cs
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);
}

}
}
3 changes: 3 additions & 0 deletions Tests/QuantConnect.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,9 @@
<Content Include="TestData\spy_adr.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="TestData\spy_crsi.csv">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="TestData\symbol-properties\symbol-properties-database.csv">
Expand Down
Loading

0 comments on commit 469d960

Please sign in to comment.