Descargar MetaTrader 5

Cuentos de robots comerciales: ¿mejor poco, pero mejor?

29 abril 2014, 16:26
Roman Zamozhnyy
0
563

Para resover el problema, primero hay que formularlo. Si considero que he encontrado una solución, hay que comprobarla en la práctica, para asegurarse de que funciona.

Sólo conozco una manera de comprobar las cosas, con mi propio dinero.

D.Livermore


Prólogo

En el artículo La última cruzada usted y yo, querido lector, vimos juntos un método (bastante interesante y poco usado en la actualidad) de representación de la información en el mercado, el gráfico de punto y forma. El script que proponemos en el artículo, permite construir gráficos, pero no presupone la automatización del comercio. Le poropongo automatizar el proceso de comercio, usando los gráficos de punto y forma para el análisis y la toma de decisiones sobre las direcciones y los volúmenes de comercio.

No voy a recordarle los principios básicos de la construcción, mejor le propongo echar un vistazo al gráfico típico:

Copyright (c) 2012-2014 Roman Rich
Euro vs US Dollar, Box-20, Reverse-3


    1.4588 | \.....\.................................................................... | 1.4588
    1.4521 | X\....X\................................................................... | 1.4521
    1.4454 | XO\.\.XO\.................................................................. | 1.4454
    1.4388 | XOX\X\XO.\................................................................. | 1.4388
    1.4322 | XOXOXOXO..\................................................................ | 1.4322
    1.4256 | XOXOXOXO...\....\.......................................................... | 1.4256
    1.4191 | XOXO/OXO....\...X\......................................................... | 1.4191
    1.4125 | XOX/.O/O.....\..XO\........................................................ | 1.4125
    1.4060 | XO/../.O......\.XO.\....................................................... | 1.4060
    1.3996 | ./.....O.......\XO..\...................................................... | 1.3996
    1.3932 | .......OX.......XO...\....................................................X | 1.3932
    1.3868 | .......OXO..X.X.XOX...\.................................................X.X | 1.3868
    1.3804 | .......OXO..XOXOXOXOX..\..............................................X.XOX | 1.3804
    1.3740 | .......OXO..XOXOXOXOXO..\.................................\...........XOXOX | 1.3740
    1.3677 | .......OXOX.XO.O.OXOXO...\................................X\..........XOXOX | 1.3677
    1.3614 | .......OXOXOX....O.OXO....\...............................XO\.........XOXOX | 1.3614
    1.3552 | .......O.OXOX...../OXO.....\..............................XO.\........XOXOX | 1.3552
    1.3490 | .........OXOX..../.O.OX.....\.............................XO..\.......XOXO. | 1.3490
    1.3428 | .........OXOX.../....OXO.....\X.\.........................XO...\\...X.XOX.. | 1.3428
    1.3366 | .........O.OX../.....OXO......XOX\........................XO....X\..XOXOX.. | 1.3366
    1.3305 | ...........OX./......OXO....X.XOXO\.....................X.XO....XO\.XOXO... | 1.3305
    1.3243 | ...........OX/.......O.O....XOXOXOX\....................XOXO....XO.\XOX.../ | 1.3243
    1.3183 | ...........O/..........OX...XOXOXOXO\...................XOXOX.X.XOX.XOX../. | 1.3183
    1.3122 | .........../...........OXO..XOXOXOXO.\..........X...X.X.XOXOXOXOXOXOXO../.. | 1.3122
    1.3062 | .......................OXOX.XOXO.OXO..\.........XOX.XOXOXOXOXOXOXOXOX../... | 1.3062
    1.3002 | .......................O.OXOXO...O/O...\........XOXOXOXOXO.OXO.OXOXO../.... | 1.3002
    1.2942 | .........................OXOX..../.O....\.......XOXOXOXOX..OX..OXOX../..... | 1.2942
    1.2882 | .........................O.OX.../..O.....\......XOXO.OXO...OX..OXOX./...... | 1.2882
    1.2823 | ...........................OX../...OX.....\.....XO...OX.../OX..O/OX/....... | 1.2823
    1.2764 | ...........................OX./....OXO.....\....X....OX../.O.../.O/........ | 1.2764
    1.2706 | ...........................OX/.....OXO..X...\...X....O../......../......... | 1.2706
    1.2647 | ...........................O/......O.OX.XOX..\..X....../................... | 1.2647
    1.2589 | .........................../.........OXOXOXO..\.X...../.................... | 1.2589
    1.2531 | .....................................OXOXOXO...\X..../..................... | 1.2531
    1.2474 | .....................................OXO.OXO....X.../...................... | 1.2474
    1.2417 | .....................................OX..O.O..X.X../....................... | 1.2417
    1.2359 | .....................................OX....OX.XOX./........................ | 1.2359
    1.2303 | .....................................O.....OXOXOX/......................... | 1.2303
    1.2246 | ...........................................OXOXO/.......................... | 1.2246
    1.2190 | ...........................................OXO./........................... | 1.2190
    1.2134 | ...........................................OX.............................. | 1.2134
    1.2078 | ...........................................O............................... | 1.2078
    1.2023 | ........................................................................... | 1.2023

             222222222222222222222222222222222222222222222222222222222222222222222222222
             000000000000000000000000000000000000000000000000000000000000000000000000000
             111111111111111111111111111111111111111111111111111111111111111111111111111
             111111111111111111111111112222222222222222222222222222222333333333333333344
             ...........................................................................
             000000000001111111111111110000000000000000000000011111111000000000000011100
             788888899990000001111112221122233445566666677888900001222123444567778901213
             ...........................................................................
             200011211220111220011231220101212121201112222001100010001002123110112020231
             658801925683489071404504193396436668111288937260415979579417630739120547713
                                                                                        
             000100001012111111110111111100112010210001111101101101011111111101011101110
             910501876933613095500253237788652909250001557626626824655375907538165785367
             :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
             550433251023230204310404232105354323532031240033315125241340044324523153453
             000000000000000000000000000000000000000000000000000000000000000000000000000

