NOTA: Este tutorial de NinjaScript tiene un enfoque básico y no pretende cubrir todos los aspectos desde cero. Se asume que el lector tiene ciertos conocimientos previos sobre programación y trading. A lo largo del tutorial, repasaremos los puntos más importantes y se proporcionarán ejemplos explicados paso a paso, pensados para aquellos que están iniciándose en el uso de NinjaScript. Sin embargo, para una comprensión más completa, se recomienda consultar la documentación oficial de NinjaTrader aquí o recurrir a otros manuales especializados en NinjaScript.
Sumario:
Programación en NinjaScript (I)
- Introducción a NinjaScript
- Configuración Inicial
- Conceptos Básicos de Programación en NinjaScript
- Creación de Indicadores Personalizados
- Desarrollo de Estrategias de Trading
- Uso de Datos de Mercado y Series Temporales
Programación en NinjaScript (II)
- Gestión de Eventos y Operaciones en Tiempo Real
- Interacción con APIs Externas
- Consejos y Mejores Prácticas
- Casos de Uso Reales
- Recursos Adicionales y Continuación del Aprendizaje
- Conclusión
Capítulo 1: Introducción
1.1. ¿Qué es NinjaScript?
NinjaScript es un lenguaje de programación basado en C# que se utiliza dentro de la plataforma NinjaTrader para crear y personalizar indicadores técnicos, estrategias de trading automatizadas y otras herramientas relacionadas con el análisis de mercados financieros. Al estar basado en C#, NinjaScript permite a los usuarios acceder a un potente conjunto de funciones para diseñar scripts avanzados, integrando lógica condicional, cálculos matemáticos complejos y visualización gráfica en tiempo real.
NinjaScript facilita la automatización de estrategias de trading y la creación de herramientas personalizadas, lo que proporciona una ventaja significativa en el análisis y ejecución de operaciones en los mercados.
1.2. Breve Historia de NinjaScript y su Relación con NinjaTrader
NinjaScript nació como una extensión de la plataforma NinjaTrader, que fue lanzada por primera vez en 2003. NinjaTrader es una de las plataformas más populares para el análisis técnico y el trading en tiempo real, utilizada tanto por traders profesionales como por aficionados. Su capacidad para proporcionar datos de mercado en tiempo real y ejecutar operaciones la ha hecho especialmente atractiva en los mercados de futuros, forex, acciones y otros activos financieros.
El lenguaje NinjaScript fue desarrollado para ofrecer a los traders una manera flexible y personalizable de ampliar las capacidades de NinjaTrader. Al principio, la plataforma solo ofrecía funcionalidades limitadas, pero con la integración de NinjaScript, los usuarios ganaron la posibilidad de crear sus propios indicadores y estrategias automatizadas. Al estar basado en C#, un lenguaje muy conocido en el mundo de la programación, NinjaScript se ha convertido en una herramienta accesible y poderosa para los traders que desean llevar sus ideas de trading al siguiente nivel.
1.3. ¿Por qué Aprender NinjaScript? Aplicaciones y Ventajas
Aprender NinjaScript es una inversión valiosa para cualquier trader que quiera maximizar su eficiencia y capacidad en los mercados. Las principales razones para aprender NinjaScript incluyen:
- Automatización del Trading: Una de las ventajas más importantes de NinjaScript es la posibilidad de crear estrategias automatizadas, que permiten ejecutar operaciones basadas en reglas predefinidas sin intervención manual. Esto reduce el impacto emocional y garantiza que se sigan estrictamente los criterios de trading.
- Personalización: Aunque NinjaTrader ofrece una variedad de herramientas prediseñadas, NinjaScript permite a los usuarios diseñar indicadores y estrategias únicas que se ajusten a su estilo de trading y necesidades específicas.
- Optimización: Los traders pueden usar NinjaScript para optimizar sus estrategias, probándolas con datos históricos y ajustando variables clave para mejorar su rendimiento antes de aplicarlas en tiempo real.
- Flexibilidad y Escalabilidad: Dado que NinjaScript se basa en C#, es extremadamente flexible. Los traders pueden incorporar algoritmos complejos, acceder a API externas o crear interfaces personalizadas para mejorar la funcionalidad de NinjaTrader.
En resumen, NinjaScript no solo aumenta el control sobre el proceso de trading, sino que también permite a los traders aprovechar la automatización, reducir el error humano y mejorar sus análisis, lo que puede conducir a mejores decisiones y resultados en el mercado.
Capítulo 2: Configuración Inicial
2.1. Requisitos de Software y Hardware
Antes de empezar a trabajar con NinjaScript, es importante asegurarse de contar con los requisitos de software y hardware adecuados para garantizar un rendimiento óptimo.
- Requisitos de Software:
- Sistema Operativo: Windows 10 o superior (NinjaTrader solo es compatible con Windows).
- NinjaTrader: Versión 8 o superior. Se puede descargar de forma gratuita, aunque algunas funcionalidades avanzadas requieren una licencia de pago.
- .NET Framework: NinjaScript está basado en C# y requiere que el sistema tenga instalado el .NET Framework 4.8 o superior.
- Requisitos de Hardware:
- Procesador: Procesador de al menos 2 GHz, aunque se recomienda un procesador más rápido (4 núcleos o más) para manejar múltiples gráficos y datos en tiempo real.
- Memoria RAM: 4 GB como mínimo, pero se recomienda 8 GB o más para un rendimiento fluido.
- Disco Duro: Al menos 200 MB para la instalación de NinjaTrader, aunque se sugiere un disco SSD para tiempos de carga más rápidos.
- Conexión a Internet: Una conexión de banda ancha confiable es crucial para recibir datos en tiempo real de manera eficiente.
2.2. Instalación de NinjaTrader y Configuración del Entorno de Desarrollo
A continuación, detallamos los pasos para instalar NinjaTrader y configurar tu entorno de desarrollo para empezar a programar en NinjaScript.
Paso 1: Descargar NinjaTrader
- Ve al sitio web oficial de NinjaTrader.
- Crea una cuenta gratuita si aún no la tienes y descarga la versión más reciente de NinjaTrader.
- Una vez descargado, ejecuta el instalador y sigue las instrucciones en pantalla.
Paso 2: Primer inicio de sesión y configuración inicial
Al iniciar NinjaTrader por primera vez:
- Se te pedirá que configures tu conexión de datos. Si no tienes acceso a datos en tiempo real, puedes optar por datos históricos proporcionados por NinjaTrader.
- Personaliza la interfaz a tus preferencias. Puedes configurar múltiples monitores para gráficos, estrategias y paneles de control.
Paso 3: Acceso al NinjaScript Editor
Para empezar a trabajar con NinjaScript:
- En la barra de herramientas principal de NinjaTrader, selecciona «Nuevo» y luego «NinjaScript Editor».
- Aquí podrás crear y editar scripts personalizados para indicadores, estrategias y más. La interfaz del editor es sencilla y similar a otros entornos de desarrollo integrados (IDE), lo que facilita la escritura de código.
Ejemplo Práctico: Crear tu Primer Script
Vamos a crear un sencillo indicador que cambia el color de las velas cuando el precio de cierre es mayor que el precio de apertura.
- Abre el NinjaScript Editor.
- Haz clic en «Nuevo» y selecciona «Indicador».
- El siguiente código cambiará el color de las velas a verde si el precio de cierre es mayor que el de apertura, o a rojo si es menor:
protected override void OnBarUpdate()
{
if (Close[0] > Open[0])
BarColor = Brushes.Green;
else
BarColor = Brushes.Red;
}
- Guarda tu script como
ColorCandle. Luego, ve a un gráfico, haz clic derecho, selecciona «Indicadores» y agrega tu nuevo indicador.
Explicación del código:
Este código es parte de una función OnBarUpdate() en NinjaScript, la cual se ejecuta cada vez que hay una nueva actualización de barra (o vela) en el gráfico. Aquí está la explicación detallada:
protected override void OnBarUpdate():- Esta es una función especial en NinjaScript que se ejecuta automáticamente cada vez que llega un nuevo tick de datos o se cierra una nueva barra en el gráfico, dependiendo de la configuración de la estrategia.
overrideindica que estamos sobrescribiendo la implementación de la funciónOnBarUpdate()que es parte del ciclo de vida estándar en NinjaScript para estrategias e indicadores.
if (Close[0] > Open[0]):- Aquí se compara el precio de cierre con el precio de apertura de la barra actual.
Close[0]: Se refiere al precio de cierre de la barra actual.[0]indica el índice de la barra más reciente.Open[0]: Se refiere al precio de apertura de la barra actual.- Si el precio de cierre es mayor que el precio de apertura (
Close[0] > Open[0]), significa que la barra es alcista (el precio subió durante el intervalo de tiempo de la barra).
BarColor = Brushes.Green;:- Si la condición del
ifes verdadera (es decir, la barra es alcista), entonces se cambia el color de la barra a verde usandoBrushes.Green. Esto es común para representar barras o velas alcistas.
- Si la condición del
else:- Si la condición del
ifno es verdadera, es decir, el precio de cierre es menor o igual que el precio de apertura, la barra se considera bajista o neutra.
- Si la condición del
BarColor = Brushes.Red;:- Si la barra es bajista (el precio de cierre es menor que el precio de apertura), entonces se cambia el color de la barra a rojo usando
Brushes.Red. Este es el color tradicional para barras o velas que indican una bajada en el precio.
- Si la barra es bajista (el precio de cierre es menor que el precio de apertura), entonces se cambia el color de la barra a rojo usando
Resumen: Este código simplemente cambia el color de las barras en un gráfico basado en si la barra es alcista o bajista:
- Verde si la barra es alcista (el precio de cierre es mayor que el de apertura).
- Rojo si la barra es bajista (el precio de cierre es menor o igual que el de apertura).
Visualización: Esto permite a los traders identificar rápidamente las barras o velas alcistas y bajistas en el gráfico, facilitando la lectura y análisis visual del comportamiento del precio.
2.3. Introducción a la Interfaz de NinjaScript Editor
El NinjaScript Editor es la herramienta principal para escribir y editar código dentro de NinjaTrader. Su interfaz se compone de varias secciones clave:
- Explorador de Archivos: Aquí se encuentran todos los scripts disponibles (indicadores, estrategias, etc.). Es posible acceder rápidamente a cada archivo desde esta sección.
- Área de Código: Donde se escribe y edita el código.
- Ventana de Salida: Aquí se muestran los errores de compilación y otros mensajes importantes relacionados con el código.

