Descargar MetaTrader 5

Transferir el código de un indicador al código de un asesor experto. Conclusión

11 mayo 2016, 12:30
Nikolay Kositsin
0
370

Introducción

En artículos anteriores (Transferir el código de un indicador al código de un asesor experto. Estructura del indicador y Transferir el código de un indicador al código de un asesor experto. Diseños estructurales generales de un asesor experto y de funciones de indicador) hemos analizado un esquema general de escritura de una función de indicador sobre la base de un código de indicador ya disponible y hemos definido su correlación con un asesor experto. Ahora es el momento de volver a escribir un código de un asesor experto real. Comencemos.

Código fuente del asesor experto

Tenemos el siguiente código de un asesor experto:

//+------------------------------------------------------------------+
//|                                         ASCTrend1RAVI_Expert.mq4 |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//---- INPUT PARAMETERS
extern int RAVI_Timeframe = 240;
extern int ASCT_Timeframe = 1440;
//---- FILTER OF TRADE CALCULATION DIRECTION
extern int Buy_Sell_Custom = 2; //0-Buy, 1-Sell, 2-Buy+Sell
//---- INPUT PARAMETERS OF EA FOR BUY TRADES 
extern double Money_Management_Up=0.1;
extern int RISK_Up = 3;
extern int Period1_Up = 7; 
extern int Period2_Up = 65; 
extern int MA_Metod_Up = 0;
extern int PRICE_Up = 0;
extern int STOPLOSS_Up = 50;
extern int TAKEPROFIT_Up = 100;
//---- INPUT PARAMETERS OF EA FOR SELL TRADES 
extern double Money_Management_Dn = 0.1;
extern int RISK_Dn = 3;
extern int Period1_Dn = 7; 
extern int Period2_Dn = 65; 
extern int MA_Metod_Dn = 0;
extern int PRICE_Dn = 0;
extern int STOPLOSS_Dn = 50;
extern int TAKEPROFIT_Dn = 100;
//---- Emulated indicator buffers
double RAVI_Up[3];
double RAVI_Dn[3];
double ASCTrend1_Up[2];
double ASCTrend1_Dn[2];
//+------------------------------------------------------------------+
//| Custom Expert functions                                          |
//+------------------------------------------------------------------+
#include <Lite_EXPERT.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   if(RAVI_Timeframe != 1)
       if(RAVI_Timeframe != 5)
           if(RAVI_Timeframe != 15)
               if(RAVI_Timeframe != 30)
                   if(RAVI_Timeframe != 60)
                       if(RAVI_Timeframe != 240)
                           if(RAVI_Timeframe != 1440)
                               Print("Parameter RAVI_Timeframe cannot" + 
                                     " be equal to " + RAVI_Timeframe+"!!!");
//---- 
   if(ASCT_Timeframe != 1)
       if(ASCT_Timeframe != 5)
           if(ASCT_Timeframe != 15)
               if(ASCT_Timeframe != 30)
                   if(ASCT_Timeframe != 60)
                       if(ASCT_Timeframe != 240)
                           if(ASCT_Timeframe != 1440)
                               Print("Parameter ASCT_Timeframe cannot" + 
                                     " be equal "+ASCT_Timeframe+"!!!");
//----                      
   if(RAVI_Timeframe > ASCT_Timeframe) 
       Print("Parameter ASCT_Timeframe should not be less" + 
             " than RAVI_Timeframe");                   
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation  
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Up*2 + 1 + 1)
       return(0);
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Dn*2 + 1 + 1)
       return(0);
//----
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Up, Period2_Up + 4))
       return(0);
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Dn, Period2_Dn + 4))
       return(0);
//---- Declaration of variables
   static double ASCTrend1_Trend_Up, ASCTrend1_Trend_Dn;
   static int LastBars;
   int    bar;
   bool   BUY_Sign, SELL_Sign; 
//---- CALCULATION OF INDICATOR VALUES AND LOADING THEM TO BUFFERS
   if(LastBars != iBars(NULL, RAVI_Timeframe))
       switch(Buy_Sell_Custom)
         {
           case 0: 
             {           
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Up[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Up, Period2_Up,
                                            MA_Metod_Up, PRICE_Up, 0, bar); 
               //----+
               ASCTrend1_Up[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 0, 1); 
               ASCTrend1_Up[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 1, 1); 
               //----+
               ASCTrend1_Trend_Up = ASCTrend1_Up[1] - ASCTrend1_Up[0];       
               break;
             }
           //---- 
           case 1: 
             {            
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Dn[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Dn, 
                                            Period2_Dn, MA_Metod_Dn, 
                                            PRICE_Dn, 0, bar);
               //----+              
               ASCTrend1_Dn[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 0, 1); 
               ASCTrend1_Dn[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 1, 1); 
               //----+
               ASCTrend1_Trend_Dn =ASCTrend1_Dn[1] - ASCTrend1_Dn[0];
               break;
             }
           //----
           default:
             {
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Up[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Up, 
                                            Period2_Up, MA_Metod_Up, 
                                            PRICE_Up, 0, bar); 
               //----+
               ASCTrend1_Up[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 0, 1); 
               ASCTrend1_Up[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 1, 1);
               //----+ 
               ASCTrend1_Trend_Up = ASCTrend1_Up[1] - ASCTrend1_Up[0];
               //----+             
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Dn[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Dn, 
                                            Period2_Dn, MA_Metod_Dn,
                                            PRICE_Dn, 0, bar);
               //----+             
               ASCTrend1_Dn[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 0, 1); 
               ASCTrend1_Dn[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 1, 1); 
               //----+
               ASCTrend1_Trend_Dn = ASCTrend1_Dn[1] - ASCTrend1_Dn[0];
             }
         }
