0

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;
    }
}
nop
  • 4,711
  • 6
  • 32
  • 93

1 Answers1

0

I did it myself. If you have any suggestions/improvements, please note them in the comments.

public class RelativeStrengthIndex : Indicator
{
    private readonly int _period;

    private InputData _previousInput;
    private int _index;
    private decimal _gain;
    private decimal _loss;
    private decimal _averageGain;
    private decimal _averageLoss;

    public RelativeStrengthIndex(int period)
    {
        _period = period;

        _index = 0;
        _gain = 0;
        _loss = 0;
        _averageGain = 0;
        _averageLoss = 0;
    }

    public override decimal ComputeNextValue(InputData input)
    {
        // Formula: https://stackoverflow.com/questions/38481354/rsi-vs-wilders-rsi-calculation-problems?rq=1
        _index++;

        if (_previousInput != null)
        {
            var diff = input.Value - _previousInput.Value;
            _previousInput = input;

            if (_index <= _period)
            {
                if (diff >= 0)
                {
                    _totalGain += diff;
                }
                else
                {
                    _totalLoss -= diff;
                }
            }

            if (_index < _period)
            {
                return 0;
            }
            else if (_index == _period)
            {
                _averageGain = _totalGain / _period;
                _averageLoss = _totalLoss / _period;

                decimal rs = _averageGain / _averageLoss;
                return 100 - (100 / (1 + rs));
            }
            else // if (_index >= _period + 1)
            {
                if (diff >= 0)
                {
                    _averageGain = ((_averageGain * (_period - 1)) + diff) / _period;
                    _averageLoss = (_averageLoss * (_period - 1)) / _period;
                }
                else
                {
                    _averageGain = (_averageGain * (_period - 1)) / _period;
                    _averageLoss = ((_averageLoss * (_period - 1)) - diff) / _period;
                }

                decimal rs = _averageGain / _averageLoss;
                return 100 - (100 / (1 + rs));
            }
        }

        _previousInput = input;
        return 0;
    }

    public override void Reset()
    {
        _previousInput = null;
        _index = 0;
        _gain = 0;
        _loss = 0;
        _averageGain = 0;
        _averageLoss = 0;
    }
}
nop
  • 4,711
  • 6
  • 32
  • 93