No voy a asegurar que las posibilidades comerciales se vean a simple vista en este gráfico, sólo propongo comprobar dos hipótesis comerciales:

Comercie según la tendencia: compre en el mercado del toro y venda en el del oso.

Comercie con órdenes stop, determinadas antes de la entrada al mercado.

  • compramos sobre la línea de apoyo una orden stop por encima del máximo de la anterior columna de "Х", vendemos bajo la línea de resistencia una orden stop por debajo del mínimo de la anterior columna de "О", la orden stop seguidora se encuentra al nivel del viraje;

Dejad que el beneficio crezca.

Cierre las operaciones que muestren pérdidas (las mejores operaciones no dejan ver el beneficio inmediatamente).

  • compramos al romper la línea de resistencia, vendemos al romper la línea de apoyo; stop loss se encuentra al nivel del viraje, y la orden stop seguidora en la línea de tendencia.


Pero, ¿con qué volumen entramos?

Preste atención a las citas mostradas más arriba, del clásico de las especulaciones en bolsa: Comercie con órdenes stop. Yo prefiero entrar en el mercado con un volumen tal, que me permita, al activarse una orden Stop loss, perder una parte del saldo que no sea superior a un tanto por ciento asumible para mí de esa cantidad (lo que Ralph Vince, en sus matemáticas de la gestión de capital llamaba la F óptima). El riesgo de pérdidas con una operación es una variable que se puede optimizar maravillosamente (en el código que se muestra más abajo se representa con opt_f).

De este modo, tenemos una función de establecimiento de órdenes de compra/venta con un mecanismo de cálculo del volumen, que depende del riesgo que pueda usted asumir en una operación:

//+------------------------------------------------------------------+
//| Función de establecimiento de órdenes con                        |
//| un volumen calculado por anticipado                              |
//+------------------------------------------------------------------+
void PlaceOrder()
  {
//--- Vairable de cálculo del lote
   uint digits_2_lot=(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS);
   double trade_risk=AccountInfoDouble(ACCOUNT_EQUITY)*opt_f;
   double one_tick_loss_min_lot=SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE_LOSS)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
//--- Rellenamos los campos básicos para el requerimiento
   trade_request.magic=magic;
   trade_request.symbol=symbol;
   trade_request.action=TRADE_ACTION_PENDING;
   trade_request.tp=NULL;
   trade_request.comment=NULL;
   trade_request.type_filling=NULL;
   trade_request.stoplimit=NULL;
   trade_request.type_time=NULL;
   trade_request.expiration=NULL;
   if(is_const_lot==true)
     {
      order_vol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN);
     }
   else
     {
      order_vol=trade_risk/(MathAbs(trade_request.price-trade_request.sl)*MathPow(10,digits_2_lot)*one_tick_loss_min_lot)*SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP);
      order_vol=MathMax(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN));
      if(SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT)!=0) order_vol=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_LIMIT));
      order_vol=NormalizeDouble(order_vol,(int)MathAbs(MathLog10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP))));
     }
//--- Establecemos la orden
   while(order_vol>0)
     {
      trade_request.volume=MathMin(order_vol,SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX));
      if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
      order_vol=order_vol-SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX);
     };
   ticket=trade_result.order;
  };


