The disadvantage of the code below is that it calculates the RSI for all prices at once. What if I have to add a new price? It's going to recalculate everything just because one additional price in the array. It will take longer than if I backup AverageGain/AverageLoss.
public class RSI
{
private readonly int _period;
public RSI(int period)
{
_period = period;
}
public decimal[] Calculate(decimal[] prices)
{
var rsi = new decimal[prices.Length];
decimal gain = 0m;
decimal loss = 0m;
rsi[0] = 0m;
for (int i = 1; i <= _period; ++i)
{
var diff = prices[i] - prices[i - 1];
if (diff >= 0)
{
gain += diff;
}
else
{
loss -= diff;
}
}
decimal avrg = gain / _period;
decimal avrl = loss / _period;
decimal rs = gain / loss;
rsi[_period] = 100m - (100m / (1m + rs));
for (int i = _period + 1; i < prices.Length; ++i)
{
var diff = prices[i] - prices[i - 1];
if (diff >= 0)
{
avrg = ((avrg * (_period - 1)) + diff) / _period;
avrl = (avrl * (_period - 1)) / _period;
}
else
{
avrl = ((avrl * (_period - 1)) - diff) / _period;
avrg = (avrg * (_period - 1)) / _period;
}
rs = avrg / avrl;
rsi[i] = 100m - (100m / (1m + rs));
}
return rsi;
}
}
My idea is to make something that remembers the previous input and calculate the RSI based on it, just like this one. Right now, this code is not working because _period
is not involved at all. Those people in that github made it, but I'm still struggling to do it because of all these inherited classes/interfaces. I don't want to implement all of the interfaces/abstract classes. The one I extracted below are enough for me.
// Trying to achieve this:
var candles = _client.GetKlines(bot.Symbol, KlineInterval.OneHour).Data.ToList();
RelativeStrengthIndex rsi = new RelativeStrengthIndex(12);
for (int i = 0; i < candles.Count - 1; i++)
{
var result = rsi.ComputeNextValue(new InputData(candles[i].Close));
Console.WriteLine($"RSI(12) = {result}");
}
// New candle data
var newResult = rsi.ComputeNextValue(new InputData(0.01256m));
Console.WriteLine($"RSI(12) = {newResult}");
public abstract class Indicator
{
public abstract decimal ComputeNextValue(InputData input);
public abstract void Reset();
}
public class InputData
{
public decimal Value { get; private set; }
public InputData(decimal value)
{
Value = value;
}
}
public class RelativeStrengthIndex : Indicator
{
private int _period;
private InputData _previousInput;
public decimal AverageGain { get; private set; }
public decimal AverageLoss { get; private set; }
public RelativeStrengthIndex(int period)
{
_period = period;
}
public override decimal ComputeNextValue(InputData input)
{
if (_previousInput != null && input.Value >= _previousInput.Value)
{
AverageGain = input.Value - _previousInput.Value;
AverageLoss = 0m;
}
else if (_previousInput != null && input.Value < _previousInput.Value)
{
AverageGain = 0m;
AverageLoss = _previousInput.Value - input.Value;
}
_previousInput = input;
if (AverageLoss == 0m)
{
return 100m;
}
var rs = AverageGain / AverageLoss;
return 100m - (100m / (1 + rs));
}
public override void Reset()
{
_previousInput = default;
}
}