Inicio Trading Sistemático Publicación

Promedio Móvil: Asesor Experto para MetaTrader 5

Archivos adjuntos
1921.zip (1.81 KB, Descargar 2 veces)

El Asesor Experto (EA) de Promedio Móvil viene incluido en el paquete estándar de la MetaTrader 5. Este EA es un claro ejemplo de cómo operar utilizando el indicador de Promedio Móvil.

El archivo del EA llamado Moving Average.mq5 se encuentra en la carpeta "terminal_data_folder\MQL5\Experts\Examples\Moving Average\". Este Asesor Experto ejemplifica el uso de indicadores técnicos, funciones de historial de operaciones y clases de trading de la Biblioteca Estándar. Además, el EA cuenta con un sistema de gestión de dinero basado en los resultados de las operaciones.

Veamos la estructura de este Asesor Experto y cómo funciona.

1. Propiedades del EA

//+------------------------------------------------------------------+ //|                                              Moving Averages.mq5 | //|                   Copyright 2009-2013, MetaQuotes Software Corp. | //|                                              https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2009-2013, MetaQuotes Software Corp." #property link      "https://www.mql5.com" #property version   "1.00"

Las primeras cinco líneas contienen un comentario, y las siguientes tres establecen las propiedades del programa MQL5 (copyright, enlace, versión) mediante directivas de preprocesador #property.

Cuando ejecutas el Asesor Experto, estas propiedades se muestran en la pestaña "Común":


Figura 1. Parámetros Comunes del EA Promedio Móvil


1.2. Archivos Incluidos

A continuación, la directiva #include indica al compilador que incluya el archivo "Trade.mqh".

Este archivo forma parte de la Biblioteca Estándar y contiene la clase CTrade para facilitar el acceso a funciones de trading.

#include <Trade\Trade.mqh>

El nombre del archivo incluido se muestra entre corchetes "<>", por lo que la ruta se establece en relación con el directorio: "terminal_data_folder\Include\".

1.3 Entradas

A continuación, se definen el tipo, nombre, valores predeterminados y un comentario. Su función se muestra en la figura 2.

input double MaximumRisk        = 0.02;    // Riesgo Máximo en porcentaje input double DecreaseFactor     = 3;       // Factor de disminución input int    MovingPeriod       = 12;      // Periodo del Promedio Móvil input int    MovingShift        = 6;       // Desplazamiento del Promedio Móvil

Los parámetros MaximumRisk y DecreaseFactor se utilizarán para la gestión de dinero, mientras que MovingPeriod y MovingShift definirán el periodo y el desplazamiento del indicador técnico de Promedio Móvil que se utilizará para verificar las condiciones de trading.

El texto del comentario en la línea del parámetro de entrada, junto con los valores predeterminados, se mostrará en la pestaña "Opciones" en lugar del nombre del parámetro de entrada:


Fig. 2. Parámetros de Entrada del EA Promedio Móvil

1.4. Variables Globales

A continuación, se declara la variable global ExtHandle. Esta se utilizará para almacenar el identificador del indicador de Promedio Móvil.

//--- int   ExtHandle=0;

Le siguen seis funciones, cuya finalidad se describe en el comentario antes del cuerpo de la función:

  1. TradeSizeOptimized() - Calcular el tamaño óptimo del lote;
  2. CheckForOpen() - Verificar las condiciones para abrir posición;
  3. CheckForClose() - Verificar las condiciones para cerrar posición;
  4. OnInit() - Función de inicialización del Asesor;
  5. OnTick() - Función de tick del Asesor;
  6. OnDeinit() - Función de desinicialización del Asesor;

Las últimas tres funciones son funciones de manejo de eventos; las primeras tres son funciones de servicio que se llaman en su código.

2. Funciones de Manejo de Eventos

2.1. La función de inicialización OnInit()

La función OnInit() se llama una vez durante el primer inicio del Asesor Experto. Generalmente, en el controlador de eventos OnInit() se prepara el EA para su funcionamiento: se verifican los parámetros de entrada, se inicializan indicadores y parámetros, etc. En caso de errores críticos, cuando el trabajo posterior no tiene sentido, la función se sale con un código de retorno INIT_FAILED.

//+------------------------------------------------------------------+ //| Función de inicialización del Asesor                                   | //+------------------------------------------------------------------+ int OnInit(void)   { //---    ExtHandle=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE);    if(ExtHandle==INVALID_HANDLE) {      printf("Error al crear el indicador MA");      return(INIT_FAILED);      } //---    return(INIT_SUCCEEDED);   } 

Dado que el trading del EA se basa en el indicador de Promedio Móvil, al llamar a iMA(), se crea el indicador de Promedio Móvil y su identificador se guarda en la variable global ExtHandle.

En caso de error, OnInit() se sale con un código de retorno INIT_FAILED, que es la forma correcta de completar la operación del EA/indicador en caso de una inicialización fallida.


2.2. La función OnTick()

La función OnTick() se llama cada vez que se recibe una nueva cotización para el símbolo del gráfico en el que se ejecuta el EA.