Pero, ¿según qué criterio debemos optimizar?

En realidad, sólo existen dos criterios de optimización: o bien se minimiza el drawdown (retroceso en la curva de resultados) con un nivel de beneficios establecido, o bien se maximiza el saldo con un nivel establecido de drawdown. Yo prefiero optimizar según el segundo criterio:

//+------------------------------------------------------------------+
//| Resultado del funcionamiento de la estretegia                    |
//| en el modo de simulación                                         |
//+------------------------------------------------------------------+
double OnTester()
  {
   if(TesterStatistics(STAT_EQUITY_DDREL_PERCENT)>(risk*100))
      return(0);
   else
      return(NormalizeDouble(TesterStatistics(STAT_PROFIT),(uint)SymbolInfoInteger(symbol,SYMBOL_DIGITS)));
  };

donde risk, es el nivel de drawdown, superado el cual, la estrategia no se considera asumible para mí. Es, igualmente, una de las variables optimizables.


Pero, ¿cuándo es necesario optimizar de nuevo?

Hay quien realiza optimizaciones otra vez cuando transcurre un intervalo de tiempo (por ejemplo, una vez a la semana, al mes), hay quien las realiza tras un cierto número de operaciones (tras 50 o 100 operaciones), otros lo hacen en función del comportamiento del mercado. En realidad, si la optimización es o no imprescindible, es algo que viene dictado sólo por el criterio de optimización del punto anterior. Si el sistema permanece por debajo del parámetro permisible de risk, entonces llevamos a cabo una optimzación, si no, pues no tocamos nada. En mi caso, no considero asumible un drawdown superior al 10%. De esta forma, si el drawdown supera este valor en el proceso de funcionamiento real, realizo una nueva optimización.


Pero, ¿lo optimizamos todo de golpe?

Por desgracia, en el simulador de estrategias, por el momento, no se ha implementado la posibilidad de optimizar las variables externas para el modo "Todos los símbolos, elegidos en la ventana de "Observación del mercado"". Por eso, la elección de un instrumento de mercado, al igual que sucede con otros parámetros externos optimizables, se realizará de la siguiente manera, bastante obvia:

//+------------------------------------------------------------------+
//| Enumeración de los símbolos                                      |
//+------------------------------------------------------------------+
enum  SYMBOLS
  {
   AA=1,
   AIG,
   AXP,
   BA,
   C,
   CAT,
   DD,
   DIS,
   GE,
   HD,
   HON,
   HPQ,
   IBM,
   IP,
   INTC,
   JNJ,
   JPM,
   KO,
   MCD,
   MMM,
   MO,
   MRK,
   MSFT,
   PFE,
   PG,
   QQQ,
   T,
   SPY,
   UTX,
   VZ,
   WMT,
   XOM
  };
//+------------------------------------------------------------------+
//| Función de elección del símbolo                                  |
//+------------------------------------------------------------------+
void  SelectSymbol()
  {
   switch(selected_symbol)
     {
      case  1: symbol="#AA";   break;
      case  2: symbol="#AIG";  break;
      case  3: symbol="#AXP";  break;
      case  4: symbol="#BA";   break;
      case  5: symbol="#C";    break;
      case  6: symbol="#CAT";  break;
      case  7: symbol="#DD";   break;
      case  8: symbol="#DIS";  break;
      case  9: symbol="#GE";   break;
      case 10: symbol="#HD";   break;
      case 11: symbol="#HON";  break;
      case 12: symbol="#HPQ";  break;
      case 13: symbol="#IBM";  break;
      case 14: symbol="#IP";   break;
      case 15: symbol="#INTC"; break;
      case 16: symbol="#JNJ";  break;
      case 17: symbol="#JPM";  break;
      case 18: symbol="#KO";   break;
      case 19: symbol="#MCD";  break;
      case 20: symbol="#MMM";  break;
      case 21: symbol="#MO";   break;
      case 22: symbol="#MRK";  break;
      case 23: symbol="#MSFT"; break;
      case 24: symbol="#PFE";  break;
      case 25: symbol="#PG";   break;
      case 26: symbol="#QQQ";  break;
      case 27: symbol="#T";    break;
      case 28: symbol="#SPY";  break;
      case 29: symbol="#UTX";  break;
      case 30: symbol="#VZ";   break;
      case 31: symbol="#WMT";  break;
      case 32: symbol="#XOM";  break;
      default: symbol="#SPY";  break;
     };
  };

