Skip to content

Commit

Permalink
Refactor CRSI indicator
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
JosueNina committed Nov 19, 2024
1 parent a6c9ac0 commit 6cbe9b9
Show file tree
Hide file tree
Showing 4 changed files with 523 additions and 567 deletions.
10 changes: 5 additions & 5 deletions Algorithm/QCAlgorithm.Indicators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -555,21 +555,21 @@ public ChandeMomentumOscillator CMO(Symbol symbol, int period, Resolution? resol

/// <summary>
/// Creates a new Connors Relative Strength Index (CRSI) indicator, which combines the traditional Relative Strength Index (RSI),
/// Streak RSI (SRSI), and Rate of Change (ROC) to provide a more robust measure of market strength.
/// Streak RSI (SRSI), and Percent Change of Daily Returns 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="rocPeriod">The period for Rate of Change (ROC) calculation.</param>
/// <param name="lookBackPeriod">The look-back period for calculating the Percent Change of Daily Returns.</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 RSI(Symbol symbol, int rsiPeriod, int rsiPeriodStreak, int rocPeriod, Resolution? resolution = null, Func<IBaseData, decimal> selector = null)
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},{rocPeriod})", resolution);
var connorsRelativeStrengthIndex = new ConnorsRelativeStrengthIndex(name, rsiPeriod, rsiPeriodStreak, rocPeriod);
var name = CreateIndicatorName(symbol, $"CRSI({rsiPeriod},{rsiPeriodStreak},{lookBackPeriod})", resolution);
var connorsRelativeStrengthIndex = new ConnorsRelativeStrengthIndex(name, rsiPeriod, rsiPeriodStreak, lookBackPeriod);
InitializeIndicator(connorsRelativeStrengthIndex, resolution, selector, symbol);
return connorsRelativeStrengthIndex;
}
Expand Down
81 changes: 46 additions & 35 deletions Indicators/ConnorsRelativeStrengthIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
*/

using System;
using System.Linq;

