Saltar a contenido

Indicadores paso a paso: ejemplos numéricos

Este capítulo calcula a mano los indicadores más usados del banco —SMA, EMA, RSI, ATR, Bollinger, MACD y ADX— sobre una misma serie de 12 barras. El objetivo no es memorizar fórmulas, sino ver dónde empieza a valer cada indicador (first_valid), con qué semilla arranca y por qué dos motores correctos pueden dar números distintos.

Qué vas a aprender

  • Las convenciones canónicas del proyecto (§5.1 de la guía técnica, docs/FEATURES.md).
  • A calcular SMA, EMA (semilla SMA, alpha = 2/(n+1)), RSI de Wilder, ATR, Bollinger, MACD y ADX.
  • A leer el first_valid de cada uno y su regla.
  • Las diferencias entre motores: semilla de la EMA en Nautilus, RSI ×100, ma_type=wilder, Donchian inclusivo.
  • Qué hacer con los NaN.

De dónde salen los números

Todas las cifras de este capítulo se han calculado con los kernels canónicos del proyecto (finazbench/features/canonical.py, ext_oscillators_a.py::macd, ext_oscillators_b.py::dmi) sobre la serie de ejemplo, redondeadas a 4 decimales. Usamos periodos pequeños (3 y 5) para que quepan en 12 barras; con los periodos habituales (14, 20…) las reglas son idénticas.


0. La serie de ejemplo

t close high low
0 10,0 10,3 9,8
1 10,5 10,7 10,1
2 10,2 10,6 9,9
3 10,8 11,1 10,6
4 11,0 11,2 10,6
5 10,7 11,2 10,5
6 11,3 11,6 11,0
7 11,6 11,8 11,2
8 11,2 11,6 11,0
9 11,9 12,2 11,6
10 12,1 12,3 11,7
11 11,8 12,1 11,6

1. SMA: media simple

Fórmula: SMA(n)[t] = (x[t−n+1] + … + x[t]) / n, sobre n observaciones válidas y consecutivas. Un NaN no se sustituye por cero.

first_valid = n − 1. Con n = 3: índice 2.

t close SMA(3) Cálculo
0 10,0 NaN faltan barras
1 10,5 NaN faltan barras
2 10,2 10,2333 (10,0 + 10,5 + 10,2) / 3
3 10,8 10,5000 (10,5 + 10,2 + 10,8) / 3
4 11,0 10,6667
5 10,7 10,8333
6 11,3 11,0000
7 11,6 11,2000

Diferencia entre motores

VectorTA calcula la SMA con suma corrida: sobre 632k barras acumula hasta 1,8e-9 de error absoluto. Por eso la tolerancia registrada de sma es atol = 1e-9, no 1e-12.


2. EMA: media exponencial con semilla SMA

Fórmula canónica:

  • Semilla = SMA de los primeros n valores válidos, en el índice n − 1.
  • Después: EMA[t] = α·x[t] + (1 − α)·EMA[t−1], con α = 2/(n+1).

SMA de 20 y EMA de 20 sobre la misma serie

Figura 1. SMA(20) y EMA(20) sobre la misma serie; la EMA arranca en el índice 19 con la SMA de los 20 primeros cierres.

Con n = 3, α = 2/4 = 0,5. first_valid = n − 1 = 2.

t close EMA(3) canónica Cálculo
2 10,2 10,2333 semilla = SMA(3)
3 10,8 10,5167 0,5·10,8 + 0,5·10,2333
4 11,0 10,7583 0,5·11,0 + 0,5·10,5167
5 10,7 10,7292
6 11,3 11,0146
7 11,6 11,3073
8 11,2 11,2536
9 11,9 11,5768

2.1 La misma EMA en otros motores

Motor Semilla Valor en t = 2 t = 3 t = 4
Canónica SMA de los 3 primeros 10,2333 10,5167 10,7583
Nautilus (nt_seed_first_value) primer valor (EMA[0] = 10,0) y recursión desde 0 10,2250 10,5125 10,7563
Diferencia 0,0083 0,0042 0,0021
VectorTA batch (vta_running_mean_masked) media corrida desde el índice 0, sin NaN coincide con la canónica desde n − 1; el proyecto pone la máscara

