Programación en NinjaScript (II)

Trading Meetings  » Tutoriales »  Programación en NinjaScript (II)
0 Comments 01:37

 
 

Sumario:

Programación en NinjaScript (I)

  1. Introducción a NinjaScript
  2. Configuración Inicial
  3. Conceptos Básicos de Programación en NinjaScript
  4. Creación de Indicadores Personalizados
  5. Desarrollo de Estrategias de Trading
  6. Uso de Datos de Mercado y Series Temporales

Programación en NinjaScript (II)

  1. Gestión de Eventos y Operaciones en Tiempo Real
  2. Interacción con APIs Externas
  3. Consejos y Mejores Prácticas
  4. Casos de Uso Reales
  5. Recursos Adicionales y Continuación del Aprendizaje
  6. Conclusión
 
 

En trading automatizado, es crucial poder responder a eventos de mercado en tiempo real para tomar decisiones inmediatas que afecten las estrategias de trading. NinjaScript ofrece eventos clave que permiten monitorear el comportamiento del mercado y actuar en consecuencia.

Eventos en NinjaScript

Algunos de los eventos más importantes que puedes manejar en tiempo real son:

  1. OnBarUpdate():
    • Se llama en cada nueva barra o tick de datos. Es el evento principal donde se ejecuta la lógica de tu estrategia o indicador.
    • Se utiliza para implementar condiciones de trading, cálculos de indicadores, y más.
  2. OnMarketData():
    • Se ejecuta en tiempo real cuando llega nueva información de mercado como el precio de compra, venta, o el último precio de un activo.
    • Útil para operar con datos en tiempo real a nivel de tick.
  3. OnOrderUpdate():
    • Se activa cuando hay actualizaciones en las órdenes enviadas, como cuando una orden es ejecutada, modificada, cancelada o rechazada.
    • Ideal para hacer seguimiento del estado de las órdenes.
  4. OnExecutionUpdate():
    • Se ejecuta cuando una orden se convierte en una ejecución.
    • Es útil para obtener detalles exactos del precio y tamaño de la ejecución de una orden.
  5. OnPositionUpdate():
    • Este evento se activa cuando hay cambios en la posición actual de un instrumento, como una nueva entrada o salida de una operación.
    • Permite gestionar el tamaño de la posición y monitorear su evolución.
  6. OnConnectionStatusUpdate():
    • Se activa cuando cambia el estado de conexión de la plataforma, como conexión perdida o reconexión a un servidor de datos o broker.
    • Útil para gestionar problemas de conectividad en tiempo real.
  7. OnAccountItemUpdate():
    • Este evento recibe actualizaciones de los elementos de la cuenta, como balance, margen o equity.
    • Es útil para monitorear el estado de tu cuenta en tiempo real.
  8. OnStateChange():
    • Se ejecuta cada vez que cambia el estado de un indicador o estrategia, como la inicialización, carga de datos históricos o cuando se empieza a operar en tiempo real.
    • Ideal para configurar el entorno y variables antes de que se inicie la lógica principal.

Estos eventos son esenciales para desarrollar estrategias y gestionar operaciones en tiempo real con NinjaScript.

Ejemplo: Respuesta a un Cambio de Precio en Tiempo Real

protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
{

  if (marketDataUpdate.MarketDataType == MarketDataType.Last)
  {
    Print(
$"Último precio: {marketDataUpdate.Price}");

    if (marketDataUpdate.Price > Close[0] + 10) // Precio mayor al cierre anterior más 10
    {
      EnterLong();
// Ejecuta una compra
    }
  }
}


En este ejemplo, el código monitorea el precio más reciente y ejecuta una orden de compra si el precio supera el cierre anterior por 10 puntos.

Explicación del código:

Este código implementa una lógica en el evento OnMarketData(), que se ejecuta cada vez que llega nueva información de mercado. Aquí explicamos brevemente cómo funciona:

  1. OnMarketData(MarketDataEventArgs marketDataUpdate): Este método recibe datos de mercado en tiempo real, como el precio de compra, venta o el último precio de un activo.
  2. if (marketDataUpdate.MarketDataType == MarketDataType.Last): Verifica si el tipo de dato recibido es el último precio de la barra actual.
  3. Print($"Último precio: {marketDataUpdate.Price}"): Imprime el último precio recibido en la consola.
  4. if (marketDataUpdate.Price > Close[0] + 10): Comprueba si el último precio es mayor que el precio de cierre de la barra anterior más 10 unidades.
  5. EnterLong();: Si se cumple la condición, se ejecuta una compra (posición larga) en el mercado.

Este código permite tomar decisiones de compra en función de los datos de precio recibidos en tiempo real.

Ejercicio práctico:

Modifica el ejemplo para que en lugar de una compra, se ejecute una venta si el precio cae por debajo del mínimo de la barra anterior.

Ver solución:

protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
{

  if (marketDataUpdate.MarketDataType == MarketDataType.Last)
  {
    Print(
$"Último precio: {marketDataUpdate.Price}");

    // Verifica si el precio cae por debajo del mínimo de la barra anterior
    if (marketDataUpdate.Price < Low[1]) // Precio menor al mínimo de la barra anterior
    {
      EnterShort(); // Ejecuta una venta (posición corta)
    }
  }
}


Explicación de las modificaciones:

  1. if (marketDataUpdate.Price < Low[1]): Cambiamos la condición para que verifique si el último precio es menor que el mínimo de la barra anterior. Low[1] representa el mínimo de la barra anterior.
  2. EnterShort();: En lugar de EnterLong(); (compra), se usa EnterShort(); para ejecutar una venta en corto si se cumple la condición.

Con este cambio, el código ahora tomará una posición corta cuando el precio caiga por debajo del mínimo de la barra anterior.


La ejecución de órdenes es el corazón de cualquier estrategia de trading automatizada. NinjaScript proporciona métodos sencillos para colocar, modificar y cancelar órdenes en tiempo real.

Tipos de Órdenes

NinjaScript admite varios tipos de órdenes, como:

  • EnterLong() / EnterShort():
    • Orden de compra/venta a mercado.
  • ExitLong() / ExitShort():
    • Cierre de posición larga o corta.
  • SubmitOrderUnmanaged():
    • Permite enviar órdenes de forma más avanzada sin la gestión automática del sistema.
  • EnterLongLimit() / EnterShortLimit():
    • Permite enviar una orden de compra o venta limitada a un precio específico.
    • Uso: Entra en largo o corto solo si el precio alcanza un nivel predeterminado.

EnterLongLimit(double price);
EnterShortLimit(
double price);


  • EnterLongStop() / EnterShortStop():
    • Envia una orden de compra o venta stop. Esta se convierte en una orden a mercado cuando el precio alcanza el nivel definido.
    • Uso: Sirve para entrar en operaciones solo cuando el mercado cruza un precio de stop.

EnterLongStop(double stopPrice);
EnterShortStop(
double stopPrice);


  • EnterLongStopLimit() / EnterShortStopLimit():
    • Combina una orden stop y una limit: primero se activa si el precio cruza el nivel de stop y luego solo se ejecuta si el precio sigue dentro de un rango específico.
    • Uso: Ofrece un control adicional sobre el precio de ejecución.

EnterLongStopLimit(double stopPrice, double limitPrice);
EnterShortStopLimit(
double stopPrice, double limitPrice);


  • ExitLongLimit() / ExitShortLimit():
    • Orden de salida limitada para una posición larga o corta.
    • Uso: Cierra una posición solo si el mercado alcanza un precio objetivo.

ExitLongLimit(double limitPrice);
ExitShortLimit(
double limitPrice);


  • ExitLongStop() / ExitShortStop():
    • Orden de salida mediante stop para una posición larga o corta.
    • Uso: Cierra la posición cuando el precio alcanza el nivel de stop.

ExitLongStop(double stopPrice);
ExitShortStop(
double stopPrice);


  • ExitLongStopLimit() / ExitShortStopLimit():
    • Combina una orden de stop y límite para salir de una posición, permitiendo una mayor precisión.
    • Uso: Sale de una operación solo si se cumplen las condiciones del stop y el límite.

ExitLongStopLimit(double stopPrice, double limitPrice);
ExitShortStopLimit(
double stopPrice, double limitPrice);


  • CancelOrder():
    • Permite cancelar una orden previamente enviada si aún no ha sido ejecutada.

CancelOrder(orderObject);


Estas órdenes ofrecen flexibilidad para manejar tanto la entrada como la salida de posiciones con condiciones específicas. En conjunto, permiten diseñar estrategias con diferentes tipos de órdenes dependiendo de las condiciones del mercado.

Ejemplo: Ejecución de Órdenes Condicionales

protected override void OnBarUpdate()
{

  if (Close[0] > Open[0]) // Condición para ejecutar la compra
  {
    EnterLong();
// Compra a mercado
  }

  if (Close[0] < Open[0]) // Condición para ejecutar la venta
  {
    EnterShort();
// Venta a mercado
  }
}


Este código ejecuta una compra si el precio de cierre es mayor que el de apertura, y una venta si el precio de cierre es menor. Estas órdenes se ejecutan en cada actualización de barra.

Explicación del código:

  1. Condición de compra:
    • if (Close[0] > Open[0]): Si el precio de cierre de la barra actual (Close[0]) es mayor que el precio de apertura (Open[0]), es decir, si la barra es alcista, la estrategia ejecuta una compra.
    • EnterLong();: Se toma una posición de compra a mercado (posición larga).
  2. Condición de venta:
    • if (Close[0] < Open[0]): Si el precio de cierre de la barra actual es menor que el precio de apertura, es decir, si la barra es bajista, la estrategia ejecuta una venta.
    • EnterShort();: Se toma una posición de venta a mercado (posición corta).

En resumen, la estrategia compra cuando la barra es alcista y vende cuando la barra es bajista, sin considerar ningún otro factor.

Ejercicio Práctico:

Modifica el ejemplo para incluir órdenes Stop Loss y Take Profit en ambas operaciones.

Ver solución:

protected override void OnBarUpdate()
{

  // Verifica si la barra es alcista para ejecutar la compra
  if (Close[0] > Open[0])
  {

    // Ejecuta la compra a mercado
    EnterLong();

    // Establece un Stop Loss de 10 ticks y un Take Profit de 20 ticks
    SetStopLoss(CalculationMode.Ticks, 10); // Stop Loss de 10 ticks
    SetProfitTarget(CalculationMode.Ticks, 20); // Take Profit de 20 ticks
  }

  // Verifica si la barra es bajista para ejecutar la venta
  if (Close[0] < Open[0])
  {

    // Ejecuta la venta a mercado
    EnterShort();

    // Establece un Stop Loss de 10 ticks y un Take Profit de 20 ticks
    SetStopLoss(CalculationMode.Ticks, 10); // Stop Loss de 10 ticks
    SetProfitTarget(CalculationMode.Ticks, 20); // Take Profit de 20 ticks
  }
}


Explicación de las modificaciones:

  1. SetStopLoss(CalculationMode.Ticks, 10): Establece un Stop Loss de 10 ticks, lo que limita las pérdidas si el mercado se mueve en contra de la posición.
  2. SetProfitTarget(CalculationMode.Ticks, 20): Establece un Take Profit de 20 ticks, lo que toma ganancias si el mercado se mueve a favor de la posición.