//---- Variable initialization
   LastBars = iBars(NULL, RAVI_Timeframe);
//---- DEFINING SIGNALS FOR TRADES
   switch(Buy_Sell_Custom)
     {
       //----
       case 0: 
         {
           if(RAVI_Up[1] - RAVI_Up[2] < 0)
               if(RAVI_Up[0] - RAVI_Up[1] > 0)
                   if(ASCTrend1_Trend_Up > 0)
                       BUY_Sign = true;
           break;
         }
       //----+ +---------------------------+ 
       case 1: 
         {
           if(RAVI_Dn[1] - RAVI_Dn[2] > 0)
               if(RAVI_Dn[0] - RAVI_Dn[1] < 0)
                   if(ASCTrend1_Trend_Dn < 0)
                       SELL_Sign = true;
           break;
         }
       //----
       default:
         {
           if(RAVI_Up[1] - RAVI_Up[2] < 0)
               if(RAVI_Up[0] - RAVI_Up[1] > 0)
                   if(ASCTrend1_Trend_Up > 0)
                       BUY_Sign = true;
           //----                 
           if(RAVI_Dn[1] - RAVI_Dn[2] > 0)
               if(RAVI_Dn[0] - RAVI_Dn[1] < 0)
                   if(ASCTrend1_Trend_Dn < 0)
                       SELL_Sign = true;
         }
     }
//---- EXECUTING TRADES
   switch(Buy_Sell_Custom)
     {
       //----
       case 0: 
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           break;
         }
       //----
       case 1: 
         {
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn,
                         TAKEPROFIT_Dn);
           break;
         }
       //----
       default:
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn,
                         TAKEPROFIT_Dn);
         }
     }
   return(0);
  }
//+------------------------------------------------------------------+

Primero, necesitamos analizar este asesor experto. El asesor experto tiene por finalidad realizar un trading de 4 horas y según los gráficos diarios en un par de divisas elegido por un operador. El asesor experto solo puede abrir una posición en un par de divisas en esta dirección de trading. El asesor experto tiene dos algoritmos de cálculo independientes para ejecutar las operaciones de compra y venta, y por esta razón puede abrir posiciones opuestas.

Para una optimización más rápida del asesor experto en el probador de estrategias, este contiene una variable externa Buy_Sell_Custom: si esta es igual a cero, el asesor experto calcula solo las señales Buy. Si la variable Buy_Sell_Custom es igual a uno, solo se calculan las señales Sell. Si necesitamos ambos cálculos, la variable externa Buy_Sell_Custom debe ser igual a dos. Las demás variables están divididas en dos grupos: para las operaciones Buy y Sell.

Las señales de trading son los cambios de dirección del movimiento del indicador personalizado RAVI.mq4, el filtro, el corte de señales falsas es la dirección de la tendencia determinada por el indicador personalizado ASCTrend1.mq4. El asesor experto tiene dos llamadas del indicador ASCTrend1.mq4 para la recepción desde cero y los primeros valores fuente del búfer del indicador. El signo de su diferencia determina la dirección de la tendencia. El asesor experto tiene dos llamadas del indicador RAVI.mq4, para recibir dos variantes (Buy y Sell) de valores fuente para los dos algoritmos de cálculo.

Supongo que el significado de la mayoría de parámetros de entrada del asesor experto debe ser compresible a partir de sus nombres. Las variables MA_Metod_Up y MA_Metod_Dn determinan el método de promediación. Pueden tener distintos valores desde cero a tres. Las variables PRICE_Up y PRICE_Dn tienen valores de constantes de precio y los límites de su cambio son desde cero a seis.

Este asesor experto no es sin duda una pérdida, y a pesar de su simplicidad muestra muy buenos resultados en una buena optimización. Por tanto, merece la pena invertir algo de esfuerzo en transformarlo desde un conjunto de archivos en un único archivo autosuficiente. El código del asesor experto usa el archivo Lite_EXPERT.mqh, que son realmente dos funciones personalizadas, que al ser llamadas hace que el asesor experto ejecute las transacciones:

//+------------------------------------------------------------------+
//|                                                  Lite_EXPERT.mqh |
//|                                   Version  January 7, 2007 Final |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
//---- Declaring a global variable for remembering the time 
//     of the last reference to a server
int LastTime; 
//+------------------------------------------------------------------+
//| OpenBuyOrder()                                                   |
//+------------------------------------------------------------------+
int OpenBuyOrder(bool BUY_Signal, double Money_Management, int STOPLOSS, 
                 int TAKEPROFIT)
  {
   if(!BUY_Signal)
       return(0); 
   if(TimeLocal() - LastTime < 11)
       return(0); 
   int total = OrdersTotal();
//---- Checking if there is an open position on this trading 
//     pair in Buy direction
   for(int ttt = total - 1; ttt >= 0; ttt--)     
       if(OrderSelect(ttt, SELECT_BY_POS, MODE_TRADES))
           if((OrderSymbol() == Symbol()) && (OrderType() == 0))
               return(0); 
//---- 
   double ask = NormalizeDouble(Ask, Digits);              
   double bid = NormalizeDouble(Bid, Digits);
//----
   if(ask == 0.0)
       return(-1);  
   if(bid == 0.0)
       return(-1);  
//----  
   double LotVel;      
   double tickVel = MarketInfo(Symbol(), MODE_TICKVALUE);    
   if(tickVel == 0)
       return(-1); 
//----
   if(Money_Management > 0)
       LotVel = tickVel*AccountEquity()*Money_Management / 10000.0;
   else 
       LotVel = -tickVel*10000*Money_Management / 10000.0;
//----
   double Lot = NormalizeDouble(LotVel, 1);  
   if(Lot < 0.1)
       return(-1);     
//----+ Open Buy position
   double Stoploss = NormalizeDouble(bid - STOPLOSS*Point, Digits);  
   double TakeProfit = NormalizeDouble(ask + TAKEPROFIT*Point, Digits);
//----
   int ticket = OrderSend(Symbol(), OP_BUY, Lot, ask, 3, Stoploss, 
                          TakeProfit, NULL, 0, 0, CLR_NONE); 
   //----
   LastTime = TimeLocal(); 
   //----
   if(ticket > 0)
       return(1);
   else 
       return(-1);
  }  
//+------------------------------------------------------------------+
//|  OpenSellOrder()                                                 |
//+------------------------------------------------------------------+
int OpenSellOrder(bool SELL_Signal, double Money_Management, int STOPLOSS, 
                  int TAKEPROFIT)
  { 
   if(!SELL_Signal)
       return(0); 
   if(TimeLocal() - LastTime < 11)
       return(0); 
   int total = OrdersTotal();
//---- Checking if there is an open position on this trading 
//     pair in Sell direction 
   for(int kkk = total - 1; kkk >= 0; kkk--)      
       if(OrderSelect(kkk, SELECT_BY_POS, MODE_TRADES))
           if((OrderSymbol() == Symbol()) && (OrderType() == 1))
               return(0); 
//---- 
   double bid = NormalizeDouble(Bid, Digits); 
   double ask = NormalizeDouble(Ask, Digits);
//----
   if(bid == 0.0)
       return(-1); 
   if(ask == 0.0)
       return(-1); 
//----      
   double LotVel;      
   double tickVel = MarketInfo(Symbol(), MODE_TICKVALUE);
   if(tickVel == 0.0)
       return(-1);   
//----   
   if(Money_Management > 0)
       LotVel = tickVel*AccountEquity()*Money_Management / 10000.0;
   else 
       LotVel = -tickVel*10000*Money_Management / 10000.0;
//----
   double Lot = NormalizeDouble(LotVel, 1); 
   if(Lot < 0.1)
       return(-1);       
//----+ Open Sell position 
   double Stoploss = NormalizeDouble(ask + STOPLOSS*Point, Digits);         
   double TakeProfit = NormalizeDouble(bid - TAKEPROFIT*Point, Digits);
//----   
   int ticket = OrderSend(Symbol(), OP_SELL, Lot, bid, 3, Stoploss, 
                          TakeProfit, NULL, 0, 0,CLR_NONE);
//----   
   LastTime = TimeLocal(); 
//----
   if(ticket > 0)
       return(1);
   else 
       return(-1);
  }              
//+------------------------------------------------------------------+

Supongo que estas funciones son muy sencillas y no debe haber ninguna dificultad con ellas. Este es el algoritmo de referencia para las funciones:

OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up);
OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn);

Aquí, si BUY_Sign=true la función OpenBuyOrder() abre una posición larga y si SELL_Sign=true, la función OpenSellOrder() abre una posición corta. Significado de los parámetros: Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up, Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn se comprenden a partir de sus nombres.

