Avanzado: ¿cómo funcionan los indicadores?
Aprende del código de SMA, EMA, RSI, MACD, ATR y las bandas de Bollinger y crea una variante propia.
Es más fácil adaptar un indicador si entiendes cómo obtiene su valor. ¿Qué entra en el cálculo? ¿Qué recuerda para la siguiente vela? ¿Qué cambia al usar otra media u otra fuente de precios? El código fuente también responde a estas preguntas.
La biblioteca ta de PyneCore está escrita en Pyne code. Sus variables Persistent, llamadas y condiciones utilizan el funcionamiento aprendido en el libro. Todo el código está disponible, de modo que puedes seguir un indicador incorporado igual que tu programa.
Este capítulo avanzado enseña a leer código fuente y a experimentar. No necesitas entender toda la biblioteca de una vez. Empieza por una función corta y sigue las llamadas en las que se basa.
Abre la implementación real
- Abre
moving_averages.pyen PyneIDE. - En
ta.ema(close, 10), mantén Command en macOS o Ctrl en Windows o Linux y haz clic enema. También sirve Go to Definition del menú contextual. - El editor abre
ta.pydel PyneCore instalado en la definición deema. - Lee la descripción y después el cuerpo. Si llama a otra función, puedes seguir hasta su definición.

Ya probaste los parámetros y el salto a definiciones en Ayuda mientras escribes código. La misma operación abre RSI, MACD y las demás funciones. La referencia completa de ta ayuda a encontrar funciones e interpretar parámetros.
El código de biblioteca y Edge: al principio de ta.py encontrarás @pyne lib. La biblioteca no está escrita con las restricciones de Edge: también utiliza herramientas generales de Python, funciones auxiliares internas y otras anotaciones de tipo. Su ejecución vela a vela y la gestión del estado te resultarán conocidas. PyneFloat es una anotación numérica de la biblioteca y na_float representa un número ausente.
Los fragmentos siguientes muestran los cálculos internos; no son programas Edge completos. Para tu versión Edge, escribe el algoritmo comprendido con las herramientas permitidas. Conserva el archivo ta.py instalado y experimenta en archivos propios.
SMA: la suma que hay detrás de la fórmula
Dos líneas del cálculo de ta.sma:
length = int(length)
return lib_math.sum(source, length) / lengthLa media es la suma de la ventana dividida entre su cantidad de elementos. Después de convertir la longitud en entero, lib_math.sum realiza la suma. El estado no está en el cuerpo de sma, sino en el cálculo de suma llamado. Si sigues la llamada a través de math.py, llegas a _math_stateful.py.
Conviene recordarlo al leer código: una función corta también puede tener estado. El funcionamiento de las funciones llamadas forma parte del cálculo. La media móvil escrita a mano sumaba los valores de la misma ventana con un bucle propio.
EMA y RMA: actualizar la media conservada
Tras el calentamiento, EMA realiza este paso:
last_val = last_val + alpha * (source - last_val)
return last_vallast_val es la media anterior y source el valor actual. Modificamos la media con una parte de la diferencia entre ambos. En EMA, alpha vale 2 / (length + 1). Con longitud diez añadimos aproximadamente el 18,18 por ciento de la diferencia entre el precio nuevo y la media anterior.
En el código, alpha y last_val son Persistent. Inicialmente last_val está ausente, así que la función intenta iniciarlo con una SMA de igual longitud:
if not (last_val == last_val):
last_val = sma(source, length)
return last_valMientras no haya suficientes datos para la SMA, el resultado sigue ausente. Una vez obtenida la media inicial, actualizamos el valor anterior. not (last_val == last_val) comprueba la ausencia: un valor ausente no es igual a sí mismo. En tus ejemplos usabas na(value) para esto.
RMA, el suavizado de Wilder, empieza de forma similar pero se actualiza de otro modo:
last_val = (last_val * (length - 1) + source) / length
return last_valCon longitud diez, el valor nuevo pesa una décima parte. EMA le da más peso con la misma longitud. Por eso reaccionan de forma distinta a la misma serie de precios.
El código completo también muestra cómo trata entradas ausentes y el caso simplificado de longitud uno. Dos versiones con la misma fórmula pueden diferir por el valor inicial, el tratamiento de ausencias o las velas en que se ejecutan. También importa el orden de las operaciones de coma flotante; consérvalo para comparar exactamente.
RSI: dos variaciones suavizadas por separado
RSI trata por separado aumentos y descensos de la fuente. Estas son las líneas principales en PyneCore:
rma_u = rma(builtins.max(source - prev_src, 0.0), length)
rma_d = rma(builtins.max(prev_src - source, 0.0), length)
prev_src = sourceprev_src es el valor válido anterior de la fuente, conservado en Persistent. Si el precio sube de 100 a 103, el cambio ascendente es 3 y el descendente 0. Si baja de 103 a 101, son 0 y 2. RMA suaviza cada serie por separado; los dos lugares de llamada tienen estados independientes.
prev_src se actualiza después de calcular las diferencias. Es el mismo orden del ejercicio de conservar el valor anterior.
La relación entre las dos variaciones suavizadas produce RSI:
if rma_d <= 1e-10:
return 100.0
if rma_u <= 1e-10:
return 0.0
return 100 - 100 / (1 + rma_u / rma_d)La ausencia de movimiento descendente da 100; la de ascendente, 0. El código también trata valores muy pequeños y comprueba primero la parte descendente: si ambas están por debajo del umbral, devuelve 100. Estos detalles no se deducen de una fórmula aislada.
El primer precio válido aún no genera una variación. RSI lo guarda y solo inicia los dos suavizados con el siguiente. Su calentamiento depende de la cantidad de variaciones. RSI no muestra probabilidades: 70 no significa un 70 por ciento de posibilidades de que suba el precio.
MACD: un nuevo indicador a partir de medias
MACD calcula la diferencia entre dos EMA y después otra EMA de esa diferencia. Los pasos principales, sin las comprobaciones de ausencia:
fast = ema(source, fastlen)
slow = ema(source, slowlen)
macd_val = fast - slow
signal = ema(macd_val, siglen)Los tres valores devueltos:
return macd_val, signal, macd_val - signalLa línea MACD es la diferencia entre la media rápida y la lenta. La línea de señal es su versión suavizada. El histograma es la diferencia entre ambas, por lo que vale cero cuando coinciden.
Una llamada devuelve tres resultados, que puedes recoger en tres variables:
macd_line, signal_line, histogram = ta.macd(close, 12, 26, 9)El orden importa: línea MACD, señal, histograma. Mientras no estén listas las dos medias de precios, la diferencia está ausente. Después la señal necesita su propio calentamiento. Las ausencias iniciales de un indicador compuesto también proceden de sus cálculos internos.
ATR: el salto de precio también cuenta en el rango
ATR tiene una implementación corta:
return rma(tr(True), length)Ya conoces RMA; aquí suaviza True Range, o rango verdadero. Es el máximo de estas tres distancias:
- diferencia entre máximo y mínimo actuales;
- distancia del máximo actual al cierre anterior;
- distancia del mínimo actual al cierre anterior.
Si el cierre anterior es 100, el mínimo actual 108 y el máximo 110, el rango propio de la vela es solo 2. El verdadero es max(2, 10, 8), es decir, 10: también considera el salto desde el cierre anterior.
En la primera vela, cuando falta el cierre anterior, tr(True) utiliza high - low. Internamente lee el cierre anterior de un dato que proporciona el entorno. ATR muestra la magnitud del movimiento en unidades de precio, no su dirección.
Bandas de Bollinger: media y desviación típica
La línea central de ta.bb es una SMA. Los límites se separan de ella un múltiplo de la desviación típica. El código calcula primero la desviación y la media:
std_dev = stdev(series, length)
middle = sma(series, length)Con un centro válido, devuelve:
std_dev *= mult
return middle, middle + std_dev, middle - std_devLa desviación típica describe la dispersión de los valores de la ventana alrededor de la media. Con centro 100, desviación 3 y multiplicador 2, el límite superior es 106 y el inferior 94. También devuelve tres valores, pero en otro orden que MACD: centro, superior, inferior.
Tu propia versión: reproduce primero la original
Crea bands_workshop.py. Nuestra función construye las bandas con partes más sencillas de la biblioteca. Calculamos también el límite superior de ta.bb para comparar.
"""
@pyne edge
"""
from pynecore.lib import close, color, input, plot, script, ta
def custom_bands(source: float, length: int, multiplier: float) -> tuple[float, float, float]:
middle = ta.sma(source, length)
deviation = ta.stdev(source, length)
upper = middle + multiplier * deviation
lower = middle - multiplier * deviation
return middle, upper, lower
@script.indicator("Bands Workshop", overlay=True)
def main(
length: int = input.int(20, title="Length", minval=2, maxval=200),
multiplier: float = input.float(2.0, title="Deviation multiplier", minval=0.1, maxval=5.0),
):
builtin_middle, builtin_upper, builtin_lower = ta.bb(close, length, multiplier)
own_middle, own_upper, own_lower = custom_bands(close, length, multiplier)
plot(builtin_upper, "Built-in upper", color=color.blue, linewidth=3)
plot(own_upper, "Custom upper", color=color.orange)
plot(own_middle, "Custom middle", color=color.green)
plot(own_lower, "Custom lower", color=color.gray)tuple[float, float, float] indica un resultado de tres números. return middle, upper, lower devuelve la terna y los tres nombres antes de la llamada reciben sus valores por separado. No dibujamos ahora builtin_middle ni builtin_lower, pero puedes usarlos después para comprobar.
Ejecuta con los datos de demostración. El límite superior propio y el incorporado se superponen: la línea naranja fina recorre la azul más gruesa. Sitúa el puntero sobre una vela y compara Built-in upper y Custom upper. Con longitud veinte, las primeras diecinueve velas no tienen ventana completa.