Este código implementa un manejo básico de riesgos, donde las órdenes se cierran automáticamente al alcanzar el Stop Loss o el Take Profit.


Una vez que las estrategias están en ejecución, es fundamental monitorear su desempeño y ajustar ciertos parámetros en tiempo real. Esto puede mejorar los resultados y minimizar el riesgo.

Monitoreo en NinjaScript

Puedes usar eventos como OnExecutionUpdate() o OnPositionUpdate() para monitorizar los estados de las órdenes y posiciones. Además, el método Print() es muy útil para depurar y obtener información en la ventana de salida.

Los principales eventos para monitorear y gestionar estrategias en tiempo real incluyen:

  • OnBarUpdate()
    • Descripción: Es el evento más común y fundamental. Se ejecuta cada vez que se genera una nueva barra de datos (candlestick) y es donde se implementan la mayoría de las lógicas de trading.
    • Uso: Monitorea las barras a medida que se completan, permitiendo analizar precios de apertura, cierre, máximos, mínimos, volumen, etc.
    • Ejemplo:

protected override void OnBarUpdate()
{

  if (Close[0] > Open[0])
  EnterLong();
// Compra si el precio de cierre es mayor que la apertura
}


  • OnMarketData()
    • Descripción: Se activa cuando llega nueva información de mercado en tiempo real, como los últimos precios de compra/venta, volumen, etc.
    • Uso: Monitorea datos en tiempo real, como actualizaciones de precios o volumen.
    • Ejemplo:

protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
{

  if (marketDataUpdate.MarketDataType == MarketDataType.Last)
  {
    Print(
$"Último precio: {marketDataUpdate.Price}");
  }
}


  • OnOrderUpdate()
    • Descripción: Se ejecuta cada vez que una orden cambia de estado (enviada, ejecutada, cancelada, etc.).
    • Uso: Monitorea y controla el estado de las órdenes.
    • Ejemplo:

protected override void OnOrderUpdate(Order order, double limitPrice, double stopPrice, int quantity, double filledPrice, OrderState orderState)
{
  Print(
$"Estado de la orden: {orderState}");
}


  • OnExecutionUpdate()
    • Descripción: Se llama cuando se produce una ejecución de una orden (cuando se completa total o parcialmente).
    • Uso: Controla las ejecuciones, lo que permite ajustar estrategias o monitorear resultados.
    • Ejemplo:

protected override void OnExecutionUpdate(Execution execution)
{
  Print(
$"Ejecutado a precio: {execution.Price}");
}


  • OnPositionUpdate()
    • Descripción: Se activa cada vez que la posición de la cuenta cambia (cuando una operación se abre, se incrementa o se cierra).
    • Uso: Monitorea cambios en las posiciones para realizar ajustes.
    • Ejemplo:

protected override void OnPositionUpdate(Position Position)
{
  Print(
$"Posición actual: {position.MarketPosition}");
}


  • OnAccountItemUpdate()
    • Descripción: Se ejecuta cuando cambia un valor del balance de la cuenta, como el margen disponible o el saldo.
    • Uso: Monitorea cambios en la cuenta para gestión de riesgo o cálculos financieros.
    • Ejemplo:

protected override void OnAccountItemUpdate(Account account, AccountItem accountItem, double value)
{
  Print(
$"Saldo de la cuenta: {value}");
}


  • OnConnectionStatusUpdate()
    • Descripción: Se activa cuando cambia el estado de conexión a los proveedores de datos o brokers.
    • Uso: Detecta cambios en la conexión para tomar decisiones como detener el trading si se pierde la conexión.
    • Ejemplo:

protected override void OnConnectionStatusUpdate(ConnectionStatusEventArgs connectionStatusUpdate)
{
  Print(
$"Estado de conexión: {connectionStatusUpdate.Status}");
}


  • OnFundamentalData()
    • Descripción: Se activa cuando se reciben datos fundamentales, como informes de ganancias u otros datos económicos.
    • Uso: Útil para estrategias basadas en análisis fundamental.
    • Ejemplo:

protected override void OnFundamentalData(FundamentalDataEventArgs fundamentalData)
{
  Print(
$"Dato fundamental recibido: {fundamentalData.Value}");
}


  • OnDataLoaded()
    • Descripción: Se llama cuando todos los datos históricos necesarios para una estrategia o indicador han sido cargados.
    • Uso: Útil para realizar cálculos o análisis una vez que los datos históricos están disponibles.
    • Ejemplo:

protected override void OnDataLoaded()
{
  Print(
"Datos históricos cargados");
}


Estos eventos son esenciales para manejar datos de mercado en tiempo real, gestionar órdenes, y monitorear posiciones y el estado de la cuenta en NinjaTrader.

Ejemplo: Monitoreo de una Orden Ejecutada

protected override void OnExecutionUpdate(Execution execution, string executionId, double price, int quantity, MarketPosition marketPosition)
{
  Print(
$"Orden ejecutada: {execution.Order.Name}, Precio: {price}, Cantidad: {quantity}");
}


Este ejemplo muestra cómo obtener información sobre una orden ejecutada en tiempo real, permitiendo monitorear el precio, cantidad y posición de la operación.

Ajuste Dinámico de Parámetros

Puedes ajustar dinámicamente los parámetros de la estrategia (como tamaño de la posición o límites de riesgo) basándote en las condiciones actuales del mercado:

protected override void OnBarUpdate()
{

  if (Position.MarketPosition == MarketPosition.Long && Close[0] < Open[0])
  {

    SetStopLoss(CalculationMode.Ticks, 10); // Ajusta el stop loss a 10 ticks si el precio cae
  }
}


Explicación del código:

  1. Condición de la posición larga:
    • Position.MarketPosition == MarketPosition.Long verifica si actualmente tienes una posición larga (es decir, has comprado).
  2. Condición del cierre menor que la apertura:
    • Close[0] < Open[0] verifica si el precio de cierre de la barra actual es menor que el precio de apertura, lo que indica que el precio ha bajado en la barra actual.
  3. Ajuste del stop loss:
    • SetStopLoss(CalculationMode.Ticks, 10) establece un stop loss de 10 ticks (puntos) por debajo del precio actual, para proteger la posición si el mercado sigue moviéndose en contra.

En resumen, si tienes una posición de compra y el precio cae (cierra por debajo de la apertura), el código ajusta el stop loss a 10 ticks para limitar las pérdidas.

Ejercicio Práctico:

Crea una estrategia que ajuste el tamaño de la posición dependiendo de la volatilidad del mercado, medida a través del rango de las últimas 10 barras.

Ver solución:

protected override void OnBarUpdate()
{

  if (CurrentBar < 10) // Asegurarse de que haya suficientes barras para el cálculo
    return;

  // Calcular el rango promedio de las últimas 10 barras
  double rangoPromedio = 0.0;
  for (int i = 0; i < 10; i++)
  {

    double rangoBarra = High[i] - Low[i];
    rangoPromedio += rangoBarra;
  }
  rangoPromedio /=
10; // Promedio del rango

  // Definir un tamaño de posición basado en la volatilidad (por ejemplo, mayor volatilidad -> menor tamaño de posición)

  double factorDeVolatilidad = 1000; // Puedes ajustar este factor según el nivel de riesgo deseado
  int tamañoPosicion = (int)(factorDeVolatilidad / rangoPromedio);

  // Condición para entrar en largo (barra alcista)
  if (Close[0] > Open[0])
  {
    EnterLong(tamañoPosicion);
// Compra ajustando el tamaño de la posición
  }

  // Condición para entrar en corto (barra bajista)
  if (Close[0] < Open[0])
  {
    EnterShort(tamañoPosicion);
// Venta ajustando el tamaño de la posición
  }
}


Explicación:

  1. Cálculo de la volatilidad: El código calcula el rango promedio de las últimas 10 barras, donde el rango de cada barra es High - Low. Esto refleja la volatilidad del mercado durante ese período.
  2. Ajuste del tamaño de la posición: El tamaño de la posición se ajusta inversamente en función del rango promedio, es decir, si la volatilidad es mayor, el tamaño de la posición es menor. Un factor de volatilidad (factorDeVolatilidad) controla el nivel de ajuste.
  3. Entrada en operaciones: La estrategia entra en largo si la barra es alcista y en corto si es bajista, ajustando el tamaño de la posición según la volatilidad.

Este ejercicio muestra cómo adaptar el tamaño de las posiciones al riesgo del mercado en función de la volatilidad reciente.


 
 

El NinjaTrader permite interactuar con APIs externas, lo que abre un mundo de posibilidades para obtener datos, realizar análisis avanzados o ejecutar órdenes en plataformas externas. En NinjaScript, puedes hacer esto utilizando clases como System.Net.Http.HttpClient para conectar y comunicarte con APIs externas.

Ejemplo: Conexión Básica a una API Externa

using System.Net.Http;

protected override void OnStateChange()
{

  if (State == State.Configure)
  {
    ConnectToAPI();
  }
}


private async void ConnectToAPI()
{
  HttpClient client =
new HttpClient();
  HttpResponseMessage response =
await client.GetAsync("https://api.example.com/data");
  string result = await response.Content.ReadAsStringAsync();
  Print(result);
}


Este ejemplo muestra cómo realizar una petición GET a una API externa. La respuesta obtenida puede ser utilizada para alimentar un indicador personalizado o una estrategia.

Aparte de System.Net.Http.HttpClient, existen varias clases y bibliotecas en .NET que puedes utilizar para conectarte a APIs externas y manejar solicitudes HTTP. Aquí tienes algunas alternativas:

  • WebRequest y HttpWebRequest (clases más antiguas):
    • Estas clases son más antiguas y proporcionan un control detallado sobre las solicitudes HTTP, pero su uso está más orientado a proyectos legacy.
    • WebRequest es la clase base abstracta, y HttpWebRequest es una implementación concreta para HTTP.
    • Ejemplo de uso básico:

WebRequest request = WebRequest.Create("https://api.example.com/data");
request.Method =
"GET";
using (WebResponse response = request.GetResponse())
{

  using (Stream dataStream = response.GetResponseStream())
  {
    StreamReader reader =
new StreamReader(dataStream);
    string responseFromServer = reader.ReadToEnd();
    Console.WriteLine(responseFromServer);
  }
}


  • RestSharp (biblioteca externa popular):
    • RestSharp es una biblioteca de terceros diseñada específicamente para facilitar el trabajo con APIs REST.
    • Tiene una sintaxis más sencilla y es adecuada para interactuar con servicios que exponen APIs RESTful.
    • Ejemplo básico de uso con RestSharp:

var client = new RestClient("https://api.example.com");
var request = new RestRequest("data", Method.GET);
var response = client.Execute(request);
Console.WriteLine(response.Content);


  • Flurl.Http (biblioteca externa):
    • Flurl es una biblioteca externa que facilita el manejo de URLs y solicitudes HTTP, ofreciendo una sintaxis fluida y soportando promesas asíncronas.
    • Es una opción atractiva para los desarrolladores que buscan un enfoque moderno y conciso para trabajar con APIs.
    • Ejemplo de uso con Flurl:

var response = await "https://api.example.com/data"
  .GetJsonAsync();
Console.WriteLine(response.Property);


  • WebClient (clase más simple):
    • WebClient es una clase más sencilla pero más limitada en comparación con HttpClient.
    • Es útil para tareas rápidas como descargar datos o enviar datos a una API.
    • Ejemplo básico:

using (WebClient client = new WebClient())
{

  string result = client.DownloadString("https://api.example.com/data");
  Console.WriteLine(result);
}


En general, se recomienda HttpClient o RestSharp para aplicaciones modernas, ya que son más flexibles, eficientes y fáciles de usar, especialmente con APIs RESTful.

Ejercicio Práctico:

Crea una función que realice una petición POST a una API de trading y envíe una orden. Simula la respuesta de la API y muestra el resultado en la ventana de salida.

Ver solución:

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

class Program
{
  static async Task Main(string[] args)
  {
    // Simulación de parámetros de la orden
    string symbol = "AAPL";
    string side = "buy"; // Puede ser "buy" o "sell"
    int quantity = 10;
    double price = 150.00;

    // Llamar a la función para enviar la orden
    await EnviarOrdenTrading(symbol, side, quantity, price);
  }

  // Función que realiza una petición POST a la API de trading
  static async Task EnviarOrdenTrading(string symbol, string side, int quantity, double price)
  {
    // Crear un cliente HTTP
    using (HttpClient client = new HttpClient())
    {
      // URL simulada de la API de trading
      string url = "https://api.tradingexample.com/orders";

      // Crear el contenido de la petición en formato JSON
      var orden = new
      {
        symbol = symbol,
        side = side,
        quantity = quantity,
        price = price
      };
      string jsonOrden = Newtonsoft.Json.JsonConvert.SerializeObject(orden);
      StringContent content = new StringContent(jsonOrden, Encoding.UTF8, "application/json");

      try
      {
        // Realizar la petición POST a la API
        HttpResponseMessage response = await client.PostAsync(url, content);

        // Simular respuesta de éxito de la API
        string respuestaSimulada = @"
        {
          ""status"": ""success"",
          ""orderId"": ""12345"",
          ""message"": ""Order executed successfully""
        }";
        Console.WriteLine("Simulando respuesta de la API:");
        Console.WriteLine(respuestaSimulada);

        // Analizar la respuesta (en este caso simulada)
        var resultado = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(respuestaSimulada);

        // Mostrar resultado en la consola
        Console.WriteLine($"Estado de la orden: {resultado.status}");
        Console.WriteLine($"ID de la orden: {resultado.orderId}");
        Console.WriteLine($"Mensaje: {resultado.message}");
      }
      catch (Exception ex)
      {
        // Manejo de errores en caso de fallo de la solicitud
        Console.WriteLine("Error al realizar la petición: " + ex.Message);
      }
    }
  }
}

Explicación del código:

  1. Simulación de parámetros de la orden:
    • Se definen parámetros como el símbolo (symbol), la cantidad de acciones (quantity), el precio (price), y si la orden es de compra o venta (side).
  2. EnviarOrdenTrading:
    • Esta es la función principal que realiza la solicitud POST a una API de trading.
    • Usa HttpClient para hacer la petición a una URL simulada.
    • Los datos de la orden se envían en formato JSON.
  3. Simulación de la respuesta de la API:
    • Simula una respuesta JSON con el estado de la orden, un ID y un mensaje de confirmación.
    • El JSON es deserializado para extraer la información relevante y mostrarla en la consola.
  4. Control de errores:
    • Se incluye un bloque try-catch para manejar posibles errores durante la ejecución de la petición.

Ejecución:

  • Al ejecutar el código, se verá una simulación de una respuesta de la API que indica que la orden fue ejecutada con éxito.
  • La respuesta y los detalles de la orden se muestran en la ventana de salida.

Este ejemplo te da una base para realizar peticiones POST a APIs de trading y cómo trabajar con las respuestas que recibes de ellas.


Con NinjaScript, puedes integrar APIs de proveedores de datos externos o brokers para ampliar las capacidades de tu plataforma. Esto es útil si quieres obtener datos de mercado más detallados o ejecutar órdenes en un broker que no esté directamente conectado con NinjaTrader.

Ejemplo: Integración con una API de Datos

private async void FetchMarketData()
{
  HttpClient client =
new HttpClient();
  HttpResponseMessage response =
await client.GetAsync("https://api.marketdata.com/v1/quotes?symbol=ES");
  string jsonData = await response.Content.ReadAsStringAsync();

  dynamic data = JsonConvert.DeserializeObject(jsonData);
  Print(
$"Precio actual de ES: {data.lastPrice}");
}


En este ejemplo, se utiliza la API de un proveedor de datos de mercado para obtener la cotización actual de un activo. Los datos recuperados pueden utilizarse para complementar las estrategias.

Explicación del código:

Este código realiza una petición GET a una API para obtener datos de mercado y muestra el precio actual de un símbolo específico («ES») en la consola. Aquí está el desglose:

  1. HttpClient: Se crea un cliente HTTP que se usa para hacer solicitudes web.
  2. GetAsync: Hace una solicitud GET a la API de datos de mercado con la URL proporcionada.
  3. ReadAsStringAsync: Obtiene el contenido de la respuesta de la API en formato JSON.
  4. JsonConvert.DeserializeObject: Convierte el JSON recibido en un objeto dinámico de C#.
  5. Print: Muestra el precio actual del símbolo «ES» (data.lastPrice) en la consola.

El uso de async y await permite que la operación se realice de forma asíncrona, evitando bloquear el hilo principal mientras se espera la respuesta de la API.

Ejemplo: Integración con una API de Broker

La mayoría de los brokers ofrecen APIs para ejecutar órdenes. A través de estas, puedes automatizar órdenes o gestionar posiciones.

private async void SendOrderToBroker()
{

  var orderData = new
  {
    symbol =
"AAPL",
    quantity =
100,
    side =
"buy",
    type =
"market"
  };

  HttpClient client =
new HttpClient();
  var content = new StringContent(JsonConvert.SerializeObject(orderData), Encoding.UTF8, "application/json");
  HttpResponseMessage response =
await client.PostAsync("https://api.broker.com/orders", content);
  string result = await response.Content.ReadAsStringAsync();
  Print(result);
}


Este código muestra cómo enviar una orden de compra al broker mediante una API. Puedes personalizar los parámetros para ajustarlos a tus necesidades de trading.

Explicación del código:

Este código envía una orden de compra a una API de un broker mediante una solicitud POST. Aquí está el desglose:

  1. orderData: Se crea un objeto anónimo que contiene los datos de la orden, como el símbolo («AAPL»), cantidad (100), dirección de la orden («buy») y tipo de orden («market»).
  2. HttpClient: Se utiliza para realizar solicitudes HTTP.
  3. StringContent: Convierte el objeto orderData a un JSON y lo formatea como contenido JSON para la solicitud.
  4. PostAsync: Envía la solicitud POST con los datos de la orden a la URL de la API del broker.
  5. ReadAsStringAsync: Lee la respuesta de la API y la convierte a texto.
  6. Print: Muestra el resultado de la respuesta de la API en la consola.

Este proceso se realiza de forma asíncrona para no bloquear el flujo principal mientras se espera la respuesta de la API.

Ejercicio Práctico:

Implementa una integración con la API de tu broker preferido y automatiza la colocación de órdenes cuando se cumpla una determinada condición de mercado.

Ver solución:

Objetivo: Implementar una integración con la API de un broker y automatizar la colocación de órdenes cuando se cumpla una condición de mercado, como que el precio de cierre actual sea mayor que una media móvil.

Requisitos:

  1. API del broker: Necesitarás acceso a la API del broker que elijas. Debes obtener las credenciales (API key, URL de acceso, etc.) de la API proporcionada por el broker.
  2. Condición de mercado: La estrategia tomará decisiones automáticas de compra o venta según una condición de mercado, por ejemplo, si el precio de cierre actual es mayor que la media móvil de 50 periodos.

Ejemplo de Código para Integración

Este código mostrará cómo integrar la API del broker y enviar una orden cuando se cumpla una condición simple basada en el precio de cierre y una media móvil.

using System;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;

public class AutoOrderStrategy : Strategy
{
  private HttpClient client;

  protected override void OnStateChange()
  {

    if (State == State.SetDefaults)
    {
      Description =
"Estrategia que coloca órdenes automáticas cuando se cumplen condiciones de mercado.";
      client =
new HttpClient();
    }
  }


  protected override void OnBarUpdate()
  {

    // Solo operar si hay suficientes barras para calcular la media móvil
    if (CurrentBar < 50)
      return;

    double mediaMovil50 = SMA(50)[0];
    double precioCierre = Close[0];

    // Condición de compra: el precio de cierre está por encima de la media móvil de 50
    if (precioCierre > mediaMovil50)
    {

      // Llamar a la función para enviar la orden
      SendOrderToBroker("buy", "AAPL", 100);
    }
  }


  private async void SendOrderToBroker(string side, string symbol, int quantity)
  {

    var orderData = new
    {
      symbol = symbol,
      quantity = quantity,
      side = side,
      type =
"market"
    };

    var content = new StringContent(JsonConvert.SerializeObject(orderData), Encoding.UTF8, "application/json");

    try
    {
      // Endpoint de la API del broker (a ajustar según el broker que utilices)
      HttpResponseMessage response = await client.PostAsync("https://api.broker.com/orders", content);

      if (response.IsSuccessStatusCode)
      {

        string result = await response.Content.ReadAsStringAsync();
        Print(
$"Orden ejecutada: {result}");
      }

      else
      {
        Print(
$"Error en la orden: {response.StatusCode}");
      }
    }

    catch (Exception ex)
    {
      Print(
$"Error al conectar con la API del broker: {ex.Message}");
    }
  }
}


Explicación del Código:

  1. Condición de Mercado: La estrategia verifica si el precio de cierre actual (Close[0]) está por encima de la media móvil de 50 periodos (SMA(50)[0]).
  2. Automatización de Órdenes: Si la condición se cumple, se llama a la función SendOrderToBroker, que realiza una solicitud POST a la API del broker para enviar una orden de compra o venta. La orden es de tipo market y utiliza el símbolo «AAPL» como ejemplo.
  3. Manejo de Respuesta: Se maneja la respuesta de la API. Si la orden es exitosa, se imprime un mensaje en la consola; de lo contrario, se muestra el error.

Pasos Adicionales:

  1. API Key y Autenticación: Algunos brokers requieren autenticación mediante un token o API key. Deberás añadir los encabezados correspondientes a las solicitudes HTTP.
  2. Simulación y Pruebas: Antes de operar en real, utiliza los entornos de prueba (sandbox) del broker para asegurarte de que la integración funcione correctamente.
  3. Condiciones adicionales: Puedes añadir más condiciones para colocar órdenes de venta o ajustar el tamaño de la posición según la volatilidad, utilizando indicadores como el ATR.

Este ejercicio te ayudará a automatizar la colocación de órdenes en función de condiciones de mercado mediante una integración real con un broker.


