Introducción

La revisión periódica del funcionamiento de un portfolio de estrategias es un factor crítico dentro de la gestión de la operativa. Cuando trabajamos con portfolios de estrategias intradiarias necesitamos establecer puntos horarios de control. En dichos puntos se revisa el funcionamiento de las estrategias para comprobar si ha habido algún error en su funcionamiento. Y si ha sido así para corregirlo y tomar las medidas necesarias. Los puntos de control horarios se establecen en función de la frecuencia operativa de las estrategias; cuanto mayor sea esta, más reducido debe ser el tiempo entre los puntos de control.

Puede haber un sinfín de razones por las cuales la operativa no haya podido ser revisada en un determinado punto de control. La cuestión es: ¿Cómo podemos recibir una alerta cuando esto haya ocurrido? En este artículo vamos a ver un indicador que sirve para alertarnos justamente cuando la operativa no se haya revisado en el punto de control. Este indicador ha sido creado para NinjaTrader 8, aunque puede ser fácilmente programado en otras plataformas.

Desde hace tiempo vengo utilizando este indicador en varios espacios de trabajo de NinjaTrader 8. Y a día de hoy puedo decir que se ha convertido en una herramienta simple pero muy eficaz en la gestión de la operativa.

Importante: Para que el indicador funcione correctamente en NinjaTrader 8 es necesario que el Email haya sido configurado en la opción Share Services. En este enlace encontrarás un listado de la configuración requerida para los servidores de correo más comunes. Así mismo, si quieres configurar una cuenta de Gmail este vídeo te resultara útil para ello.

Funcionamiento del indicador

El indicador Triple-Alert debe ser aplicado en un gráfico de un minuto para que funcione correctamente. La lógica es muy sencilla; en el momento en el que se inserta en el gráfico comienza a contar el número de barras  transcurridas, lo cual equivale al número de minutos transcurridos. Cuando este valor alcanza el número de minutos configurado para el envío de las alertas, estas son enviadas vía e-mail.

En una sesión normal, donde las revisiones se realizan correctamente, el indicador no enviará ninguna alerta de monitorización. Cada vez que se revisa el espacio de trabajo el indicador debe ser reiniciado. Si esto se hace en cada punto de control nunca llegará al número de minutos configurado para el envío de las alertas. Por el contrario, si el indicador no es reiniciado puede llegar a enviar hasta tres alertas diferentes por falta de monitorización. Estas son enviadas de un modo secuencial:

  • Primera alerta: Nos envía un e-mail cuando la operativa no se ha revisado en los últimos n minutos. El número de minutos aquí configurado debe ser ligeramente superior al tiempo existente entre los puntos de control. Por defecto hemos configurado 60 minutos en la programación del indicador.
  • Segunda alerta: Por defecto cuando hayan transcurrido 75 minutos.
  • Tercera alerta: Por defecto cuando hayan transcurrido 90 minutos.

Las alertas cesan cuando el indicador es reiniciado, lo cual nos fuerza a revisar el espacio de trabajo.

Adicionalmente también consta de una última alerta que se envía al finalizar la sesión cuando el espacio de trabajo no se ha revisado en los últimos 15 minutos. Por defecto esta alerta se envía a las 22:05h.

Seguidamente podemos ver una imagen en la que se muestra su funcionamiento. El Plot muestra el número minutos transcurrido desde la última revisión. Y en el cuadro de texto rojo se muestra la hora en la que se hizo ésta:

Configuración del indicador

Como hemos visto el indicador debe ser insertado en un gráfico de un minuto con plantilla de sesión 24/7. Para que el número de minutos se cuenten correctamente es recomendable utilizar un activo que tenga un gran volumen. Activos poco líquidos pueden pasar minutos sin cruzar contratos, y esto puede producir retardos en el indicador. Si trabajamos con futuros del CME la mejor alternativa es utilizar el futuro del e-mini SP500 (ES). Si trabajamos con EUREX nos puede servir el futuro del Bono alemán a 10 años (FGBL) o el futuro del Euro Stoxx 50 (FESX).

Veamos ahora los parámetros que debemos configurar al insertarlo en un gráfico:

  • AlertaMonitorizacion1: Número de minutos transcurridos para el envío de la 1ª alerta.
  • AlertaMonitorizacion2: Minutos transcurridos para el envío de la 2ª alerta.
  • AlertaMonitorizacion3: Minutos transcurridos para el envío de la 3ª alerta.
  • AlertaFindeSesion: Horario en el que se envía la alerta al finalizar la sesión cuando el espacio de trabajo no se ha revisado en los últimos 15 minutos.
  • HoraInicioAlerta: Hora a partir de la cual comienzan a procesarse las tres alertas por falta de monitorización.
  • HoraFinAlerta: Hora a partir de la cual dejan de procesarse las tres alertas por falta de monitorización.
  • EspacioTrabajo: Nombre del espacio de trabajo en el que aplicamos el indicador. Si aplicamos el indicador en distintos espacios de trabajo es importante que asignemos aquí el nombre del mismo. De este modo cuando recibamos las alertas sabremos rápidamente en qué espacio de trabajo se han generado.
  • CorreoElectrónico: Correo electrónico en el que queremos recibir las alertas.

