English Русский 中文 Deutsch 日本語 Português
preview
Estrategia comercial de reversión a la media simple

Estrategia comercial de reversión a la media simple

MetaTrader 5Trading | 13 octubre 2023, 12:52
833 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Introducción

La reversión a la media es una técnica de negociación de contratendencia en la que el tráder espera que el precio regrese a algún tipo de equilibrio, que generalmente se mide usando una media u otro indicador estadístico de la tendencia promediada. En este artículo analizaremos una estrategia comercial muy simple de reversión a la media.


Breve introducción a la estrategia

Los mercados suelen moverse en ciclos irregulares. Esto significa que cuando miramos los gráficos, tendemos a ver altibajos y fases relativamente planas: la clave para comerciar e invertir es nuestra capacidad de identificar cambios en dichas fases, también llamadas regímenes de mercado.

La reversión a la media puede adoptar la forma de una media móvil, donde si el mercado se aleja demasiado de ella, probablemente regresará a su zona.


¿Qué es la reversión a la media?

La reversión a la media es un término financiero referido al supuesto de que el precio de un activo tenderá a un precio medio con el tiempo.

El uso de la reversión a la media como estrategia de gestión del tiempo implica tanto definir el rango comercial de un valor como calcular el precio medio utilizando métodos cuantitativos. La reversión a la media es un fenómeno que se puede observar en una amplia variedad de datos de series temporales, incluidos los datos de precio, el beneficio y el valor de balance.

Cuando el precio de mercado actual está por debajo del precio medio anterior, el valor se considerará una compra atractiva con la expectativa de que el precio aumentará. Cuando el precio de mercado actual está por encima del precio medio anterior, se esperará que el precio de mercado baje. En otras palabras, se esperará que ante cualquier desviación el precio tienda a volver a la media. Este conocimiento sirve como piedra angular de muchas estrategias comerciales.

Los informes bursátiles suelen ofrecer datos de media móvil durante periodos como 50 y 100 días. Aunque los informes ofrecen valores promedio, todavía resulta necesario identificar los precios altos y bajos durante el periodo de estudiado.

La reversión a la media parece ser un método más científico a la hora de seleccionar puntos de compra y venta de activos que los gráficos porque se obtienen valores numéricos precisos de datos históricos para determinar los valores de compra/venta en lugar de intentar interpretar el movimiento de precios utilizando gráficos (análisis técnico), aunque el indicador RSI y el rango verdadero promedio (ATR) son intentos relativamente nuevos de captar este patrón.

La reversión a la media ocurre en muchas clases de activos e incluso en los tipos de cambio, pero el proceso puede llevar años y, por lo tanto, tiene poco valor para el inversor a corto plazo.

La reversión a la media debe exhibir una forma de simetría, ya que los activos pueden estar por encima de su media histórica con tanta frecuencia como por debajo.

Un modelo de reversión a la media histórica no tendrá en cuenta al cien por cien el comportamiento real del precio de un activo. Por ejemplo, puede aparecer nueva información que afecte de forma permanente la valoración a largo plazo del activo básico. En caso de quiebra, el activo puede dejar de cotizar y nunca recuperar su media histórica anterior.

En finanzas, el término "reversión a la media" tiene un significado ligeramente distinto al de "reversión o regresión a la media" en estadística. Jeremy Siegel utiliza el término "reversión a la media" para describir un principio general, una serie temporal financiera, en la que "los rendimientos pueden ser muy volátiles a corto plazo pero muy estables a largo plazo". Cuantitativamente, la desviación estándar de los rendimientos anuales promedio disminuye más rápido que la inversa del periodo de mantenimiento, lo cual implica que el proceso no supone un paseo aleatorio y que a los periodos de menores rendimientos les siguen periodos compensatorios de mayores rendimientos, como sucede con un negocio estacional.


Vamos a ilustrar esto con el siguiente ejemplo.

compra

Pero ¿cómo podemos saber cuándo el mercado ha ido "demasiado lejos"? Usaremos un método muy simple basado únicamente en la posición del precio respecto a la media móvil.


Desarrollo de la estrategia

Ahora que tenemos la distancia normalizada de 50 periodos entre el mercado y su media móvil de 200 periodos, estamos listos para codificar las siguientes señales comerciales:

  • La señal de compra se generará cada vez que el índice normalizado baje del nivel 100 después de ser igual a 100, mientras el precio de cierre actual está por debajo del precio de cierre de hace cinco periodos y por debajo de la media móvil de 200 periodos.
  • La señal de venta se generará cada vez que el índice normalizado baje del nivel 100 después de ser igual a 100, mientras el precio de cierre actual está por encima del precio de cierre de hace cinco periodos y por encima de la media móvil de 200 periodos.