(Valores de Nautilus comprobados para este manual con ExponentialMovingAverage(3).update_raw de nautilus-trader 1.231.0 dentro de la imagen finaz/bench:0.2.0-x86-64-v3: 10,0 → 10,25 → 10,225 → 10,5125 → 10,75625; initialized pasa a True en t = 2.)

La firma de un error de semilla

La diferencia se reduce a la mitad en cada barra (factor 1 − α). Con n = 20 decae más despacio, pero decae: de 1e-1 en la barra 19 a menos de 1e-9 a las 5.000 barras. Es exactamente lo que muestra el informe de paridad de P01 nativo: primera divergencia en la barra 19.


3. RSI de Wilder

Fórmula canónica:

  1. Diferencias d[t] = close[t] − close[t−1] (empiezan en el índice 1).
  2. Ganancias G = max(d, 0) y pérdidas L = max(−d, 0).
  3. Semilla: media simple de las primeras n diferencias; después suavizado de Wilder (RMA, α = 1/n): avg[t] = (avg[t−1]·(n−1) + valor[t]) / n.
  4. RSI = 100 − 100/(1 + avgG/avgL). Casos aparte: plano = 50, solo ganancias = 100, solo pérdidas = 0.

Precio y RSI de 14 con bandas 30 y 70

Figura 2. RSI(14) con las zonas de sobrecompra (> 70) y sobreventa (< 30); primer valor válido en el índice 14.

first_valid = n (porque las diferencias empiezan en 1). Con n = 3: índice 3.

t close d G L avgG avgL RSI(3)
1 10,5 +0,5 0,5 0 NaN
2 10,2 −0,3 0 0,3 NaN
3 10,8 +0,6 0,6 0 (0,5+0+0,6)/3 = 0,3667 (0+0,3+0)/3 = 0,1000 78,5714
4 11,0 +0,2 0,2 0 (0,3667·2+0,2)/3 = 0,3111 (0,1·2+0)/3 = 0,0667 82,3529
5 10,7 −0,3 0 0,3 0,2074 0,1444 58,9474
6 11,3 +0,6 77,8409
7 11,6 +0,3 83,5269

Comprobación de t = 3: RS = 0,3667 / 0,1 = 3,667RSI = 100 − 100/4,667 = 78,57.

RSI en Nautilus: ×100 y una barra antes

RelativeStrengthIndex de Nautilus 1.231.0 devuelve 0..1 y arranca en n − 1 (13 para n = 14), variante nt_init_period_minus_1_scaled_0_1. El proveedor del proyecto multiplica por 100 (conversión de unidad): sin eso, un umbral de 30 no se cruzaría nunca y P02 no operaría.

Discrepancia abierta: el RSI de Nautilus no converge con su ma_type por defecto

El ejemplo examples/api/05_indicators_rsi14_nvda.response.json (RSI 14 sobre NVDA 1min, 631.735 barras) termina así en la última barra (2026-09-14 20:00Z): 25,98 en VectorTA y 21,65 en Nautilus. Tras más de 600.000 barras una diferencia de semilla ya habría desaparecido, así que no es la semilla. La causa, comprobada para este manual dentro de la imagen finaz/bench:0.2.0-x86-64-v3 (nautilus-trader 1.231.0) sobre 5.000 barras sintéticas:

  • RelativeStrengthIndex(14) sin ma_type usa por defecto la media exponencial (MovingAverageType.EXPONENTIAL, α = 2/(n+1)), no la de Wilder (α = 1/n). Diferencia máxima con el RSI de Wilder en las últimas 100 barras: ≈15 puntos; no converge nunca.
  • Con ma_type = MovingAverageType.WILDER, la misma diferencia es 2,8e-14: solo queda la semilla, que se disipa.

El proveedor del proyecto (finazbench/features/provider_nautilus.py) instancia el indicador con RelativeStrengthIndex(period), es decir, con la media exponencial. Es coherente con que la fila rsi de Nautilus en el benchmark de primitivas salga PARITY_FAIL y con que, en ese ejemplo, parity venga null. El texto de examples/api/README.md («~1e-13 al final») y la clasificación «semilla distinta, converge» de docs/FEATURES.md no cuadran con esa respuesta: tómalo como una discrepancia abierta del repositorio, no como una propiedad del RSI.


4. TR y ATR