La interacción con APIs no solo te permite obtener datos o ejecutar órdenes, sino también automatizar una amplia variedad de procesos, como la generación de informes, actualización de carteras o la gestión de riesgos de manera externa.

Ejemplo: Automatización de Actualización de Cartera

private async void UpdatePortfolio()
{
  HttpClient client =
new HttpClient();
  HttpResponseMessage response =
await client.GetAsync("https://api.broker.com/portfolio");
  string jsonData = await response.Content.ReadAsStringAsync();

  dynamic portfolio = JsonConvert.DeserializeObject(jsonData);
  Print(
$"Valor actual de la cartera: {portfolio.totalValue}");
}


Este ejemplo muestra cómo automatizar la actualización de la cartera conectándose a la API de un broker y obteniendo el valor actual de la cartera en tiempo real.

Explicación del código:

Este código realiza una solicitud GET a la API de un broker para obtener información sobre la cartera (portafolio) del usuario.

  • HttpClient se utiliza para enviar la solicitud a la URL de la API.
  • await espera la respuesta de la API de forma asíncrona.
  • La respuesta se convierte en un string y se deserializa en un objeto dinámico con JsonConvert.
  • Finalmente, el valor total de la cartera se imprime en la consola usando Print().

Es útil para obtener el valor actual del portafolio y mostrarlo al usuario.

Ejercicio Práctico:

Automatiza un proceso que descargue los datos de tu cuenta de trading cada hora y guarde la información en un archivo para análisis posterior.

Ver solución:

using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System.Timers;
using Newtonsoft.Json;

public class TradingAccountDataDownloader
{
  private static Timer timer;

  public static void Main()
  {

    // Configurar el temporizador para que se ejecute cada hora (3600000 milisegundos)
    timer = new Timer(3600000);
    timer.Elapsed += OnTimedEvent;
    timer.AutoReset =
true;
    timer.Enabled =
true;

    // Mantener el programa en ejecución
    Console.WriteLine("Iniciando la descarga de datos cada hora...");
    Console.ReadLine();
  }


  private static async void OnTimedEvent(Object source, ElapsedEventArgs e)
  {

    await DownloadAndSaveAccountData();
  }


  private static async Task DownloadAndSaveAccountData()
  {

    try
    {
      HttpClient client =
new HttpClient();
      HttpResponseMessage response =
await client.GetAsync("https://api.broker.com/accountData");
      string jsonData = await response.Content.ReadAsStringAsync();

      dynamic accountData = JsonConvert.DeserializeObject(jsonData);
      string accountValue = accountData.totalValue.ToString();

      // Crear un nombre de archivo con la fecha y hora actual
      string fileName = $"AccountData_{DateTime.Now.ToString("yyyyMMdd_HHmmss")}.txt";

      // Guardar los datos en un archivo de texto
      File.WriteAllText(fileName, jsonData);

      Console.WriteLine(
$"Datos guardados en {fileName}: Valor actual de la cuenta: {accountValue}");
    }

    catch (Exception ex)
    {
      Console.WriteLine(
"Error al descargar o guardar los datos: " + ex.Message);
    }
  }
}


Explicación del código:

  1. Timer: Configura un temporizador que se ejecuta cada hora (3600000 ms). Cada vez que se dispara el evento Elapsed, llama a la función DownloadAndSaveAccountData.
  2. DownloadAndSaveAccountData(): Hace una petición a la API del broker para obtener los datos de la cuenta de trading.
  3. Guardar en archivo: La información obtenida se guarda en un archivo con la fecha y hora en el nombre, ideal para análisis histórico.

Pasos adicionales:

  1. Personalizar la URL de la API con los detalles específicos de tu broker.
  2. Manejo de autenticación: Si la API requiere autenticación, asegúrate de agregar los headers necesarios (como tokens API o credenciales).
  3. Analizar el archivo posteriormente: Podrás usar los archivos guardados para realizar análisis de rendimiento o gestión de riesgos.

Este proceso permitirá automatizar la descarga de datos de la cuenta de trading cada hora y almacenar esta información para análisis futuros.


 
 

Al desarrollar en NinjaScript, es fundamental mantener un código eficiente para garantizar que tus estrategias y scripts personalizados se ejecuten de manera fluida, incluso cuando procesan grandes volúmenes de datos de mercado. Aquí te presento algunos consejos para optimizar el rendimiento:

  • Uso eficiente de bucles: Minimiza el uso de bucles innecesarios y evita recalcular valores repetidos. Por ejemplo, si tienes que realizar un cálculo que se puede guardar y reutilizar en varias partes de tu código, almacénalo en una variable.
  • Carga de indicadores externos: Si tu estrategia utiliza indicadores personalizados, asegúrate de que estos no se recalculen en cada tick si no es necesario. Usa Calculate.OnEachTick solo cuando realmente lo necesites.

Ejemplo:

protected override void OnBarUpdate()
{

  if (CurrentBar < 20) return; // Evita cálculos si no hay suficientes barras

  // Almacena el valor calculado para evitar repetición de cálculos

  double smaValue = SMA(20)[0];

  if (Close[0] > smaValue)
    EnterLong();
}


Explicación del código:

Este código crea una estrategia simple que compra (ejecuta una orden larga) cuando el precio de cierre de la barra actual es mayor que el valor de la media móvil simple (SMA) de 20 periodos. Aquí tienes una breve explicación:

  • if (CurrentBar < 20) return;: Se asegura de que haya al menos 20 barras disponibles antes de ejecutar cualquier cálculo, ya que la SMA de 20 periodos necesita al menos 20 datos.
  • double smaValue = SMA(20)[0];: Calcula el valor de la SMA de 20 periodos para la barra actual y lo almacena en la variable smaValue.
  • if (Close[0] > smaValue): Si el precio de cierre de la barra actual es mayor que la SMA, se cumple la condición.
  • EnterLong();: Ejecuta una orden de compra a mercado si la condición anterior es verdadera.

Ejercicio Práctico:

Optimiza un script que utilice múltiples indicadores y comprueba la diferencia de rendimiento antes y después de aplicar las mejoras.

Ver solución:

En este ejercicio, optimizaremos un script de NinjaScript que utiliza varios indicadores para tomar decisiones de trading. El objetivo es reducir la redundancia de cálculos y mejorar el rendimiento general del código. Luego, realizaremos un backtesting para comparar el rendimiento antes y después de la optimización.

Paso 1: Código Original con Múltiples Indicadores

Aquí tienes un ejemplo de código que utiliza varios indicadores sin optimización:

protected override void OnBarUpdate()
{

  if (CurrentBar < 20) return; // Evitar cálculos si no hay suficientes barras

  // Calcula el valor de SMA y RSI para cada barra

  double smaValue = SMA(20)[0];
  double rsiValue = RSI(14, 3)[0];
  double atrValue = ATR(14)[0];

  // Condición de compra: precio por encima de la SMA y RSI en sobrecompra
  if (Close[0] > smaValue && rsiValue < 30)
  {
    EnterLong();
  }


  // Condición de venta: precio por debajo de la SMA y RSI en sobreventa
  if (Close[0] < smaValue && rsiValue > 70)
  {
    EnterShort();
  }
}


En este código original, recalculamos indicadores para cada barra, lo que genera redundancia y reduce la eficiencia.

Paso 2: Optimización del Código

La optimización puede lograrse almacenando los cálculos de indicadores en variables que se reutilizan. Esto evita el cálculo repetido del mismo indicador varias veces en la misma barra.

private double smaValue;
private double rsiValue;

protected override void OnBarUpdate()
{

  if (CurrentBar < 20) return; // Evitar cálculos si no hay suficientes barras

  // Calcula el valor de SMA y RSI solo una vez por barra y reutiliza estos valores

  if (BarsInProgress == 0) // Verifica que estás en la serie principal de datos
  {
    smaValue = SMA(20)[0]; // Calcula solo si no se ha hecho antes
    rsiValue = RSI(14, 3)[0]; // Calcula solo si no se ha hecho antes
  }

  // Condición de compra: precio por encima de la SMA y RSI en sobreventa
  if (Close[0] > smaValue && rsiValue < 30)
  {
    EnterLong();
  }


  // Condición de venta: precio por debajo de la SMA y RSI en sobrecompra
  if (Close[0] < smaValue && rsiValue > 70)
  {
    EnterShort();
  }
}


En este código optimizado, reducimos el número de cálculos innecesarios al calcular indicadores solo cuando sea necesario, y reutilizamos esos valores.

Diferencias de Optimización:

  • Cálculo de indicadores: En la versión optimizada, los indicadores se calculan solo una vez por barra y no se recalculan a menos que se cambie de barra.
  • Evitar cálculos innecesarios: Se eliminan cálculos que no son utilizados (como el ATR, que en el código original no se estaba usando). En un entorno real, podrías realizar el cálculo de ATR solo si planeas usarlo más adelante.

Resultado Esperado:

  • Mejor eficiencia: Menos cálculos innecesarios, especialmente en estrategias que usan múltiples indicadores.
  • Menor consumo de recursos: Al reducir los cálculos repetidos, optimizas el uso de la CPU, lo cual es importante para estrategias en tiempo real o en grandes volúmenes de datos.

Paso 3: Realizar Backtesting y Análisis

  1. Antes de la optimización: Ejecuta el script original en un backtest con datos históricos para ver el rendimiento en términos de métricas clave como el beneficio neto, la tasa de aciertos, y el número de operaciones.
  2. Después de la optimización: Ejecuta el script optimizado en el mismo conjunto de datos y compara los resultados con los obtenidos en el primer backtest.

Análisis:

  • Diferencias de rendimiento: Observa si la optimización mejora la velocidad de ejecución, reduce la latencia, o aumenta la eficiencia del sistema.
  • Tasa de aciertos y retorno: Evalúa si los cambios tienen algún impacto en el resultado de las operaciones, especialmente en mercados volátiles o rápidos.

Conclusión: Esta comparación permitirá ver cómo la optimización de cálculos puede impactar el rendimiento del script y mejorar las decisiones de trading.


El mantenimiento de tus scripts de NinjaScript es crucial, especialmente si deseas seguir actualizándolos a medida que NinjaTrader lanza nuevas versiones. Aquí algunos consejos:

  • Modularización: Divide tu código en funciones y clases reutilizables. Esto facilita la actualización y el mantenimiento de secciones individuales del código sin afectar el resto.
  • Actualizaciones periódicas: Asegúrate de que tu código sea compatible con las últimas versiones de NinjaTrader. Mantén un registro de las versiones y los cambios realizados para facilitar la actualización.

Ejemplo de código modular:

private void EjecutarEntrada()
{

  if (Close[0] > SMA(20)[0])
  EnterLong();
}


Explicación del código:

Este código define una función llamada EjecutarEntrada, que toma una decisión de compra en función de una condición simple:

  • Condición: Si el precio de cierre actual (Close[0]) es mayor que el valor de la media móvil simple (SMA) de 20 periodos.
  • Acción: Si la condición se cumple, ejecuta una orden de compra (EnterLong()).

En resumen, la función busca oportunidades para abrir una posición larga (compra) cuando el precio supera la media móvil de 20 periodos, lo que sugiere una posible tendencia alcista.

Ejercicio Práctico:

Refactoriza una estrategia compleja dividiéndola en funciones más pequeñas y mantenibles. Identifica qué partes del código podrían requerir actualizaciones frecuentes y cómo modularizarlas.

Ver solución:

Objetivo: Toma una estrategia de trading compleja y refactorízala dividiéndola en funciones más pequeñas para mejorar su legibilidad, mantenimiento y modularidad. Identifica qué partes del código requieren actualizaciones frecuentes, y cómo estas se pueden separar en funciones o módulos.

Estrategia Original (Ejemplo Simplificado)

Supongamos que la estrategia original tiene varias reglas de entrada y salida, gestión de riesgos, y se ejecuta de manera secuencial en el método OnBarUpdate().

protected override void OnBarUpdate()
{

  // Condiciones para una compra
  if (Close[0] > SMA(20)[0] && RSI(14, 3)[0] < 30)
  {
    SetStopLoss(CalculationMode.Percent,
0.02);
    SetProfitTarget(CalculationMode.Percent,
0.05);
    EnterLong();
  }


  // Condiciones para una venta
  if (Close[0] < SMA(20)[0] && RSI(14, 3)[0] > 70)
  {
    SetStopLoss(CalculationMode.Percent,
0.02);
    SetProfitTarget(CalculationMode.Percent,
0.05);
    EnterShort();
  }


  // Gestión de posiciones
  if (Position.MarketPosition == MarketPosition.Long && Close[0] < EMA(10)[0])
  {
    ExitLong();
  }


  if (Position.MarketPosition == MarketPosition.Short && Close[0] > EMA(10)[0])
  {
    ExitShort();
  }
}


Refactorización

Para hacer el código más mantenible, podemos dividirlo en funciones más pequeñas y manejables, lo que facilita su actualización.

protected override void OnBarUpdate()
{

  // Verifica si hay suficientes datos para los cálculos
  if (CurrentBar < 20) return;
}


private void EjecutarEntrada()
{

  if (EsCompraValida())
  {
    ConfigurarOrdenes();
    EnterLong();
  }


  if (EsVentaValida())
  {
    ConfigurarOrdenes();
    EnterShort();
  }
}


private bool EsCompraValida()
{

  return Close[0] > SMA(20)[0] && RSI(14, 3)[0] < 30;
}


private bool EsVentaValida()
{

  return Close[0] < SMA(20)[0] && RSI(14, 3)[0] > 70;
}


private void ConfigurarOrdenes()
{
  SetStopLoss(CalculationMode.Percent,
0.02);
  SetProfitTarget(CalculationMode.Percent,
0.05);
}


private void GestionarPosicion()
{

  if (Position.MarketPosition == MarketPosition.Long && Close[0] < EMA(10)[0])
  {
    ExitLong();
  }

  if (Position.MarketPosition == MarketPosition.Short && Close[0] > EMA(10)[0])
  {
    ExitShort();
  }
}


Identificación de Partes Modulares

  • Condiciones de entrada (EsCompraValida y EsVentaValida): Pueden actualizarse según nuevas estrategias o reglas de trading. Modularizarlas permite que los cambios se realicen en un solo lugar.
  • Configuración de órdenes (ConfigurarOrdenes): La lógica de stop loss y take profit es independiente y puede actualizarse sin cambiar las reglas de entrada y salida.
  • Gestión de posiciones (GestionarPosicion): Si las reglas de gestión de riesgo cambian, la función es lo suficientemente modular para modificarla sin afectar la lógica de entrada.

Ventajas de la Refactorización

  • Mantenimiento: Si las reglas de trading cambian, puedes modificar funciones específicas en lugar de todo el código.
  • Legibilidad: Cada parte de la estrategia tiene una función clara y concreta, lo que facilita su comprensión.
  • Modularidad: Puedes probar o modificar secciones específicas de la estrategia sin afectar el resto.

Una buena práctica es documentar siempre tu código. Esto no solo ayuda a otros desarrolladores que puedan trabajar con tu código, sino que también facilita el mantenimiento futuro.