Por lo tanto, puede que no sea la estrategia más sencilla dadas las circunstancias, pero no deja de resultar intuitiva. La función de señal será la siguiente:

Dada la información del apartado anterior, tendremos objetivos claros para comenzar a desarrollar una estrategia:

  • La señal de compra se generará siempre que el mercado se sitúe tan por debajo de su media móvil que sea probable que vuelva a la media superior.
  • La señal de venta se generará siempre que el mercado se sitúe tan por encima de su media móvil que sea probable que vuelva a la media inferior.

Vamos a realizar cambios en la estrategia para intentar obtener mejores resultados cuando la esta no funcione (cuando la equidad caiga). En particular, realizaremos el siguiente cambio:

- En lugar de mirar los cierres recientes, buscaremos los máximos y mínimos para intentar filtrar las mejores órdenes.

Las órdenes se abrirán con menos frecuencia.

A continuación le mostramos el código modificado:

if(previousValue==100)
        {
         if(Normalizado<100 && array_ma[0]>tick.bid  && rates[5].high < rates[1].low )
           {
            Print("Open Order Buy");
            Alert(" Buying");
            Orden="Buy";
            sl=NormalizeDouble(tick.ask - ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.bid + pttp*_Point,_Digits);
            //trade.Buy(get_lot(tick.bid),_Symbol,tick.bid,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,get_lot(tick.bid),tick.bid,sl,tp,"Buy");
           }
         if(Normalizado<100 && array_ma[0]<tick.ask  && rates[5].low > rates[1].high )
           {
            Print("Open Order Sell");
            Alert(" Selling");
            Orden="Sell";
            sl=NormalizeDouble(tick.bid + ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.ask - pttp*_Point,_Digits);
            //trade.Sell(get_lot_s(tick.ask),_Symbol,tick.ask,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,get_lot(tick.ask),tick.ask,sl,tp,"Sell");

           }
        }

Con estos cambios, la estrategia ganará menos, pero parece más estable (stop loss = 4000 puntos).

4500 sl 30 min


datos 4500 sl 30 min


El asesor deberá optimizarse para obtener los mejores resultados. También podemos cambiar el código. Este es un ejemplo de lo que podríamos ver con una estrategia con un stop loss de 300 en un periodo de 30 minutos. Simplemente encontraremos la mejor coincidencia según el símbolo y el periodo de tiempo utilizado.

350 sl 30 min


datos 350 sl 30 min


El gráfico parece más estable con un stop loss de 300 puntos, pero más rentable con un stop loss de 4000 puntos. Necesitaremos optimizar el stop loss para encontrar un equilibrio para el periodo de 30 minutos. Le sugiero que lo haga por sí mismo.



Código

A continuación le mostramos los parámetros de entrada:


input ENUM_LOT_TYPE        inp_lot_type               = LOT_TYPE_FIX;              // type of lot
input double               inp_lot_fix                = 0.01;                        // fix lot
input double               inp_lot_risk               = 0.01;      
input bool     InpPrintLog          = false;       // Print log
ulong                    Expert_MagicNumber       =66777;            //
bool                     Expert_EveryTick         =false;            //
input ENUM_TIMEFRAMES my_timeframe=PERIOD_CURRENT;                  // Timeframe
int    handle_iMA;
input int                  Inp_MA_ma_period     = 200;          // MA: averaging period
input int                  Inp_MA_ma_shift      = 5;           // MA: horizontal shift
input ENUM_MA_METHOD       Inp_MA_ma_method     = MODE_SMA;    // MA: smoothing type
input ENUM_APPLIED_PRICE   Inp_MA_applied_price = PRICE_CLOSE; // MA: type of price
int shift = 49; // loockback normalization
input int ptsl = 350; // points for stoploss
input int pttp = 5000; // points for takeprofit