True Range: TR[0] = H[0] − L[0]; después TR[t] = max(H−L, |H − C[t−1]|, |L − C[t−1]|). ATR(n) = RMA(TR, n) (semilla SMA de los n primeros TR, luego α = 1/n).

ATR de 10 y Supertrend con cambios de dirección

Figura 3. El ATR mide la amplitud típica de las barras; Supertrend lo usa para colocar una banda que cambia de lado cuando el cierre la cruza.

Como TR vale desde el índice 0, first_valid(ATR) = n − 1 (no n). Con n = 3: índice 2.

t H L C[t−1] H−L |H−Cprev| |L−Cprev| TR ATR(3)
0 10,3 9,8 0,5 0,5 NaN
1 10,7 10,1 10,0 0,6 0,7 0,1 0,7 NaN
2 10,6 9,9 10,5 0,7 0,1 0,6 0,7 0,6333
3 11,1 10,6 10,2 0,5 0,9 0,4 0,9 0,7222
4 11,2 10,6 10,8 0,6 0,4 0,2 0,6 0,6815
5 11,2 10,5 11,0 0,7 0,2 0,5 0,7 0,6877

Cálculo: ATR[2] = (0,5 + 0,7 + 0,7)/3 = 0,6333; ATR[3] = (0,6333·2 + 0,9)/3 = 0,7222.

Diferencias entre motores

VectorTA reproduce el ATR canónico (4,4e-16). Nautilus usa nt_seed_first_value_sma_inner: misma validez (n − 1), otra semilla que converge.


5. Bandas de Bollinger

Fórmula: middle = SMA(n); upper/lower = middle ± k·stddev(n) con desviación poblacional (ddof = 0) calculada ventana a ventana. first_valid = n − 1.

Bandas de Bollinger de 20 y 2 con reentradas

Figura 4. Bandas de Bollinger(20, 2): media ± 2 desviaciones; las reentradas son la señal de reversión de P04.

Con n = 3, k = 2:

t close middle stddev upper lower
2 10,2 10,2333 0,2055 10,6443 9,8224
3 10,8 10,5000 0,2449 10,9899 10,0101
4 11,0 10,6667 0,3399 11,3465 9,9868
5 10,7 10,8333 0,1247 11,0828 10,5839
6 11,3 11,0000 0,2449 11,4899 10,5101

stddev[2] = √(((10,0−10,2333)² + (10,5−10,2333)² + (10,2−10,2333)²)/3) = 0,2055.

Dos trampas documentadas

  • VectorTA usa E[x²] − E[x]², que pierde ~6 dígitos cuando la media es grande frente a la dispersión: tolerancia registrada rtol = 1e-5. Además devuelve (upper, middle, lower) y el contrato fija (middle, upper, lower): se reordena.
  • Nautilus centra las bandas en el precio típico (H+L+C)/3 (nt_typical_price): es una definición distinta y no converge nunca.

6. MACD

Fórmula (P03): macd = EMA(fast) − EMA(slow); signal = EMA(signal) de macd, sembrada con la SMA de los primeros signal valores finitos de macd; hist = macd − signal.

MACD, señal e histograma

Figura 5. MACD (EMA12 − EMA26), su señal (EMA9) y el histograma; los cruces de signo del histograma son los cruces MACD/señal.

first_valid: macd en slow − 1; signal e hist en slow + signal − 2. Con los defaults 12/26/9 son 25 y 33. En el ejemplo usamos 3/5/3: macd desde 4, signal desde 6.

t EMA(3) EMA(5) macd signal hist
4 10,7583 10,5000 0,2583 NaN NaN
5 10,7292 10,5667 0,1625 NaN NaN
6 11,0146 10,8111 0,2035 0,2081 −0,0046
7 11,3073 11,0741 0,2332 0,2207 0,0126
8 11,2536 11,1160 0,1376 0,1791 −0,0415
9 11,5768 11,3774 0,1995 0,1893 0,0102

signal[6] = (0,2583 + 0,1625 + 0,2035)/3 = 0,2081 (semilla SMA); después α = 2/(3+1) = 0,5: signal[7] = 0,5·0,2332 + 0,5·0,2081 = 0,2207.

MacdStream de VectorTA

Sus valores coinciden, pero la línea macd arranca en 33 en vez de 25 porque espera a la señal. Perder 8 barras válidas es cambiar el first_valid, así que no se usa para el carril incremental.