En caso necesario, puede añadir a la enumeración y la función de elección del símbolo (que llamaremos en OnInit()) los instrumentos que necesite.


¿Y qué tipo de robot hemos conseguido?

Operador de ticks:

//+------------------------------------------------------------------+
//| El operador de ticks típico es OnTick()                          |
//|     El gráfico lo construiremos sólo conforme a las barras       |
//|     formadas y desde el principio                                |
//|     Comprobamos si no hay ahora mismo una barra nueva            |
//|     Si hay una barra nueva, entonces, si tenemos una posición,   |
//|     comprobamos si no hay que virar el stop loss,                |
//|     y en caso de que no tengamos posición, comprobamos           |
//|     si hay condiciones para abrir una operación                  |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Si tenemos una barra nueva
   if(IsNewBar()==true)
     {
      RecalcIndicators();
      //--- ¿Modo simulación/optimizador?
      if((MQLInfoInteger(MQL_TESTER)==true) || (MQLInfoInteger(MQL_OPTIMIZATION)==true))
        {
         //--- ¿Nos encontramos ya en el periodo de simulación?
         if(cur_bar_time_dig[0]>begin_of_test)
           {
            //--- Si hay una posición abierta conforme al símbolo
            if(PositionSelect(symbol)==true)
               //--- comprobamos si hay que desplazar SL, y si es necesario, se mueve
               TrailCondition();
            //--- y si no hay posición
            else
            //--- comprobamos si hay que abrir una posición, y si es necesario, la abrimos
               TradeCondition();
           }
        }
      else
        {
         //--- Si hay una posición abierta conforme al símbolo
         if(PositionSelect(symbol)==true)
            //--- comprobamos si hay que desplazar SL, y si es necesario, se mueve
            TrailCondition();
         //--- y si no hay posición
         else
         //--- comprobamos si hay que abrir una posición, y si es necesario, la abrimos
            TradeCondition();
        }

     };
  };

Para la estrategia №1 "compramos por encima de la línea de apoyo, una orden stop por encima del máximo de la anterior columna de "Х", vendemos bajo la línea de resistencia, una orden stop por debajo del mínimo de la anterior columna de "О", la orden stop seguidora se encuentra al nivel del viraje":

//+------------------------------------------------------------------+
//| Función de comprobación de las condiciones comerciales           |
//| para la apertura de una operación                                |
//+------------------------------------------------------------------+
void TradeCondition()
  {
   if(order_col_number!=column_count)
      //--- ¿Se le han estancado algunas órdenes referentes a un símbolo?
     {
      if(OrdersTotal()>0)
        {
         //--- ¡Bórrelas!
         for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++)
           {
            ticket=OrderGetTicket(loc_count_1);
            if(!OrderSelect(ticket)) Print("Failed to select order #",ticket);
            if(OrderGetString(ORDER_SYMBOL)==symbol)
              {
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
              };
           };
         order_col_number=column_count;
         return;
        }
      else
        {
         order_col_number=column_count;
         return;
        }
     }
   else
      if((MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK)) && 
         (pnf[column_count-1].column_type=='X') && 
         (pnf[column_count-1].max_column_price<=pnf[column_count-3].max_column_price))
        {
         //--- Se han cumplido las condiciones para BUY, comprobamos si hay órdenes pendientes BUY sobre el símbolo con el precio necesario
         trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price+double_box),digit_2_orders);
         trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].max_column_price-(reverse-1)*double_box),digit_2_orders);
         trade_request.type=ORDER_TYPE_BUY_STOP;
         if(OrderSelect(ticket)==false)
            //--- No, no hay órdenes pendientes, establecemos la orden
           {
            PlaceOrder();
            order_col_number=column_count;
           }
         else
         //--- o bien, si hay órdenes pendientes
           {
            //--- nos preguntamos ¿cuál será el tipo y el precio de la orden pendiente?
            if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || 
               ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
              {
               //--- Si ese no es el tipo, o el precio es distinto, cerramos la orden
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
               //--- y abrimos con el precio necesario
               PlaceOrder();
               order_col_number=column_count;
              };
           };
         return;
        }
   else
      if((MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK)) && 
         (pnf[column_count-1].column_type=='O') && 
         (pnf[column_count-1].min_column_price>=pnf[column_count-3].min_column_price))
        {
         //--- Se han cumplido las condiciones de SELL, así que comprobamos si hay órdenes pendientes SELL sobre el símbolo con el precio necesario
         trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price-double_box),digit_2_orders);
         trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-3].min_column_price+(reverse-1)*double_box),digit_2_orders);
         trade_request.type=ORDER_TYPE_SELL_STOP;
         if(OrderSelect(ticket)==false)
            //--- No, no hay órdenes pendientes, establecemos la orden
           {
            PlaceOrder();
            order_col_number=column_count;
           }
         else
         //--- o bien, si hay órdenes pendientes
           {
            //--- nos preguntamos ¿cuál será el tipo y el precio de la orden pendiente?
            if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || 
               ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
              {
               //--- Si ese no es el tipo, o el precio es distinto, cerramos la orden
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
               //--- y abrimos con el precio necesario
               PlaceOrder();
               order_col_number=column_count;
              };
           };
         return;
        }
   else
      return;
  };