La primera línea especificará el tipo de lote utilizado, que es un tamaño de lote fijo. La siguiente línea establecerá el tamaño del lote fijo, seguido del tamaño del lote de riesgo. En la próxima línea se especificará un valor booleano que determinará si el mensaje debe enviarse al registro o no. La siguiente línea establecerá el número mágico del asesor. En la siguiente línea se especificará un valor booleano que determinará si el asesor debe ejecutarse en cada tick o no. La próxima línea establecerá el marco temporal para el asesor. Las siguientes líneas establecerán los parámetros del indicador de media móvil, como el periodo de promediación, el desplazamiento horizontal, el tipo de suavizado y el tipo de precio. La línea siguiente establecerá el desplazamiento para normalizar la historia. Las siguientes dos líneas establecerán los puntos de stop loss y take profit.

En OnInit():

int OnInit()
  {
//---
   handle_iMA=iMA(_Symbol,my_timeframe,Inp_MA_ma_period,Inp_MA_ma_shift,
                  Inp_MA_ma_method,Inp_MA_applied_price);

// Initialize the variable here if needed
   previousValue = 0.0;

//---
   return(INIT_SUCCEEDED);
  }

Este código ha sido escrito en MQL5 y se utilizará para inicializar una variable. La primera línea de código creará un identificador de la función iMA para calcular la media móvil de un símbolo determinado durante un periodo de tiempo específico. Los parámetros de la función iMA se establecerán en los valores de las variables de entrada Inp_MA_ma_period, Inp_MA_ma_shift, Inp_MA_ma_method e Inp_MA_applied_price. La segunda línea de código inicializará la variable previousValue con el valor 0.0. La última línea de código retornará INIT_SUCCEEDED, lo que indicará que la inicialización ha resultado exitosa.

En OnTick():

MqlTick tick;
   double last_price = tick.ask;
   SymbolInfoTick(_Symbol,tick);

y

   if(SymbolInfoTick(_Symbol,tick))
      last=tick.last;

   double Last = NormalizeDouble(last,_Digits);

Este código ha sido escrito en MQL5 y se utilizará para comparar el precio de oferta actual de un símbolo con el último precio de oferta. La primera línea creará una variable denominada tick del tipo MqlTick. La segunda línea almacenará el último precio de oferta en la variable last_price. La tercera línea recuperará la información de tick para el símbolo especificado en la variable _Symbol y la guardará en la variable tick. La cuarta línea comprobará si el precio de oferta actual es mayor que el último precio de oferta almacenado en la variable last_price. En caso afirmativo, se realizará alguna acción.

Este código se utilizará para calcular el porcentaje de spread de un símbolo determinado. El cálculo se comenzará obteniendo el precio del último símbolo utilizando la función SymbolInfoTick(). Luego, el último precio se normalizará al número de dígitos especificados por el parámetro _Digits. Si el último precio normalizado es superior a 0, los precios de oferta y demanda del símbolo se extraerán y normalizarán. El spread se calculará restando el precio de oferta normalizado (bid) del precio de oferta normalizado (ask). Luego, el spread se dividirá por el valor del punto del símbolo (calculado utilizando la función Pow()) para obtener el spread en puntos. Finalmente, el diferencial del punto se dividirá por el último precio normalizado y se multiplicará por 100 para obtener el porcentaje del spread. Si el porcentaje de spread es menor o igual que el parámetro Max_Spread, se realizarán ciertas acciones.

Para la media móvil usaremos lo siguiente:

   handle_iMA=iMA(_Symbol,my_timeframe,Inp_MA_ma_period,Inp_MA_ma_shift,
                  Inp_MA_ma_method,Inp_MA_applied_price);
//---
   double array_ma[];
   ArraySetAsSeries(array_ma,true);
   int start_pos=0,count=3;
   if(!iGetArray(handle_iMA,0,start_pos,count,array_ma))
      return;
   string text="";
   for(int i=0; i<count; i++)
      text=text+IntegerToString(i)+": "+DoubleToString(array_ma[i],Digits()+1)+"\n";
//---
   Comment(text);

bool iGetArray(const int handle,const int buffer,const int start_pos,
               const int count,double &arr_buffer[])
  {
   bool result=true;
   if(!ArrayIsDynamic(arr_buffer))
     {
      //if(InpPrintLog)
      PrintFormat("ERROR! EA: %s, FUNCTION: %s, this a no dynamic array!",__FILE__,__FUNCTION__);
      return(false);
     }
   ArrayFree(arr_buffer);
//--- reset error code
   ResetLastError();
//--- fill a part of the iBands array with values from the indicator buffer
   int copied=CopyBuffer(handle,buffer,start_pos,count,arr_buffer);
   if(copied!=count)
     {
      //--- if the copying fails, tell the error code
      //if(InpPrintLog)
      PrintFormat("ERROR! EA: %s, FUNCTION: %s, amount to copy: %d, copied: %d, error code %d",
                  __FILE__,__FUNCTION__,count,copied,GetLastError());
      //--- quit with zero result - it means that the indicator is considered as not calculated
      return(false);
     }
   return(result);
  }