//+------------------------------------------------------------------+ //| Función de tick del Asesor                                             | //+------------------------------------------------------------------+ void OnTick(void)   { //---    if(PositionSelect(_Symbol))       CheckForClose();    else       CheckForOpen(); //---   }

La función PositionSelect() se utiliza para definir si hay una posición abierta para el símbolo actual.

Si hay posiciones abiertas, se llama a la función CheckForClose(), que analiza el estado actual del mercado y cierra la posición abierta; de lo contrario, se llama a CheckForOpen(), que verifica las condiciones de entrada al mercado y abre una nueva posición si se cumplen dichas condiciones.


2.3. La función de desinicialización OnDeInit()

OnDeInit() se llama cuando se elimina un EA del gráfico. Si un programa coloca objetos gráficos durante su operación, estos pueden ser eliminados del gráfico.

//+------------------------------------------------------------------+ //| Función de desinicialización del Asesor                                 | //+------------------------------------------------------------------+ void OnDeinit(const int reason)   {   } //+------------------------------------------------------------------+

En este caso, no se realizan acciones durante la desinicialización del Asesor Experto.


3. Funciones de Servicio

3.1. Función TradeSizeOptimized()

Esta función calcula y devuelve el valor del tamaño óptimo del lote para abrir una posición con el nivel de riesgo y resultados de trading especificados.

//+------------------------------------------------------------------+ //| Calcular tamaño óptimo del lote                                       | //+------------------------------------------------------------------+ double TradeSizeOptimized(void)   {    double price=0.0;    double margin=0.0; //--- Calcular el tamaño del lote    if(!SymbolInfoDouble(_Symbol,SYMBOL_ASK,price))       return(0.0);    if(!OrderCalcMargin(ORDER_TYPE_BUY,_Symbol,1.0,price,margin))       return(0.0);    if(margin<=0.0)       return(0.0);    double lot=NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)*MaximumRisk/margin,2); //--- calcular la longitud de la serie de operaciones perdedoras consecutivas    if(DecreaseFactor>0) {      //--- solicitar todo el historial de trading       HistorySelect(0,TimeCurrent());      //--       int    orders=HistoryDealsTotal();  // el total de operaciones       int    losses=0;                    // el número de operaciones perdedoras en la serie       for(int i=orders-1;i>=0;i--)         {          ulong ticket=HistoryDealGetTicket(i);          if(ticket==0)            {             Print("HistoryDealGetTicket fallido, sin historial de trading");             break;            }          //--- verificar el símbolo de la operación          if(HistoryDealGetString(ticket,DEAL_SYMBOL)!=_Symbol)             continue;          //--- verificar el beneficio          double profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);          if(profit>0.0)             break;          if(profit<0.0)             losses++;     }       //---       if(losses>1)          lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);    } //--- normalizando y verificando los valores permitidos del volumen de trading    double stepvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);    lot=stepvol*NormalizeDouble(lot/stepvol,0);    double minvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);    if(lot<minvol)       lot=minvol;    double maxvol=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);    if(lot>maxvol)       lot=maxvol; //--- devolver el valor del volumen de trading    return(lot);   } 

La función SymbolInfoDouble() se utiliza para comprobar la disponibilidad de precios para el símbolo actual; luego, se utiliza OrderCalcMargin() para solicitar el margen requerido para realizar una orden (en este caso, una orden de compra). El tamaño inicial del lote se determina a partir del valor del margen necesario para colocar una orden, el margen libre de la cuenta (AccountInfoDouble(ACCOUNT_FREEMARGIN)) y el valor máximo de riesgo permitido especificado en el parámetro de entrada MaximumRisk.

Si el valor del parámetro de entrada DecreaseFactor es positivo, se analiza el historial de operaciones y se ajusta el tamaño del lote teniendo en cuenta la información sobre la serie máxima de operaciones perdedoras: el tamaño inicial del lote se multiplica por el tamaño (1-perdidas/DecreaseFactor).

A continuación, el volumen de trading se "redondea" al valor que es múltiplo del paso mínimo permitido de volumen (stepvol) para el símbolo actual. También se solicitan los valores mínimo (minvol) y máximo (maxvol) del volumen de trading, y si el valor del lote excede los límites permitidos, se ajusta. Como resultado, la función devuelve el valor calculado del volumen de trading.


3.2. Función CheckForOpen()

CheckForOpen() se utiliza para verificar las condiciones de apertura de posición y abrirla cuando se cumplen las condiciones de trading (en este caso, cuando el precio cruza el promedio móvil).