Escritura de una función de indicador Get_ASCTrend1Series()

Al final del artículo anterior (Transferir el código de un indicador al código de un asesor experto. Diseños estructurales generales de un asesor experto y de funciones de indicador) He presentado la función de indicador Get_RAVISeries(). Será bastante útil ahora. Todo lo que necesitamos es escribir una función análoga para el indicador ASCTrend1.mq4. Ahora, basándonos en el código de este indicador escribiremos la función Get_ASCTrend1Series(). Vamos a echar un vistazo al código de este indicador:

//+------------------------------------------------------------------+
//|                                                        ASCTrend1 |
//|                                        Ramdass - Conversion only |
//+------------------------------------------------------------------+
//---- drawing the indicator in the main window
#property indicator_chart_window 
//---- number of indicator buffers
#property indicator_buffers 2 
//---- indicator color
#property indicator_color1 Magenta
#property indicator_color2 Aqua
//---- width of indicator lines
#property indicator_width1 2
#property indicator_width2 2
//---- INPUT PARAMETERS OF THE INDICATOR
extern int RISK = 3;
//---- indicator buffers
double val1[];
double val2[];
//+------------------------------------------------------------------+
//| ASCTrend1 initialization function                                |
//+------------------------------------------------------------------+
int init()
  {
//---- Chart drawing style
   SetIndexStyle(0, DRAW_HISTOGRAM, 0, 2);
   SetIndexStyle(1, DRAW_HISTOGRAM, 0, 2);
//---- 2 indicator buffers are used for calculation
   SetIndexBuffer(0, val1);
   SetIndexBuffer(1, val2);
//---- setting indicator values that will be unseen on the chart
   SetIndexEmptyValue(0, 0.0);
   SetIndexEmptyValue(1, 0.0);
//---- name for data windows and labels for subwindows
   IndicatorShortName("ASCTrend1");
   SetIndexLabel(0, "DownASCTrend1");
   SetIndexLabel(1, "UpASCTrend1");
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| ASCTrend1                                                        |
//+------------------------------------------------------------------+
int start()
  {
//---- introducing memory variables  
   static double x1, x2;
//---- Introducing floating point variables
   double value1, value2, value3, TrueCount, Range, AvgRange, MRO1, MRO2;
//---- Introducing integer variables and getting already calculated bars
   int MaxBar, iii, kkk, bar, value10, value11, 
       counted_bars = IndicatorCounted();
//---- checking for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last calculated bar must be recalculated 
   if(counted_bars > 0)
       counted_bars--;
//---- variable initialization
   value10 = 3 + RISK*2;
//---- checking whether the bars number is enough for further calculation
   if((Bars <= value10) || (Bars < 10)) 
       return(0);
//---- defining the number of the oldest bar, 
//     starting from which all bars will be fully recalculated 
   MaxBar = Bars - 1 - value10;
//---- defining the number of the oldest bar, 
//     starting from which only new bars will be recalculated 
   bar = Bars - 1 - counted_bars;
//---- zero initialization
   if(bar >= MaxBar)
     {
       x1 = 67 + RISK;
       x2 = 33 - RISK;
       bar = MaxBar;
       //----
       for(kkk = Bars - 1; kkk >= MaxBar; kkk--) 
         {
           val1[kkk] = 0.0;
           val2[kkk] = 0.0;
         }
     }
//---- THE MAIN CYCLE OF INDICATOR CALCULATION
   while(bar >= 0)
     {  
       Range = 0.0;
       AvgRange = 0.0;
       for(iii = 0; iii <= 9; iii++) 
       AvgRange += MathAbs(High[bar+iii] - Low[bar+iii]);
       //----
       Range = AvgRange / 10;
       iii = 0;
       TrueCount = 0;
       while(iii < 9 && TrueCount < 1)
         {
           if(MathAbs(Open[bar+iii] - Close[bar+iii]) >= Range*2.0) 
           TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO1 = bar + iii; 
       else 
           MRO1 = -1;
       //----
       iii = 0;
       TrueCount = 0;
       while(iii < 6 && TrueCount < 1)
         {
           if(MathAbs(Close[bar+iii+3] - Close[bar+iii]) >= Range*4.6) 
           TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO2 = bar + iii; 
       else 
           MRO2 = -1;
       //----
       if(MRO1 > -1)
           value11 = 3; 
       else 
           value11 = value10;
       //----
       if(MRO2 > -1)
           value11 = 4; 
       else 
           value11 = value10;
       //----
       value2 = 100 - MathAbs(iWPR(NULL, 0, value11, bar));
       //---- 
       val1[bar] = 0;
       val2[bar] = 0;
       //---- 
       if(value2 > x1)
         {
           val1[bar] = Low [bar];
           val2[bar] = High[bar];
         }
       //---- 
       if(value2 < x2)
         {
           val1[bar] = High[bar]; 
           val2[bar] = Low [bar];
         }
       //---- 
       bar--;
     }
   return(0);
  }
//+------------------------------------------------------------------+



He optimizado este indicador. La versión original está en el archivo adjunto ASCTrend1_Old!. mq4. Ahora vamos a preparar la función Get_ASCTrend1Series(). Para ello, transformaremos el código del indicador ASCTrend1.mq4 de acuerdo con el esquema de la mejora de su código descrito en el artículo anterior. Como resultado, obtendremos la siguiente función personalizada:

//+------------------------------------------------------------------+
//|                                          Get_ASCTrend1Series.mqh |
//|                        Copyright © 2006,        Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru |
//+------------------------------------------------------------------+
bool Get_ASCTrend1Series(int Number, string symbol,int timeframe, 
                         bool NullBarRecount, int RISK, 
                         double& InputBuffer[])    
  {
//---- getting the number of all bars of a chart
   int IBARS = iBars(symbol, timeframe);  
//---- Checking whether the bars number is enough for further calculation
   if((IBARS < 3 + RISK*2 + 1)||(IBARS < 10))
       return(false);
//---- EMULATION OF INDICATOR BUFFERS
   if(ArraySize(InputBuffer) < IBARS)
     {
       ArraySetAsSeries(InputBuffer, false);
       //----  
       ArrayResize(InputBuffer, IBARS); 
       //----
       ArraySetAsSeries(InputBuffer, true);
     } 
//----+  introducing static memory variables
   static double x1[], x2[];
   static int IndCounted[]; 
//----+ changing size of static variables
   if(ArraySize(IndCounted) < Number + 1)
     {
       ArrayResize(x1, Number + 1); 
       ArrayResize(x2, Number + 1); 
       ArrayResize(IndCounted, Number + 1); 
     }
 //----+ introducing integer variable
   int LastCountBar;
//----+ Checking whether the zero bar recalculation is allowed
   if(!NullBarRecount)
       LastCountBar = 1;
//----+ Introducing floating point variables 
   double value1, value2, value3, val1, val2;
   double TrueCount, Range, AvgRange, MRO1, MRO2;
//----+ Introducing integer variables and getting already calculated bars
   int MaxBar, iii, kkk, bar, value10, value11,
       counted_bars = IndCounted[Number];
//----+ Remembering the number of all bars of the chart
   IndCounted[Number] = IBARS - 1;
//---- variable initialization
   value10 = 3 + RISK*2;
//---- defining the number of the oldest bar, 
//     starting from which new bars will be recalculated
   bar = IBARS - counted_bars - 1; 
//---- defining the number of the oldest bar, 
//     starting from which all bars will be recalculated
   MaxBar = IBARS - 1 - value10;
//---- zero initialization 
   if(bar > MaxBar)
     {
       bar = MaxBar;
       x1[Number] = 67 + RISK;
       x2[Number] = 33 - RISK;
       //----
       ArrayInitialize(InputBuffer, 0.0); 
     } 
//---- THE MAIN CYCLE OF INDICATOR CALCULATION
   while(bar >= LastCountBar)
     {  
       Range = 0.0;
       AvgRange = 0.0;
       for(iii = 0; iii <= 9; iii++) 
           AvgRange += MathAbs(iHigh(symbol, timeframe, bar + iii) -
                               iLow(symbol, timeframe, bar + iii));
       //----
       Range = AvgRange / 10;
       iii = 0;
       TrueCount = 0;
       while(iii < 9 && TrueCount < 1)
         {
           if(MathAbs(iOpen(symbol, timeframe, bar + iii) -
                      iClose(symbol, timeframe, bar + iii)) >= Range*2.0) 
               TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO1 = bar + iii; 
       else 
           MRO1 = -1;
       //----
       iii = 0;
       TrueCount = 0;
       while(iii < 6 && TrueCount < 1)
         {
           if(MathAbs(iClose(symbol, timeframe, bar + iii + 3) -
                      iClose(symbol, timeframe, bar + iii)) >= Range*4.6) 
               TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO2 = bar + iii; 
       else 
           MRO2 = -1;
       //----
       if(MRO1 > -1)
           value11 = 3; 
       else 
           value11 = value10;
       if(MRO2 > -1)
           value11 = 4; 
       else 
           value11 = value10;
       //----
       value2 = 100 - MathAbs(iWPR(symbol, timeframe, value11, bar));
       //---- 
       val1 = 0;
       val2 = 0;
       InputBuffer[bar] = 0;
       //---- 
       if(value2 > x1[Number])
         {
           val1 = iLow (symbol, timeframe, bar);
           val2 = iHigh(symbol, timeframe, bar);
         }
       if(value2 < x2[Number])
         {
           val1 = iHigh(symbol, timeframe, bar);
           val2 = iLow(symbol, timeframe, bar);
         } 
       InputBuffer[bar]=val2-val1;
       bar--;
     }
   //----+  
   return(true);
  }
//+------------------------------------------------------------------+

Por supuestos, esta función es más compleja que la descrita al final del artículo anterior. Pero aunque el código fuente del indicador fue escrito y optimizado sin errores, el proceso de creación de esta función no presenta muchos problemas.

Después de esto, la función debe ser probada para observar la correspondencia de sus valores con los del indicador, en base a los cuales fue creada la función. Para ello, debemos crear una vez más un asesor experto probador para la función Get_ASCTrend1Series():

//+------------------------------------------------------------------+
//|                                      Get_ASCTrend1SeriesTest.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        http://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "http://www.metaquotes.net/"
//---- INPUT PARAMETERS OF THE EA
extern bool NullBarRecount = true;
//---- indicator buffers
double IndBuffer0[];
double IndBuffer1[];
double IndBuffer2[];
//+------------------------------------------------------------------+
//| Get_ASCTrend1Series() functions                                  |
//+------------------------------------------------------------------+
#include <Get_ASCTrend1Series.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- 
   double Ind_Velue,Resalt;
//---- 
   if(!Get_ASCTrend1Series(0, Symbol(), 0, NullBarRecount, 1, IndBuffer0))
       return(0);
   if(!Get_ASCTrend1Series(1, Symbol(), 240, NullBarRecount, 3, IndBuffer1))
       return(0);
   if(!Get_ASCTrend1Series(2, Symbol(), 1440, NullBarRecount, 5, IndBuffer2))
       return(0);
//---- getting indicator values for the test 0
   Ind_Velue = iCustom(NULL, 0, "ASCTrend1", 1, 1, 2) - 
               iCustom(NULL, 0, "ASCTrend1", 1, 0, 2); 
   Resalt = IndBuffer0[2] - Ind_Velue; 
   Print("0:     " + Ind_Velue + "    " + IndBuffer0[2] + 
         "    " + Resalt + "");
//---- getting indicator values for the test 1
   Ind_Velue = iCustom(NULL, 240, "ASCTrend1", 3, 1, 2) - 
               iCustom(NULL, 240, "ASCTrend1", 3, 0, 2); 
   Resalt = IndBuffer1[2] - Ind_Velue; 
   Print("H4:    " + Ind_Velue + "    " + IndBuffer1[2] + 
         "    " + Resalt + "");
//---- getting indicator values for the test 2
   Ind_Velue = iCustom(NULL, 1440, "ASCTrend1", 5, 1, 2) - 
               iCustom(NULL, 1440, "ASCTrend1", 5, 0, 2); 
   Resalt = IndBuffer2[2] - Ind_Velue; 
   Print("Daily: " + Ind_Velue + "    " + IndBuffer2[2] + 
         "    " + Resalt + "");
//----
   return(0);
  }
//+------------------------------------------------------------------+

Comenzamos la prueba de este asesor experto en el probador de estrategias y después de ello comprobamos el archivo de registro para asegurarnos de que los valores de la función son completamente idénticos a los del indicador en términos de la utilización de los valores del búfer del indicador emulados.



Transformación final del código inicial del asesor experto

Y ahora finalmente podemos trasformar el código del asesor experto sustituyendo las llamadas del indicador personalizado por las llamadas de la función del indicador personalizado:

//+------------------------------------------------------------------+
//|                                      NewASCTrend1RAVI_Expert.mq4 |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//---- INPUT PARAMETERS
extern int RAVI_Timeframe = 240;
extern int ASCT_Timeframe = 1440;
//---- FILTER OF TRADE CALCULATION DIRECTION
extern int Buy_Sell_Custom = 2; // 0-Buy, 1-Sell, 2-Buy+Sell
//---- INPUT PARAMETERS OF EA FOR BUY TRADES 
extern double Money_Management_Up = 0.1;
extern int RISK_Up = 3;
extern int Period1_Up = 7; 
extern int Period2_Up = 65; 
extern int MA_Metod_Up = 0;
extern int PRICE_Up = 0;
extern int STOPLOSS_Up = 50;
extern int TAKEPROFIT_Up = 100;
//---- INPUT PARAMETERS OF EA FOR SELL TRADES 
extern double Money_Management_Dn = 0.1;
extern int RISK_Dn = 3;
extern int Period1_Dn = 7; 
extern int Period2_Dn = 65; 
extern int MA_Metod_Dn = 0;
extern int PRICE_Dn = 0;
extern int STOPLOSS_Dn = 50;
extern int TAKEPROFIT_Dn = 100;
//---- Emulated indicator buffers
double RAVI_Up[];
double RAVI_Dn[];
double ASCTrend1_Up[];
double ASCTrend1_Dn[];
//+------------------------------------------------------------------+
//| Get_ASCTrend1Series functions()                                  |
//+------------------------------------------------------------------+
#include <Get_ASCTrend1Series.mqh>
//+------------------------------------------------------------------+
//| Get_RAVISeries functions()                                       |
//+------------------------------------------------------------------+
#include <Get_RAVISeries.mqh>
//+------------------------------------------------------------------+
//| Custom Expert functions                                          |
//+------------------------------------------------------------------+
#include <Lite_EXPERT.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   if(RAVI_Timeframe != 1)
       if(RAVI_Timeframe != 5)
           if(RAVI_Timeframe != 15)
               if(RAVI_Timeframe != 30)
                   if(RAVI_Timeframe != 60)
                       if(RAVI_Timeframe != 240)
                           if(RAVI_Timeframe != 1440)
                               Print("Parameter RAVI_Timeframe cannot" + 
                                     " be equal to " +
                                     RAVI_Timeframe + "!!!");
//---- 
   if(ASCT_Timeframe != 1)
       if(ASCT_Timeframe != 5)
           if(ASCT_Timeframe != 15)
               if(ASCT_Timeframe != 30)
                   if(ASCT_Timeframe != 60)
                       if(ASCT_Timeframe != 240)
                           if(ASCT_Timeframe != 1440)
                               Print("Parameter ASCT_Timeframe cannot" + 
                                     " be equal to "+
                                     ASCT_Timeframe+"!!!");
//----                      
   if(RAVI_Timeframe > ASCT_Timeframe) 
       Print("Parameter ASCT_Timeframe should not be " + 
             "less than RAVI_Timeframe");                   
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation  
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Up*2 + 1 + 1)
       return(0);
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Dn*2 + 1 + 1)
       return(0);
//----
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Up, Period2_Up + 4))
       return(0);
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Dn, Period2_Dn + 4))
       return(0);