Este código se utilizará para calcular y mostrar la media móvil de un símbolo determinado en un marco temporal determinado. La función iMA() se utilizará para calcular la MA y la función iGetArray() se usará para recuperar los valores de la media móvil del búfer de indicador. La función ArraySetAsSeries() se usará para configurar el array como una serie, mientras que las funciones IntegerToString() y DoubleToString() se utilizarán para convertir los valores del array a líneas. Finalmente, la función Comment() se utilizará para mostrar los valores de media móvil en el gráfico.


Para evitar obtener errores de volumen al abrir órdenes, usaremos el siguiente código:

//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
double get_lot(double price)
  {
   if(inp_lot_type==LOT_TYPE_FIX)
      return(normalize_lot(inp_lot_fix));
   double one_lot_margin;
   if(!OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,1.0,price,one_lot_margin))
      return(inp_lot_fix);
   return(normalize_lot((AccountInfoDouble(ACCOUNT_BALANCE)*(inp_lot_risk/100))/ one_lot_margin));
  }
//+------------------------------------------------------------------+
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
double normalize_lot(double lt)
  {
   double lot_step = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);
   lt = MathFloor(lt / lot_step) * lot_step;
   double lot_minimum = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   lt = MathMax(lt, lot_minimum);
   return(lt);
  }

Este código se utilizará para calcular el tamaño del lote de una orden de compra. La primera función, get_lot(), tomará el precio como argumento y retornará el tamaño del lote. El tamaño del lote estará determinado por el tipo de lote, que será fijo o se basará en un porcentaje del riesgo. Si el tipo de lote es fijo, la función retornará el tamaño de lote normalizado. Si el tipo de lote se basa en el porcentaje del riesgo, la función calculará el margen para un lote, calculará el tamaño del lote según el balance y el porcentaje del riesgo y retornará el tamaño de lote normalizado. La segunda función, normalize_lot(), tomará el tamaño del lote como argumento y retornará el tamaño de lote normalizado. El tamaño de lote normalizado se calculará dividiendo el tamaño del lote por el incremento de volumen y luego multiplicándolo por el incremento de volumen. Luego, el tamaño de lote normalizado se comparará con el tamaño de lote mínimo y se retornará el mayor de los dos.

Para abrir órdenes, usaremos el siguiente código:

trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,get_lot(tick.bid),tick.bid,sl,tp,"Buy");

Este código se ha escrito en MQL5 y se utilizará para abrir una nueva posición en el mercado. El primer parámetro será el símbolo del activo comerciado. El segundo parámetro será el tipo de orden, en este caso una orden de compra. El tercer parámetro será el tamaño del lote, que se calculará utilizando la función get_lot() y el precio de oferta actual. El cuarto parámetro será el precio de oferta actual. Los parámetros quinto y sexto serán los niveles de stop loss y take profit, respectivamente. El último parámetro será un comentario que se añadirá a la orden.

El código inicial se corresponderá con la estrategia inicial, cambiando solo las condiciones de cierre con máximo y mínimo (para obtener resultados más fiables).

   if(0<=Normalizado<=100 )
     {
      //------------------------------------------------------------------------------
      if(previousValue==100)
        {
         if(Normalizado<100 && array_ma[0]>tick.bid  && rates[5].high < rates[1].low )
           {
            Print("Open Order Buy");
            Alert(" Buying");
            Orden="Buy";
            sl=NormalizeDouble(tick.ask - ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.bid + pttp*_Point,_Digits);
            //trade.Buy(get_lot(tick.bid),_Symbol,tick.bid,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,get_lot(tick.bid),tick.bid,sl,tp,"Buy");
           }
         if(Normalizado<100 && array_ma[0]<tick.ask  && rates[5].low > rates[1].high )
           {
            Print("Open Order Sell");
            Alert(" Selling");
            Orden="Sell";
            sl=NormalizeDouble(tick.bid + ptsl*_Point,_Digits);
            tp=NormalizeDouble(tick.ask - pttp*_Point,_Digits);
            //trade.Sell(get_lot_s(tick.ask),_Symbol,tick.ask,sl,tp);
            trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,get_lot(tick.ask),tick.ask,sl,tp,"Sell");
           }
        }
     }
   previousValue = Normalizado;
   if(Orden=="Sell" && rates[0].low <array_ma[0])
     {
      trade.PositionClose(_Symbol,5);
      Print("cerro sell");
      return;
     }
   if(Orden=="Buy" && rates[0].high >array_ma[0])
     {
      trade.PositionClose(_Symbol,5);
      Print("cerro buy");
      return;
     }
  }