Actualización del indicador

Debemos reiniciar el indicador tras realizar la revisión en cada punto de control. Hay diferentes formas de hacerlo. La más rápida es pinchar con el botón derecho sobre el gráfico y seguidamente seleccionar Reload NinjaScript, o directamente presionar F5:

¡Y listo! Cada vez que hacemos esto el indicador comenzará a contar el número de minutos desde cero.

Recibiendo las alertas

Una alternativa consiste en crear una cuenta de correo específica para recibir las alertas de trading. Así nos aseguramos de recibir aquí únicamente las alertas producidas en los espacios de trabajo. Posteriormente podremos configurar una notificación sonora que nos avise de los correos recibidos en esta cuenta.

Otra alternativa consiste en seguir utilizando nuestra cuenta de correo sin necesidad de crear una nueva. En este caso necesitamos recibir una alerta cuando hayamos recibido un email enviado a través de NinjaTrader. Para ello podemos utilizar una aplicación en nuestro teléfono que nos permita marcar los emails enviados por un remitente específico, y posteriormente asignar una notificación sonora para los correos recibidos de ese remitente. Hay bastantes aplicaciones que nos servirían para ello. Aquí se explica cómo hacer esto utilizando las aplicaciones de correo que vienen por defecto en los dispositivos iOS y Android.

Por último, a mi me gusta configurar las notificaciones en una aplicación instalada en el PC y en otra instalada en el teléfono móvil. En ambos casos es conveniente configurar sonidos suficientemente ruidosos como para que no se nos pase ninguna alerta.

A continuación se muestra un ejemplo de una alerta recibida en el correo:

Código del indicador para NinjaTrader 8

#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Gui.Tools;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.DrawingTools;
#endregion

//This namespace holds Indicators in this folder and is required. Do not change it. 
namespace NinjaTrader.NinjaScript.Indicators
{
	public class TripleAlert : Indicator
	{
		// VARIABLES INTERNAS
		private int contador = 0;
		private bool timestamp = false;
		private bool AvisoCierre = false;
		private DateTime ts;
				
		protected override void OnStateChange()
		{
			if (State == State.SetDefaults)
			{
				Description									= @"";
				Name										= "TripleAlert";
				Calculate									= Calculate.OnBarClose;
				IsOverlay									= false;
				DisplayInDataBox							= true;
				DrawOnPricePanel							= true;
				DrawHorizontalGridLines						= true;
				DrawVerticalGridLines						= true;
				PaintPriceMarkers							= true;
				ScaleJustification							= NinjaTrader.Gui.Chart.ScaleJustification.Right;
				//Disable this property if your indicator requires custom values that cumulate with each new market data event. 
				//See Help Guide for additional information.
				IsSuspendedWhileInactive					= true;
				AlertaMonitorizacion1					= 60;
				AlertaMonitorizacion2					= 75;
				AlertaMonitorizacion3					= 90;
				AlertaFindeSesion    				    = 2205;
				EspacioTrabajo 							= "Simulación NT8";
				HoraInicioAlerta 						= 900;
				HoraFinAlerta 							= 2200;
				CorreoElectronico						= "alertas_trading@gmail.com";
				AddPlot(new Stroke(Brushes.Black, 2), PlotStyle.Bar, "Minutos");
			}
		}