//---- Declaring variables for blocking zero bar recalculation 
   static int LastBars;
//---- Declaring logic variables for trend signals
   bool   BUY_Sign, SELL_Sign;
//---- CALCULATION OF INDICATOR VALUES AND LOADING THEM TO BUFFERS 
   if(LastBars != iBars(NULL, RAVI_Timeframe))
       switch(Buy_Sell_Custom)
         {
           case 0: 
             {
               Get_RAVISeries(0, Symbol(), RAVI_Timeframe, false, 
                              Period1_Up, Period2_Up, MA_Metod_Up,
                              PRICE_Up, RAVI_Up);
               //----
               Get_ASCTrend1Series(0, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Up, ASCTrend1_Up);  
               break;
             }
           case 1: 
             {            
               Get_RAVISeries(1, Symbol(), RAVI_Timeframe, false, 
                              Period1_Dn, Period2_Dn, MA_Metod_Dn, 
                              PRICE_Dn, RAVI_Dn);
               //----
               Get_ASCTrend1Series(1, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Dn, ASCTrend1_Dn); 
               break;
             }
           default:
             {
               Get_RAVISeries(0, Symbol(), RAVI_Timeframe, false,
                              Period1_Up, Period2_Up, MA_Metod_Up,
                              PRICE_Up, RAVI_Up);
               //----
               Get_ASCTrend1Series(0, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Up, ASCTrend1_Up); 
               //----              
               Get_RAVISeries(1, Symbol(), RAVI_Timeframe, false,
                              Period1_Dn, Period2_Dn,MA_Metod_Dn,
                              PRICE_Dn,RAVI_Dn);
               //----
               Get_ASCTrend1Series(1, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Dn, ASCTrend1_Dn); 
             }
         }
       //---- Variable initialization
       LastBars = iBars(NULL, RAVI_Timeframe);
       //---- DEFINING SIGNALS FOR TRADES 
       switch(Buy_Sell_Custom)
         {
       case 0: 
         {
           if(RAVI_Up[2] - RAVI_Up[3] < 0)
               if(RAVI_Up[1] - RAVI_Up[2] > 0)
                   if(ASCTrend1_Up[1] > 0)
                       BUY_Sign = true;
           break;
         }
       case 1: 
         {
           if(RAVI_Dn[2] - RAVI_Dn[3] > 0)
               if(RAVI_Dn[1] - RAVI_Dn[2] < 0)
                   if(ASCTrend1_Dn[1] < 0)
                       SELL_Sign = true;
           break;
         }
       default:
         {
           if(RAVI_Up[2] - RAVI_Up[3] < 0)
               if(RAVI_Up[1] - RAVI_Up[2] > 0)
                   if(ASCTrend1_Up[1] > 0)
                       BUY_Sign = true;
           //----+                  
           if(RAVI_Dn[2] - RAVI_Dn[3] > 0)
               if(RAVI_Dn[1] - RAVI_Dn[2] < 0)
                   if(ASCTrend1_Dn[1] < 0)
                       SELL_Sign = true;
         }
     }
