El proyecto final reúne en un archivo lo aprendido: medias móviles, inputs, condiciones, una función propia, órdenes y representación. El objetivo es poder relacionar cada decisión con los datos y las reglas escritas.

Describe el programa sin código

La estrategia observa el cruce de dos EMA. Con una tercera media, una SMA más larga, comprueba si el cierre está por encima de ella.

La entrada larga exige que la EMA rápida cruce desde abajo a la lenta y que el cierre supere la SMA de filtro de tendencia. Las longitudes deben seguir rápida < lenta < tendencia. Si no cumplen el orden, no hay órdenes de entrada.

La posición recibe un cierre de mercado al cruzarse las EMA hacia abajo. La entrada también tiene stop y objetivo en ticks. La simulación usa una unidad fija, comisión del 0,1 por ciento y dos ticks de slippage.

Estas son las reglas del ejercicio. No inferimos que el programa vaya a ser rentable en algún mercado. Comprobaremos el funcionamiento con los datos fijos de demostración.

El programa completo

Crea final_strategy.py:

"""
@pyne edge
"""
from pynecore.lib import close, color, input, location, plot, plotshape, script, shape, strategy, ta


def lengths_are_ordered(fast: int, slow: int, trend: int) -> bool:
    return fast < slow and slow < trend


@script.strategy(
    "Filtered Crossing Strategy",
    overlay=True,
    initial_capital=10000,
    default_qty_type=strategy.fixed,
    default_qty_value=1,
    pyramiding=1,
    commission_type=strategy.commission.percent,
    commission_value=0.1,
    slippage=2,
)
def main(
    fast_length: int = input.int(9, title="Fast EMA length", minval=1, maxval=200),
    slow_length: int = input.int(21, title="Slow EMA length", minval=1, maxval=200),
    trend_length: int = input.int(50, title="Trend SMA length", minval=1, maxval=200),
    use_trend_filter: bool = input.bool(True, title="Use trend filter"),
    stop_ticks: int = input.int(100, title="Stop distance (ticks)", minval=1),
    target_ticks: int = input.int(200, title="Target distance (ticks)", minval=1),
):
    fast_average = ta.ema(close, fast_length)
    slow_average = ta.ema(close, slow_length)
    trend_average = ta.sma(close, trend_length)
    crosses_up = ta.crossover(fast_average, slow_average)
    crosses_down = ta.crossunder(fast_average, slow_average)
    valid_lengths = lengths_are_ordered(fast_length, slow_length, trend_length)
    trend_passes = not use_trend_filter or close > trend_average
    entry_signal = valid_lengths and crosses_up and trend_passes
    if entry_signal:
        strategy.entry("Long", strategy.long)
    if crosses_down:
        strategy.close("Long")
    strategy.exit("Bracket", "Long", loss=stop_ticks, profit=target_ticks)
    plot(fast_average, "Fast EMA", color=color.blue, linewidth=2)
    plot(slow_average, "Slow EMA", color=color.orange, linewidth=2)
    plot(trend_average, "Trend SMA", color=color.gray, linewidth=2)
    plotshape(entry_signal, "Entry signal", style=shape.triangleup,
              location=location.belowbar, color=color.green)

Guárdalo y ejecútalo con los datos de demostración. El gráfico muestra tres medias y triángulos verdes para la condición de entrada. Las marcas de operaciones indican las entradas y salidas ejecutadas.

El gráfico de final_strategy.py muestra EMA rápida y lenta, SMA de tendencia, señales de entrada y operaciones ejecutadas.
El proyecto final utiliza tres medias, un filtro activable y reglas de salida asociadas a las entradas.

¿Qué se calcula en cada vela?

Las tres medias y los dos cruces se calculan antes de las condiciones. Lo hacemos aunque el filtro impida después entrar. Así su estado recorre toda la serie de velas.

La función propia lengths_are_ordered examina tres números y devuelve un valor lógico. No tiene estado de velas propio. valid_lengths guarda el resultado actual para las expresiones siguientes.

trend_passes es verdadero si el filtro está desactivado o el cierre supera la SMA. input.bool proporciona una entrada lógica activable. not use_trend_filter es verdadero con el filtro apagado, por lo que entonces no hace falta la segunda condición.