//+------------------------------------------------------------------+
//| Función de comprobación de las condiciones                       |
//| para establecer stop loss                                        |
//+------------------------------------------------------------------+
void TrailCondition()
  {
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box),digit_2_orders);
   else
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box),digit_2_orders);
   if(PositionGetDouble(POSITION_SL)!=trade_request.sl)
      PlaceTrailOrder();
  };

Para la estrategia №2 "compramos al romper la línea de resistencia, vendemos al romper la línea de apoyo; stop loss se encuentra al nivel del viraje, y la orden stop seguidora en la línea de tendencia":

//+------------------------------------------------------------------+
//| Función de comprobación de las condiciones comerciales           |
//| para la apertura de una operación                                |
//+------------------------------------------------------------------+
void TradeCondition()
  {
   if(order_col_number!=column_count)
      //--- ¿Se le han estancado algunas órdenes referentes a un símbolo?
     {
      if(OrdersTotal()>0)
        {
         //--- ¡Bórrelas!
         for(int loc_count_1=0;loc_count_1<OrdersTotal();loc_count_1++)
           {
            ticket=OrderGetTicket(loc_count_1);
            if(!OrderSelect(ticket)) Print("Failed to select order #",ticket);
            if(OrderGetString(ORDER_SYMBOL)==symbol)
              {
               trade_request.order=ticket;
               trade_request.action=TRADE_ACTION_REMOVE;
               if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
              };
           };
         order_col_number=column_count;
         return;
        }
      else
        {
         order_col_number=column_count;
         return;
        }
     }
   else
   if(MathPow(10,pnf[column_count-1].resist_price)>SymbolInfoDouble(symbol,SYMBOL_ASK))
     {
      //--- Se han cumplido las condiciones para BUY, comprobamos si hay órdenes pendientes BUY sobre el símbolo con el precio necesario
      trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price),digit_2_orders);
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].resist_price-(reverse-1)*double_box),digit_2_orders);
      trade_request.type=ORDER_TYPE_BUY_STOP;
      if(OrderSelect(ticket)==false)
         //--- No, no hay órdenes pendientes, establecemos la orden
        {
         PlaceOrder();
         order_col_number=column_count;
        }
      else
      //--- o bien, si hay órdenes pendientes
        {
         //--- nos preguntamos ¿cuál será el tipo y el precio de la orden pendiente?
         if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) || 
            ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
           {
            //--- Si ese no es el tipo, o el precio es distinto, cerramos la orden
            trade_request.order=ticket;
            trade_request.action=TRADE_ACTION_REMOVE;
            if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
            //--- y abrimos con el precio necesario
            PlaceOrder();
            order_col_number=column_count;
           };
        };
      return;
     }
   else
   if(MathPow(10,pnf[column_count-1].resist_price)<SymbolInfoDouble(symbol,SYMBOL_ASK))
     {
      //--- Se han cumplido las condiciones para SELL, comprobamos si hay órdenes pendientes SELL sobre el símbolo con el precio necesario
      trade_request.price=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price),digit_2_orders);
      trade_request.sl=NormalizeDouble(MathPow(10,pnf[column_count-1].supp_price+(reverse-1)*double_box),digit_2_orders);
      trade_request.type=ORDER_TYPE_SELL_STOP;
      if(OrderSelect(ticket)==false)
         //--- No, no hay órdenes pendientes, establecemos la orden
        {
         PlaceOrder();
         order_col_number=column_count;
        }
      else
      //--- o bien, si hay órdenes pendientes
        {
         //--- nos preguntamos ¿cuál será el tipo y el precio de la orden pendiente?
         if((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP) || 
            ((OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) && (OrderGetDouble(ORDER_PRICE_OPEN)!=trade_request.price)))
           {
            //--- Si ese no es el tipo, o el precio es distinto, cerramos la orden
            trade_request.order=ticket;
            trade_request.action=TRADE_ACTION_REMOVE;
            if(!OrderSend(trade_request,trade_result)) Print("Failed to send order #",trade_request.order);
            //--- y abrimos con el precio necesario
            PlaceOrder();
            order_col_number=column_count;
           };
        };
      return;
     }
   else
      return;
  };
