RSI seems simple until you try to implement it correctly. The formula everyone knows: RSI = 100 - (100 / (1 + RS)), where RS = Average Gain / Average Loss. The part that's almost always wrong in open-source implementations: how you calculate "Average Gain" and "Average Loss."
The wrong way (simple moving average)
// WRONG - This is what most tutorials show const avgGain = gains.slice(-14).reduce((a, b) => a + b, 0) / 14; const avgLoss = losses.slice(-14).reduce((a, b) => a + b, 0) / 14;
This produces RSI values that don't match TradingView, MT4, or any professional platform.
The correct way (Wilder's Exponential Smoothing)
Wilder's method works like this:
- Initial average gain/loss: simple average of the first 14 periods
- Every subsequent period:
avgGain = (prevAvgGain × 13 + currentGain) / 14
This is a specific form of exponential smoothing with alpha = 1/14. The smoothed values "remember" more history, making Wilder's RSI slightly smoother and slower to react than a naive implementation.
let avgGain = gains.slice(0, 14).reduce((a, b) => a + b, 0) / 14;
let avgLoss = losses.slice(0, 14).reduce((a, b) => a + b, 0) / 14;
for (let i = 14; i < gains.length; i++) {
avgGain = (avgGain * 13 + gains[i]) / 14;
avgLoss = (avgLoss * 13 + losses[i]) / 14;
rsiValues[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
}ADX: the hardest indicator to implement correctly
ADX is significantly more complex than RSI. It involves three sequential calculations:
- True Range (TR):
max(high - low, |high - prevClose|, |low - prevClose|)— the inter-candle gap terms (|high - prevClose|and|low - prevClose|) are the part most implementations miss. Without them, gapped markets (Crypto on weekends) produce incorrect ATR and ADX values.
- Directional Movement (+DM / -DM):
upMove = high - prevHigh,downMove = prevLow - low. If upMove > 0 and upMove > downMove:+DM = upMove, else 0. If downMove > 0 and downMove > upMove:-DM = downMove, else 0.
- Wilder Smoothing on TR, +DM, -DM: Uses the same smoothing as RSI:
smoothedTR = prevSmoothedTR - (prevSmoothedTR / 14) + currentTR.
- +DI / -DI:
+DI = (smoothed+DM / smoothedTR) × 100.
- DX:
|+DI - -DI| / (+DI + -DI) × 100.
- ADX: Wilder smoothing on DX values over 14 periods.
When you implement all six steps correctly, the ADX value matches TradingView's reading to within rounding error. When any step is wrong — particularly the inter-candle gap terms in TR — the ADX diverges, especially on assets with significant overnight gaps or weekend crypto moves.
---