7. DMI y ADX

Fórmula canónica (dmi):

  1. up = H[t] − H[t−1], down = L[t−1] − L[t]. +DM = up si up > down y up > 0 (desigualdad estricta: el empate no genera DM); simétrico para −DM. DM[0] = 0.
  2. ±DI = 100 · RMA(±DM, n) / ATR(n) (si ATR == 0, DI = 0).
  3. DX = 100 · |+DI − −DI| / (+DI + −DI).
  4. ADX = RMA de DX sembrada con la SMA de los primeros n DX válidos.

first_valid: DI en n − 1; ADX en 2n − 2 (26 para n = 14). Con n = 3: DI desde 2, ADX desde 4.

t +DI(3) −DI(3) ADX(3)
2 21,0526 10,5263 NaN
3 35,3846 6,1538 NaN
4 29,8913 4,3478 59,4356
5 19,7487 7,7199 54,2207
6 29,5171 4,6663 60,3799
7 30,5988 3,3437 67,0192

La candidata de VectorTA arranca una barra después

vector_ta.adx/di arrancan en 27 (canónico 26) y sus valores difieren incluso alineando una barra: es otra semilla (VARIANT). El proyecto sirve dmi con su cálculo canónico.


8. Resumen de first_valid

Canal de Donchian de 20 con rupturas

Figura 6. Donchian(20) con ventana estrictamente anterior: el cierre de t puede salir del canal, y eso es la ruptura.

Indicador Regla Con n = 3 Con los defaults habituales
SMA(n) n − 1 2 SMA(20) → 19
EMA(n) n − 1 2 EMA(20) → 19
RSI(n) n 3 RSI(14) → 14 (Nautilus: 13)
ATR(n) n − 1 2 ATR(14) → 13
Bollinger(n, k) n − 1 2 (20, 2) → 19
MACD(f, s, g) s − 1 / s + g − 2 4 / 6 (3/5/3) 12/26/9 → 25 / 33
DMI/ADX(n) DI n − 1, ADX 2n − 2 2 / 4 14 → 13 / 26
Donchian(n) n (ventana estrictamente anterior) 3 20 → 20 (Nautilus inclusivo: 19)

9. ¿Y los NaN?

Regla de oro

Los tres proveedores rechazan NaN e infinitos en la entrada con un ValueError que nombra la columna y el índice. VectorTA, sin esa comprobación, devolvería un número plausible y falso.

Dentro de la salida, NaN significa «todavía no hay valor» (calentamiento) y viaja con una máscara de validez (FeatureResult.valid_mask), nunca reconstruida por convención. Rellenar el calentamiento con ceros fabricaría señales: un cruce de medias «se dispararía» en cuanto la media deja de calentar. Por eso los cruces exigen los cuatro valores válidos (a[t] > b[t] y a[t−1] <= b[t−1]).


Resumen

  • Cada indicador tiene una fórmula canónica, una semilla y un first_valid declarados.
  • EMA: semilla SMA y α = 2/(n+1); RMA (Wilder): misma semilla, α = 1/n.
  • RSI válido desde n; ATR desde n − 1; ADX desde 2n − 2; MACD señal desde slow + signal − 2.
  • Las diferencias entre motores son de semilla (convergen: EMA de Nautilus) o de definición (no convergen: Bollinger de precio típico, Donchian inclusivo y, con el ma_type por defecto, el RSI de Nautilus, que suaviza con media exponencial en vez de Wilder).
  • Los NaN de entrada se rechazan; los de calentamiento viajan con máscara.

Para practicar

  1. Continúa la tabla del RSI(3) hasta t = 7 y comprueba que obtienes 83,5269.
  2. Calcula la EMA(3) con semilla «primer valor» hasta t = 8 y verifica que la diferencia con la canónica se divide por dos en cada barra.
  3. Con la serie de ejemplo, calcula el Donchian(3) superior en t = 5 con ventana estrictamente anterior y con ventana inclusiva. ¿Cuál permitiría a P11 «romper el máximo»?
  4. ¿Cuántas barras de historia necesita un MACD 12/26/9 antes de la primera señal válida? ¿Y un stochrsi{14,14,3,3}? (Pista: capítulo de primitivas de fase B.)