En NinjaScript, puedes hacer comentarios de dos maneras:

  1. Comentarios de una sola línea: Se utilizan dos barras inclinadas (//). Todo el texto después de // en la misma línea se considera un comentario y no se ejecuta.

// Este es un comentario de una sola línea
int valor = 10; // Este es otro comentario


  1. Comentarios de múltiples líneas: Se utilizan /* para abrir el comentario y */ para cerrarlo. Todo el texto entre estos dos delimitadores se considera un comentario.

/*
Este es un comentario
de múltiples líneas
*/

int valor = 20;


  • Comentarios claros y concisos: Añade comentarios a las secciones importantes de tu código explicando la lógica detrás de tus decisiones.
  • Nombrado descriptivo: Utiliza nombres claros para tus variables y funciones. Evita abreviaturas que puedan causar confusión.
  • Estandarización: Sigue un estándar de comentarios para que la estructura de tu código sea consistente.

Ambos tipos de comentarios son útiles para documentar el código y proporcionar aclaraciones sobre su funcionamiento, tanto para ti como para cualquiera que revise el código.

Ejercicio Práctico:

Documenta una estrategia o indicador existente. Asegúrate de incluir comentarios que expliquen la lógica de tus cálculos y decisiones estratégicas.

Ver solución:

Objetivo: El objetivo de este ejercicio es documentar una estrategia de trading existente. Para ello, se incluirán comentarios detallados en el código que expliquen cada parte del proceso, la lógica detrás de los cálculos y las decisiones estratégicas tomadas.

Estrategia de Ejemplo: Cruce de Medias Móviles con Gestión de Riesgos

/*
Estrategia basada en el cruce de medias móviles de 50 y 200 periodos.
Compra cuando la media móvil rápida cruza por encima de la lenta.
Vende cuando la media móvil rápida cruza por debajo de la lenta.
Gestión de riesgos utilizando Stop Loss y Take Profit.
*/

public class CruceMediasMoviles : Strategy
{
  private int periodoRapido = 50; // Periodo de la media móvil rápida
  private int periodoLento = 200; // Periodo de la media móvil lenta
  private double stopLoss = 0.02; // Stop Loss en porcentaje
  private double takeProfit = 0.05; // Take Profit en porcentaje

  /*
  Método que se ejecuta en cada actualización de barra.
  Aquí se toman las decisiones de compra o venta.
  */

  protected override void OnBarUpdate()
  {

    // Asegúrate de que haya suficientes datos para calcular las medias móviles.
    if (CurrentBar < periodoLento)
      return; // Si no hay suficientes barras, sale de la función.

    // Calcula la media móvil rápida (50 periodos).

    double smaRapida = SMA(periodoRapido)[0];

    // Calcula la media móvil lenta (200 periodos).
    double smaLenta = SMA(periodoLento)[0];

    // Compra si la media móvil rápida cruza por encima de la lenta.
    if (CrossAbove(smaRapida, smaLenta, 1))
    {

      // Configura el Stop Loss y el Take Profit antes de entrar en la posición.
      SetStopLoss(CalculationMode.Percent, stopLoss);
      SetProfitTarget(CalculationMode.Percent, takeProfit);


      // Ejecuta una compra a mercado.
      EnterLong();
    }

    // Vende si la media móvil rápida cruza por debajo de la lenta.
    if (CrossBelow(smaRapida, smaLenta, 1))
    {

      // Configura el Stop Loss y el Take Profit antes de entrar en la posición.
      SetStopLoss(CalculationMode.Percent, stopLoss);
      SetProfitTarget(CalculationMode.Percent, takeProfit);


      // Ejecuta una venta a mercado.
      EnterShort();
    }
  }


  /*
  Método para manejar la gestión de posiciones.
  Se activa cuando una orden es ejecutada.
  */

  protected override void OnExecutionUpdate(Execution execution, double orderPrice, double orderQuantity, MarketPosition position, string orderId, DateTime time)
  {

    // Verifica si la orden fue ejecutada y muestra la información en la salida.
    Print($"Orden ejecutada: {execution.OrderType} en {orderPrice} con cantidad {orderQuantity}");
  }


  /*
  Configuración inicial de la estrategia.
  Este método se ejecuta antes de comenzar la estrategia.
  */

  protected override void OnStateChange()
  {

    // Inicializa la estrategia cuando el estado es SetDefaults.
    if (State == State.SetDefaults)
    {
      Description =
"Estrategia basada en el cruce de medias móviles.";
      Name =
"CruceMediasMoviles"; // Nombre de la estrategia.
      Calculate = Calculate.OnEachTick; // Calcula en cada tick para mayor precisión.
    }
  }
}


Explicación del Código y Documentación

  1. Comentarios Generales:
    • Se incluye un resumen de la estrategia en el comentario inicial del código. Explica de manera general qué es lo que hace la estrategia: cruces de medias móviles y gestión de riesgos.
  2. Variables:
    • periodoRapido: Es el periodo de la media móvil rápida (50 periodos en este caso).
    • periodoLento: Es el periodo de la media móvil lenta (200 periodos en este caso).
    • stopLoss y takeProfit: Se usan para definir el nivel de riesgo y recompensa, establecidos en un porcentaje.
  3. Comentarios en OnBarUpdate():
    • El método principal donde se evalúan las condiciones del mercado.
    • Primero, se verifica que haya suficientes barras para calcular las medias móviles.
    • Luego, se calculan las medias móviles rápida y lenta.
    • Se incluyen comentarios que explican cuándo se ejecuta una compra o venta, dependiendo de si la media rápida cruza por encima o por debajo de la lenta.
  4. Gestión de Posiciones:
    • El método OnExecutionUpdate() gestiona la ejecución de las órdenes y confirma en la salida cuándo se ejecuta una compra o venta.
  5. Estado Inicial:
    • El método OnStateChange() establece la configuración inicial de la estrategia, como el nombre y cuándo debe ejecutarse (en cada tick).

Resultado

Este ejercicio ayuda a que cualquier persona que lea el código, incluso sin un conocimiento profundo de la estrategia, pueda entender claramente qué hace y por qué se toman determinadas decisiones estratégicas.


Es fácil cometer errores comunes al desarrollar en NinjaScript, especialmente cuando se trabaja con datos en tiempo real. Aquí algunos de los errores más frecuentes y cómo evitarlos:

  • Acceder a datos antes de que estén disponibles: Asegúrate de que tienes suficientes datos antes de realizar cálculos con funciones como SMA(). Usa siempre condiciones como if (CurrentBar < n) para evitar estos errores.
  • Errores de sincronización en estrategias multitimeframe: Cuando trabajes con múltiples marcos temporales, asegúrate de que las series de datos estén correctamente sincronizadas para evitar decisiones erróneas basadas en datos de diferentes tiempos.
  • Uso incorrecto de variables globales: Asegúrate de que las variables globales están correctamente inicializadas para evitar errores inesperados.

Ejemplo de control de acceso a datos:

protected override void OnBarUpdate()
{

  if (CurrentBar < 20) return; // Asegúrate de tener suficientes barras
  double smaValue = SMA(20)[0];

  if (Close[0] > smaValue)
    EnterLong();
}


Ejercicio Práctico:

Revisa una estrategia para identificar posibles errores comunes y aplícale correcciones. Asegúrate de que no haya errores al acceder a datos o en la sincronización de marcos temporales.

Ver solución:

Objetivo: Este ejercicio te ayudará a identificar y corregir errores comunes en una estrategia de trading, prestando especial atención al acceso correcto a los datos históricos y la sincronización de marcos temporales (timeframes).

Código Original de la Estrategia

protected override void OnBarUpdate()
{

  // Condición de compra cuando el cierre actual es mayor a la apertura
  if (Close[0] > Open[0])
    EnterLong();


  // Condición de venta cuando el cierre actual es menor a la apertura
  if (Close[0] < Open[0])
    EnterShort();
}


Problemas Comunes Detectados

  1. Acceso Incorrecto a Datos Históricos:
    • La estrategia no verifica si hay suficientes datos disponibles antes de acceder a ellos. Esto puede causar errores cuando se trabaja con indicadores o series de datos que requieren un número mínimo de barras.
  2. Falta de Manejo de Timeframes:
    • No se considera la posible desincronización entre diferentes marcos temporales si la estrategia se ejecuta en múltiples series de datos.
  3. Falta de Protección de Posición:
    • El código no comprueba si ya hay una posición abierta antes de intentar ejecutar una nueva operación, lo que podría generar duplicación de órdenes y errores lógicos.

Estrategia Corregida

protected override void OnBarUpdate()
{

  // Verifica si hay suficientes barras para ejecutar la estrategia
  if (CurrentBar < 20) return; // Evita cálculos en barras iniciales

  // Condición para evitar órdenes duplicadas

  if (Position.MarketPosition == MarketPosition.Flat)
  {

    // Condición de compra cuando el cierre actual es mayor a la apertura
    if (Close[0] > Open[0])
    {
      EnterLong();
    }

    // Condición de venta cuando el cierre actual es menor a la apertura
    else if (Close[0] < Open[0])
    {
      EnterShort();
    }
  }
}


protected override void OnStateChange()
{

  // Configuración de la estrategia
  if (State == State.SetDefaults)
  {
    Description =
"Estrategia corregida para manejo de errores comunes.";
    Name =
"EstrategiaCorregida";
    Calculate = Calculate.OnEachTick;
// Calcula en cada tick para mayor precisión
  }

  // Sincronización de marcos temporales
  if (State == State.Configure)
  {
    AddDataSeries(Data.BarsPeriodType.Minute,
5); // Añade una serie de 5 minutos
  }
}


Explicación de las Correcciones:

  1. Verificación de Datos Disponibles:
    • Se añade una condición para que no se ejecuten cálculos si no hay al menos 20 barras disponibles (CurrentBar < 20). Esto evita errores cuando se accede a datos antes de que haya suficiente información.
  2. Protección Contra Órdenes Duplicadas:
    • Se añade una comprobación para asegurarse de que no haya ninguna posición abierta antes de ejecutar una nueva operación (Position.MarketPosition == MarketPosition.Flat). Esto evita la duplicación de órdenes y posibles errores de lógica.
  3. Sincronización de Timeframes:
    • Se utiliza el método AddDataSeries() en OnStateChange() para añadir una serie de datos adicionales (por ejemplo, un marco temporal de 5 minutos). Esto ayuda a evitar errores de desincronización entre diferentes marcos temporales.

Resultado Esperado:

  • La estrategia es ahora más robusta al manejar correctamente los datos históricos y prevenir la ejecución de órdenes duplicadas. Además, es capaz de sincronizarse con diferentes marcos temporales de manera eficiente, lo que reduce los errores relacionados con la desincronización en estrategias multiserie.

Este proceso de revisión es crucial para garantizar que la estrategia funcione correctamente en un entorno en vivo y para mejorar la consistencia y estabilidad a lo largo del tiempo.


 
 

En este capítulo, exploraremos ejemplos prácticos y casos de uso reales que te permitirán aplicar los conceptos aprendidos a lo largo del tutorial. Veremos cómo implementar indicadores personalizados y estrategias basadas en modelos avanzados como el machine learning, y cómo puedes adaptarlos para tu operativa diaria.

Uno de los usos más comunes de NinjaScript es la creación de indicadores personalizados. En este caso, crearemos un indicador que identifique un patrón de velas japonés, como el «Doji», utilizado para señalar posibles cambios de tendencia.

Código del Indicador:

protected override void OnBarUpdate()
{
  // Verifica si tenemos suficientes datos
  if (CurrentBar < 1) return;

  // Definimos las condiciones para un Doji: Apertura y Cierre muy cercanos
  if (Math.Abs(Open[0] - Close[0]) <= (High[0] - Low[0]) * 0.1)
  {
    // Dibujar una marca en el gráfico cuando se detecta un Doji
    Draw.Dot(this, "doji" + CurrentBar, true, 0, Low[0] - TickSize, Brushes.Yellow);
  }
}

Este código analiza las velas en cada barra y si se detecta un Doji (donde la apertura y el cierre son muy cercanos entre sí), se marca con un punto amarillo en el gráfico.

Explicación del código:

Este código detecta un patrón de vela «Doji» en un gráfico y marca su presencia visualmente. Un Doji es un patrón en el que el precio de apertura y el de cierre están muy cercanos, lo que indica indecisión en el mercado.

Explicación paso a paso:

  1. Verificación de datos:
    • El código comienza comprobando si hay suficientes barras para analizar (CurrentBar < 1). Si no es así, termina la ejecución de la función.
  2. Condición para el Doji:
    • La condición identifica un Doji cuando la diferencia entre la apertura y el cierre es muy pequeña, en este caso, menor o igual al 10% del rango (alto-bajo) de la barra.
  3. Visualización:
    • Si se detecta un Doji, se dibuja un punto amarillo (Draw.Dot) por debajo del mínimo de la barra para marcarlo en el gráfico.

Este código facilita la identificación visual de velas Doji en gráficos de precios.

Ejercicio Práctico:

Modifica los parámetros para detectar otros tipos de velas, como «Martillos» o «Estrellas Fugaces».

Ver solución:

Objetivo:

Modificar el código para detectar velas tipo «Martillo», un patrón que generalmente aparece al final de una tendencia bajista, indicando una posible reversión alcista.

Descripción:

Un Martillo es una vela con cuerpo pequeño y una mecha inferior larga, lo que sugiere que los vendedores inicialmente empujaron el precio hacia abajo, pero los compradores luego recuperaron terreno antes del cierre de la barra.

Parámetros clave para identificar un Martillo:

  • El cuerpo (diferencia entre la apertura y el cierre) es pequeño.
  • La mecha inferior (diferencia entre el bajo y el mínimo) es al menos el doble del tamaño del cuerpo.
  • La mecha superior (diferencia entre el máximo y el cierre) es muy pequeña o inexistente.

Código Modificado:

protected override void OnBarUpdate()
{
    // Verifica si tenemos suficientes datos
    if (CurrentBar < 1) return;

    // Definir el tamaño del cuerpo y las mechas de la vela
    double cuerpo = Math.Abs(Open[0] - Close[0]);
    double mechaInferior = Low[0] - Math.Min(Open[0], Close[0]);
    double mechaSuperior = High[0] - Math.Max(Open[0], Close[0]);

    // Condiciones para identificar un Martillo
    if (cuerpo <= (High[0] - Low[0]) * 0.3  // Cuerpo pequeño
        && mechaInferior >= cuerpo * 2  // Mecha inferior al menos el doble del cuerpo
        && mechaSuperior <= cuerpo * 0.3)  // Mecha superior muy pequeña
    {
        // Dibujar una marca en el gráfico cuando se detecta un Martillo
        Draw.Dot(this, "martillo" + CurrentBar, true, 0, Low[0] - TickSize, Brushes.Green);
    }
}

Instrucciones:

  1. Copia y pega el código en tu entorno de NinjaScript.
  2. Ajusta los parámetros de la mecha y el cuerpo si es necesario.
  3. Ejecuta la estrategia para detectar velas tipo «Martillo» en tu gráfico.

En este caso de uso, implementaremos una estrategia de trading que utiliza un modelo de machine learning para tomar decisiones basadas en datos históricos de precios. NinjaTrader permite la integración con librerías externas, lo que facilita el uso de técnicas avanzadas.

Ejemplo con Machine Learning:

Este código supone que previamente entrenamos un modelo de machine learning externo (en Python, por ejemplo), y estamos usando los resultados dentro de NinjaScript.

protected override void OnBarUpdate()
{
    // Verifica si tenemos suficientes datos
    if (CurrentBar < 100) return;

    // Supongamos que hemos importado señales generadas por un modelo de ML externo
    double mlPrediction = GetMlPrediction(Close[0], Volume[0]);

    if (mlPrediction > 0.7)
    {
        EnterLong();
    }
    else if (mlPrediction < 0.3)
    {
        EnterShort();
    }
}

Este ejemplo muestra cómo usar un valor predicho (por ejemplo, probabilidad de subida o bajada de precios) para tomar decisiones de trading. GetMlPrediction es una función ficticia que obtiene la predicción del modelo de machine learning.

Explicación del código:

Este código implementa una estrategia que toma decisiones de compra o venta basadas en predicciones generadas por un modelo de Machine Learning (ML). A continuación se explica brevemente:

  1. Verificación de datos: Se asegura de que haya al menos 100 barras de datos antes de ejecutar la lógica de la estrategia (if (CurrentBar < 100) return;).
  2. Predicción del modelo de ML: Utiliza una función externa GetMlPrediction(), que recibe como parámetros el precio de cierre actual (Close[0]) y el volumen (Volume[0]). Esta función devuelve una predicción, presumiblemente entre 0 y 1.
  3. Toma de decisiones:
    • Si la predicción es mayor a 0.7, indica una alta probabilidad de alza, por lo que se ejecuta una orden de compra con EnterLong().
    • Si la predicción es menor a 0.3, indica una alta probabilidad de baja, por lo que se ejecuta una orden de venta con EnterShort().

Este código muestra cómo integrar señales de un modelo de Machine Learning para automatizar decisiones de trading.

Ejercicio Práctico:

Implementa una estrategia que combine un indicador técnico (como una media móvil) con señales de un modelo de machine learning para mejorar las decisiones de trading.

Ver solución:

La estrategia comprueba si el precio está por encima de la media móvil y luego utiliza una señal del modelo de machine learning para decidir si entrar en una posición larga o corta.

Descripción

La estrategia utilizará una media móvil de 50 periodos para identificar la tendencia general y un modelo de machine learning (simulado) para proporcionar señales adicionales de entrada.

// Importar las librerías necesarias
using System;
using NinjaTrader.Cbi;
using NinjaTrader.Gui.Tools;
using NinjaTrader.NinjaScript;
using NinjaTrader.Data;
using NinjaTrader.Gui.Tools;

public class EstrategiaCombinada : Strategy
{
    private double mlPrediction;

    protected override void OnBarUpdate()
    {
        // Verifica si tenemos suficientes datos
        if (CurrentBar < 50) return;

        // Calcular la media móvil de 50 periodos
        double smaValue = SMA(50)[0];

        // Obtener la predicción del modelo de ML
        mlPrediction = GetMlPrediction(Close[0], Volume[0]);

        // Comprobación de condiciones para entrar en largo
        if (Close[0] > smaValue && mlPrediction > 0.7)
        {
            EnterLong(); // Ejecutar compra
        }
        // Comprobación de condiciones para entrar en corto
        else if (Close[0] < smaValue && mlPrediction < 0.3)
        {
            EnterShort(); // Ejecutar venta
        }
    }

    // Simulación de una función que obtiene la predicción del modelo de ML
    private double GetMlPrediction(double closePrice, double volume)
    {
        // Lógica para obtener la predicción del modelo de ML
        // Esta función debería conectarse a un modelo de ML real.
        // Para este ejercicio, retornaremos un valor simulado.
        return new Random().NextDouble(); // Simulación de una predicción aleatoria
    }
}

Explicación del Código

  1. Verificación de Datos: Se asegura de que haya al menos 50 barras de datos antes de ejecutar la lógica de la estrategia.
  2. Cálculo de la Media Móvil: Utiliza el método SMA(50)[0] para calcular la media móvil simple de 50 periodos.
  3. Predicción del Modelo de ML: Se llama a la función GetMlPrediction(), que simula la obtención de una predicción de un modelo de machine learning. Esta función puede conectarse a un modelo real si está disponible.
  4. Decisiones de Trading:
    • Si el precio de cierre actual es mayor que la media móvil y la predicción del modelo de ML es mayor que 0.7, se ejecuta una orden de compra (EnterLong()).
    • Si el precio de cierre actual es menor que la media móvil y la predicción del modelo de ML es menor que 0.3, se ejecuta una orden de venta (EnterShort()).

Consideraciones Adicionales

  • Este código es un punto de partida y puede requerir ajustes dependiendo de los requisitos específicos de tu estrategia y de la calidad de las predicciones del modelo de machine learning.
  • También sería útil implementar un manejo de riesgos, como establecer órdenes de Stop Loss y Take Profit, así como validar y ajustar el modelo de ML que se esté utilizando.

Otro uso común es crear estrategias que reaccionen ante un cambio significativo en el volumen de negociación. En este caso, crearemos una estrategia que abre posiciones cuando el volumen supera un umbral específico, lo que puede señalar un movimiento fuerte en el mercado.

Código de la Estrategia:

protected override void OnBarUpdate()
{
    // Verifica si tenemos suficientes datos
    if (CurrentBar < 10) return;

    // Calcula la media móvil del volumen
    double volumeSMA = SMA(Volume, 10)[0];

    // Si el volumen actual es significativamente mayor que el promedio, abrimos una posición
    if (Volume[0] > volumeSMA * 1.5)
    {
        EnterLong();
    }
}

Explicación del código:

Este código implementa una estrategia de trading basada en el análisis del volumen del mercado, específicamente utilizando una media móvil del volumen. Aquí tienes una explicación breve de cada parte del código:

  1. Verificación de Datos:
    • if (CurrentBar < 10) return;: Este bloque asegura que el código solo se ejecute si hay al menos 10 barras disponibles. Si no hay suficientes datos, la función se detiene, evitando cálculos que podrían resultar en errores.
  2. Cálculo de la Media Móvil del Volumen:
    • double volumeSMA = SMA(Volume, 10)[0];: Aquí se calcula la media móvil simple (SMA) del volumen de las últimas 10 barras. Volume es un array que contiene los volúmenes de las barras, y el índice [0] se refiere al valor actual de la SMA.
  3. Condición para Entrar en una Posición:
    • if (Volume[0] > volumeSMA * 1.5): Esta condición evalúa si el volumen de la barra actual (Volume[0]) es al menos 1.5 veces mayor que la media móvil del volumen calculada anteriormente (volumeSMA). Si esta condición se cumple, indica un aumento significativo en la actividad comercial.
  4. Ejecución de la Orden de Compra:
    • EnterLong();: Si la condición del volumen se cumple, se ejecuta una orden de compra a mercado, abriendo una posición larga.

En resumen, el código implementa una estrategia que abre una posición larga cuando el volumen de trading actual es significativamente mayor (1.5 veces) que la media móvil del volumen de las últimas 10 barras. Esto puede indicar un interés inusualmente alto en el activo, lo que podría preceder a un movimiento de precio al alza.

Ejercicio Práctico:

Ajusta los parámetros de esta estrategia para adaptarlos a diferentes tipos de activos y observa cómo reacciona el mercado ante grandes variaciones de volumen.

Ver solución:

Para ajustar los parámetros de la estrategia presentada en el código de la sección 10.3 y adaptarlos a diferentes tipos de activos, es importante considerar varias variables que pueden influir en la efectividad de la estrategia. A continuación, te muestro cómo puedes realizar estos ajustes y qué aspectos deberías observar:

Pasos para Ajustar la Estrategia

  1. Ajustar el Período de la Media Móvil:
    • Prueba diferentes longitudes para la media móvil del volumen (por ejemplo, 5, 10, 20 o 50 períodos) y observa cuál se adapta mejor a la volatilidad del activo. Activos más volátiles pueden beneficiarse de periodos más cortos.
double volumeSMA = SMA(Volume, 20)[0]; // Cambia 10 por 20

  1. Modificar el Umbral de Volumen:
    • Cambia el multiplicador del volumen para determinar cuándo considerar que hay una señal significativa (por ejemplo, 1.5, 2.0 o 3.0). Un umbral más alto puede ser apropiado para activos menos volátiles.
if (Volume[0] > volumeSMA * 2.0) // Cambia 1.5 por 2.0

  1. Evaluar Diferentes Activos:
    • Implementa y prueba la estrategia en distintos activos (acciones, futuros, criptomonedas) para observar cómo la reacción del mercado ante grandes variaciones de volumen varía. Algunas clases de activos pueden mostrar respuestas más rápidas a cambios en el volumen.
  2. Realizar Análisis de Resultados:
    • Después de realizar los ajustes, analiza los resultados. Puedes guardar estadísticas como la tasa de éxito de las operaciones, el rendimiento total, y el drawdown. Esto te permitirá ver qué combinaciones de parámetros son más efectivas.
protected override void OnBarUpdate()
{
    // Verifica si tenemos suficientes datos
    if (CurrentBar < 20) return;  // Aumenta el número mínimo de barras

    // Calcula la media móvil del volumen ajustada a 20 barras
    double volumeSMA = SMA(Volume, 20)[0];

    // Ajusta el umbral a 2.0 veces el promedio para activos menos volátiles
    if (Volume[0] > volumeSMA * 2.0)
    {
        EnterLong();
    }
}

Observaciones del Mercado

  • Reacción ante Aumentos de Volumen: Observa si hay un movimiento significativo en el precio cuando se cumplen las condiciones de volumen. ¿Se producen cambios rápidos en el precio? ¿Se forman patrones específicos?
  • Volatilidad: Nota cómo el nivel de volatilidad de cada activo afecta la eficacia de la estrategia. En activos más volátiles, los umbrales y periodos de media móvil podrían necesitar ajustes más agresivos.
  • Estacionalidad o Noticias: Considera cómo los eventos del mercado (noticias, informes de ganancias, etc.) pueden influir en el volumen y los movimientos de precios, y ajusta la estrategia en consecuencia.

Conclusión

Al realizar estos ajustes y observaciones, podrás adaptar la estrategia de volumen a diferentes contextos de mercado, mejorando su efectividad y comprensión de cómo el volumen impacta las decisiones de trading.


Como último ejemplo, desarrollaremos una estrategia simple de cruce de medias móviles que solo opere cuando el mercado cumple ciertos criterios adicionales, como el volumen o la volatilidad.

Código de la Estrategia:

protected override void OnBarUpdate()
{
    if (CurrentBar < 50) return;

    double fastSMA = SMA(10)[0];
    double slowSMA = SMA(30)[0];

    // Filtro adicional: Solo operamos si el ATR es alto (mayor volatilidad)
    double atr = ATR(14)[0];
    
    if (fastSMA > slowSMA && atr > 2)
    {
        EnterLong();
    }
    else if (fastSMA < slowSMA && atr > 2)
    {
        EnterShort();
    }
}

Esta estrategia solo abre posiciones cuando la media rápida cruza la media lenta, pero añade un filtro de volatilidad (ATR) para asegurarse de que el mercado se esté moviendo lo suficiente para justificar la operación.

Explicación del código:

Este código implementa una estrategia de cruce de medias móviles (SMA) con un filtro basado en el indicador de volatilidad ATR (Average True Range):

  • Condición de inicio: Si no hay al menos 50 barras, no se ejecuta la lógica (if (CurrentBar < 50) return;).
  • Medias Móviles: Se calculan dos SMA, una rápida de 10 periodos y una lenta de 30 periodos.
  • Filtro de Volatilidad: Se añade un filtro adicional con el ATR de 14 periodos, que mide la volatilidad del mercado. Solo se toman decisiones si el ATR es mayor que 2, lo que indica mayor volatilidad.
  • Condiciones de Entrada:
    • Si la SMA rápida es mayor que la lenta y el ATR es mayor que 2, se ejecuta una compra (EnterLong()).
    • Si la SMA rápida es menor que la lenta y el ATR es mayor que 2, se ejecuta una venta (EnterShort()).

Este código combina una estrategia de medias móviles con un filtro de volatilidad para operar solo en condiciones de mayor movimiento del mercado.

Ejercicio Práctico:

Implementa esta estrategia y ajusta el filtro de volatilidad según el tipo de activo que estés operando.

Ver solución:

Objetivo: Implementar una estrategia de cruce de medias móviles con un filtro basado en la volatilidad (ATR) y ajustar el valor del filtro dependiendo del activo que estés operando.

Pasos para implementar la estrategia:

  1. Configurar la Estrategia Básica:
    • Definir dos medias móviles simples: una rápida (10 periodos) y una lenta (30 periodos).
    • Añadir el indicador de volatilidad ATR con un período de 14.
    • Configurar el filtro de volatilidad para operar solo cuando el ATR supere un umbral específico.
  2. Ajustar el Filtro de Volatilidad:
    • Dependiendo del activo (índices, acciones, futuros, criptomonedas), ajustar el valor del ATR para adaptarse a la volatilidad natural del activo. Por ejemplo:
      • Índices: ATR > 1.5
      • Acciones: ATR > 2.0
      • Criptomonedas: ATR > 3.0
  3. Prueba y Ajuste:
    • Implementar y probar la estrategia en diferentes activos.
    • Observa cómo el ajuste del ATR afecta la cantidad de señales de compra y venta.

Código de Ejemplo:

protected override void OnBarUpdate()
{
    // Verifica si tenemos suficientes datos
    if (CurrentBar < 50) return;

    // Calcula las medias móviles rápidas y lentas
    double fastSMA = SMA(10)[0];
    double slowSMA = SMA(30)[0];

    // Calcula el ATR para medir la volatilidad
    double atr = ATR(14)[0];
    
    // Ajusta el filtro de volatilidad dependiendo del tipo de activo
    double atrThreshold;
    
    if (Instrument.MasterInstrument.Name == "ES") // E-mini S&P 500 (índice)
        atrThreshold = 1.5;
    else if (Instrument.MasterInstrument.Name == "AAPL") // Acciones
        atrThreshold = 2.0;
    else if (Instrument.MasterInstrument.Name == "BTCUSD") // Criptomonedas
        atrThreshold = 3.0;
    else
        atrThreshold = 2.0;  // Valor por defecto

    // Ejecuta la estrategia si la volatilidad es mayor que el umbral
    if (fastSMA > slowSMA && atr > atrThreshold)
    {
        EnterLong();
    }
    else if (fastSMA < slowSMA && atr > atrThreshold)
    {
        EnterShort();
    }
}

Tareas del ejercicio:

  1. Implementa la estrategia en tu plataforma NinjaTrader.
  2. Ajusta el filtro de volatilidad dependiendo del activo que estés operando, usando diferentes umbrales de ATR.
  3. Prueba la estrategia en varios activos y analiza el impacto del ajuste del ATR en la frecuencia de las señales y en la rentabilidad.
  4. Documenta los resultados y realiza ajustes según sea necesario para optimizar el rendimiento en diferentes mercados.

Este ejercicio te ayudará a desarrollar habilidades en la combinación de indicadores técnicos con filtros de volatilidad y en la personalización de estrategias para diferentes tipos de activos.


 
 

A lo largo de este tutorial, has aprendido a programar en NinjaScript y a desarrollar estrategias e indicadores personalizados. Sin embargo, el aprendizaje nunca termina. En este capítulo, te proporcionaremos recursos adicionales que te ayudarán a continuar tu desarrollo en NinjaScript y mejorar tus habilidades como programador de trading automatizado.

La documentación oficial de NinjaTrader es uno de los recursos más valiosos a la hora de profundizar en NinjaScript. Está actualizada y organizada de forma que puedas encontrar rápidamente información sobre las diferentes funciones y herramientas.

  • Documentación de NinjaScript: Esta guía oficial cubre todos los aspectos de la programación en NinjaScript, desde los conceptos básicos hasta las funciones avanzadas como la creación de indicadores personalizados, estrategias y análisis de datos.
  • Foros de Soporte de NinjaTrader: Los foros son una excelente fuente de conocimiento, ya que otros usuarios pueden haber experimentado problemas similares a los tuyos. En el foro de NinjaTrader podrás encontrar preguntas resueltas y participar en discusiones con otros traders y programadores.

Ejercicio Práctico:

Regístrate en los foros de NinjaTrader y busca una pregunta sobre un problema que hayas encontrado durante el desarrollo de un indicador o estrategia. Responde a una pregunta si sientes que puedes contribuir.


Existen varios recursos educativos como libros y cursos en línea que pueden ayudarte a profundizar en el uso de NinjaScript y las estrategias de trading automatizadas.

  • Libros Recomendados:
    • «NinjaTrader’s NinjaScript Programmer’s Launch Pad» por Scott Dagget: Una introducción práctica a la codificación NinjaScript para traders.
    • «The Beginner Programming Guide For Ninja Trader 8»: Dirigido a principiantes, introduce la programación básica de NinjaScript para NinjaTrader 8.
    • «The Absolute Beginner’s Guide to NinjaScript for Automated Trading» por Sajjad Hussain: Se centra en el comercio automatizado utilizando NinjaScript, ideal para principiantes.
  • Cursos en Línea:
    • NinjaTrader’s Official Training Courses: NinjaTrader ofrece cursos oficiales sobre su plataforma, incluidos tutoriales sobre NinjaScript. Estos cursos son impartidos por expertos de la plataforma y proporcionan formación de primera mano sobre cómo aprovechar al máximo las herramientas disponibles.
    • Udemy y Coursera también ofrecen cursos de trading programático que, aunque no están centrados exclusivamente en NinjaScript, pueden ayudarte a mejorar tus habilidades en algoritmos y desarrollo de estrategias.

Ejercicio Práctico:

Elige uno de los libros o cursos mencionados y comienza un plan de estudio. Implementa una estrategia o indicador siguiendo lo aprendido en el libro o curso elegido.


El networking con otros traders y programadores es clave para mantenerse actualizado y aprender nuevas técnicas. Hay varias comunidades en línea donde puedes compartir ideas, estrategias y recibir feedback.

  • NinjaTrader Ecosystem: Es una comunidad oficial de NinjaTrader que incluye desarrolladores, brokers, y traders que colaboran para mejorar el uso de la plataforma.
  • GitHub – NinjaTrader: Es un excelente recurso donde muchos programadores comparten proyectos en código abierto relacionados con NinjaScript y otras plataformas de trading. Puedes buscar repositorios con estrategias de trading e indicadores avanzados.
  • Grupos de Redes Sociales y Discord: Existen varios grupos en LinkedIn, Facebook y Discord donde los traders discuten sobre estrategias y la implementación de sistemas automatizados con NinjaScript. Estos grupos son ideales para interactuar con otros profesionales y mantenerse al día con las nuevas tendencias.

Ejercicio Práctico:

Únete a una de estas comunidades en línea, como GitHub o Discord, y colabora en un proyecto open-source o participa en una discusión técnica sobre estrategias de trading.


 
 

A lo largo de este tutorial, hemos cubierto una amplia gama de conceptos esenciales sobre la programación en NinjaScript. Desde la configuración inicial del entorno de desarrollo hasta la creación de estrategias y herramientas avanzadas, ahora tienes una base sólida para aprovechar al máximo la plataforma NinjaTrader. Aquí hay un resumen de los principales aprendizajes:

  • Estructura de NinjaScript: Comprendimos cómo funcionan los scripts en NinjaTrader, la importancia de las variables, funciones y métodos, así como el manejo de eventos de mercado en tiempo real.
  • Creación de Indicadores y Estrategias: Vimos cómo desarrollar tus propios indicadores personalizados y estrategias de trading automatizado, integrando funciones como Stop Loss y Take Profit.
  • Optimización y Backtesting: Aprendimos a optimizar nuestras estrategias y realizar backtesting eficiente para evaluar su desempeño.
  • Uso de Datos de Mercado: Exploramos cómo acceder y manipular datos de mercado, trabajar con series temporales y crear gráficos personalizados.
  • Automatización y APIs: Exploramos la integración de NinjaScript con APIs externas para automatizar procesos y mejorar la conectividad con brokers y servicios de datos.

Ahora que ya dominas los fundamentos, es momento de avanzar hacia el perfeccionamiento y la personalización de tus estrategias y herramientas. Aquí algunos pasos a seguir:

  1. Perfecciona tu Optimización: Experimenta con diferentes enfoques de backtesting, como walk-forward y out-of-sample, para asegurar que tus estrategias sean sólidas bajo diferentes condiciones de mercado.
  2. Crea Indicadores Avanzados: A medida que te familiarices más con NinjaScript, intenta crear indicadores más sofisticados que puedan integrarse con otras herramientas y mejorar tus decisiones de trading.
  3. Explora Nuevas APIs: Conecta tu entorno de NinjaScript con nuevas APIs externas para acceder a fuentes de datos adicionales o automatizar procesos complejos, como el análisis de grandes volúmenes de datos.
  4. Participa en la Comunidad: A medida que desarrollas más tus habilidades, aprovecha la comunidad de NinjaTrader y otros recursos como foros y documentación oficial para aprender de otros programadores y traders.

El último paso en tu camino como programador de NinjaScript es desarrollar y afinar tus propias estrategias y herramientas personalizadas. Aquí algunos consejos para llevar tu trabajo al siguiente nivel:

  • Itera y Mejora: No tengas miedo de ajustar y mejorar tus scripts con el tiempo. La clave de una buena estrategia o herramienta es su capacidad para adaptarse a cambios en el mercado.
  • Realiza Backtesting y Forward Testing Regularmente: Asegúrate de poner a prueba cualquier nueva herramienta o estrategia bajo diferentes condiciones de mercado para evitar sobreajustes y asegurar su rentabilidad en situaciones reales.
  • Mantente Actualizado: El mundo del trading y las plataformas de desarrollo evoluciona rápidamente. Mantente al día con las actualizaciones de NinjaTrader y las nuevas técnicas de trading automatizado.

Ejercicio Práctico Final:

Crea una estrategia personalizada desde cero, implementando todas las funciones aprendidas: creación de un indicador, integración con datos en tiempo real, optimización mediante backtesting y ajuste de las estrategias en función de los resultados. ¡Este es el primer paso para convertirte en un experto en NinjaScript!

Ver solución:

Aquí te dejo un ejemplo de una estrategia personalizada que combina varios conceptos aprendidos, como indicadores, manejo de datos en tiempo real, optimización y ajuste dinámico de posiciones:

// Estrategia personalizada: combina SMA, ATR, datos en tiempo real y optimización
public class CustomStrategy : Strategy
{
    private double atrValue, smaValue;
    private double stopLoss = 20;
    private double takeProfit = 40;
    
    protected override void OnStateChange()
    {
        if (State == State.SetDefaults)
        {
            // Configuración inicial
            Description = "Estrategia que utiliza SMA, ATR y datos en tiempo real.";
            Calculate = Calculate.OnEachTick;
            EntriesPerDirection = 1;
            EntryHandling = EntryHandling.AllEntries;
        }
    }

    protected override void OnBarUpdate()
    {
        if (CurrentBar < 50) return;

        // Cálculo de indicadores: SMA y ATR
        smaValue = SMA(20)[0];
        atrValue = ATR(14)[0];

        // Condiciones de entrada
        if (Close[0] > smaValue && atrValue > 2)
        {
            EnterLong("MyLongEntry");
            SetStopLoss(CalculationMode.Ticks, stopLoss);
            SetProfitTarget(CalculationMode.Ticks, takeProfit);
        }
        else if (Close[0] < smaValue && atrValue > 2)
        {
            EnterShort("MyShortEntry");
            SetStopLoss(CalculationMode.Ticks, stopLoss);
            SetProfitTarget(CalculationMode.Ticks, takeProfit);
        }
    }

    // Datos en tiempo real: se puede ajustar más lógica de trading en tiempo real
    protected override void OnMarketData(MarketDataEventArgs marketDataUpdate)
    {
        if (marketDataUpdate.MarketDataType == MarketDataType.Last)
        {
            Print($"Precio en tiempo real: {marketDataUpdate.Price}");
        }
    }
}

Explicación:

  1. Indicadores:
    • Se calculan la media móvil simple (SMA) y el indicador de volatilidad ATR (Average True Range).
  2. Datos en Tiempo Real:
    • Usa el evento OnMarketData() para mostrar los datos de mercado en tiempo real, que podría usarse para análisis adicional o decisiones rápidas.
  3. Condiciones de Entrada y Salida:
    • Se abre una posición larga si el cierre está por encima del SMA y el ATR indica alta volatilidad.
    • De forma similar, se abre una posición corta si el cierre está por debajo del SMA.
  4. Optimización:
    • Los valores de stopLoss y takeProfit pueden ser optimizados utilizando el motor de backtesting de NinjaTrader.
  5. Ajuste Dinámico:
    • La estrategia ajusta su comportamiento en función de la volatilidad (ATR) para gestionar mejor las condiciones de mercado.

Este es un ejemplo de cómo combinar los conceptos aprendidos en una estrategia que pueda ser utilizada y optimizada para mejorar su rendimiento en diferentes activos.


 
 


  


Deja una respuesta