namespace QuantConnect.Indicators
{
/// <summary>
/// Represents the Connors Relative Strength Index (CRSI), a combination of
/// the standard Relative Strength Index (RSI), a Streak RSI (SRSI), and
/// Rate of Change (ROC).
/// the traditional Relative Strength Index (RSI), a Streak RSI (SRSI), and
/// Percent Change of Daily Returns.
/// This index is designed to provide a more robust measure of market strength
/// by combining momentum, streak behavior, and price change.
/// </summary>
Expand All @@ -37,34 +38,36 @@ public class ConnorsRelativeStrengthIndex : Indicator, IIndicatorWarmUpPeriodPro
private readonly RelativeStrengthIndex _srsi;

/// <summary>
/// Stores recent price data for calculating the Rate of Change (ROC).
/// Stores recent daily returns (price changes) for calculating relative magnitude.
/// </summary>
private readonly RollingWindow<decimal> _recentPrices;
private readonly RollingWindow<decimal> _rollingData;

/// <summary>
/// Tracks the current trend streak (positive or negative) of price movements.
/// </summary>
private int _trendStreak;

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="rocPeriod">The period for the Rate of Change ROC calculation.</param>
public ConnorsRelativeStrengthIndex(string name, int rsiPeriod, int rsiPeriodStreak, int rocPeriod) : base(name)
/// <param name="lookBackPeriod">The period for calculating the Percent Change of Daily Returns.</param>
public ConnorsRelativeStrengthIndex(string name, int rsiPeriod, int rsiPeriodStreak, int lookBackPeriod) : base(name)
{
_rsi = new RelativeStrengthIndex(rsiPeriod);
_srsi = new RelativeStrengthIndex(rsiPeriodStreak);
_recentPrices = new RollingWindow<decimal>(rocPeriod + 1);
_rollingData = new RollingWindow<decimal>(lookBackPeriod);
_trendStreak = 0;
WarmUpPeriod = Math.Max(rsiPeriod, Math.Max(rsiPeriodStreak, rocPeriod + 1));
WarmUpPeriod = Math.Max(rsiPeriod, Math.Max(rsiPeriodStreak, lookBackPeriod));
}

/// <summary>
/// Initializes a new instance of the ConnorsRelativeStrengthIndex with specified RSI, Streak RSI,
/// and ROC periods, using a default name format based on the provided parameters.
/// and Percent Change 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)
Expand All @@ -73,67 +76,75 @@ public ConnorsRelativeStrengthIndex(int rsiPeriod, int rsiPeriodStreak, int rocP

/// <summary>
/// Gets a value indicating whether the indicator is ready for use.
/// The indicator is ready when all its components (RSI, SRSI, and ROC) are ready.
/// The indicator is ready when all its components (RSI, SRSI, and Percent Change of Daily Returns) are ready.
/// </summary>
public override bool IsReady => _rsi.IsReady && _srsi.IsReady && _recentPrices.IsReady;
public override bool IsReady => _rsi.IsReady && _srsi.IsReady && _rollingData.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 ROC).
/// This is the maximum period of all components (RSI, SRSI, and Percent Change of Daily Returns).
/// </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 the Price Percentage Change.
/// The CRSI is calculated as the average of the traditional RSI, Streak RSI, and the Percent Change of Daily Returns.
/// </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 ROC into a single value.
/// <returns>The computed CRSI value, which combines the RSI, Streak RSI, and Percent Change of Daily Returns into a single value.
/// Returns zero if the indicator is not yet ready.</returns>
protected override decimal ComputeNextValue(IndicatorDataPoint input)
{
//RSI
_rsi.Update(input);

//SRSI
var previousValue = (_recentPrices.Count > 0) ? _recentPrices[0] : input.Value;
var change = input.Value - previousValue;
// 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))
var relativeMagnitude = 0m;
var dailyReturn = 0m;
if (_previousInput != null)
{
_trendStreak = 0;
}
// Increment or decrement the trend streak based on price change direction
_trendStreak += change > 0 ? 1 : change < 0 ? -1 : 0;
_srsi.Update(new IndicatorDataPoint(input.EndTime, _trendStreak));
//SRSI
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
_trendStreak += change > 0 ? 1 : change < 0 ? -1 : 0;

//ROC
_recentPrices.Add(input.Value);
// Get the initial price from the rolling window
var initialPrice = _recentPrices[_recentPrices.Count - 1];
if (initialPrice == 0)
{
return decimal.Zero;
//Percent Change of Daily Returns
if (_previousInput.Value == 0)
{
return decimal.Zero;
}
dailyReturn = change / _previousInput.Value;
if (_rollingData.IsReady)
{
relativeMagnitude = 1m * _rollingData.Where(x => x < dailyReturn).Count() / _rollingData.Count * 100m;
}
}
var rateOfChange = (input.Value - initialPrice) / initialPrice;
_srsi.Update(new IndicatorDataPoint(input.EndTime, _trendStreak));
_previousInput = input;

//CRSI
if (IsReady)
{
return (_rsi.Current.Value + _srsi.Current.Value + rateOfChange) / 3;
_rollingData.Add(dailyReturn);
return (_rsi.Current.Value + _srsi.Current.Value + relativeMagnitude) / 3;
}
_rollingData.Add(dailyReturn);
return decimal.Zero;
}

/// <summary>
/// Resets the indicator to its initial state. This clears all internal data and resets
/// the RSI, Streak RSI, and ROC, as well as the trend streak counter.
/// the RSI, Streak RSI, and Percent Change of Daily Returns, as well as the trend streak counter.
/// </summary>
public override void Reset()
{
_rsi.Reset();
_srsi.Reset();
_recentPrices.Reset();
_rollingData.Reset();
_trendStreak = 0;
base.Reset();
}
Expand Down
8 changes: 4 additions & 4 deletions Tests/Indicators/ConnorsRelativeStrengthIndexTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class ConnorsRelativeStrengthIndexTests : CommonIndicatorTests<IndicatorD
{
protected override IndicatorBase<IndicatorDataPoint> CreateIndicator()
{
return new ConnorsRelativeStrengthIndex("test", 3, 2, 100);
return new ConnorsRelativeStrengthIndex(3, 2, 100);
}
protected override string TestFileName => "spy_crsi.csv";

Expand All @@ -45,9 +45,9 @@ public void IsReadyAfterPeriodUpdates()
{
var rsiPeriod = 2;
var rsiPeriodStreak = 3;
var rocPeriod = 4;
var crsi = new ConnorsRelativeStrengthIndex(rsiPeriod, rsiPeriodStreak, rocPeriod);
int minInputValues = Math.Max(rsiPeriod, Math.Max(rsiPeriodStreak, rocPeriod + 1));
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);
Expand Down
Loading

0 comments on commit 6cbe9b9

Please sign in to comment.