//---- EXECUTING TRADES
   switch(Buy_Sell_Custom)
     {
       case 0: 
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           break;
         }
       case 1: 
         {
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, 
                         TAKEPROFIT_Dn);
           break;
         }
       default:
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, 
                         TAKEPROFIT_Dn);
         }
     }
   return(0);
  }
//+------------------------------------------------------------------+

Por supuesto, la última transformación del código del asesor experto requiere algunos cambios en el interior de la función start() en la parte "DEFINING SIGNALS FOR TRADES", debido a los cambios en las posiciones de las celdas usadas en los búferes del indicador emulado en comparación con los búferes fuente.

Prueba de los resultados de los asesores expertos final y fuente

Ahora podemos cargar el asesor experto final en un probador de estrategias y comparar los resultados de su funcionamiento en el historial con los mismos resultados del asesor experto antes de la transformación. Y no vemos ninguna diferencia en los resultados del trading al probar el asesor experto con el historial en ambos casos. Ambas variantes del asesor experto dan absolutamente los mismos resultados de la prueba si los periodos de prueba son los mismos, los mismos valores de las variables externas se cargan en los asesores expertos y se usa el mismo método de prueba.

Pero lo que está claro a primera vista es que en todos los casos el asesor experto final trabaja más lentamente que el asesor experto fuente.







