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_validde 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
nvalores válidos, en el índicen − 1. - Después:
EMA[t] = α·x[t] + (1 − α)·EMA[t−1], conα = 2/(n+1).
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:
- Diferencias
d[t] = close[t] − close[t−1](empiezan en el índice 1). - Ganancias
G = max(d, 0)y pérdidasL = max(−d, 0). - Semilla: media simple de las primeras
ndiferencias; después suavizado de Wilder (RMA,α = 1/n):avg[t] = (avg[t−1]·(n−1) + valor[t]) / n. RSI = 100 − 100/(1 + avgG/avgL). Casos aparte: plano = 50, solo ganancias = 100, solo pérdidas = 0.
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,667 → RSI = 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)sinma_typeusa 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).
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.
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 registradartol = 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.
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):
up = H[t] − H[t−1],down = L[t−1] − L[t].+DM = upsiup > downyup > 0(desigualdad estricta: el empate no genera DM); simétrico para−DM.DM[0] = 0.±DI = 100 · RMA(±DM, n) / ATR(n)(siATR == 0,DI = 0).DX = 100 · |+DI − −DI| / (+DI + −DI).- ADX = RMA de DX sembrada con la SMA de los primeros
nDX 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¶
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_validdeclarados. - EMA: semilla SMA y
α = 2/(n+1); RMA (Wilder): misma semilla,α = 1/n. - RSI válido desde
n; ATR desden − 1; ADX desde2n − 2; MACD señal desdeslow + 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_typepor 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¶
- Continúa la tabla del RSI(3) hasta t = 7 y comprueba que obtienes 83,5269.
- 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.
- 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»?
- ¿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.)