diff --git a/docs/indicators/indicators.md b/docs/indicators/indicators.md
index 897a417a..4ef85e51 100644
--- a/docs/indicators/indicators.md
+++ b/docs/indicators/indicators.md
@@ -23,7 +23,7 @@
|KEL - Keltner Channels||GetKeltner|||
|NATR - Normalized Average True Range||GetAtr|||
|CHN - Price Channel Indicator|||||
-|RSI - Relative Strength Index||GetRsi|||
+|RSI - Relative Strength Index|`Rsi`|GetRsi|||
|SAR - Parabolic Stop and Reverse||GetParabolicSar|||
|SRSI - Stochastic RSI||GetStochRsi|||
|STARC - Starc Bands||GetStarcBands|||
diff --git a/lib/averages/Jma.cs b/lib/averages/Jma.cs
index c8e8eba0..0d3777cb 100644
--- a/lib/averages/Jma.cs
+++ b/lib/averages/Jma.cs
@@ -32,7 +32,7 @@ public class Jma : AbstractBase
///
/// Thrown when period is less than 1.
///
- public Jma(int period, int phase = 0, double factor = 0.45)
+ public Jma(int period, int phase = 0, double factor = 0.45, int buffer = 10)
{
if (period < 1)
{
@@ -42,7 +42,7 @@ public class Jma : AbstractBase
_period = period;
_phase = Math.Clamp((phase * 0.01) + 1.5, 0.5, 2.5);
- _vsumBuff = new CircularBuffer(10);
+ _vsumBuff = new CircularBuffer(buffer);
_avoltyBuff = new CircularBuffer(65);
_beta = factor * (_period - 1) / (factor * (_period - 1) + 2);
@@ -56,7 +56,7 @@ public class Jma : AbstractBase
/// The source object to subscribe to for value updates.
/// The period over which to calculate the Jvolty.
/// The phase parameter for the JMA-style calculation.
- public Jma(object source, int period, int phase = 0) : this(period, phase)
+ public Jma(object source, int period, int phase = 0, double factor = 0.45, int buffer = 10) : this(period, phase, factor, buffer)
{
var pubEvent = source.GetType().GetEvent("Pub");
pubEvent?.AddEventHandler(source, new ValueSignal(Sub));
@@ -148,7 +148,7 @@ public class Jma : AbstractBase
_prevDet0 = det0;
double ma2 = ma1 + _phase * det0;
- double det1 = ((ma2 - _prevJma) * (1 - _alpha) * (1 - _alpha) ) + (_alpha * _alpha * _prevDet1);
+ double det1 = ((ma2 - _prevJma) * (1 - _alpha) * (1 - _alpha)) + (_alpha * _alpha * _prevDet1);
_prevDet1 = det1;
double jma = _prevJma + det1;
_prevJma = jma;
diff --git a/lib/volatility/Rsi.cs b/lib/volatility/Rsi.cs
new file mode 100644
index 00000000..9252281d
--- /dev/null
+++ b/lib/volatility/Rsi.cs
@@ -0,0 +1,62 @@
+using System;
+
+namespace QuanTAlib;
+
+///
+/// Represents a Relative Strength Index (RSI) calculator following Wilder's algorithm.
+///
+public class Rsi : AbstractBase
+{
+ private readonly Rma _avgGain;
+ private readonly Rma _avgLoss;
+ private double _prevValue, _p_prevValue;
+
+ public Rsi(int period = 14)
+ {
+ if (period < 1)
+ throw new ArgumentOutOfRangeException(nameof(period));
+ _avgGain = new(period, useSma: true);
+ _avgLoss = new(period, useSma: true);
+ _index = 0;
+ WarmupPeriod = period + 1;
+ Name = $"RSI({period})";
+ }
+
+ protected override void ManageState(bool isNew)
+ {
+ if (isNew)
+ {
+ _index++;
+ _p_prevValue = _prevValue;
+ }
+ else
+ {
+ _prevValue = _p_prevValue;
+ }
+ }
+
+ protected override double Calculation()
+ {
+ ManageState(Input.IsNew);
+
+ if (_index == 1)
+ {
+ _prevValue = Input.Value;
+ }
+
+ double change = Input.Value - _prevValue;
+ double gain = Math.Max(change, 0);
+ double loss = Math.Max(-change, 0);
+ _prevValue = Input.Value;
+
+ _avgGain.Calc(gain, IsNew: Input.IsNew);
+ _avgLoss.Calc(loss, IsNew: Input.IsNew);
+
+ double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
+
+
+ return rsi;
+
+
+ }
+}
diff --git a/lib/volatility/Rsx.cs b/lib/volatility/Rsx.cs
new file mode 100644
index 00000000..6d44c26d
--- /dev/null
+++ b/lib/volatility/Rsx.cs
@@ -0,0 +1,64 @@
+using System;
+
+namespace QuanTAlib;
+
+///
+/// Jurik's superior replacement for RSI
+///
+public class Rsx : AbstractBase
+{
+ private readonly Rma _avgGain;
+ private readonly Rma _avgLoss;
+ private readonly Jma _rsx;
+ private double _prevValue, _p_prevValue;
+
+ public Rsx(int period = 14, int phase = 0, double factor = 0.55)
+ {
+ if (period < 1)
+ throw new ArgumentOutOfRangeException(nameof(period));
+ _avgGain = new(period);
+ _avgLoss = new(period);
+ _rsx = new(8, 100, 0.25, 3);
+ _index = 0;
+ WarmupPeriod = period + 1;
+ Name = $"RSX({period})";
+ }
+
+ protected override void ManageState(bool isNew)
+ {
+ if (isNew)
+ {
+ _index++;
+ _p_prevValue = _prevValue;
+ }
+ else
+ {
+ _prevValue = _p_prevValue;
+ }
+ }
+
+ protected override double Calculation()
+ {
+ ManageState(Input.IsNew);
+
+ if (_index == 1)
+ {
+ _prevValue = Input.Value;
+ }
+
+ double change = Input.Value - _prevValue;
+ double gain = Math.Max(change, 0);
+ double loss = Math.Max(-change, 0);
+ _prevValue = Input.Value;
+
+ _avgGain.Calc(gain, IsNew: Input.IsNew);
+ _avgLoss.Calc(loss, IsNew: Input.IsNew);
+
+ double rsi = (_avgLoss.Value > 0) ? 100 - (100 / (1 + (_avgGain.Value / _avgLoss.Value))) : 100;
+ double rsx = _rsx.Calc(rsi, Input.IsNew);
+
+ return rsx;
+
+
+ }
+}
diff --git a/quantower/Averages/MacdIndicator.cs b/quantower/Averages/MacdIndicator.cs
index b8eb4f0b..56365f3f 100644
--- a/quantower/Averages/MacdIndicator.cs
+++ b/quantower/Averages/MacdIndicator.cs
@@ -62,7 +62,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
SignalSeries = new(name: $"SIGNAL", color: Color.Yellow, width: 2, style: LineStyle.Solid);
HistogramSeries = new(name: $"HISTOGRAM", color: Color.White, width: 2, style: LineStyle.Solid);
HistSlopeSeries = new(name: $"SLOPE", color: Color.Transparent, width: 2, style: LineStyle.Solid);
-
+ HistSlopeSeries.Visible = false;
AddLineSeries(MainSeries);
AddLineSeries(SignalSeries);
@@ -119,7 +119,7 @@ public class MacdIndicator : Indicator, IWatchlistIndicator
for (int i = rightIndex; i < leftIndex; i++)
{
int barX = (int)converter.GetChartX(this.HistoricalData.Time(i));
- int barY = (int)converter.GetChartY(HistogramSeries![i]);
+ int barY = (int)converter.GetChartY(HistogramSeries![i]*2.0);
int barY0 = (int)converter.GetChartY(0);
int HistBarWidth = this.CurrentChart.BarsWidth - 2;
diff --git a/quantower/Volatility/RsiIndicator.cs b/quantower/Volatility/RsiIndicator.cs
new file mode 100644
index 00000000..fd0a4eec
--- /dev/null
+++ b/quantower/Volatility/RsiIndicator.cs
@@ -0,0 +1,67 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class RsiIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Periods", sortIndex: 1, 1, 2000, 1, 0)]
+ public int Periods { get; set; } = 14;
+
+ [InputParameter("Data source", sortIndex: 5, variants: [
+ "Open", SourceType.Open,
+ "High", SourceType.High,
+ "Low", SourceType.Low,
+ "Close", SourceType.Close,
+ "HL/2 (Median)", SourceType.HL2,
+ "OC/2 (Midpoint)", SourceType.OC2,
+ "OHL/3 (Mean)", SourceType.OHL3,
+ "HLC/3 (Typical)", SourceType.HLC3,
+ "OHLC/4 (Average)", SourceType.OHLC4,
+ "HLCC/4 (Weighted)", SourceType.HLCC4
+ ])]
+ public SourceType Source { get; set; } = SourceType.Close;
+
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Rsi? rsi;
+ protected string? SourceName;
+ protected LineSeries? RsiSeries;
+ public int MinHistoryDepths => Periods + 1;
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public RsiIndicator()
+ {
+ Name = "RSI - Relative Strength Index";
+ Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions.";
+ SeparateWindow = true;
+ SourceName = Source.ToString();
+ RsiSeries = new($"RSI {Periods}", Color.Blue, 2, LineStyle.Solid);
+ AddLineSeries(RsiSeries);
+ }
+
+ protected override void OnInit()
+ {
+ rsi = new Rsi(Periods);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TValue input = this.GetInputValue(args, Source);
+ rsi!.Calc(input);
+
+ RsiSeries!.SetValue(rsi.Value);
+ RsiSeries!.SetMarker(0, Color.Transparent);
+ }
+
+ public override string ShortName => $"RSI ({Periods}:{SourceName})";
+
+#pragma warning disable CA1416 // Validate platform compatibility
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintSmoothCurve(args, RsiSeries!, rsi!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}
diff --git a/quantower/Volatility/RsxIndicator.cs b/quantower/Volatility/RsxIndicator.cs
new file mode 100644
index 00000000..5158bc77
--- /dev/null
+++ b/quantower/Volatility/RsxIndicator.cs
@@ -0,0 +1,67 @@
+using System.Drawing;
+using TradingPlatform.BusinessLayer;
+
+namespace QuanTAlib;
+
+public class RsxIndicator : Indicator, IWatchlistIndicator
+{
+ [InputParameter("Rsi Period", sortIndex: 1, 1, 2000, 1, 0)]
+ public int Period { get; set; } = 14;
+
+ [InputParameter("Data source", sortIndex: 5, variants: [
+ "Open", SourceType.Open,
+ "High", SourceType.High,
+ "Low", SourceType.Low,
+ "Close", SourceType.Close,
+ "HL/2 (Median)", SourceType.HL2,
+ "OC/2 (Midpoint)", SourceType.OC2,
+ "OHL/3 (Mean)", SourceType.OHL3,
+ "HLC/3 (Typical)", SourceType.HLC3,
+ "OHLC/4 (Average)", SourceType.OHLC4,
+ "HLCC/4 (Weighted)", SourceType.HLCC4
+ ])]
+ public SourceType Source { get; set; } = SourceType.Close;
+
+ [InputParameter("Show cold values", sortIndex: 21)]
+ public bool ShowColdValues { get; set; } = true;
+
+ private Rsx? rsx;
+ protected string? SourceName;
+ protected LineSeries? RsxSeries;
+ public int MinHistoryDepths => Period + 1;
+ int IWatchlistIndicator.MinHistoryDepths => MinHistoryDepths;
+
+ public RsxIndicator()
+ {
+ Name = "RSX - Jurik Trend Strengt Index";
+ Description = "Measures the speed and magnitude of recent price changes to evaluate overbought or oversold conditions.";
+ SeparateWindow = true;
+ SourceName = Source.ToString();
+ RsxSeries = new($"RSX {Period}", Color.Blue, 2, LineStyle.Solid);
+ AddLineSeries(RsxSeries);
+ }
+
+ protected override void OnInit()
+ {
+ rsx = new(Period);
+ base.OnInit();
+ }
+
+ protected override void OnUpdate(UpdateArgs args)
+ {
+ TValue input = this.GetInputValue(args, Source);
+ rsx!.Calc(input);
+
+ RsxSeries!.SetValue(rsx.Value);
+ RsxSeries!.SetMarker(0, Color.Transparent);
+ }
+
+ public override string ShortName => $"RSX ({Period}:{SourceName})";
+
+#pragma warning disable CA1416 // Validate platform compatibility
+ public override void OnPaintChart(PaintChartEventArgs args)
+ {
+ base.OnPaintChart(args);
+ this.PaintSmoothCurve(args, RsxSeries!, rsx!.WarmupPeriod, showColdValues: ShowColdValues, tension: 0.2);
+ }
+}