using System; namespace QuanTAlib; /// /// RMA: Relative Moving Average (also known as Wilder's Moving Average) /// RMA is similar to EMA but uses a different smoothing factor. /// /// /// Key characteristics: /// - Uses no buffer, relying only on the previous RMA value. /// - The weight of new data points (alpha) is calculated as 1 / period. /// - Provides a smoother curve compared to SMA and EMA, reacting more slowly to price changes. /// /// Calculation method: /// RMA = (Previous RMA * (period - 1) + New Data) / period /// /// Sources: /// - https://www.tradingview.com/pine-script-reference/v5/#fun_ta{dot}rma /// - https://www.investopedia.com/terms/w/wilders-smoothing.asp /// public class Rma : AbstractBase { private readonly int _period; private double _lastRma; private readonly double _alpha; private double _savedLastRma; public Rma(int period) { if (period < 1) { throw new ArgumentException("Period must be greater than or equal to 1.", nameof(period)); } _period = period; WarmupPeriod = period * 2; _alpha = 1.0 / _period; // Wilder's smoothing factor Name = $"Rma({_period})"; Init(); } public Rma(object source, int period) : this(period) { var pubEvent = source.GetType().GetEvent("Pub"); pubEvent?.AddEventHandler(source, new ValueSignal(Sub)); } public override void Init() { base.Init(); _lastRma = 0; _savedLastRma = 0; } protected override void ManageState(bool isNew) { if (isNew) { _savedLastRma = _lastRma; _lastValidValue = Input.Value; _index++; } else { _lastRma = _savedLastRma; } } protected override double Calculation() { ManageState(Input.IsNew); double rma; if (_index == 1) { rma = Input.Value; } else if (_index <= _period) { // Simple average during initial period rma = (_lastRma * (_index - 1) + Input.Value) / _index; } else { // Wilder's smoothing method rma = _alpha * (_lastRma - Input.Value) + _lastRma; } _lastRma = rma; IsHot = _index >= WarmupPeriod; return rma; } }