//+------------------------------------------------------------------+ //| Verificar condiciones para abrir posición                               | //+------------------------------------------------------------------+ void CheckForOpen(void)   {    MqlRates rt[2]; //--- copiar los valores de precios    if(CopyRates(_Symbol,_Period,0,2,rt)!=2)      {       Print("CopyRates de ",_Symbol," fallido, sin historial");       return;      } //--- Operar solo en el primer tick de la nueva barra    if(rt[1].tick_volume>1)       return; //--- Obtener el valor actual del indicador de Promedio Móvil     double   ma[1];    if(CopyBuffer(ExtHandle,0,0,1,ma)!=1)      {       Print("CopyBuffer desde iMA fallido, sin datos");       return;      } //--- verificar las señales    ENUM_ORDER_TYPE signal=WRONG_VALUE;    if(rt[0].open>ma[0] && rt[0].close<ma[0])       signal=ORDER_TYPE_SELL    // condición de venta    else        {          if(rt[0].open<ma[0] && rt[0].close>ma[0ORDER_TYPE_BUY  // condición de compra      } //--- verificaciones adicionales    if(signal!=WRONG_VALUE)       if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))          if(Bars(_Symbol,_Period)>100)            {             CTrade trade;             trade.PositionOpen(_Symbol,signal,TradeSizeOptimized(),                                SymbolInfoDouble(_Symbol,signal==ORDER_TYPE_SELL ? SYMBOL_BID:SYMBOL_ASK),                                0,0);            } //---   } 

Al operar utilizando el promedio, debes verificar si el precio cruza el promedio móvil. Utilizando la función CopyRates(), se copian dos valores de los precios actuales en el array de estructuras rt[], donde rt[1] corresponde a la barra actual, y rt[0] a la barra completada.

Una nueva barra se inicia verificando el volumen de ticks de la barra actual; si es igual a 1, entonces ha comenzado una nueva barra. Es importante señalar que este método de detección de una nueva barra puede fallar en algunos casos (cuando las cotizaciones llegan en paquetes), por lo que se debe realizar la detección del inicio de una nueva barra guardando y comparando el tiempo de la cotización actual (ver IsNewBar).

El valor actual del indicador de Promedio Móvil se solicita mediante la función CopyBuffer() y se guarda en el array ma[] que contiene solo un valor. Luego, el programa verifica si el precio ha cruzado el promedio móvil y realiza verificaciones adicionales (si es posible operar con el EA y la existencia de barras en el historial). Si todo es correcto, se abre una posición apropiada para el símbolo llamando al método PositionOpen() del objeto de trading (una instancia de CTrade).

El precio de apertura de la posición se establece utilizando la función SymbolInfoDouble(), que devuelve el precio Bid o Ask dependiendo del valor de la variable signal. El volumen de la posición se determina llamando a TradeSizeOptimized() como se describió anteriormente.


3.3. Función CheckForClose()

CheckForClose() verifica las condiciones para cerrar la posición y la cierra si se cumplen las condiciones.

//+------------------------------------------------------------------+ //| Verificar condiciones para cerrar posición                              | //+------------------------------------------------------------------+ void CheckForClose(void)   {    MqlRates rt[2]; //--- Copiar valores de precios    if(CopyRates(_Symbol,_Period,0,2,rt)!=2)      {       Print("CopyRates de ",_Symbol," fallido, sin historial");       return;      } //--- Operar solo en el primer tick de la nueva barra    if(rt[1].tick_volume>1)       return; //--- obtener el valor actual del indicador de Promedio Móvil    double   ma[1];    if(CopyBuffer(ExtHandle,0,0,1,ma)!=1)      {       Print("CopyBuffer desde iMA fallido, sin datos");       return;      } //--- obtener el tipo de posición seleccionada anteriormente usando PositionSelect()    bool signal=false;    long type=PositionGetInteger(POSITION_TYPE);    if(type==(long)POSITION_TYPE_BUY   && rt[0].open>ma[0] && rt[0].close<ma[0])       signal=true;    if(type==(long)POSITION_TYPE_SELL  && rt[0].open<ma[0] && rt[0].close>ma[0])       signal=true; //--- verificaciones adicionales    if(signal)       if(TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))          if(Bars(_Symbol,_Period)>100)            {             CTrade trade;             trade.PositionClose(_Symbol,3);            } //---   } 

El algoritmo de la función CheckForClose() es similar al de CheckForOpen(). Dependiendo de la dirección de las posiciones abiertas, se verifican las condiciones de su cierre (cruzar el precio por debajo del promedio móvil para una compra o por encima para una venta). Una posición abierta se cierra llamando al método PositionClose() del objeto de trading (instancia de CTrade).


4. Pruebas de Retroceso

Los mejores valores de los parámetros se pueden encontrar utilizando el Probador de Estrategias del terminal MetaTrader 5.

Por ejemplo, al optimizar el parámetro MovingPeriod en el intervalo 2012.01.01-2013.08.01, se obtienen los mejores resultados con MovingPeriod=45:

Resultados de Pruebas de Retroceso del Asesor Experto de Promedio Móvil

Resultados de Pruebas de Retroceso del Asesor Experto de Promedio Móvil

Conclusiones:

El Asesor Experto de Promedio Móvil incluido en el paquete estándar de la MetaTrader 5 es un ejemplo de uso de indicadores técnicos, funciones de historial de operaciones y clases de trading de la Biblioteca Estándar. Además, el EA incluye un sistema de gestión de dinero que se basa en los resultados de las operaciones.


Publicaciones relacionadas

Comentarios (0)