Divergencias con RSI (STMT)

En esta entrada vamos a ver como localizar divergencias con STMT entre el precio y el indicador RSI.

Divergence

Divergencias

Una divergencia se da cuando el precio sube y el indicador baja y viceversa. Será alcista cuando el indicador es alcista y bajista en caso contrario. Para la divergencia alcista se toman en consideración los mínimos de las barras mientras que para la bajista, los máximos.

La teoría es bastante sencilla y hay mucha información accesible en Internet así que vamos a centrarnos en cómo detectarlas automáticamente.

¿Qué vamos a considerar para clasificar un tramo como divergencia?

  • La pendiente de la recta de regresión y de la recta de los mínimos/máximos del precio (también para el caso del indicador) deben ser parecidas. Si, por ejemplo, las primeras barras del tramo tienen mucha pendiente hacia abajo y el resto van planas o incluso ascendiendo, la pendiente de los mínimos va a ser muy pronunciada cuando realmente el precio no está tan bajo en el resto del tramo. Si la recta de regresión y la recta de mínimos tienen una pendiente parecida evitamos este tipo de casos.
  • La pendiente de la recta de mínimos/máximos debe superar cierto umbral. Con esto se evita considerar pendientes casi planas.

Código fuente

Veamos el código de STMT necesario para la detección de divergencias con las condiciones antes indicada.

Empecemos con la carga de los históricos/indicadores y su trazado en el control de gráficos:

private void Reload()
{
   this._chart.Clear();

   using (HistoricSerie historic = newHistoricSerie(
      this._visualChart, "010015DX", CompressionType.Minutes, 1, this._date, this._date.EndOfDay()))
   {
      if (historic.Dates.Count > 0)
      {
         using (RSI rsi = new RSI(historic))
         {
            rsi.Load();

            this._chart.PlotHistoric(historic);

            this._chart.AddChartArea(rsi.Name);

            this._chart.PlotHistoric(rsi, 1);
            this._chart.GetLineSerie(rsi[1]).Color = Color.Black;
            this._chart.GetLineSerie(rsi[2]).Color = Color.Black;

            this.PlotDivergences(historic, rsi, 10, rsi.Period);
         }
      }
   }
}

Como el método puede invocarse más de una vez, lo primero que se hace es limpiar el gráfico para borrar sus series, líneas, etc.

A continuación se crea el histórico del futuro del DAX en compresión de 1 minuto. Se carga un solo día, el actual. Con las teclas F9 y F10 se puede ir retrocediendo/avanzando para cambiar el día. Si un día no tiene datos, no se hace nada. En caso contrario, se crea el indicador RSI aplicado sobre el histórico.

Con PlotHistoric se carga el histórico en el gráfico. AddChartArea crea una ventana extra donde, con PlotHistoric, se carga el RSI. Las bandas del RSI se pintan ambas de color negro.

El método PlotDivergences es el encargado de buscar las divergencias y mostrarlas en el gráfico.

private void PlotDivergences(
   HistoricSerie historic, RSI rsi, int barCount, int startIndex)
{
   if (startIndex + barCount - 1 >= historic.Dates.Count)
   {
      return;
   }

   int endIndex;
   if (this.PlotDivergences(historic, rsi, barCount, startIndex, out endIndex, true) ||
      this.PlotDivergences(historic, rsi, barCount, startIndex, out endIndex, false))
   {
      this.PlotDivergences(historic, rsi, barCount, endIndex + 1);
   }
   else
   {
      this.PlotDivergences(historic, rsi, barCount, startIndex + 1);
   }
}

Una comprobación inicial para evitar errores cuando no hay suficientes datos.

Las dos llamadas a PlotDivergences que tiene el if difieren solo en el último parámetro, que es quien indica si se están buscando divergencias en los máximos o en los mínimos. Así, dicha condición puede leerse como “Si hay divergencias en los mínimos o hay divergencias en los máximos…”. Si las hay, se vuelve a invocar a PlotDivergences empezando donde ha terminado la divergencia encontrada. Si no las hay, se prueba en la siguiente barra.

La parte más importante es el siguiente método:

private bool PlotDivergences(
   HistoricSerie historic, RSI rsi, int barCount, int startIndex, out int endIndex, bool minimum)
{
   const double maxSlopeDiff = 0.15;
   const double minSlope = 0.3;
   endIndex = Math.Min(historic.Dates.Count, startIndex + barCount) - 1;

   Line historicLine = minimum ? historic.Min : historic.Max;
   EcuationLine historicAvgEcuation = 
      historicLine.LinealRegression(startIndex, endIndex);
   EcuationLine historicEcuation = 
      GetLineEcuation(historicLine, startIndex, endIndex, minimum);
   bool validHistoricSlope =
      Math.Abs(historicAvgEcuation.A - historicEcuation.A) <= maxSlopeDiff &&
      EqualSign(historicAvgEcuation.A, historicEcuation.A);
   if (validHistoricSlope)
   {
      validHistoricSlope = minimum ?
         historicEcuation.A < -minSlope :
         historicEcuation.A > minSlope;
   }

   double historicStartPrice = historicEcuation[0];
   double historicEndPrice = historicEcuation[endIndex - startIndex];
   double historicDiff = historicEndPrice - historicStartPrice;

   int offset = rsi.Period - 1;
   EcuationLine rsiEcuation = 
      rsi.MainLine.LinealRegression(startIndex - offset, endIndex - offset);
   EcuationLine rsiEcuationMin = 
      GetLineEcuation(rsi.MainLine, startIndex - offset, endIndex - offset, true);
   bool validRsiSlope = 
      Math.Abs(rsiEcuation.A - rsiEcuationMin.A) <= maxSlopeDiff &&
      EqualSign(rsiEcuation.A, rsiEcuationMin.A);
   if (validRsiSlope)
   {
      validRsiSlope = minimum ?
         rsiEcuationMin.A > minSlope :
         rsiEcuationMin.A < -minSlope;
   }

   double rsiStartPrice = rsiEcuationMin[0];
   double rsiEndPrice = rsiEcuationMin[endIndex - startIndex];
   double rsiDiff = rsiEndPrice - rsiStartPrice;

   if (!EqualSign(historicDiff, rsiDiff) && 
      validHistoricSlope && 
      validRsiSlope)
   {
      DateTime startDate = historic.Dates[startIndex];
      DateTime endDate = historic.Dates[endIndex];
      this.AddChartLine(startDate, endDate, historicStartPrice, historicEndPrice);
      this.AddChartLine(startDate, endDate, rsiStartPrice, rsiEndPrice, 1);

      return true;
   }

   return false;
}

Veámoslo poco a poco:

Line historicLine = minimum ? historic.Min : historic.Max;
EcuationLine historicAvgEcuation = historicLine.LinealRegression(startIndex, endIndex);
EcuationLine historicEcuation = GetLineEcuation(historicLine, startIndex, endIndex, minimum);

bool validHistoricSlope =
   Math.Abs(historicAvgEcuation.A - historicEcuation.A) <= maxSlopeDiff &&
   EqualSign(historicAvgEcuation.A, historicEcuation.A);

if (validHistoricSlope)
{
   validHistoricSlope = minimum ?
      historicEcuation.A < -minSlope :
      historicEcuation.A > minSlope;
}

double historicStartPrice = historicEcuation[0];
double historicEndPrice = historicEcuation[endIndex - startIndex];
double historicDiff = historicEndPrice - historicStartPrice;

Se obtienen las ecuaciones de la recta de regresión y la que pasa por los mínimos/máximos. Se comprueba que la pendiente de ambas rectas no difiera mucho y que tengan el mismo signo (o ascendientes o descendientes). Si esto se cumple, se comprueba también que la pendiente no sea muy plana.

A continuación se obtiene el precio inicial y final de la recta que pasa por los mínimos/máximos, que se usará posteriormente para pintar la recta en el gráfico. La diferencia entre los precios se compara posteriormente con la diferencia entre los valores del RSI para verificar que tengan distinto signo, es decir, que los precios suban y el RSI baje o viceversa.

Con el RSI se hacen básicamente las mismas operaciones. Hay que tener en cuenta que el RSI tiene un desplazamiento con respecto al histórico ya que hasta que no hay Period barras en el histórico, el RSI no genera su primer valor. Es por eso que los índices usados para el RSI tienen un offset aplicado.

Si todas las condiciones ya mencionadas se cumplen se ejecuta este código:

DateTime startDate = historic.Dates[startIndex];
DateTime endDate = historic.Dates[endIndex];
this.AddChartLine(startDate, endDate, historicStartPrice, historicEndPrice);
this.AddChartLine(startDate, endDate, rsiStartPrice, rsiEndPrice, 1);

Que crea las rectas en el gráfico, tanto en el histórico como en el RSI. Se devuelve true en este caso para continuar buscando divergencias a partir de esta en lugar de continuar empezando en la barra siguiente como se hace cuando no se encuentra divergencia.

Apuntes finales

Se trata de un ejemplo muy sencillo. El algoritmo usado para determinar si se trata o no de una divergencia es extremadamente simple y admite muchas mejoras.

Una muy interesante es la de utilizar lógica difusa para determinar si una pendiente es o no suficientemente grande para considerarla válida. En épocas donde los precios apenas recorren 50 puntos al día, una pendiente no muy pronunciada podría considerarse relevante pues las pendientes con esa volatilidad no suelen ser mucho mayores. En épocas de mayor volatilidad, esa misma pendiente podría considerarse como poco relevante, casi plana, y no tomarse en consideración para la detección de divergencias. Además, si se cambia el futuro del DAX por el par euro/dólar el valor prefijado para las pendientes debe revisarse pues las escalas de precios cambian mucho. Usando lógica difusa se podría desarrollar un escáner de divergencias y aplicarlo a un conjunto de símbolos cualquiera.

Otro punto a tener en cuenta es la detección de las zonas a considerar para el estudio de la divergencia. En el ejemplo se buscan tramos de 10 barras fijos, pero en una aplicación con objetivos más serios hay que agregar lógica extra para determinar dónde empezar y terminar cada tramo a estudiar.

Archivo ZipDescargar STMT y Divergence.

Anuncios
Galería | Esta entrada fue publicada en Programación y etiquetada . Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s