		protected override void OnBarUpdate()
		{
			// Impedimos que el indicador sea calculado en datos históricos.
			if(State == State.Historical) return;
			
			// Calculamos los minutos transcurridos desde la implementación del indicador en el gráfico.
			if(ToTime(Time[0])!= ToTime(Time[1]))
				contador++;
			
			// Alertas por falta de monitorización.
			if(ToTime(Time[0]) >= HoraInicioAlerta*100 && ToTime(Time[0]) <=  HoraFinAlerta*100)
			{
				// Envío de 1ª email de alerta.
				if(contador == AlertaMonitorizacion1)
				{
					SendMail(CorreoElectronico, 
					EspacioTrabajo +": ¡1ª Alerta Monitorización! "+AlertaMonitorizacion1 +" min sin revisión!", 
							"No se ha revisado en "+AlertaMonitorizacion1 +" minutos. Mail enviado a las: "+ Time[0]); 					
				}			
				
				// Envío de 2ª email de alerta.
				if(contador == AlertaMonitorizacion2)
				{
						SendMail(CorreoElectronico, 
							EspacioTrabajo +": ¡2ª Alerta Monitorización! "+AlertaMonitorizacion2 +" min sin revisión!",  
							"No se ha revisado en "+AlertaMonitorizacion2 +" minutos. Mail enviado a las: "+ Time[0]);
				}			
					
				// Envío de 3ª email de alerta.
				if(contador == AlertaMonitorizacion3)
				{
					SendMail(CorreoElectronico, 
						EspacioTrabajo +": ¡3ª Alerta Monitorización! "+AlertaMonitorizacion3 +" min sin revisión!", 
						"No se ha revisado en "+AlertaMonitorizacion3 +" minutos. Mail enviado a las: "+ Time[0]);
				}
			}	
				
			// Envío de mail a las 22:05 si el Workspace no se ha revisado en los últimos 15 minutos.	
			if (contador >= 15 && ToTime(Time[0])>= AlertaFindeSesion *100 && AvisoCierre == false)
			{
				SendMail(CorreoElectronico, 
							"Fin de operativa. Revisar el Workspace de "+ EspacioTrabajo, 
							"El Workspace no se ha revisado en los últimos "+contador +" minutos. Mail enviado a las: "+ Time[0]);
				AvisoCierre = true;
			}	
			
			// Registramos la última hora de revisión (hora a la que se reinicia el indicador).
			if(timestamp == false)
			{				
				ts = Time[1];
				timestamp = true;					
			}
			
			// Fijamos en el gráfico un texto que muestra la hora en la que se ha hecho la última revisión.
			Draw.TextFixed(this, "tag1", "Última Revisión: "+ts+"", TextPosition.BottomLeft, Brushes.White, 
							ChartControl.Properties.LabelFont, Brushes.Black, Brushes.Red, 100);
			
			// Plot[0]: Muestra los minutos transcurridos desde la última revisión.
			Value[0] = (contador);
		}

		#region Properties
		[NinjaScriptProperty]
		[Range(1, int.MaxValue)]
		[Display(Name="AlertaMonitorizacion1", Description="Primera alerta por falta de monitorización", Order=1, GroupName="Parameters")]
		public int AlertaMonitorizacion1
		{ get; set; }
		
		[NinjaScriptProperty]
		[Range(1, int.MaxValue)]
		[Display(Name="AlertaMonitorizacion2", Description="Segunda alerta por falta de monitorización", Order=2, GroupName="Parameters")]
		public int AlertaMonitorizacion2
		{ get; set; }
		
		[NinjaScriptProperty]
		[Range(1, int.MaxValue)]
		[Display(Name="AlertaMonitorizacion3", Description="Tercera alerta por falta de monitorización", Order=3, GroupName="Parameters")]
		public int AlertaMonitorizacion3
		{ get; set; }
		
		[NinjaScriptProperty]
		[Range(1, int.MaxValue)]
		[Display(Name="AlertaFindeSesion", Description="Alerta al finalizar la sesión si no se ha revisado la operativa", Order=4, GroupName="Parameters")]
		public int AlertaFindeSesion
		{ get; set; }
		
		[NinjaScriptProperty]
		[Range(1, int.MaxValue)]
		[Display(Name="HoraInicioAlerta", Description="Hora de inicio de las alertas", Order=5, GroupName="Parameters")]
		public int HoraInicioAlerta
		{ get; set; }
		
		[NinjaScriptProperty]
		[Range(1, int.MaxValue)]
		[Display(Name="HoraFinAlerta", Description="Hora fin de las alertas", Order=6, GroupName="Parameters")]
		public int HoraFinAlerta
		{ get; set; }

		[NinjaScriptProperty]
		[Display(Name="EspacioTrabajo", Description="Nombre del Espacio de Trabajo", Order=7, GroupName="Parameters")]
		public string EspacioTrabajo
		{ get; set; }	
				
		[NinjaScriptProperty]
		[Display(Name="CorreoElectronico", Description="Correo electrónico que recibe las alertas", Order=8, GroupName="Parameters")]
		public string CorreoElectronico
		{ get; set; }			
				
		[Browsable(false)]
		[XmlIgnore]
		public Series<double> Minutos
		{
			get { return Values[0]; }
		}
		#endregion

	}
}

#region NinjaScript generated code. Neither change nor remove.