- Explorador de NinjaScript: Muestra archivos, carpetas y permite la gestión adicional de archivos.
- Barra de herramientas: Al mover el ratón sobre cada ícono, se mostrará la función del botón del ícono.
- Números de línea.
- Marcado de modificación de línea: Las banderas amarillas indican modificaciones de línea no guardadas, mientras que las banderas verdes indican modificaciones guardadas.
- Pestañas para crear nuevos scripts a través del asistente de NinjaScript y trabajar en múltiples scripts.
Funcionalidades Principales
- Autocompletado: A medida que escribes código, el editor sugiere funciones y variables relevantes, lo que acelera el desarrollo.
- Compilador en Tiempo Real: Cada vez que guardas el código, NinjaScript lo compila automáticamente y te notifica si existen errores.
- Depurador: Permite identificar y solucionar errores en el código, proporcionando información sobre las variables y el flujo de ejecución.
Con esta configuración inicial, ya tienes las bases para comenzar a programar en NinjaScript y personalizar tus herramientas de trading. En el próximo capítulo, profundizaremos en los conceptos básicos de programación.
Capítulo 3: Conceptos Básicos de Programación en NinjaScript
3.1. Estructura de un Script
Un script en NinjaScript sigue una estructura similar a la de cualquier programa escrito en C#. En general, cada script de NinjaScript contiene los siguientes componentes básicos:
- Declaración de la clase: Define el nombre del script y hereda de la clase
IndicatoroStrategy. - Métodos principales: Como
Initialize(), donde se configuran los parámetros iniciales, yOnBarUpdate(), que se ejecuta cada vez que llega un nuevo dato de mercado.
Ejemplo de Estructura Básica de un Indicador
public class MiIndicador : Indicator
{
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Mi primer indicador en NinjaScript";
Name = "MiIndicador";
}
}
protected override void OnBarUpdate()
{
// Lógica del indicador
Plot[0] = Close[0];
}
}
Explicación del código:
Este código define un indicador personalizado en NinjaScript llamado MiIndicador (el nombre que yo quiera darle), que hereda de la clase base Indicator. A continuación se explica cada parte del código:
1. Clase MiIndicador
public class MiIndicador : Indicator
Aquí se declara la clase MiIndicador, que hereda de la clase Indicator. Esto significa que MiIndicador es un tipo de indicador y puede aprovechar las funcionalidades integradas de NinjaScript para trabajar con gráficos y datos de mercado.
2. Método OnStateChange()
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Mi primer indicador en NinjaScript";
Name = "MiIndicador";
}
}
OnStateChange(): Este método es invocado automáticamente por NinjaScript cuando el estado del script cambia.State == State.SetDefaults: Se ejecuta cuando el indicador está en el estado de establecer sus valores predeterminados. Aquí es donde se configuran propiedades básicas del indicador, como su descripción y nombre.Description: Proporciona una breve descripción del indicador.Name: Asigna el nombre del indicador como «MiIndicador», que aparecerá en la interfaz de NinjaTrader.
3. Método OnBarUpdate()
protected override void OnBarUpdate()
{
// Lógica del indicador
Plot[0] = Close[0];
}
OnBarUpdate(): Este método se ejecuta en cada actualización de barra (candela o tick) en el gráfico. Aquí es donde se coloca la lógica que define cómo se comporta el indicador en función de los datos de mercado.Plot[0] = Close[0];: En este caso, el indicador simplemente toma el precio de cierre de la barra actual (Close[0]) y lo asigna al primer valor de salida o «plot». Esto significa que el indicador trazará en el gráfico el valor de cierre de cada barra, creando una línea que sigue los precios de cierre.
Resumen
OnStateChange(): Configura el nombre y la descripción del indicador.OnBarUpdate(): Define la lógica del indicador, en este caso, trazando el precio de cierre de cada barra.
Es un ejemplo simple de cómo crear un indicador básico en NinjaScript, pero a medida que se complejiza el código, se pueden incluir más cálculos o reglas para crear indicadores personalizados avanzados.
3.2. Variables, Tipos de Datos y Operadores
En NinjaScript, al igual que en C#, existen varios tipos de datos y operadores para realizar cálculos y almacenar información.
- Tipos de Datos:
int: Enteros.double: Números con decimales.bool: Valores booleanos (trueofalse).string: Cadenas de texto.
- Operadores: Se utilizan para realizar operaciones matemáticas y lógicas
+: operación suma-: resta*: multiplicación/: división>: es mayor que<: es menor que==: es igual a!=: es distinto de- etc.
Ejemplo de Variables y Operadores
int contador = 0;double mediaMovil = 0.0;bool esAlcista = true;mediaMovil = (High[0] + Low[0]) / 2;if (Close[0] > mediaMovil)
esAlcista = true;
else esAlcista = false;
Explicación del código:
Declaración de Variables
int contador = 0;double mediaMovil = 0.0;bool esAlcista = true;
int contador = 0;: Declara una variable entera llamadacontadory la inicializa con el valor0. Los enteros (int) son útiles para contar iteraciones o eventos.double mediaMovil = 0.0;: Declara una variable de tipodouble(número decimal) llamadamediaMovily la inicializa con el valor0.0. Este tipo de variable es útil para almacenar valores que pueden tener decimales, como el cálculo de promedios.bool esAlcista = true;: Declara una variable booleana llamadaesAlcistay la inicializa con el valortrue(verdadero). Los booleanos solo pueden sertrueofalsey se utilizan para representar condiciones o estados lógicos.
Cálculo de la Media Móvil
mediaMovil = (High[0] + Low[0]) / 2;
Esta línea de código calcula la media del precio más alto (High[0]) y el precio más bajo (Low[0]) de la barra actual (barra índice 0) y asigna el resultado a la variable mediaMovil. El cálculo es el promedio simple de estos dos valores:
High[0]: Representa el precio más alto de la barra actual.Low[0]: Representa el precio más bajo de la barra actual.
El resultado es el punto medio entre el máximo y el mínimo de la barra.
Condición if-else
if (Close[0] > mediaMovil)
esAlcista = true;
else esAlcista = false;
Aquí se evalúa si el precio de cierre actual (Close[0]) es mayor que la media móvil calculada. Dependiendo del resultado:
- Si
Close[0]es mayor quemediaMovil, se asignatruea la variableesAlcista, indicando que la barra actual es alcista. - Si
Close[0]es menor o igual quemediaMovil, se asignafalse, indicando que la barra es bajista o está por debajo de la media.
Resumen
contador: Variable entera que podría ser utilizada más adelante en el código para contar algo (como el número de barras procesadas).mediaMovil: Calcula el punto medio entre el precio máximo y mínimo de la barra actual.esAlcista: Determina si el precio de cierre está por encima de la media móvil, en cuyo caso se considera alcista.
Este código ilustra el uso básico de variables, operadores aritméticos y condicionales en NinjaScript.
3.3. Funciones y Métodos
Los métodos permiten encapsular bloques de código que pueden ser reutilizados. En NinjaScript, algunos métodos importantes son los predefinidos como OnBarUpdate(), pero también puedes crear tus propios métodos.
Ejemplo de Método Personalizado
private double CalcularMediaMovil(int periodos)
{
double suma = 0;
for (int i = 0; i < periodos; i++)
{
suma += Close[i];
}
return suma / periodos;
}
protected override void OnBarUpdate()
{
double media14 = CalcularMediaMovil(14);
Print("La media móvil de 14 periodos es: " + media14);
}
En este ejemplo, el método CalcularMediaMovil recibe un parámetro periodos y devuelve la media de los precios de cierre para esos periodos.
Explicación del código:
Función CalcularMediaMovil
private double CalcularMediaMovil(int periodos)
{
double suma = 0;
for (int i = 0; i < periodos; i++)
{
suma += Close[i];
}
return suma / periodos;
}
Esta función calcula una media móvil simple para un número de períodos especificado. Aquí está el desglose:
- Definición de la función:
private double CalcularMediaMovil(int periodos)
- La función es privada (
private), lo que significa que solo puede ser usada dentro de la clase en la que se encuentra. - El valor de retorno es de tipo double (número decimal).
- El parámetro
int periodosindica cuántos períodos (o barras) se utilizarán para el cálculo de la media móvil.
- Inicialización de la variable
suma:
double suma = 0;
- Aquí se declara una variable llamada
sumade tipodouble, que almacenará la suma de los precios de cierre de las últimasnbarras.
- Bucle
for:
for (int i = 0; i < periodos; i++)
{
suma += Close[i];
}
- El bucle
forrecorre el número de períodos especificado. Por ejemplo, si el período es 14, se recorrerán las últimas 14 barras. Close[i]representa el precio de cierre de la barrai.Close[0]es el precio de cierre de la barra actual.Close[1]es el precio de cierre de la barra anterior, y así sucesivamente.
- En cada iteración, se suma el precio de cierre de la barra actual al valor de
suma.
- Cálculo de la media móvil:
return suma / periodos;
- Una vez que se han sumado los precios de cierre de las últimas
nbarras, el código divide la suma total por el número de períodos para obtener la media móvil. - El resultado es devuelto por la función.
Método OnBarUpdate
protected override void OnBarUpdate()
{
double media14 = CalcularMediaMovil(14);
Print("La media móvil de 14 periodos es: " + media14);
}
Este método se ejecuta en cada nueva barra que se crea en el gráfico y es parte del ciclo de vida de los indicadores y estrategias en NinjaScript.
- Llamada a la función
CalcularMediaMovil:
double media14 = CalcularMediaMovil(14);
- Aquí se llama a la función
CalcularMediaMovilcon un argumento de 14, lo que significa que se calculará la media móvil simple de las últimas 14 barras. - El valor resultante se guarda en la variable
media14.
- Imprimir el valor de la media móvil:
Print("La media móvil de 14 periodos es: " + media14);
- El método
Printse usa para mostrar el valor de la media móvil calculada en la ventana de salida de NinjaScript. - En este caso, se imprimirá algo como:
"La media móvil de 14 periodos es: 123.45"(dependiendo del valor calculado).
Resumen
- La función
CalcularMediaMoviltoma un número de períodos como argumento y calcula la media móvil simple sumando los precios de cierre de las últimasnbarras y dividiendo la suma entre el número de períodos. - En el método
OnBarUpdate, se llama a la función con un período de 14 y se imprime el resultado. - Este código es una implementación básica para calcular y mostrar la media móvil simple de las últimas 14 barras en NinjaScript.
3.4. Ciclos y Condicionales
Los ciclos y las estructuras condicionales son fundamentales en cualquier lenguaje de programación. En NinjaScript, estas se utilizan frecuentemente para analizar datos históricos y tomar decisiones de trading.
Condicionales:
Las instrucciones if, else if y else permiten realizar operaciones basadas en condiciones.
-
if: Evalúa una condición. Si la condición es verdadera, el bloque de código dentro delifse ejecuta. else if: Se usa después de unifpara evaluar otra condición si la primera fue falsa. Permite tener múltiples condiciones.else: Se ejecuta si ninguna de las condiciones anteriores (delifo loselse if) es verdadera. Funciona como un «de lo contrario».
if (Close[0] > Open[0])
Print("El precio subió en esta vela.");
else
Print("El precio bajó en esta vela.");
Explicación del código:
if (condición): Evalúa una expresión o condición. Si es verdadera (true), ejecuta el bloque de código dentro delif.else: Si la condición delifes falsa (false), ejecuta el bloque de código dentro delelse.
Ciclos
for, while y foreach se usan para iterar sobre colecciones de datos o realizar operaciones repetitivas.
for: Se utiliza para repetir un bloque de código un número específico de veces. Ideal cuando conoces de antemano cuántas veces debe ejecutarse el bucle.
for (int i = 0; i < 10; i++)
{
Print("Precio de cierre de hace " + i + " barras: " + Close[i]);
}
Explicación del código:
1. Inicialización: int i = 0
- Aquí se declara e inicializa la variable
icomo un entero (int) y se le asigna el valor0. ies la variable de control del bucle, que cambiará en cada iteración. Comienza en 0, ya que las posiciones en los arreglos y otras estructuras de datos suelen empezar en 0.
2. Condición: i < 10
- Esta es la condición que se evalúa antes de cada iteración del bucle.
- El bucle continuará ejecutándose mientras la condición sea verdadera.
- En este caso, el bucle se repetirá mientras
isea menor que 10.
3. Incremento: i++
- Después de cada iteración del bucle, se ejecuta esta expresión.
i++es una forma abreviada dei = i + 1, lo que significa queise incrementa en 1 en cada ciclo.- De esta forma, la variable de control
iva tomando los valores 0, 1, 2, 3… 9.
Funcionamiento General
- El bucle comienza con
i = 0. - Se evalúa la condición
i < 10. Si es verdadera, se ejecuta el código dentro del bucle. - Después de ejecutar el bloque de código, se incrementa
ien 1. - El proceso se repite hasta que
isea 10, momento en el cual la condicióni < 10se vuelve falsa y el bucle termina.
while: Repite un bloque de código mientras una condición sea verdadera. Es útil cuando no se sabe cuántas veces se ejecutará el bucle, pero se conoce la condición de parada.
int i = 0;
while (i < 10)
{
Print("Precio de cierre de hace " + i + " barras: " + Close[i]);
i++; // Incrementa la variable i en cada iteración
}
Explicación del código:
- Inicialización: Se declara e inicializa la variable
i = 0antes de que comience el bucle. - Condición del bucle: La condición
i < 10se verifica al inicio de cada iteración del bucle. Mientras esta condición sea verdadera, el bucle continuará ejecutándose. - Incremento: Dentro del bucle,
i++incrementa la variable en 1 en cada iteración, lo que asegura que el bucle eventualmente termine cuandoillegue a 10.
foreach: Se utiliza para iterar sobre cada elemento de una colección (como un array o una lista). Es útil para recorrer todos los elementos de forma secuencial.
double[] precios = { 100.5, 101.3, 99.8 };
foreach (double precio in precios)
{
Print("Precio: " + precio);
}
Explicación del código:
Este código crea un array de precios y utiliza un bucle foreach para recorrerlo e imprimir cada uno de los valores. Aquí te explico paso a paso:
double[] precios = { 100.5, 101.3, 99.8 };:- Se declara un array llamado
preciosque contiene tres valores de tipodouble(números decimales): 100.5, 101.3 y 99.8.
- Se declara un array llamado
foreach (double precio in precios):- El bucle
foreachitera a través de cada elemento del arrayprecios. - En cada iteración, la variable
preciotoma el valor del elemento actual en el array. Primero tomará el valor 100.5, luego 101.3, y finalmente 99.8.
- El bucle
Print("Precio: " + precio);:- En cada iteración, se llama a la función
Print()para mostrar el texto «Precio: » seguido del valor deprecioactual en la consola o el entorno de desarrollo.
- En cada iteración, se llama a la función
Resultado: El código imprimirá en la consola los siguientes valores:
Precio: 100.5
Precio: 101.3
Precio: 99.8
Este es un ejemplo sencillo de cómo usar foreach para recorrer todos los elementos de un array y realizar una acción con cada uno de ellos.
3.5. Depuración de Código
Depurar el código es una parte esencial del proceso de desarrollo. En NinjaScript, puedes usar la función Print() para mostrar valores y ayudarte a identificar problemas en tu código.
Ejemplo de Depuración
protected override void OnBarUpdate()
{
Print("Cierre actual: " + Close[0]);
Print("Media de 14 periodos: " + SMA(Close, 14)[0]);
}
Este ejemplo imprime el precio de cierre actual y la media móvil de 14 periodos en la ventana de salida, lo que permite verificar si los cálculos son correctos.
Caso de Uso Real: Depurar una Estrategia Automatizada
Supongamos que creas una estrategia de cruce de medias móviles. Si no está funcionando correctamente, puedes utilizar Print() para verificar los valores de las medias móviles y las condiciones de entrada/salida.
if (SMA(Close, 14)[0] > SMA(Close, 50)[0])
{
Print("Media rápida por encima de la lenta: " + SMA(Close, 14)[0] + " > " + SMA(Close, 50)[0]);
EnterLong();
}
else
{
Print("Media rápida por debajo de la lenta: " + SMA(Close, 14)[0] + " <= " + SMA(Close, 50)[0]);
ExitLong();
}
Explicación del código:
- Condición
if:SMA(Close, 14)[0] > SMA(Close, 50)[0]: Compara el valor actual (posición 0) de la media móvil de 14 periodos con el valor actual de la media móvil de 50 periodos.- Si la media móvil de 14 periodos (media rápida) es mayor que la de 50 periodos (media lenta), se imprime un mensaje que indica esta relación y se ejecuta la función
EnterLong(), que abre una posición de compra (larga).
- Condición
else:- Si la condición anterior no se cumple, es decir, si la media rápida es menor o igual que la media lenta, se imprime un mensaje indicando esta relación y se ejecuta
ExitLong(), que cierra la posición de compra.
- Si la condición anterior no se cumple, es decir, si la media rápida es menor o igual que la media lenta, se imprime un mensaje indicando esta relación y se ejecuta
Resumen:
El código abre una posición larga cuando la media rápida está por encima de la media lenta, y la cierra cuando la media rápida está por debajo de la media lenta.
En este capítulo, hemos cubierto los conceptos básicos de programación en NinjaScript, desde la estructura de un script hasta el uso de variables, ciclos, condicionales, y la depuración de código. En los próximos capítulos, exploraremos técnicas avanzadas para construir indicadores y estrategias más sofisticadas.
Capítulo 4: Creación de Indicadores Personalizados
4.1. ¿Qué son los Indicadores en NinjaTrader?
Los indicadores en NinjaTrader son herramientas visuales y cuantitativas que permiten analizar el comportamiento de los precios, el volumen y otros datos del mercado. Se utilizan para identificar patrones, detectar tendencias y puntos de inversión, y tomar decisiones de trading informadas. Los indicadores en NinjaTrader pueden ser estándar (como medias móviles, RSI, MACD) o personalizados, diseñados específicamente por el trader para ajustarse a sus estrategias.
NinjaScript ofrece una flexibilidad enorme para crear indicadores personalizados, ya que permite realizar cálculos complejos y visualizarlos de manera gráfica en los gráficos de precios. Esto es esencial para traders que buscan una ventaja personalizada en el análisis del mercado.
4.2. Primer Indicador Personalizado: Creación Paso a Paso
Vamos a crear un indicador básico que cambia el color de las velas según si el precio de cierre es mayor o menor que el precio de apertura.
Paso 1: Crear un Nuevo Indicador en NinjaScript
- Abre NinjaTrader.
- Ve a Nuevo > NinjaScript Editor.
- Selecciona Nuevo > Indicador y dale un nombre, por ejemplo,
CandleColorIndicator.
Paso 2: Estructura Básica del Indicador
Este es un código básico para crear el indicador que colorea las velas dependiendo de si son alcistas o bajistas.
public class CandleColorIndicator : Indicator
{
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Cambia el color de las velas dependiendo de si el cierre es mayor o menor que la apertura.";
Name = "CandleColorIndicator";
}
}
protected override void OnBarUpdate()
{
if (Close[0] > Open[0])
BarColor = Brushes.Green; // Cambia el color a verde si la vela es alcista
else
BarColor = Brushes.Red; // Cambia el color a rojo si la vela es bajista
}}
Paso 3: Aplicar el Indicador al Gráfico
Una vez creado y compilado, puedes aplicar el indicador a un gráfico de la siguiente manera:
- Abre un gráfico en NinjaTrader.
- Haz clic derecho en el gráfico, selecciona Indicadores.
- Busca
CandleColorIndicatoren la lista y agrégalo.
Explicación del código:
- Clase
CandleColorIndicator:- Hereda de la clase base
Indicator, lo que significa que es un indicador personalizado que se puede aplicar a un gráfico en NinjaTrader.
- Hereda de la clase base
- Método
OnStateChange():- Se ejecuta cuando el estado del indicador cambia.
- En el estado
State.SetDefaults, se configuran las propiedades del indicador:Description: Explica que el indicador cambia el color de las velas según si el cierre es mayor o menor que la apertura.Name: Asigna el nombre del indicador comoCandleColorIndicator.
- Método
OnBarUpdate():- Se ejecuta en cada nueva barra de datos o cuando la barra actual se actualiza.
- Si el precio de cierre actual (
Close[0]) es mayor que el precio de apertura actual (Open[0]), se cambia el color de la vela a verde (Brushes.Green), indicando que es una vela alcista. - De lo contrario, si el cierre es menor o igual que la apertura, se cambia el color a rojo (
Brushes.Red), indicando que es una vela bajista.
Resumen:
Este indicador colorea las velas de verde si son alcistas (cierre mayor que la apertura) y de rojo si son bajistas (cierre menor que la apertura), ayudando a visualizar rápidamente la tendencia de las velas en un gráfico.
Ejercicio Práctico:
Modifica este indicador para que coloree las velas en amarillo cuando el precio de cierre sea igual al de apertura, lo que podría indicar una vela Doji.
Ver solución:
public class CandleColorIndicator : Indicator
{
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Cambia el color de las velas dependiendo de si el cierre es mayor o menor que la apertura.";
Name = "CandleColorIndicator";
}
}
protected override void OnBarUpdate()
{
if (Close[0] > Open[0])
BarColor = Brushes.Green; // Cambia el color a verde si la vela es alcista
else if (Close[0] < Open[0])
BarColor = Brushes.Red; // Cambia el color a rojo si la vela es bajista
else
BarColor = Brushes.Yellow; // Cambia el color a amarillo si el cierre es igual a la apertura (Doji)
}
}
- Hemos agregado una nueva condición
else if (Close[0] == Open[0])para detectar si el precio de cierre es igual al precio de apertura. - Si esta condición se cumple, el color de la vela se cambia a amarillo (
Brushes.Yellow), lo que indica una posible formación de una vela Doji, que generalmente representa indecisión en el mercado.
4.3. Modificación y Optimización de Indicadores
Uno de los beneficios clave de NinjaScript es la posibilidad de modificar y optimizar los indicadores para ajustarlos a diferentes condiciones de mercado. A continuación, veremos cómo ajustar el indicador para que sea más flexible.
Agregar Parámetros Personalizables
Puedes agregar parámetros que el usuario pueda modificar desde la interfaz de NinjaTrader, como los colores de las velas.
public class CandleColorIndicator : Indicator
{
private Brush colorAlcista;
private Brush colorBajista;
[NinjaScriptProperty]
[Display(Name = "Color Alcista", Order = 1)]
public Brush ColorAlcista { get; set; }
[NinjaScriptProperty]
[Display(Name = "Color Bajista", Order = 2)]
public Brush ColorBajista { get; set; }
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Cambia el color de las velas dependiendo de si el cierre es mayor o menor que la apertura.";
Name = "CandleColorIndicator";
ColorAlcista = Brushes.Green;
ColorBajista = Brushes.Red;
}
}
protected override void OnBarUpdate()
{
if (Close[0] > Open[0])
BarColor = ColorAlcista;
else
BarColor = ColorBajista;
}
}
Ahora, el usuario puede modificar los colores desde el panel de propiedades del indicador.
Explicación del código:
Este código define un indicador llamado CandleColorIndicator que cambia el color de las velas según si el cierre es mayor o menor que la apertura. La particularidad de este ejemplo es que permite configurar los colores de las velas alcistas y bajistas desde las propiedades de NinjaScript.
- Variables privadas:
colorAlcistaycolorBajistase utilizan como referencia para almacenar los colores de las velas alcistas y bajistas.
- Propiedades:
- Las propiedades
ColorAlcistayColorBajistaestán decoradas con los atributos[NinjaScriptProperty]y[Display], lo que permite configurar los colores a través de la interfaz gráfica de NinjaTrader. Estas propiedades permiten al usuario elegir los colores deseados para las velas alcistas y bajistas.
- Las propiedades
- OnStateChange:
- En el estado
State.SetDefaults, se establecen los valores por defecto de las propiedades, asignando el color verde para las velas alcistas y rojo para las bajistas.
- En el estado
- OnBarUpdate:
- En cada barra, se comprueba si el cierre es mayor o menor que la apertura:
- Si el precio de cierre es mayor que el de apertura, se aplica el color definido en la propiedad
ColorAlcistaa la vela. - Si el precio de cierre es menor, se aplica el color de
ColorBajista.
- Si el precio de cierre es mayor que el de apertura, se aplica el color definido en la propiedad
- En cada barra, se comprueba si el cierre es mayor o menor que la apertura:
Este código permite personalizar los colores de las velas en función de las preferencias del usuario sin modificar el código, ya que pueden cambiarse directamente desde la interfaz gráfica de NinjaTrader.
Ejercicio Práctico:
Optimiza este indicador añadiendo un parámetro que permita al usuario ajustar cuántos periodos atrás considerar para el patrón envolvente.
Ver solución:
public class CandleColorIndicator : Indicator
{
private Brush colorAlcista;
private Brush colorBajista; private Brush int periodosEnvolvente;
[NinjaScriptProperty]
[Display(Name = "Color Alcista", Order = 1)]
public Brush ColorAlcista { get; set; }
[NinjaScriptProperty]
[Display(Name = "Color Bajista", Order = 2)]
public Brush ColorBajista { get; set; }
[NinjaScriptProperty] [Range(1, int.MaxValue)] [Display(Name = "Peridos Envolvente", Order = , Description = "Cantidad de periodos atrás para considerar en el patrón envolvente")] public int PeriodosEnvolvente { get; set; }
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Cambia el color de las velas dependiendo de si el cierre es mayor o menor que la apertura.";
Name = "CandleColorIndicator";
ColorAlcista = Brushes.Green;
ColorBajista = Brushes.Red;
}
}
protected override void OnBarUpdate()
{ // Verificar si la vela actual es alcista o bajista if (Close[0] > Open[0]) {
BarColor = ColorAlcista; }
else if (Close[0] < Open[0]) { BarColor = ColorBajista; }// Detectar patrón envolvente alcista
if (CurrentBar >= PeriodosEnvolvente)
{
bool envolventeAlcista = Open[0] < Close[PeriodosEnvolvente] && Close[0] > Open[PeriodosEnvolvente];
if (envolventeAlcista)
{
BarColor = Brushes.Blue; // Cambiar color a azul si se detecta un patrón envolvente alcista
}
}
}
}
Explicación de los cambios:
- Nueva propiedad:
PeriodosEnvolvente:- Se añade esta propiedad para que el usuario pueda definir cuántos periodos atrás considerar para el patrón envolvente alcista. Se usa
[Range(1, int.MaxValue)]para que el valor esté siempre entre 1 y un máximo entero.
- Se añade esta propiedad para que el usuario pueda definir cuántos periodos atrás considerar para el patrón envolvente alcista. Se usa
- OnBarUpdate:
- El código ahora incluye una lógica para detectar un patrón envolvente alcista. Para ello, verifica que la vela actual (cierre mayor que apertura) envuelve la vela de hace
PeriodosEnvolventebarras. - Si se cumple esta condición, la vela se colorea de azul.
- El código ahora incluye una lógica para detectar un patrón envolvente alcista. Para ello, verifica que la vela actual (cierre mayor que apertura) envuelve la vela de hace
- Condición de seguridad:
- Se añade una verificación en el
OnBarUpdatepara asegurarse de que hay suficientes barras (CurrentBar >= PeriodosEnvolvente) antes de aplicar la lógica del patrón envolvente. Esto evita errores si no hay suficientes barras en el gráfico.
- Se añade una verificación en el
Este indicador ahora permite a los usuarios ajustar los periodos para el patrón envolvente alcista, mejorando la flexibilidad del análisis.
Conclusión: En este capítulo, hemos cubierto cómo crear indicadores personalizados en NinjaScript, desde un simple cambio de color en las velas hasta la identificación de patrones avanzados como las envolventes alcistas y bajistas. También exploramos cómo optimizar y modificar los indicadores para adaptarlos a diferentes necesidades, y cómo depurar el código para garantizar que los indicadores funcionen correctamente. En los próximos capítulos, profundizaremos en estrategias automatizadas basadas en estos indicadores.
Capítulo 5: Desarrollo de Estrategias de Trading
5.1. Introducción a las Estrategias de Trading Automatizado
Las estrategias de trading automatizado permiten ejecutar órdenes de compra y venta de manera sistemática, sin intervención humana, siguiendo un conjunto predefinido de reglas. Este enfoque es clave para eliminar emociones en el trading, garantizar consistencia y aprovechar oportunidades de manera más rápida y precisa.
NinjaScript facilita la creación de estrategias automatizadas al permitir a los traders programar sus propias reglas de entrada y salida basadas en indicadores, patrones de precios, volumen, entre otros. Las estrategias pueden incluir gestión de riesgos avanzada como stop loss, take profit, y ajuste de posiciones automáticamente. Además, el entorno permite realizar backtesting, lo que ayuda a optimizar las estrategias antes de ponerlas en práctica en tiempo real.
5.2. Creación de una Estrategia Básica
Vamos a crear una estrategia básica que opere utilizando el cruce de dos medias móviles: una rápida (de 14 periodos) y una lenta (de 50 periodos).
Paso 1: Crear una Estrategia en NinjaScript
- Abre NinjaTrader.
- Ve a NinjaScript Editor y selecciona Nuevo > Estrategia.
- Nombra la estrategia, por ejemplo,
EstrategiaCruceMedias.
Paso 2: Código de la Estrategia
public class EstrategiaCruceMedias : Strategy
{
private int periodosRapidos = 14;
private int periodosLentos = 50;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Estrategia de cruce de medias móviles.";
Name = "EstrategiaCruceMedias";
Calculate = Calculate.OnEachTick;
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < periodosLentos)
return; // Espera a que haya suficientes datos
double mediaRapida = SMA(periodosRapidos)[0];
double mediaLenta = SMA(periodosLentos)[0];
if (CrossAbove(SMA(periodosRapidos), SMA(periodosLentos), 1))
EnterLong(); // Compra cuando la media rápida cruza por encima de la media lenta
else if (CrossBelow(SMA(periodosRapidos), SMA(periodosLentos), 1))
EnterShort(); // Venta en corto cuando la media rápida cruza por debajo
}
}
Este código genera una estrategia de cruce de medias móviles, que ejecuta una compra cuando la media rápida cruza por encima de la media lenta, y una venta cuando ocurre lo contrario.
Explicación del código:
- Variables
periodosRapidosyperiodosLentos:- Se definen dos variables para los periodos de las medias móviles: una rápida (14 periodos) y otra lenta (50 periodos).
OnStateChange():- Configura el estado inicial de la estrategia. Aquí se define una descripción, el nombre de la estrategia y se especifica que los cálculos se hagan en cada tick (
Calculate.OnEachTick), lo que permite ejecutar la lógica en tiempo real.
- Configura el estado inicial de la estrategia. Aquí se define una descripción, el nombre de la estrategia y se especifica que los cálculos se hagan en cada tick (
OnBarUpdate():- Este método se ejecuta en cada nuevo tick o barra. Primero, verifica si hay suficientes barras (
CurrentBar < periodosLentos), para asegurarse de que la media lenta pueda calcularse. Si no, se sale del método. - Calcula las dos medias móviles simples (SMA) basadas en los periodos definidos (rápida y lenta).
- Este método se ejecuta en cada nuevo tick o barra. Primero, verifica si hay suficientes barras (
- Cruce de medias móviles:
- Si la media rápida cruza por encima de la media lenta (
CrossAbove), se ejecuta una orden de compra (EnterLong()). - Si la media rápida cruza por debajo de la media lenta (
CrossBelow), se ejecuta una venta en corto (EnterShort()).
- Si la media rápida cruza por encima de la media lenta (
Esta estrategia es común en trading automatizado, donde las órdenes se basan en señales de cruce de medias móviles, buscando tendencias alcistas o bajistas.
Ejercicio Práctico:
Modifica la estrategia para que opere con medias móviles exponenciales (EMA) en lugar de simples (SMA).
Ver solución:
public class EstrategiaCruceMedias : Strategy
{
private int periodosRapidos = 14;
private int periodosLentos = 50;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Estrategia de cruce de medias móviles.";
Name = "EstrategiaCruceMedias";
Calculate = Calculate.OnEachTick;
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < periodosLentos)
return; // Espera a que haya suficientes datos
double mediaRapida = EMA(periodosRapidos)[0];
double mediaLenta = EMA(periodosLentos)[0];
if (CrossAbove(EMA(periodosRapidos), SMA(periodosLentos), 1))
EnterLong(); // Compra cuando la media rápida cruza por encima de la media lenta
else if (CrossBelow(SMA(periodosRapidos), SMA(periodosLentos), 1))
EnterShort(); // Venta en corto cuando la media rápida cruza por debajo
}
}
- Se reemplazaron las medias móviles simples (
SMA) por medias móviles exponenciales (EMA). - El resto de la estructura de la estrategia sigue igual, operando en base al cruce de las dos medias móviles.
Este cambio hace que la estrategia responda más rápidamente a los movimientos recientes del precio, ya que las medias móviles exponenciales otorgan mayor peso a los datos más recientes.
5.3. Implementación de Stop Loss y Take Profit
Incorporar stop loss y take profit es esencial para la gestión de riesgos en cualquier estrategia. NinjaScript ofrece métodos sencillos para definir estos niveles.
Ejemplo de Stop Loss y Take Profit
protected override void OnBarUpdate()
{
if (CurrentBar < periodosLentos)
return;
double mediaRapida = SMA(periodosRapidos)[0];
double mediaLenta = SMA(periodosLentos)[0];
if (CrossAbove(SMA(periodosRapidos), SMA(periodosLentos), 1))
{
EnterLong();
SetStopLoss(CalculationMode.Ticks, 10); // Establece un stop loss de 10 ticks
SetProfitTarget(CalculationMode.Ticks, 20); // Establece un take profit de 20 ticks
}
else if (CrossBelow(SMA(periodosRapidos), SMA(periodosLentos), 1))
{
EnterShort();
SetStopLoss(CalculationMode.Ticks, 10);
SetProfitTarget(CalculationMode.Ticks, 20);
}
}
Este código asegura que cada operación tenga niveles predefinidos de riesgo y recompensa.
Explicación del código:
Este código implementa una estrategia de cruce de medias móviles simples (SMA) con un sistema de stop loss y take profit:
- Comprobación del número de barras: Si el número de barras actuales (
CurrentBar) es menor que el período de la media lenta (periodosLentos), no se ejecuta ninguna operación, ya que no hay suficientes datos históricos. - Cálculo de medias móviles: Se calculan las medias móviles simples (SMA) para los períodos rápidos y lentos:
mediaRapidausa un período corto (periodosRapidos).mediaLentausa un período más largo (periodosLentos).
- Condiciones de cruce:
- Si la media rápida cruza por encima de la media lenta (
CrossAbove), se ejecuta una orden de compra (EnterLong()). - Si la media rápida cruza por debajo de la media lenta (
CrossBelow), se ejecuta una orden de venta en corto (EnterShort()).
- Si la media rápida cruza por encima de la media lenta (
- Gestión de riesgos:
- Tras abrir una posición, se establece un stop loss de 10 ticks para limitar pérdidas.
- Se define un take profit de 20 ticks para asegurar ganancias si el precio alcanza ese objetivo.
Este código implementa una estrategia de cruce de medias con un control básico del riesgo usando stop loss y take profit.
Ejercicio Práctico:
Ajusta el stop loss y el take profit para que se calculen en función de la volatilidad del mercado (por ejemplo, usando el ATR).
Ver solución:
private int periodosRapidos = 14;
private int periodosLentos = 50;
private double factorStopLoss = 1.5; // Factor para ajustar el stop loss basado en el ATR
private double factorTakeProfit = 3.0; // Factor para ajustar el take profit basado en el ATR
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Estrategia de cruce de medias móviles con gestión de riesgos basada en ATR.";
Name = "EstrategiaCruceMediasATR";
Calculate = Calculate.OnEachTick;
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < periodosLentos)
return;
double mediaRapida = EMA(periodosRapidos)[0]; // Cambié a EMA si prefieres exponenciales
double mediaLenta = EMA(periodosLentos)[0];
// Calcula el ATR con un período de 14 barras
double atrValor = ATR(14)[0];
// Ajusta el stop loss y take profit en función del ATR
double stopLoss = factorStopLoss * atrValor;
double takeProfit = factorTakeProfit * atrValor;
if (CrossAbove(EMA(periodosRapidos), EMA(periodosLentos), 1))
{
EnterLong();
SetStopLoss(CalculationMode.Price, Close[0] - stopLoss); // Stop loss basado en ATR
SetProfitTarget(CalculationMode.Price, Close[0] + takeProfit); // Take profit basado en ATR
}
else if (CrossBelow(EMA(periodosRapidos), EMA(periodosLentos), 1))
{
EnterShort();
SetStopLoss(CalculationMode.Price, Close[0] + stopLoss);
SetProfitTarget(CalculationMode.Price, Close[0] - takeProfit);
}
}
Explicación de los cambios:
- ATR:
- Se utiliza el ATR con un período de 14 barras para medir la volatilidad del mercado (
ATR(14)).
- Se utiliza el ATR con un período de 14 barras para medir la volatilidad del mercado (
- Cálculo del Stop Loss y Take Profit:
- El stop loss se ajusta en función de la volatilidad multiplicada por un factor (
factorStopLoss), que es ajustable (en este caso, 1.5 veces el ATR). - El take profit también se ajusta de manera proporcional, usando un factor (
factorTakeProfit) de 3 veces el ATR.
- El stop loss se ajusta en función de la volatilidad multiplicada por un factor (
- Ejecución:
- Se establece el stop loss y take profit en función del precio actual (
Close[0]) sumado o restado del valor calculado en función del ATR.
- Se establece el stop loss y take profit en función del precio actual (
Este enfoque ajusta automáticamente el riesgo y el objetivo de beneficio de acuerdo con las condiciones del mercado, basándose en la volatilidad actual medida por el ATR.
5.4. Optimización y Backtesting de Estrategias
Backtesting es el proceso de probar una estrategia en datos históricos para evaluar su rendimiento. NinjaTrader permite realizar backtesting de forma sencilla con el Simulador de Estrategias, donde se puede analizar el desempeño de una estrategia antes de ejecutarla en tiempo real.
Tipos de Backtesting
- In-sample: Prueba la estrategia en una parte del conjunto de datos históricos. Es útil para el ajuste inicial de los parámetros.
- Out-of-sample: Evalúa la estrategia en datos que no fueron utilizados para la optimización. Esto verifica la robustez del sistema.
- Walk-forward: Este método divide los datos en múltiples bloques de prueba y validación, y mueve la ventana temporal hacia adelante. Es más realista para evaluar cómo se comportará la estrategia en diferentes condiciones de mercado.
Ventajas y Desventajas
- In-sample: Fácil y rápido, pero tiende a sobreoptimizar la estrategia.
- Out-of-sample: Proporciona una evaluación más realista, pero es menos eficiente en tiempo.
- Walk-forward: El más robusto, ya que prueba en diferentes períodos, pero también el más complejo y costoso en tiempo de computación.
Ejemplo de Backtesting
// Simulación de backtesting con diferentes parámetros
AddDataSeries(Data.BarsPeriodType.Minute, 5); // Prueba en intervalos de 5 minutos
Usa la Simulación de Estrategias en NinjaTrader para analizar los resultados del backtesting y optimizar los parámetros de la estrategia, como los periodos de las medias móviles o los niveles de stop loss.
Ejercicio Práctico:
Realiza un backtesting de tu estrategia con datos de los últimos dos años y analiza el rendimiento con diferentes configuraciones de stop loss y take profit.
Ver solución:
public class BacktestingEstrategia : Strategy
{
private int periodosRapidos = 14;
private int periodosLentos = 50;
private double stopLoss;
private double takeProfit;
[NinjaScriptProperty]
[Range(0.5, 10.0)]
[Display(Name = "Stop Loss (ATR Factor)", Order = 1)]
public double StopLossFactor { get; set; } = 1.5;
[NinjaScriptProperty]
[Range(0.5, 10.0)]
[Display(Name = "Take Profit (ATR Factor)", Order = 2)]
public double TakeProfitFactor { get; set; } = 3.0;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Estrategia de cruce de medias con backtesting sobre los últimos dos años y configuraciones variables de stop loss y take profit.";
Name = "BacktestingEstrategia";
Calculate = Calculate.OnBarClose;
IncludeCommission = true; // Incluir comisiones en el análisis
IsBackTest = true; // Indicar que es un backtesting
}
else if (State == State.Configure)
{
// Establecer el rango de fechas para los últimos dos años
AddDataSeries(Data.BarsPeriodType.Day, 1);
SetStartDate(DateTime.Now.AddYears(-2)); // Iniciar backtesting hace dos años
}
} protected override void OnBarUpdate()
{
// Asegurarse de que haya suficientes barras
if (CurrentBar < periodosLentos)
return;
double mediaRapida = EMA(periodosRapidos)[0];
double mediaLenta = EMA(periodosLentos)[0];
// Obtener el valor de ATR para la volatilidad
double atrValor = ATR(14)[0];
// Calcular stop loss y take profit con los factores proporcionados
stopLoss = StopLossFactor * atrValor;
takeProfit = TakeProfitFactor * atrValor;
// Condiciones de compra o venta en función del cruce de medias
if (CrossAbove(EMA(periodosRapidos), EMA(periodosLentos), 1))
{
EnterLong();
SetStopLoss(CalculationMode.Price, Close[0] - stopLoss);
SetProfitTarget(CalculationMode.Price, Close[0] + takeProfit);
}
else if (CrossBelow(EMA(periodosRapidos), EMA(periodosLentos), 1))
{
EnterShort();
SetStopLoss(CalculationMode.Price, Close[0] + stopLoss);
SetProfitTarget(CalculationMode.Price, Close[0] - takeProfit);
}
} protected override void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition, string orderId, DateTime time)
{
// Aquí puedes agregar cualquier lógica para analizar el rendimiento de cada operación ejecutada
if (execution.Order.OrderState == OrderState.Filled)
{
Print("Orden ejecutada: " + execution.ToString());
}
}
public override void OnRenderTarget()
{
// Al finalizar el backtest, se puede analizar el rendimiento desde la pestaña de estrategias
}
}
Explicación del código:
- Configuración de Stop Loss y Take Profit:
- Los factores de stop loss y take profit están configurados como propiedades ajustables por el usuario. Esto te permite modificar fácilmente los valores de estos parámetros sin tener que cambiar el código.
- Ambos valores se basan en el ATR (Average True Range), lo que permite que se ajusten automáticamente a la volatilidad del mercado.
- Backtesting en Datos de los Últimos Dos Años:
- La estrategia se configura para correr un backtesting en datos de los últimos dos años (
SetStartDate(DateTime.Now.AddYears(-2))). - Esto se hace en el método
OnStateChangedentro del bloqueState.Configure.
- La estrategia se configura para correr un backtesting en datos de los últimos dos años (
- Lógica de Cruce de Medias:
- La estrategia sigue la lógica clásica de cruce de medias (rápida y lenta), comprando cuando la media rápida cruza por encima de la media lenta y vendiendo en corto cuando cruza por debajo.
- SetStopLoss y SetProfitTarget:
- Para cada operación, se configura un stop loss y un take profit basados en los factores ajustables por el usuario.
- Estas órdenes se calculan usando el precio actual de cierre (
Close[0]) menos o más el valor del ATR multiplicado por el factor.
- Análisis del Rendimiento:
- Durante la ejecución de cada operación, se imprimen los detalles de las órdenes en la consola para su análisis.
- La pestaña de estrategias de NinjaTrader proporciona informes detallados del rendimiento al finalizar el backtesting, incluyendo ratios como el drawdown, profit factor, y otros.
Próximos pasos:
- Ejecuta el backtesting en NinjaTrader utilizando este script y analiza los resultados en la pestaña Estrategias.
- Ajusta los factores de stop loss y take profit para analizar cómo estos parámetros afectan el rendimiento de la estrategia.
5.5. Gestión de Riesgos
La gestión de riesgos es un componente crítico de cualquier estrategia de trading. En NinjaScript, puedes integrar varias técnicas para manejar el riesgo de manera efectiva:
- Porcentaje de capital por operación: Limita el tamaño de cada operación a un porcentaje fijo de tu capital total, por ejemplo, el 2%.
- Diversificación: Evita sobreexponerte a un solo instrumento o mercado.
- Trailing Stops: Un stop loss dinámico que se mueve a medida que el precio va a tu favor.
Ejemplo de Trailing Stop
if (CrossAbove(SMA(periodosRapidos), SMA(periodosLentos), 1))
{
EnterLong();
SetTrailStop(CalculationMode.Ticks, 15); // Stop loss dinámico que sigue el precio a medida que sube
}
Un trailing stop ajusta automáticamente el stop loss a medida que la operación es más rentable.
Explicación del código:
Este código implementa una estrategia de trading basada en el cruce de medias móviles simples (SMA) con un trailing stop, que es un tipo de stop loss dinámico que se ajusta automáticamente a medida que el precio se mueve a favor de la operación.
- Condición de entrada:
if (CrossAbove(SMA(periodosRapidos), SMA(periodosLentos), 1))
- CrossAbove: Esta función verifica si la media móvil rápida (
SMA(periodosRapidos)) cruza por encima de la media móvil lenta (SMA(periodosLentos)). - 1: El último parámetro indica que se considera un cruce de una barra.
- Si la condición es verdadera, significa que hay una señal de compra, ya que la media rápida (a corto plazo) está superando la media lenta, lo que sugiere un posible cambio de tendencia al alza.
- Apertura de una posición larga:
EnterLong();
- Si el cruce ocurre, se abre una posición larga (compra). Esto implica que el trader está apostando a que el precio subirá.
- Trailing Stop (Stop Loss Dinámico):
SetTrailStop(CalculationMode.Ticks, 15);
- SetTrailStop: Establece un stop loss dinámico, conocido como trailing stop, que sigue al precio a medida que el mercado se mueve a favor de la operación.
- CalculationMode.Ticks: Indica que el valor del trailing stop se calcula en función de ticks (unidad de movimiento mínimo de precio).
- 15 ticks: El trailing stop se ajustará para mantenerse siempre a 15 ticks del precio actual. Si el precio sube, el stop loss sube con él, pero si el precio baja, el stop loss no se mueve, protegiendo así las ganancias a medida que el mercado sube.
Resumen:
- Cuando la media rápida cruza por encima de la media lenta, se abre una posición larga.
- Un trailing stop de 15 ticks se utiliza para gestionar el riesgo. A medida que el precio sube, el stop loss sube con él, asegurando que si el mercado se da vuelta, las pérdidas estén limitadas o se bloqueen ganancias.
Este enfoque permite capturar ganancias en tendencias alcistas y protege contra retrocesos bruscos en el precio.
Caso de Uso Real: Gestión de Riesgo Basada en Volatilidad
Supón que decides ajustar el tamaño de la posición en función de la volatilidad, medida por el ATR (Average True Range). Un tamaño de operación mayor cuando la volatilidad es baja y menor cuando es alta puede ayudar a controlar el riesgo.
double volatilidad = ATR(14)[0];int tamañoPosicion = (int)(1000 / volatilidad); // Ajusta el tamaño de la posición basado en la volatilidad
EnterLong(tamañoPosicion);
Explicación del código:
Este código ajusta el tamaño de una posición de trading en función de la volatilidad del mercado, utilizando el Average True Range (ATR), un indicador técnico que mide la volatilidad del precio durante un periodo específico.
- Cálculo de la volatilidad:
double volatilidad = ATR(14)[0];
- ATR(14): Esta función calcula el Average True Range (Rango Verdadero Promedio) utilizando los últimos 14 periodos. El ATR es un indicador que mide la volatilidad del mercado al considerar la amplitud de las barras (máximo – mínimo) en el precio.
- [0]: Indica que estamos accediendo al valor del ATR de la barra actual (la más reciente).
- El valor calculado de
volatilidadrepresenta la variabilidad del precio en los últimos 14 periodos. Cuanto mayor sea el valor del ATR, más volátil es el mercado.
- Cálculo del tamaño de la posición:
int tamañoPosicion = (int)(1000 / volatilidad);
- El tamaño de la posición se ajusta de acuerdo con la volatilidad.
- 1000: Representa un valor de referencia para la inversión (puede ser capital o un valor estándar).
- (1000 / volatilidad): Se ajusta el tamaño de la posición en función de la volatilidad. Si la volatilidad es alta (el valor del ATR es grande), el tamaño de la posición será más pequeño, para gestionar el riesgo. Si la volatilidad es baja, el tamaño de la posición será más grande.
- El resultado se convierte en un número entero (int) para definir el número de contratos o acciones que se comprarán o venderán.
- Entrada en una posición larga:
EnterLong(tamañoPosicion);
- EnterLong(tamañoPosicion): Abre una posición larga (compra), con el tamaño de posición calculado en función de la volatilidad.
- El tamaño de la posición se ajusta dinámicamente para que, cuando la volatilidad sea alta, el riesgo se reduzca tomando una posición más pequeña, y cuando la volatilidad sea baja, se pueda tomar una posición mayor, maximizando la exposición.
Resumen:
Este código ajusta el tamaño de la posición en función de la volatilidad del mercado utilizando el ATR. En mercados volátiles, se reduce el tamaño de la posición para limitar el riesgo; en mercados tranquilos, se aumenta para maximizar el potencial de ganancias. Este enfoque es útil para gestionar el riesgo de manera dinámica en estrategias de trading.
Ejercicio Práctico:
Implementa una estrategia que ajuste automáticamente el tamaño de la posición basado en la volatilidad medida por el ATR.
Ver solución:
public class EstrategiaATR : Strategy
{
private int periodosATR = 14; // Períodos para el ATR
private double capital = 1000; // Capital o valor de referencia para ajustar el tamaño de la posición
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Estrategia que ajusta el tamaño de la posición basado en la volatilidad medida por el ATR.";
Name = "EstrategiaATR";
Calculate = Calculate.OnEachTick;
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < periodosATR)
return; // Espera a que haya suficientes datos para calcular el ATR
// Calcular el ATR (volatilidad) de los últimos 14 periodos
double volatilidad = ATR(periodosATR)[0];
// Ajustar el tamaño de la posición en función de la volatilidad
int tamañoPosicion = (int)(capital / volatilidad);
// Condiciones para abrir una posición larga (puedes ajustar la lógica)
if (CrossAbove(SMA(14), SMA(50), 1))
{
Print("Tamaño de la posición: " + tamañoPosicion);
EnterLong(tamañoPosicion); // Comprar con el tamaño ajustado
}
// Condiciones para abrir una posición corta (puedes ajustar la lógica)
else if (CrossBelow(SMA(14), SMA(50), 1))
{
Print("Tamaño de la posición: " + tamañoPosicion);
EnterShort(tamañoPosicion); // Vender en corto con el tamaño ajustado
}
}
}
Explicación del Código:
- Parámetros Iniciales:
periodosATR: Define el número de periodos que se utilizarán para calcular el ATR (14 es común).capital: Representa un valor de referencia para ajustar el tamaño de la posición (puede ser el capital disponible).
- OnStateChange():
- Aquí se definen las configuraciones básicas de la estrategia, como su nombre y descripción.
- OnBarUpdate():
- Primero, se verifica que haya suficientes barras para calcular el ATR.
- ATR(periodosATR): Se calcula la volatilidad del mercado utilizando el ATR de 14 periodos.
- El tamaño de la posición se ajusta dividiendo el capital de referencia entre el valor del ATR, lo que permite reducir el tamaño en mercados volátiles y aumentarlo en mercados tranquilos.
- Condiciones de Trading:
- Si la media móvil rápida SMA(14) cruza por encima de la media móvil lenta SMA(50), se abre una posición larga con el tamaño ajustado.
- Si la media rápida cruza por debajo de la media lenta, se abre una posición corta.
Ejercicio:
- Ajusta los parámetros como
capitalyperiodosATRsegún tus necesidades. - Cambia las condiciones de entrada y salida de acuerdo con tu estrategia preferida.
- Realiza pruebas en el backtesting de NinjaTrader para analizar el rendimiento de la estrategia ajustada por volatilidad.
Conclusión: En este capítulo, hemos cubierto el desarrollo de estrategias automatizadas en NinjaScript, desde la creación de una estrategia básica hasta la implementación de gestión de riesgos avanzada y el uso de backtesting para optimizar la estrategia. La capacidad de automatizar, probar y optimizar tus estrategias en NinjaTrader te permite tomar decisiones informadas y mejorar tu rendimiento como trader. En el próximo capítulo, exploraremos estrategias avanzadas y técnicas para combinar múltiples indicadores y señales de trading en un solo sistema robusto.
Capítulo 6: Uso de Datos de Mercado y Series Temporales
6.1. Acceso y Manipulación de Datos de Mercado
Los datos de mercado son la base sobre la que se desarrollan las estrategias de trading automatizadas. En NinjaScript, es posible acceder a los datos de mercado, como precios de apertura, cierre, máximos, mínimos y volumen, y utilizarlos para crear indicadores y estrategias personalizadas.
Acceso a los Datos de Mercado
NinjaScript proporciona métodos sencillos para acceder a los diferentes tipos de datos de mercado:
Close[0]: Precio de cierre de la barra actual.Open[0]: Precio de apertura de la barra actual.High[0]: Precio máximo de la barra actual.Low[0]: Precio mínimo de la barra actual.Volume[0]: Volumen de la barra actual.
Ejemplo de Código:
protected override void OnBarUpdate()
{
double precioCierre = Close[0];
double precioApertura = Open[0];
double maximo = High[0];
double minimo = Low[0];
double volumen = Volume[0];
Print($"Cierre: {precioCierre}, Apertura: {precioApertura}, Máximo: {maximo}, Mínimo: {minimo}, Volumen: {volumen}");
}
En este ejemplo, el código imprime en la ventana de salida los valores de la barra actual. Se pueden usar estos datos para cálculos adicionales en las estrategias o indicadores.
Ejercicio Práctico:
Crea una estrategia que compre cuando el precio de cierre de la barra actual sea mayor que el de la barra anterior, y venda cuando sea menor.
Ver solución:
public class EstrategiaCompraVentaSimple : Strategy
{
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Estrategia que compra cuando el cierre actual es mayor que el cierre anterior y vende cuando es menor.";
Name = "EstrategiaCompraVentaSimple";
Calculate = Calculate.OnEachTick;
}
}
protected override void OnBarUpdate()
{
// Asegurarse de que haya al menos una barra previa para comparar
if (CurrentBar < 1)
return;
// Compra cuando el cierre de la barra actual es mayor que el de la barra anterior
if (Close[0] > Close[1])
{
if (Position.MarketPosition != MarketPosition.Long) // Verifica si ya no hay una posición larga abierta
{
EnterLong(); // Compra
}
}
// Vende cuando el cierre de la barra actual es menor que el de la barra anterior
else if (Close[0] < Close[1])
{
if (Position.MarketPosition != MarketPosition.Short) // Verifica si ya no hay una posición corta abierta
{
EnterShort(); // Vende en corto
}
}
}
}
Explicación del Código:
- OnStateChange():
- Se define la configuración de la estrategia, dándole un nombre y descripción, y estableciendo que se evaluará en cada tick (
Calculate.OnEachTick).
- Se define la configuración de la estrategia, dándole un nombre y descripción, y estableciendo que se evaluará en cada tick (
- OnBarUpdate():
- CurrentBar < 1: Se verifica que haya al menos una barra anterior para hacer la comparación.
- Close[0] > Close[1]: Si el precio de cierre de la barra actual (
Close[0]) es mayor que el de la barra anterior (Close[1]), se ejecuta una orden de compra (EnterLong()). - Close[0] < Close[1]: Si el precio de cierre de la barra actual es menor que el de la barra anterior, se ejecuta una orden de venta en corto (
EnterShort()). - La condición
Position.MarketPositionasegura que no se abra una nueva posición si ya hay una del mismo tipo (larga o corta) activa.
Ejercicio:
- Ejecuta este código en NinjaTrader para realizar un backtesting.
- Ajusta las condiciones según tus necesidades, como incluir stop-loss o take-profit, o cambiar las condiciones de entrada.
6.2. Trabajando con Series Temporales (Time Series)
Una serie temporal es una secuencia de datos organizados cronológicamente. En trading, las series temporales suelen estar representadas por precios, volúmenes y otros valores de mercado en intervalos de tiempo específicos (por ejemplo, barras de 1 minuto, 5 minutos, etc.).
Uso de Series Temporales en NinjaScript
NinjaScript permite trabajar con series temporales de forma eficiente mediante clases como Bars y métodos como GetBar(). Además, puedes acceder a barras en diferentes intervalos de tiempo usando AddDataSeries(), lo que permite combinar múltiples marcos temporales en una sola estrategia.
Bars: Representa la serie de barras del gráfico, que contiene la información de cada barra (como el precio de apertura, cierre, máximo, mínimo, etc.). Permite acceder a la información de las barras históricas y actuales.AddDataSeries(): Este comando se utiliza para añadir series temporales adicionales a una estrategia o indicador. Por ejemplo, puedes agregar una serie de barras de 5 minutos a una estrategia basada en barras de 1 minuto para realizar análisis en múltiples marcos temporales.
AddDataSeries(Data.BarsPeriodType.Minute, 5); // Agrega una serie temporal de 5 minutos
GetBar(): Este método se usa para obtener el índice de una barra específica en función de una fecha y hora determinada. Esto es útil cuando necesitas encontrar el índice de una barra en particular en el gráfico.
int index = GetBar(new DateTime(2023, 9, 17));
BarsInProgress: En estrategias multimarco temporal (cuando se han agregado varias series conAddDataSeries()), este comando indica qué serie temporal está siendo procesada en ese momento. Ayuda a gestionar el flujo de lógica para las diferentes series temporales.
if (BarsInProgress == 1) // Serie de 5 minutos, por ejemplo
CurrentBar: Devuelve el índice de la barra actual que se está procesando. Es útil para controlar cuántas barras han sido procesadas hasta el momento o para evitar que se ejecuten cálculos antes de que haya suficientes datos.
if (CurrentBar < 20)
return; // Espera hasta tener 20 barras
BarsArray[]: Utilizado para acceder a diferentes series temporales cuando estás trabajando con múltiples marcos temporales. Cada índice deBarsArraycorresponde a una serie temporal añadida conAddDataSeries().
double close5Min = Closes[1][0]; // Precio de cierre de la serie de 5 minutos
Closes[]: Específicamente, este comando te permite acceder a los precios de cierre de diferentes series temporales cuando se trabaja con varias series. Cada índice enClosescorresponde a una serie temporal.Time[]: Devuelve el tiempo (fecha y hora) de una barra específica. Es útil cuando necesitas comparar tiempos o realizar cálculos en función de momentos específicos.
DateTime timeBar = Time[0]; // Tiempo de la barra actual
IsFirstTickOfBar: Indica si el código está siendo ejecutado en el primer tick de una nueva barra. Es útil para evitar realizar ciertos cálculos múltiples veces dentro de la misma barra.
if (IsFirstTickOfBar)
Print("Primera ejecución de la barra");
BarsSinceEntryExecution(): Devuelve el número de barras transcurridas desde que se ejecutó la última operación de entrada (compra o venta). Es útil para gestionar posiciones abiertas.
Estos comandos son fundamentales para trabajar con series temporales en NinjaScript, ya sea para análisis en un solo marco temporal o para estrategias avanzadas que involucran múltiples series.
Ejemplo: Acceso a Múltiples Marcos Temporales
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
AddDataSeries(Data.BarsPeriodType.Minute, 5); // Serie temporal de 5 minutos
AddDataSeries(Data.BarsPeriodType.Minute, 15); // Serie temporal de 15 minutos
}
}
protected override void OnBarUpdate()
{
if (BarsInProgress == 0) // Serie principal (1 minuto, por ejemplo)
{
double cierrePrincipal = Close[0];
Print($"Cierre de 1 minuto: {cierrePrincipal}");
}
if (BarsInProgress == 1) // Serie de 5 minutos
{
double cierre5Minutos = Close[0];
Print($"Cierre de 5 minutos: {cierre5Minutos}");
}
if (BarsInProgress == 2) // Serie de 15 minutos
{
double cierre15Minutos = Close[0];
Print($"Cierre de 15 minutos: {cierre15Minutos}");
}
}
Este código accede a series temporales de 1, 5 y 15 minutos dentro de una misma estrategia, lo que permite realizar análisis y operaciones basadas en múltiples marcos temporales.
Explicación del código:
OnStateChange():- Aquí se configuran las series de datos adicionales (de distintos marcos temporales) que se van a utilizar en la estrategia.
AddDataSeries(Data.BarsPeriodType.Minute, 5);: Añade una serie temporal de 5 minutos a la estrategia.AddDataSeries(Data.BarsPeriodType.Minute, 15);: Añade una serie temporal de 15 minutos.- De esta manera, además de la serie principal (que podría ser de 1 minuto si así está configurada), la estrategia ahora podrá acceder a las barras de 5 y 15 minutos.
OnBarUpdate():- Esta función se ejecuta cada vez que se recibe un nuevo tick o barra.
- El código utiliza la variable
BarsInProgresspara distinguir entre las diferentes series temporales que están siendo procesadas:BarsInProgress == 0: La estrategia está procesando la serie principal, que puede ser de 1 minuto. Imprime el precio de cierre de la barra actual en esta serie.BarsInProgress == 1: Se está procesando la serie temporal de 5 minutos. Imprime el precio de cierre de la barra actual en la serie de 5 minutos.BarsInProgress == 2: Se está procesando la serie temporal de 15 minutos. Imprime el precio de cierre de la barra actual en la serie de 15 minutos.
Objetivo:
El código permite acceder y trabajar con diferentes marcos temporales dentro de una misma estrategia. Por ejemplo, podrías usar los cierres de 5 y 15 minutos para confirmar señales de trading en la serie principal (1 minuto), o aplicar diferentes reglas según el timeframe.
Ejemplo de salida del código:
Cuando se ejecuta la estrategia, podrías ver algo como esto en la consola de salida:
Cierre de 1 minuto: 100.25
Cierre de 5 minutos: 100.40
Cierre de 15 minutos: 100.15
Esto permite al trader analizar los cierres de diferentes marcos temporales y usarlos para tomar decisiones de trading.
Ejercicio Práctico:
Modifica el ejemplo anterior para crear una estrategia que opere cuando el precio de cierre de la serie de 1 minuto esté por encima de la media móvil de la serie de 15 minutos.
Ver solución:
public class EstrategiaMultitimeframe : Strategy
{
private int periodosMediaMovil = 14;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Description = "Estrategia que opera cuando el cierre de 1 minuto está por encima de la media móvil de 15 minutos.";
Name = "EstrategiaMultitimeframe";
// Añadir series de datos de distintos marcos temporales
AddDataSeries(Data.BarsPeriodType.Minute, 5); // Serie temporal de 5 minutos
AddDataSeries(Data.BarsPeriodType.Minute, 15); // Serie temporal de 15 minutos
}
}
protected override void OnBarUpdate()
{
// Asegurarse de que hay suficientes datos en la serie de 15 minutos para calcular la media móvil
if (BarsInProgress == 2 && CurrentBars[2] < periodosMediaMovil)
return;
// Estrategia opera solo en la serie de 1 minuto (BarsInProgress == 0)
if (BarsInProgress == 0)
{
// Obtener el cierre de la barra de 1 minuto
double cierre1Minuto = Close[0];
// Obtener la media móvil de 14 periodos en la serie de 15 minutos
double mediaMovil15Minutos = SMA(BarsArray[2], periodosMediaMovil)[0];
// Imprimir los valores en la consola de salida para referencia
Print($"Cierre de 1 minuto: {cierre1Minuto}, Media móvil de 15 minutos: {mediaMovil15Minutos}");
// Si el cierre de 1 minuto está por encima de la media móvil de 15 minutos, entra en largo
if (cierre1Minuto > mediaMovil15Minutos)
{
if (Position.MarketPosition == MarketPosition.Flat)
{
EnterLong();
Print("Entrando en largo: El precio de cierre de 1 minuto está por encima de la media móvil de 15 minutos.");
}
}
// Si el cierre de 1 minuto está por debajo de la media móvil de 15 minutos, sale de la posición
else if (cierre1Minuto < mediaMovil15Minutos)
{
if (Position.MarketPosition == MarketPosition.Long)
{
ExitLong();
Print("Saliendo de largo: El precio de cierre de 1 minuto está por debajo de la media móvil de 15 minutos.");
}
}
}
}
}
Explicación de los cambios:
BarsArray[2]: Se utiliza para acceder a la serie de 15 minutos, ya que se añadió como la tercera serie temporal (índice 2).- Cálculo de la media móvil: Utiliza la función
SMA()para calcular la media móvil de 14 periodos en la serie de 15 minutos. - Condición de entrada y salida: La estrategia verifica si el cierre de la barra de 1 minuto es mayor que la media móvil de la serie de 15 minutos. Si es así, entra en largo. Si es menor, sale de la posición larga.
Resultado:
La estrategia comprará cuando el cierre de la serie de 1 minuto esté por encima de la media móvil de la serie de 15 minutos y venderá cuando el cierre esté por debajo.
6.3. Creación de Gráficos Personalizados
NinjaTrader permite a los traders visualizar datos de mercado y resultados de estrategias de forma gráfica. A través de NinjaScript, es posible crear gráficos personalizados que muestren información adicional, como indicadores visuales y líneas de tendencia.
Uso de Dibujos en los Gráficos
NinjaScript ofrece métodos para dibujar líneas, marcadores, y otras formas sobre el gráfico de precios. Por ejemplo, puedes utilizar:
Draw.Line(): Dibuja una línea en el gráfico entre dos puntos definidos. Se utiliza para marcar tendencias o niveles clave entre dos barras específicas, como precios máximos o mínimos.Draw.Text(): Coloca un texto en el gráfico en una ubicación específica. Es útil para mostrar mensajes, etiquetas o anotaciones en el gráfico, ya sea sobre una barra específica o en un área general.Draw.ArrowUp(): Dibuja una flecha apuntando hacia arriba en el gráfico en una barra específica. Generalmente se usa para señalar oportunidades de compra u otros eventos importantes en el gráfico.Draw.ArrowDown(): Dibuja una flecha apuntando hacia abajo en el gráfico. Se utiliza generalmente para señalar oportunidades de venta o eventos importantes que sugieren una caída en el precio.Draw.Dot(): Dibuja un punto o círculo en el gráfico. Es útil para marcar puntos específicos como máximos, mínimos o cruces de medias móviles.Draw.Region(): Dibuja un área sombreada entre dos niveles de precios. Se utiliza para resaltar zonas de soporte y resistencia o cualquier otra área importante en el gráfico.Draw.TriangleUp(): Dibuja un triángulo apuntando hacia arriba. Funciona de manera similar aDraw.ArrowUp(), pero con un símbolo diferente, útil para marcar puntos clave.Draw.TriangleDown(): Dibuja un triángulo apuntando hacia abajo, similar aDraw.ArrowDown(), pero con un símbolo alternativo.Draw.Square(): Dibuja un cuadrado en el gráfico. Útil para destacar un punto o zona específica, como una consolidación.Draw.Rectangle(): Dibuja un rectángulo en el gráfico. Se suele usar para marcar áreas de consolidación o zonas de trading.Draw.TextFixed(): Similar aDraw.Text(), pero coloca el texto en una ubicación fija en la ventana del gráfico (como la esquina superior derecha o inferior izquierda). Es útil para mostrar información que siempre debe estar visible, como valores de indicadores clave o estadísticas de la estrategia.Draw.VerticalLine(): Dibuja una línea vertical en el gráfico. Es útil para marcar eventos importantes en el tiempo, como el inicio o el fin de una sesión de trading.Draw.HorizontalLine(): Dibuja una línea horizontal en el gráfico, que suele utilizarse para marcar niveles de precios como soportes, resistencias o umbrales clave.
Estos comandos te permiten agregar visualizaciones adicionales al gráfico en NinjaScript, facilitando el análisis y la interpretación de la acción del precio o de los indicadores. Draw.Line(), Draw.Text(), o Draw.ArrowUp() para mostrar indicadores visuales en el gráfico.
Ejemplo: Dibujar una Línea de Tendencia
protected override void OnBarUpdate()
{
if (CurrentBar < 20)
return; // Espera hasta que haya suficientes datos
// Dibuja una línea entre los precios mínimos de la barra 10 y la barra actual
Draw.Line(this, "LineaTendencia", 10, Low[10], 0, Low[0], Brushes.Green);
}
En este ejemplo, se dibuja una línea verde en el gráfico que conecta el precio mínimo de la barra 10 con el precio mínimo de la barra actual. Este tipo de gráfico es útil para identificar tendencias o niveles clave de soporte y resistencia.
Ejercicio Práctico:
Añade una flecha en el gráfico cuando el precio de cierre esté por encima de la media móvil de 50 periodos.
Ver solución:
Este sería el código modificado para añadir una flecha en el gráfico cuando el precio de cierre esté por encima de la media móvil de 50 periodos, pero también se mantiene la lógica que dibuja una línea entre los precios mínimos de la barra 10 y la barra actual:
protected override void OnBarUpdate()
{
if (CurrentBar < 50)
return; // Espera hasta que haya suficientes datos para calcular la media móvil de 50 periodos
// Dibuja una línea entre los precios mínimos de la barra 10 y la barra actual
Draw.Line(this, "LineaTendencia", 10, Low[10], 0, Low[0], Brushes.Green);
// Calcula la media móvil de 50 periodos
double mediaMovil50 = SMA(Close, 50)[0];
// Si el precio de cierre está por encima de la media móvil de 50 periodos
if (Close[0] > mediaMovil50)
{
// Dibuja una flecha hacia arriba en el gráfico en la barra actual
Draw.ArrowUp(this, "FlechaArriba" + CurrentBar, false, 0, Low[0] - TickSize * 2, Brushes.Blue);
}
}
Explicación de los cambios:
- Cálculo de la media móvil de 50 periodos: Se utiliza la función
SMA(Close, 50)para calcular la media móvil de los últimos 50 periodos. - Condición para la flecha: Si el precio de cierre (
Close[0]) está por encima de la media móvil calculada, se dibuja una flecha hacia arriba en la barra actual. Draw.ArrowUp: Esta función dibuja una flecha apuntando hacia arriba en el gráfico. Se coloca ligeramente por debajo del mínimo de la barra actual para que sea visible.- Identificador único para la flecha:
"FlechaArriba" + CurrentBarasegura que cada flecha tenga un identificador único, basado en el número de la barra actual (CurrentBar).
Con este código, cada vez que el precio de cierre supere la media móvil de 50 periodos, se dibujará una flecha azul en el gráfico.
Conclusión: Este capítulo te ha mostrado cómo acceder y manipular los datos de mercado, trabajar con series temporales, y crear gráficos personalizados en NinjaScript. Estos conceptos son esenciales para desarrollar estrategias y análisis avanzados basados en datos reales y gráficos visualmente atractivos. En el próximo capítulo, nos adentraremos en la optimización avanzada de estrategias y el uso de técnicas de machine learning en el desarrollo de sistemas de trading.