//+------------------------------------------------------------------+
//| Función de comprobación de las condiciones                       |
//| para establecer una stop loss                                    |
//+------------------------------------------------------------------+
void TrailCondition()
  {
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
      trade_request.sl=NormalizeDouble(MathMax(SymbolInfoDouble(symbol,SYMBOL_ASK),MathPow(10,pnf[column_count-1].max_column_price-reverse*double_box)),digit_2_orders);
   else
      trade_request.sl=NormalizeDouble(MathMin(SymbolInfoDouble(symbol,SYMBOL_BID),MathPow(10,pnf[column_count-1].min_column_price+reverse*double_box)),digit_2_orders);
   if(PositionGetDouble(POSITION_SL)!=trade_request.sl)
      PlaceTrailOrder();
  };

Preste atención, querido lector, a varios detalles.

  • Los precios en los instrumentos financieros pueden oscilar en un diapasón bastante amplio, desde céntimos hasta decenas de miles (por ejemplo, las acciones y contratos sobre la diferencia en las bolsas japonesas). Por eso, al construir el gráfico de punto y forma, yo uso un logaritmo de precio para no introducir como dimensiones del box los valores desde 1 pip hasta decenas de miles de pips.
  • La matriz del gráfico de punto y forma comienza la indexación desde cero, la última columna tiene un índice correspondiente al número de columnas menos uno.
  • De estar presente la línea de apoyo, su valor será superior a -10.0, pero menor al logaritmo de precio, en ausencia de la línea de apoyo (y de resistencia también) su valor en la matriz del gráfico constituye -10.0. Por eso, las condiciones de la línea de apoyo/resistencia de prueba se guardan de la misma forma que en el código de la estrategia №2.

El código de las funciones auxiliares no lo muestro en el artículo, pero sí en los anexos. No voy a llenar el artículo con el código del propio gráfico, dado lo grande de su tamaño: podrá encontrarlo en los anexos.


Resultados del trading con robots

Para la optimización he preparado dos conjuntos de instrumentos de mercado en los archivos symbol_list_1.mhq y symbol_list_2.mhq: las parejas de divisas y los contratos sobre la diferencia en las acciones que entran en el índice Down.

Ventana de ajustes:


La ventana de parámetros en el simulador, en el primer caso, tiene el aspecto siguiente para nuestra estrategia:


Preste atención a la línea "Comienzo de la simulación". Para analizar y tomar decisiones, el robot necesita como poco unas cuantas columnas del gráfico, y al elegir un tamaño de box de 50 pips y más, la historia anual no es suficiente incluso para una columna. Por eso, para los gráficos con un tamaño de box a partir de 50 pips, recomiendo establecer un valor de intervalo del orden de tres años y más desde el comienzo del funcionamiento del robot. Puede establecer el propio comienzo del funcionamiento del robot en la ventana de parámetros, con la línea "Comienzo de la simulación". En nuestro ejemplo, en la simulación sobre un instrumento con un tamaño de box de 100 pips, comenzando desde el año 01.01.2012, en la pestaña "Ajustes" indicamos el intervalo desde el 01.01.2009, y en la pestaña "Parámetros", desde el 01.01.2012.

El valor false para el parámetro "¿Comerciamos con el lote mínimo?" nos revela que tenemos un tamaño variable de lote, dependiendo del saldo y de la variable "Riesgo por operación, en %" (en nuestro caso, un 1% por operación, pero también se puede optimizar). "Drawdown asumible, en %", es el mismo criterio de optimización de la función OnTester(). Para el ejemplo, he elegido para la optimización sólo dos variables: el instrumento comercial "Símbolo" y "Tamaño de box en pips".

La optimización la llevaremos a cabo en base a los datos del periodo 2012-2013. Es mejor agregar el robot propiamente al gráfico EURUSD, como al símbolo con mayor cobertura de ticks. En el recuadro de más abajo, mostramos un informe completo para la simulación sobre las parejas de divisas para un tamaño de box de 10, según la primera estrategia:

Pass Result Profit Expected Payoff Profit Factor Recovery Factor Sharpe Ratio Custom Equity DD % Trades selected_symbol box
0,00 0,00 -1 002,12 -18,91 0,54 -0,79 -0,24 0,00 12,67 53,00 AUDCAD 10,00
1,00 886,56 886,56 14,53 1,40 1,52 0,13 886,56 5,76 61,00 AUDCHF 10,00
2,00 0,00 -1 451,63 -10,60 0,77 -0,70 -0,09 0,00 19,92 137,00 AUDJPY 10,00
3,00 -647,66 -647,66 -17,50 0,57 -0,68 -0,24 -647,66 9,46 37,00 AUDNZD 10,00
4,00 -269,22 -269,22 -3,17 0,92 -0,26 -0,03 -269,22 9,78 85,00 AUDUSD 10,00
5,00 0,00 -811,44 -13,52 0,72 -0,64 -0,14 0,00 12,20 60,00 CADCHF 10,00
6,00 0,00 1 686,34 16,53 1,36 1,17 0,12 0,00 11,78 102,00 CHFJPY 10,00
7,00 356,68 356,68 5,66 1,13 0,40 0,06 356,68 8,04 63,00 EURAUD 10,00
8,00 0,00 -1 437,91 -25,68 0,53 -0,92 -0,25 0,00 15,47 56,00 EURCAD 10,00
9,00 0,00 -886,66 -46,67 0,34 -0,74 -0,46 0,00 11,56 19,00 EURCHF 10,00
10,00 0,00 -789,59 -21,93 0,54 -0,75 -0,26 0,00 10,34 36,00 EURGBP 10,00
11,00 0,00 3 074,86 28,47 1,62 1,72 0,20 0,00 12,67 108,00 EURJPY 10,00
12,00 0,00 -1 621,85 -19,78 0,55 -0,97 -0,25 0,00 16,75 82,00 EURNZD 10,00
13,00 152,73 152,73 2,88 1,07 0,21 0,03 152,73 6,90 53,00 EURUSD 10,00
14,00 0,00 -1 058,85 -14,50 0,65 -0,66 -0,16 0,00 15,87 73,00 GBPAUD 10,00
15,00 0,00 -1 343,47 -25,35 0,43 -0,64 -0,34 0,00 20,90 53,00 GBPCAD 10,00
16,00 0,00 -2 607,22 -44,19 0,27 -0,95 -0,59 0,00 27,15 59,00 GBPCHF 10,00
17,00 0,00 1 160,54 11,72 1,27 0,81 0,10 0,00 12,30 99,00 GBPJPY 10,00
18,00 0,00 -1 249,91 -14,70 0,69 -0,85 -0,15 0,00 14,41 85,00 GBPNZD 10,00
19,00 208,94 208,94 5,36 1,12 0,25 0,05 208,94 7,81 39,00 GBPUSD 10,00
20,00 0,00 -2 137,68 -21,17 0,53 -0,79 -0,24 0,00 25,62 101,00 NZDUSD 10,00
21,00 0,00 -1 766,80 -38,41 0,30 -0,97 -0,53 0,00 18,10 46,00 USDCAD 10,00
22,00 -824,69 -824,69 -11,95 0,73 -0,90 -0,13 -824,69 9,11 69,00 USDCHF 10,00
23,00 2 166,53 2 166,53 26,10 1,58 2,40 0,18 2 166,53 7,13 83,00 USDJPY 10,00

2 029,87 -10 213,52




13,40 1 659,00

y un cuadro sinóptico para los diferentes instrumentos y tamaños de box:

Estrategia Instrumentos Tamaño de box Trades Equity DD % Profit Result Pronóstico del saldo
1 Divisas 10 1 659 13 -10 214 2 030 2 030
1 Divisas 20 400 5 1 638 2 484 2 484
1 Acciones 50 350 4 7 599 7 599 15 199
1 Acciones 100 81 2 4 415 4 415 17 659
2 Divisas 10 338 20 -4 055 138 138
2 Divisas 20 116 8 4 687 3 986 3 986
2 Acciones 50 65 6 6 770 9 244 9 244
2 Acciones 100 12 1 -332 -332 -5 315

¿Qué podemos ver?

Siempre hay tontos muy torpes, que lo hace todo mal y en todo momento.

Y luego tenemos a los tontos de Wall Street, que consideran que hay que comerciar siempre.

No hay una sola persona en la faz de la Tierra que disponga diariamente de la información necesaria para comprar o vender las acciones y llevar a cabo su juego de una manera totalemnte lógica.

Podemos encontrarnos con una situación inesperada para mucha gente: el depósito, muy probablemente, será más alto cuantas menos operaciones se realicen. Si nosotros, sin ninguna optimización, hace dos años lanzásemos sobre las acciones nuestro experto, con un tamaño de box de 100, y con un riesgo por operación de un 1%, el robot sólo efectuaría 81 operaciones en dos años (la media anual sería de 1,25 operaciones por instrumento), nuestro depósito crecería un 44%, y además, el drawdown de la equidad sería un poco superior al 2%. ¡Si asumimos un drawdown del 10%, podríamos arriesgar en la operación un 4% y en dos años el depósito añadiría un 177%, el beneficio sería del 90% de interés, en dólares USA!