¿Cuál es la razón de un mayor tiempo para la prueba del asesor experto? Podemos suponer que de alguna forma tiene que ver con el uso de la emulación del modo de indicador del funcionamiento de los búferes y los valores devueltos por las funciones del indicador en búferes por referencia. Por supuesto, todo esto puede desarrollarse aún más omitiendo la emulación del modo del indicador del funcionamiento de los búferes. Puede echar un vistazo al asesor experto FastNewASCTrend1RAVI_Expert. mq4 y probarlo. Pero en este caso, la situación es absolutamente idéntica: este asesor experto funciona tan lentamente como el anterior. Por tanto, la siguiente conclusión es obvia: en nuestro sencillo caso de transformación del código usando indicadores absolutamente no intensivos en recursos, su sustitución por funciones personalizadas para un funcionamiento más rápido del asesor experto carece absolutamente de sentido.

Conclusión

Para los asesores expertos que usan referencias a indicadores muy intensivos en recursos, la tarea de hacerlos más rápidos sustituyendo las llamadas al indicador personalizado por llamadas a la función personalizada es por lo general irresoluble. No obstante, si uno quiere desarrollar un asesor experto absolutamente autónomo compuesto por un único archivo y omite la llamada a los indicadores personalizados, puede hacerlo. Pero esto requerirá mucho esfuerzo, ya que esta tarea no puede simplificarse. Por tanto, este deseo debe estar apoyado por razones muy sólidas.