Aunque desactives el filtro, seguimos calculando y dibujando la media. El interruptor cambia la condición de entrada, no la presentación de la línea.

Una relación de longitudes inválida

Los límites individuales solo exigen valores de 1 a 200. Aun así puedes introducir rápida 30 y lenta 10. Eso incumple el orden de la tarea.

La función comprueba por separado la relación. Con el orden incorrecto, las líneas se dibujan, pero entry_signal no puede ser verdadero. Así no se generan entradas con longitudes que contradicen el ejercicio.

Puedes comprobarlo directamente con valid_lengths en el depurador. Será falso durante toda la ejecución si los inputs guardados no son adecuados.

Una señal no siempre produce otra operación

El triángulo verde indica la condición de entrada. La estrategia envía la orden al simulador. Para ejecutarla puede haber otros requisitos: otra vela después, capital suficiente o una cantidad permitida.

Con pyramiding=1, el simulador puede no admitir otra entrada larga si ya hay una posición larga abierta. No asumas que señales y operaciones tienen el mismo número.

Al principio falta la SMA de cincuenta del filtro. Con el filtro activo no podemos considerar que el precio esté por encima, por lo que la entrada no satisface esa condición.

Comprueba el funcionamiento

Empieza con los valores predeterminados y verifica cantidad, costes y modo de ejecución en Properties. Un ajuste guardado anteriormente puede sustituir el del código.

Busca una señal verde. Examina las dos EMA en esa vela y en la anterior con el depurador o los valores del gráfico. Comprueba el cruce, el filtro SMA y la relación de longitudes. Las tres partes deben ser verdaderas.

Después busca la entrada asociada en Trades. Comprueba vela y precio de ejecución. En la salida determina si la explica un cruce, el stop o el objetivo.

Prueba casos predecibles

Para comprobar el funcionamiento, utiliza cambios con resultados previsibles:

Cambio¿Qué debes comprobar?
Longitudes rápida y lenta igualesLa comprobación impide nuevas señales de entrada
Filtro de tendencia apagadoEl cruce EMA no exige precio sobre la SMA
Distancias de stop y objetivo mayoresLa distancia puede cambiar la vela de salida
Cambio de cantidadLa señal de precio es igual; la magnitud monetaria puede variar

Haz solo un cambio de estos por ejecución. Anota primero la previsión y después el resultado. Si difieren, busca la primera decisión distinta en el depurador.

Compara los resultados

Para comparar el filtro activado y desactivado, conserva datos, cantidad, costes y longitudes. Examina la curva de Performance junto con Trades. Pocas operaciones o una posición final abierta también afectan al resumen.

Con datos históricos reales, fija el período de ajuste y otro de comprobación antes de ver resultados. Si modificas parámetros después de mirar el período de comprobación, este también pasa a formar parte del ajuste.

Los datos necesitan suficientes velas iniciales de calentamiento. Si ejecutas un fragmento por separado, reconstruyes medias y estado de posiciones. Su parte inicial puede diferir de la obtenida para ese mismo período en medio de una serie más larga.

Pruébalo por tu cuenta

Añade un interruptor Use crossings exit. Activado conserva el cierre por cruce descendente; desactivado, solo stop y objetivo cierran. Calcula el cruce descendente en ambos casos.

Solución del ejercicio

En los parámetros de main():

use_crossing_exit: bool = input.bool(True, title="Use crossings exit"),

La rama de cierre:

if use_crossing_exit and crosses_down:
    strategy.close("Long")

Conserva crosses_down = ta.crossunder(...) antes de las condiciones. strategy.exit permanece en ambas posiciones del interruptor.

Con el cierre por cruce desactivado, la posición puede seguir abierta tras un cruce descendente de EMA. Puede aparecer otra señal de entrada sin abrir otra operación porque ya existe una posición.

Crea tu propia versión

Copia el programa con otro nombre y describe una única regla nueva. Especifica con qué datos y en qué vela decide, cómo comprobarla a mano y qué input la modifica.

Calcula primero la regla en una variable separada. Dibújala o examínala en el depurador antes de convertirla en condición de entrada o salida. Así puedes buscar el error en el cálculo antes de intentar explicar todo el resultado de la estrategia.

El código, los ajustes guardados y los datos describen juntos el experimento. Conservarlos permite reproducir y comprobar la ejecución.