namespace NinjaTrader.NinjaScript.Indicators
{
	public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase
	{
		private TripleAlert[] cacheTripleAlert;
		public TripleAlert TripleAlert(int alertaMonitorizacion1, int alertaMonitorizacion2, int alertaMonitorizacion3, int alertaFindeSesion, int horaInicioAlerta, int horaFinAlerta, string espacioTrabajo, string correoElectronico)
		{
			return TripleAlert(Input, alertaMonitorizacion1, alertaMonitorizacion2, alertaMonitorizacion3, alertaFindeSesion, horaInicioAlerta, horaFinAlerta, espacioTrabajo, correoElectronico);
		}

		public TripleAlert TripleAlert(ISeries<double> input, int alertaMonitorizacion1, int alertaMonitorizacion2, int alertaMonitorizacion3, int alertaFindeSesion, int horaInicioAlerta, int horaFinAlerta, string espacioTrabajo, string correoElectronico)
		{
			if (cacheTripleAlert != null)
				for (int idx = 0; idx < cacheTripleAlert.Length; idx++)
					if (cacheTripleAlert[idx] != null && cacheTripleAlert[idx].AlertaMonitorizacion1 == alertaMonitorizacion1 && cacheTripleAlert[idx].AlertaMonitorizacion2 == alertaMonitorizacion2 && cacheTripleAlert[idx].AlertaMonitorizacion3 == alertaMonitorizacion3 && cacheTripleAlert[idx].AlertaFindeSesion == alertaFindeSesion && cacheTripleAlert[idx].HoraInicioAlerta == horaInicioAlerta && cacheTripleAlert[idx].HoraFinAlerta == horaFinAlerta && cacheTripleAlert[idx].EspacioTrabajo == espacioTrabajo && cacheTripleAlert[idx].CorreoElectronico == correoElectronico && cacheTripleAlert[idx].EqualsInput(input))
						return cacheTripleAlert[idx];
			return CacheIndicator<TripleAlert>(new TripleAlert(){ AlertaMonitorizacion1 = alertaMonitorizacion1, AlertaMonitorizacion2 = alertaMonitorizacion2, AlertaMonitorizacion3 = alertaMonitorizacion3, AlertaFindeSesion = alertaFindeSesion, HoraInicioAlerta = horaInicioAlerta, HoraFinAlerta = horaFinAlerta, EspacioTrabajo = espacioTrabajo, CorreoElectronico = correoElectronico }, input, ref cacheTripleAlert);
		}
	}
}

namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns
{
	public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase
	{
		public Indicators.TripleAlert TripleAlert(int alertaMonitorizacion1, int alertaMonitorizacion2, int alertaMonitorizacion3, int alertaFindeSesion, int horaInicioAlerta, int horaFinAlerta, string espacioTrabajo, string correoElectronico)
		{
			return indicator.TripleAlert(Input, alertaMonitorizacion1, alertaMonitorizacion2, alertaMonitorizacion3, alertaFindeSesion, horaInicioAlerta, horaFinAlerta, espacioTrabajo, correoElectronico);
		}

		public Indicators.TripleAlert TripleAlert(ISeries<double> input , int alertaMonitorizacion1, int alertaMonitorizacion2, int alertaMonitorizacion3, int alertaFindeSesion, int horaInicioAlerta, int horaFinAlerta, string espacioTrabajo, string correoElectronico)
		{
			return indicator.TripleAlert(input, alertaMonitorizacion1, alertaMonitorizacion2, alertaMonitorizacion3, alertaFindeSesion, horaInicioAlerta, horaFinAlerta, espacioTrabajo, correoElectronico);
		}
	}
}

namespace NinjaTrader.NinjaScript.Strategies
{
	public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase
	{
		public Indicators.TripleAlert TripleAlert(int alertaMonitorizacion1, int alertaMonitorizacion2, int alertaMonitorizacion3, int alertaFindeSesion, int horaInicioAlerta, int horaFinAlerta, string espacioTrabajo, string correoElectronico)
		{
			return indicator.TripleAlert(Input, alertaMonitorizacion1, alertaMonitorizacion2, alertaMonitorizacion3, alertaFindeSesion, horaInicioAlerta, horaFinAlerta, espacioTrabajo, correoElectronico);
		}

		public Indicators.TripleAlert TripleAlert(ISeries<double> input , int alertaMonitorizacion1, int alertaMonitorizacion2, int alertaMonitorizacion3, int alertaFindeSesion, int horaInicioAlerta, int horaFinAlerta, string espacioTrabajo, string correoElectronico)
		{
			return indicator.TripleAlert(input, alertaMonitorizacion1, alertaMonitorizacion2, alertaMonitorizacion3, alertaFindeSesion, horaInicioAlerta, horaFinAlerta, espacioTrabajo, correoElectronico);
		}
	}
}

#endregion

Descarga del indicador para NinjaTrader 8

Desde este enlace podrás descárgar este indicador para NinjaTrader 8.

Gracias por leer hasta aquí. Esperamos que hayas encontrado este artículo interesante.