Traducción del ruso hecha por MetaQuotes Software Corp.
Artículo original: https://www.mql5.com/ru/articles/1463

Como crear un robot de trading fiable y seguro en MQL4 Como crear un robot de trading fiable y seguro en MQL4

Este artículo trata sobre los errores más comunes que se producen en el desarrollo y uso de un asesor experto. También se describe un ejemplo de sistema de trading automatizado seguro.

Visualización simultánea de señales de varios indicadores de los cuatro periodos de tiempo Visualización simultánea de señales de varios indicadores de los cuatro periodos de tiempo

Durante el trading manual, podemos vigilar los valores de varios indicadores. Es algo distinto al trading mecánico. Si tenemos dos o tres indicadores y hemos elegido un periodo de tiempo para el trading, no es una tarea complicada. Pero ¿qué ocurre si tenemos cinco o seis indicadores y nuestra estrategia de trading requiere tener en cuenta las señales de varios periodos de tiempo?

¿Cuál es el grado de fiabilidad del trading por la noche? ¿Cuál es el grado de fiabilidad del trading por la noche?

El artículo trata las peculiaridades del trading plano durante la noche con pares de divisas cruzados. Se explica dónde podemos esperar beneficios y por qué las grandes pérdidas son muy poco probables. Se presenta también el ejemplo de un Asesor Experto desarrollado para el trading nocturno y se aborda el uso de esta estrategia en la práctica.

Métodos sencillos para predecir las direcciones de las velas japonesas Métodos sencillos para predecir las direcciones de las velas japonesas

Basta con conocer la dirección del movimiento del precio para conseguir resultados positivos en el trading. Se pueden obtener algunas informaciones acerca de la posible dirección del precio a partir de las velas japonesas. Este artículo aborda algunos métodos sencillos para predecir la dirección de las velas japonesas.