Para mayor precisión, abre los valores exportados con Open plot output CSV en la barra del gráfico. Comprueba tanto el primer punto válido como los posteriores. Líneas idénticas a simple vista pueden diferir ligeramente.
Pruébalo por tu cuenta: centro EMA
Modifica solo el centro de custom_bands: usa EMA en lugar de SMA. Conserva el cálculo de desviación y ta.bb para comparar.
Antes de ejecutar, predice: ¿seguirá coincidiendo siempre tu límite superior con el incorporado? ¿Qué ocurrirá tras un cambio brusco de precio?
Solución del ejercicio
La primera línea de cálculo de la función propia:
middle = ta.ema(source, length)Los límites se miden desde esa EMA y ta.stdev no cambia. El centro y las bandas propias generalmente se desplazan respecto a las originales. La diferencia entre los dos límites superiores coincide con EMA menos SMA, porque se suma la misma desviación y el mismo multiplicador.
Esta ya es tu variante experimental. Una línea distinta no indica por sí sola si una regla de trading mejora con ella; puedes comprobarlo en otra estrategia, con los mismos datos y ajustes.
Sigue explorando la biblioteca
Elige un indicador y explica con tus palabras qué recibe, qué recuerda y qué devuelve. Sigue las llamadas internas hasta comprender el cálculo. En RSI, por ejemplo, rsi → rma → sma lleva desde las diferencias al suavizado y al valor inicial.
Lee también las ramas para ausencias, calentamiento y casos límite. Al crear una variante, modifica primero un detalle y compara los resultados con el original. Así distinguirás un cambio deliberado de un error de cálculo.
Puedes abrir el código de las demás funciones con el mismo clic manteniendo Command o Ctrl. Un indicador largo requiere más tiempo, pero la forma de leerlo es igual: entradas, datos conservados, cálculo y resultado.