Este indicador sencillo y extremadamente versátil es uno de los grandes olvidados del arsenal de herramientas del análisis técnico. Sin embargo, como veremos en este pequeño artículo, puede resultar de gran utilidad como generador de puntos de vuelta en estrategias de reversión y también en la normalización de otros indicadores.
Para quienes no sepáis nada estadística, comenzaremos diciendo que una puntuación Z (o Z-Score) no es más que el número de desviaciones estándar que un dato de una muestra se aparta de su media. Esto podemos representarlo recurriendo a la típica campana de Gauss:
Las puntuaciones Z muy altas (o muy bajas) se encuentran en las colas de la distribución normal. Por tanto, cuanto más alejado está un dato de la media menor es su probabilidad de ocurrencia. En general podemos considerar 2 desviaciones estándar (SD) como línea divisoria entre los sucesos frecuentes e infrecuentes.
También podemos aplicar este razonamiento al trading para identificar movimientos extremos en los precios. Y esto es justo lo que hace el indicador Z-Score; señalar en qué posición se encuentra cada nuevo cierre en relación con el promedio de los n últimos cierres en una secuencia de cotizaciones. Dado que estamos aplicando el Z-Score en una ventana dinámica de datos, la media móvil y la desviación van cambiando con cada nuevo valor. Esto implica que una Z = ± 1 puede significar algo o nada en absoluto. Más que los valores fijos del indicador nos interesa su dinámica en el gráfico.
Descripción del indicador.
Este es el aspecto del indicador en un gráfico intradiario:
En la imagen aparecen 3 bandas punteadas:
- La línea de 0 nos sirve para identificar la proximidad de los precios a la media y como referencia visual de las transiciones alcista/bajista.
- La banda superior delimita las +2 SD. Los valores por encima son infrecuentes y nos avisan de un posible movimiento de reversión a la baja.
- La banda inferior delimita las -2 SD y nos avisa de una posible reversión al alza.
Interesan los puntos de vuelta sobre estas bandas, que tendrán lecturas diferentes si tratamos de construir un sistema micro-tendencial o de reversión.
- Sentido tendencial: Nos posicionamos largos cuando el indicador rebasa en sentido ascendente la línea de 0 y mantenemos la posición mientras no se cruce dicha línea en sentido descendente o en un nivel ligeramente superior de la línea (lógica inversa para cortos).
- Sentido Anti: Nos posicionamos cortos cuando el valor del indicador supera las 2 SD o cualquier otro umbral superior optimizable (lógica inversa para largos).
Este es el código del indicador para NT 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 ZScore : Indicator { private Series<double> UZS; protected override void OnStateChange() { if (State == State.SetDefaults) { Description = @"Indicador ZScore"; Name = "ZScore"; 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; Nbars = 14; FilterZ = 14; AddPlot(new Stroke(Brushes.SteelBlue, 1), PlotStyle.Line, "ZS"); AddLine(new Stroke(Brushes.DimGray,DashStyleHelper.Dash, 1), 2, "LineSup"); AddLine(new Stroke(Brushes.DimGray,DashStyleHelper.Dash, 1), 0, "LineMed"); AddLine(new Stroke(Brushes.DimGray,DashStyleHelper.Dash, 1), -2, "LineInf"); } else if (State == State.Configure) { UZS = new Series<double>(this); } } protected override void OnBarUpdate() { if (CurrentBar < Nbars) return; double OStd = StandardDeviationFast(Nbars)[0]; double AvgClose =SMA(Nbars)[0]; UZS[0] = ((Input[0]-AvgClose)/OStd); ZS[0] = SMA(UZS,FilterZ)[0]; } #region Properties [NinjaScriptProperty] [Range(1, int.MaxValue)] [Display(Name="Nbars", Description="NumeroBarras", Order=1, GroupName="Parameters")] public int Nbars { get; set; } [NinjaScriptProperty] [Range(1, int.MaxValue)] [Display(Name="FilterZ", Description="Suavización de Z Scire", Order=2, GroupName="Parameters")] public int FilterZ { get; set; } [Browsable(false)] [XmlIgnore] public Series<double> ZS { 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 ZScore[] cacheZScore; public ZScore ZScore(int nbars, int filterZ) { return ZScore(Input, nbars, filterZ); } public ZScore ZScore(ISeries<double> input, int nbars, int filterZ) { if (cacheZScore != null) for (int idx = 0; idx < cacheZScore.Length; idx++) if (cacheZScore[idx] != null && cacheZScore[idx].Nbars == nbars && cacheZScore[idx].FilterZ == filterZ && cacheZScore[idx].EqualsInput(input)) return cacheZScore[idx]; return CacheIndicator<ZScore>(new ZScore(){ Nbars = nbars, FilterZ = filterZ }, input, ref cacheZScore); } } } namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns { public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase { public Indicators.ZScore ZScore(int nbars, int filterZ) { return indicator.ZScore(Input, nbars, filterZ); } public Indicators.ZScore ZScore(ISeries<double> input , int nbars, int filterZ) { return indicator.ZScore(input, nbars, filterZ); } } } namespace NinjaTrader.NinjaScript.Strategies { public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase { public Indicators.ZScore ZScore(int nbars, int filterZ) { return indicator.ZScore(Input, nbars, filterZ); } public Indicators.ZScore ZScore(ISeries<double> input , int nbars, int filterZ) { return indicator.ZScore(input, nbars, filterZ); } } } #endregion
Como podemos ver indicador tiene 2 parámetros:
- NBars: Período utilizado para el cálculo del Z-Score. El valor de referencia depende del uso y del TF empleado. En configuraciones de reversión interesan períodos cortos (ej. 10-20 barras) y en configuraciones tendenciales períodos más largos (ej. 50-100 barras).
- FilterZ: Este parámetro es optativo y sirve para suavizar la curva empleando valores promedio del indicador. Se deben utilizar valores pequeños (ej. 4-6) para no aumentar excesivamente el retardo. Si no queremos usar el filtro ponemos el valor 1.
Uso del Z-Score para normalizar indicadores.
Muchos indicadores como los de momento y direccionalidad no están acotados entre valores máximos y mínimos bien definidos. El Z-Score nos servirá para normalizar automáticamente cualquier indicador. Bastará con aplicarlo sobre la serie del indicador a normalizar con el mismo período.
En la imagen inferior podemos ver un ADX normalizado en términos de desviaciones estándar:
Construcción de sistemas basados en el Z-Score.
Lo primero que tenemos que decir es que ningún sistema completo y para operativa real debe construirse exclusivamente sobre un indicador. Las estrategias basadas en reglas de negociación son estructuras con partes bien delimitadas: Subsistemas de entrada y cierre de posiciones, filtros, fórmulas para modular el tamaño de la posición, rangos horarios de operativa, etc.
Lo que sí podemos construir el Z-Score es un “lanzador de señales” que nos permitirá ver hasta qué punto el indicador sirve como soporte al mecanismo de entrada. La información que obtendremos será valiosa para estimar, sin otras restricciones condicionales, valores como la frecuencia operativa máxima, los retardos, los bloqueos, las interferencias entre las patas larga y corta, la fiabilidad con la que se capturan los puntos de vuelta, etc. Estos y otros elementos valorativos nos permitirán decidir si el indicador cumple su función como lanzador de señales.
Seguidamente mostramos dos configuraciones diferentes del Z-Score como lanzador de señales:
- La primera responde a una lógica antitendencial estándar: Se buscan valores extremos del indicador por encima de las 2 SD y se lanzan señales de entrada en los desbordamientos alcistas o bajistas de las bandas superior e inferior.
En el sistema de la imagen superior solo se permite una entrada y la señal se mantiene hasta el fin de la sesión. Lo que se pretende calibrar en este ejemplo es sobre todo la fiabilidad de cada punto de entrada.
- La segunda configuración corresponde al típico sistema reverse de alta candencia. Para aumentar la cadencia las entradas se producen en niveles más bajos del indicador (entre 0,5 y 1 SD), no se limita el número entradas por sesión y se permite la inversión de posiciones sin pasar por flat. Con esto se consiguen sistemas que hacen muchas más operaciones pero con un BMO muy bajo.
Lo que se trata de medir con esta configuración es la cadencia máxima. El “lanzador de señales” se comporta como un motor sobrerevolucionado en un banco de pruebas; solo interesa la velocidad de la operativa y no tanto la fiabilidad de las señales o su BMO. Sobre el papel surgen curvas tan aparentes como esta:
Pero son poco realistas e incapaces de soportar el peso de los gastos de operativa. Ahora bien, disponemos de tantísimas operaciones que hay mucho margen para añadir filtros y otras reglas de negociación pensadas para levantar el BMO.
En resumen: El Z-Score es un indicador sencillo, versátil y que permite interesantes posibilidades como generador de órdenes en estrategias con lógicas muy distintas.
2 Comentarios
Comments are closed.
Buenas Andrés.
He intentado compilar el indicador pero me da error en
double OStd = StandardDeviationFast(Nbars)[0];
Supongo que tendras una libreria para calcularlo. ¿es la desviación estandard?
un saludo y gracias
Es un indicador que no hemos diseñado nosotros. Prueba a descargarlo aquí: http://www.greattradingsystems.com/Standard+Deviation+Fast-ninjatraderindicator
Alternativamente puedes sustituirlo por el indicador de desviación estándar que viene de serie en NT8 y obtendrás prácticamente el mismo resultado.