Este código se ha escrito en MQL5 y se utilizará para abrir y cerrar órdenes en el mercado Forex. El código primero verificará si el valor de la variable Normalizado está entre 0 y 100. Si es así, verificará si el valor Normalizado anterior era 100. Si es así, verificaremos si Normalizado es menor que 100 y mayor que el array_ma[0] del precio de oferta actual, y si el máximo de los últimos 5 precios es menor que el mínimo del primer precio. Si se cumplen todas estas condiciones, el código imprimirá "Open Order Buy", alertará al usuario de que se está realizando una compra, establecerá la variable Orden en Buy, establecerá los niveles de stop loss y take profit, y abrirá una orden de compra con los parámetros especificados.

Si el valor de Normalizado es menor que 100, y el valor de array_ma[0] es menor que el precio de oferta actual, y el mínimo de los últimos 5 precios es mayor que el máximo del primer precio, el código imprimirá "Open Order Sell ", notificará al usuario sobre la venta, establecerá la variable Orden en Sell, establecerá los niveles de stop loss y take profit y abrirá una orden de venta con los parámetros especificados. Luego, el código cambiará el valor Normalizado anterior al actual. Finalmente, el código verificará si la variable Orden está configurada en Sell y si el mínimo de la tasa actual es menor que array_ma[0]. Si se cumplen estas condiciones, el código cerrará la orden de venta e imprimirá "cerro sell". De forma similar, si la variable Orden está configurada en Buy y el precio máximo actual es mayor que el valor de array_ma[0], el código cerrará la orden de compra e imprimirá "cerro buy".



Conclusión

La estrategia debe optimizarse para su aplicación en el mercado, pero la idea consiste en presentar un enfoque sobre el análisis de mercado basado en la reversión a la media.



Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/12830

Iniciamos MetaTrader VPS por primera vez: instrucciones paso a paso Iniciamos MetaTrader VPS por primera vez: instrucciones paso a paso
Todo aquel que utilice asesores comerciales o suscripciones a señales, tarde o temprano necesitará un hosting 24/7 fiable para su plataforma comercial. Le recomendamos utilizar MetaTrader VPS por varios motivos. Podrá pagar y gestionar el servicio a través de su cuenta en MQL5.community.
Redes neuronales: así de sencillo (Parte 45): Entrenando habilidades de exploración de estados Redes neuronales: así de sencillo (Parte 45): Entrenando habilidades de exploración de estados
El entrenamiento de habilidades útiles sin una función de recompensa explícita es uno de los principales desafíos del aprendizaje por refuerzo jerárquico. Ya nos hemos familiarizado antes con dos algoritmos para resolver este problema, pero el tema de la exploración del entorno sigue abierto. En este artículo, veremos un enfoque distinto en el entrenamiento de habilidades, cuyo uso dependerá directamente del estado actual del sistema.
Redes neuronales: así de sencillo (Parte 46): Aprendizaje por refuerzo dirigido a objetivos (GCRL) Redes neuronales: así de sencillo (Parte 46): Aprendizaje por refuerzo dirigido a objetivos (GCRL)
En el artículo de hoy, nos familiarizaremos con otra tendencia en el campo del aprendizaje por refuerzo. Se denomina aprendizaje por refuerzo dirigido a objetivos (Goal-conditioned reinforcement learning, GCRL). En este enfoque, el agente se entrenará para alcanzar diferentes objetivos en determinados escenarios.
Desarrollando un canal de Donchian personalizado con la ayuda de MQL5 Desarrollando un canal de Donchian personalizado con la ayuda de MQL5
Existen muchas herramientas técnicas que se pueden usar para visualizar los canales de precios. Una de esas herramientas es el canal de Donchian. En este artículo, aprenderemos cómo crear un canal de Donchian, y también a usarlo como indicador personalizado dentro de un asesor experto.