Epílogo

El curso nunca es demasiado alto como para empezar a comprar, y nunca no es lo suficientemente bajo como para empezar a vender.

Las grandes sumas se ganan esperando, no con inventos.

Las estrategias que hemos propuesto pueden ser modificadas y mostrarán incluso mayores beneficios con un drawdown no superior al 10%. No intente comerciar con demasiada frecuencia, hormiguear por los mercados, mejor encuentre un bróker que le proponga, no sólamente el "paquete estándar" de instrumentos, consistente en dos decenas de parejas de divisas y tres decenas de acciones, será mejor que le proponga tres o cuatro centenares de instrumentos (acciones, futuros). Así tendrá bastantes posibilidades de que los instrumentos no estén correlacionados y su depósito estará más protegido. Y sí, las acciones , por algún motivo, demuestran mejores resultados que las parejas de divisas.


P.S. (como publicidad)

En el Mercado, propongo el script PnF Chartist, que sirve para construir el gráfico de punto y forma en los archivos de texto de las cotizaciones de los terminales МТ4, МТ5 o Yahoo finance. Utilícelo para la búsqueda visual de patrones de comportamiento de los precios, ya que no hay mejor simulador/optimizador que nuestra propia cabeza, y tras encontrar la medida adecuada, utilice las plantillas de los expertos del artículo para comprobar la capacidad de combate de sus propios inventos.

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

Archivos adjuntos |
licence.txt (1.05 KB)
strategy_one.mq5 (37.87 KB)
strategy_two.mq5 (37.39 KB)
symbol_list_1.mqh (2.43 KB)
symbol_list_2.mqh (2.17 KB)
Recetas MQL5 - Asesor multidivisa y funcionamiento de órdenes pendientes en MQL5 Recetas MQL5 - Asesor multidivisa y funcionamiento de órdenes pendientes en MQL5

En esta ocasión veremos la creación de un asesor multidivisa, cuyo algoritmo de comercio será construido para trabajar con las órdenes pendientes Buy Stop y Sell Stop. En el artículo estudiaremos las siguientes cuestiones: el comercio en un diapasón temporal indicado, cómo establecer/modificar/eleminar órdenes pendientes, la comprobación de la última posición sobre Take Profit o Stop Loss y el control del historial de operaciones en cada símbolo.

Recetas MQL5 - Desarrollo de un indicador multidivisa para el análisis de la divergencia de precios Recetas MQL5 - Desarrollo de un indicador multidivisa para el análisis de la divergencia de precios

En este artículo veremos el desarrollo de un indicador multidivisa para el análisis de la divergencia de precios en un periodo de tiempo determinado. Ya hemos visto muchos momentos importantes en el anterior artículo sobre la programación de indicadores multidivisa: "Desarrollo de un indicador multidivisa de volatilidad en MQL5". Por eso, esta vez sólo nos detendremos en las funciones nuevas, o bien en aquellas funciones que hayan sufrido cambios significativos. Si es la primera vez que se encuentra con el tema de los indicadores multidivisa, entonces le recomendamos que lea en primer lugar el artículo anterior.

Usar WinInet.dll para el intercambio de datos entre terminales por internet Usar WinInet.dll para el intercambio de datos entre terminales por internet

En este artículo se describen los principios para trabajar con internet mediante las peticiones HTTP y el intercambio de datos entre terminales, usando un servidor intermedio. Se presenta una clase de la librería MqlNet para trabajar con los recursos de internet en el entorno MQL5. Seguir los precios por distintos brokers, intercambiar mensajes con otros traders sin salir del terminal, buscar informaciones en internet; estos son solamente algunos ejemplos que se repasan en este artículo.

Guía práctica de MQL5: Reducción del efecto del sobreajuste y el manejo de la falta de cotizaciones Guía práctica de MQL5: Reducción del efecto del sobreajuste y el manejo de la falta de cotizaciones

Sea cual sea la estrategia de trading que utilice, siempre habrá que preguntarse qué parámetros escoger para asegurar futuras ganancias. Este artículo proporciona un ejemplo de un Asesor Experto con una posibilidad de mejorar varios parámetros de símbolos a la vez. Este método está previsto para reducir el sobreajuste de los parámetros y manejar situaciones donde los datos de un solo símbolo no son suficientes para el estudio.