EA does not do the same in live and in the trades it makes in the backtest.

 
Good morning,

I would like to tell you my little problem and see if you can help me. I am new to programming.

I have programmed a robot that when doing the backtest works perfectly and carries out the operations in the way I want.

The problem is that when I switch it to real, the robot closes the operation instantly after opening it. I don't understand why this happens, when I open the robot I select the tab to allow algorithmic trading and I have also tried the tab to allow changing signals in case there was a difference. In addition, it tries to execute countless operations to close a position without sense, when it has already closed the position.

Testing the system in demo on the same computer works fine, it does not close the trade as soon as I open it. 

When I test the system in demo on another computer, it works perfectly. The only bad thing that happens is that it opens two trades in exactly the same millisecond, when it is only supposed to open one. I attach a picture of what happens:

I leave here the code to see if you see any problem. I thought it had to do with the barsTotal < bars, but I think that's correct.

#property copyright "Víctor González Tomás, VikredGT Darwinex"
#property version   "1.00"

#include <Trade/Trade.mqh>

CTrade trade;
input double lotsNo = 0.02;
input double lotsOrden = 0.04;
input double lotsInv = 0.04;
input double suma3aOperacion = 0.00;
input double sizeLastOp = 0.05;

double lotSize;
int filtroVelaGrande = 1;

ulong posTicket;
ulong posTicketPosterior;
double posOpenPrice;
double posOpenPricePosterior;
double currentPrice;
double posSL;
ulong posType;

input double kcMultiplier = 0.22;
input double multRange50 = 2.6;

int handleKC20;
int handleKC30;
   input ENUM_TIMEFRAMES Timeframe = PERIOD_M3;
   string forexPar = "EURUSD";
   string name = "Examples\\KeltnerChannel.ex5";
   input int kcEmaPeriod = 20;
   int kcEmaType = 1;
   int kcMiddleLine = 0;
   int kcEmaPrice = 0;
   input int kcRangePeriod = 10;
   int kcRangeCalc = 1;

int handleEMA30;
   input int periodMA30 = 30;
int handleEMA50;
   input int periodMA50 = 50;
int handleEMA100;
   input int periodMA100 = 100;
int handleEMA200;
   input int periodMA200 = 200;
input ENUM_MA_METHOD modeEMA = MODE_EMA;

int handleATR;
   input ENUM_TIMEFRAMES periodATR = PERIOD_M15;
   input double corteATR = 0.00045;

int barsTotal;

input int timeStartHour = 09;
input int timeStartMin = 59;
input int timeEndHour = 12;
input int timeEndMin = 04;
input int timeOpEndHour = 12;
input int timeOpEndMin = 19;
input int timeCloseHour = 15;
input int timeCloseMin = 01;

int OnInit(){
   handleKC20 = iCustom(forexPar,Timeframe,name,Timeframe,kcEmaPeriod,kcEmaType,kcMiddleLine,kcEmaPrice,0,0,kcRangePeriod,2.0,kcRangeCalc);
   handleKC30 = iCustom(forexPar,Timeframe,name,Timeframe,kcEmaPeriod,kcEmaType,kcMiddleLine,kcEmaPrice,0,0,kcRangePeriod,3.0,kcRangeCalc);
   
   handleEMA30 = iMA(forexPar,Timeframe,periodMA30,0,modeEMA,PRICE_CLOSE);
   handleEMA50 = iMA(forexPar,Timeframe,periodMA50,0,modeEMA,PRICE_CLOSE);
   handleEMA100 = iMA(forexPar,Timeframe,periodMA100,0,modeEMA,PRICE_CLOSE);
   handleEMA200 = iMA(forexPar,Timeframe,periodMA200,0,modeEMA,PRICE_CLOSE);
   
   handleATR = iATR(forexPar,periodATR,14);
   
   barsTotal = iBars(forexPar,Timeframe);
   
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason){

}

void OnTick(){
//Medimos con Barras
   int bars = iBars(forexPar,Timeframe);
   
//Timeframe  
   MqlDateTime structTime;
   TimeCurrent(structTime);
   structTime.sec = 0;
   
//Timeframe Entrada Primera Operación
   structTime.hour = timeStartHour;
   structTime.min = timeStartMin;
   datetime timeStart = StructToTime(structTime);
   
   structTime.hour = timeEndHour;
   structTime.min = timeEndMin;
   datetime timeEnd = StructToTime(structTime);
   
//Timeframe Entrada Todas Operaciones
   structTime.hour = timeOpEndHour;
   structTime.min = timeOpEndMin;
   datetime timeOpEnd = StructToTime(structTime);
   
//Timeframe CloseAll
   structTime.hour = timeCloseHour;
   structTime.min = timeCloseMin;
   datetime timeCloseAll = StructToTime(structTime);

//Bid y Ask    
   double bid = SymbolInfoDouble(forexPar,SYMBOL_BID);      
   double ask = SymbolInfoDouble(forexPar,SYMBOL_ASK);
   
//Keltner Channels  
   double kcAvg20[], kcUp20[], kcLow20[];
   CopyBuffer(handleKC20,0,0,3,kcAvg20);
   CopyBuffer(handleKC20,2,0,3,kcUp20);
   CopyBuffer(handleKC20,3,0,3,kcLow20); 
   
   double kcAvg30[], kcUp30[], kcLow30[];
   CopyBuffer(handleKC30,0,0,3,kcAvg30);
   CopyBuffer(handleKC30,2,0,3,kcUp30);
   CopyBuffer(handleKC30,3,0,3,kcLow30);   
      
//EMAs
   double EMA30[], EMA50[], EMA100[], EMA200[];
   CopyBuffer(handleEMA30,0,1,2,EMA30);
   CopyBuffer(handleEMA50,0,1,2,EMA50);
   CopyBuffer(handleEMA100,0,1,2,EMA100);
   CopyBuffer(handleEMA200,0,1,2,EMA200);
   
//ATR
   double dataATR[];
   CopyBuffer(handleATR,0,1,2,dataATR);

//Datos de Vela
   double closeMomPrice = iClose(forexPar,Timeframe,0);
   double closePrice = iClose(forexPar,Timeframe,1);
   double closePrePrice = iClose(forexPar,Timeframe,2);
   
   double highPrice = iHigh(forexPar,Timeframe,1);
   double highPrePrice = iHigh(forexPar,Timeframe,2);
   
   double lowPrice = iLow(forexPar,Timeframe,1);
   double lowPrePrice = iLow(forexPar,Timeframe,2);
   
   double openMomPrice = iOpen(forexPar,Timeframe,0);
   double openPrice = iOpen(forexPar,Timeframe,1);
   double openPrePrice = iOpen(forexPar,Timeframe,2);
   
//Keltner Channel Distance Filters (Futuro usar un Multiplier de Kc para SL)
   double kcDistance = (kcUp30[1] - kcLow30[1]);

   double distanceSellSL = ask + 0.00400;
   if(kcDistance < 0.00200){
      distanceSellSL = ask + kcDistance * 1.2;
   }
   else if(kcDistance >= 0.00300){
      distanceSellSL = ask + 0.00667;
   }
   distanceSellSL = _Point * MathRound(distanceSellSL / _Point);
   
   double distanceBuySL = bid - 0.00400;
   if(kcDistance < 0.00200){
      distanceBuySL = bid - kcDistance * 1.2;
   }
   else if(kcDistance >= 0.00300){
      distanceBuySL = bid - 0.00667;
   }
   distanceBuySL = _Point * MathRound(distanceBuySL / _Point);

//Time Filters
   bool entry1st = TimeCurrent() > timeStart && TimeCurrent() < timeEnd;
   bool entryAll = TimeCurrent() > timeStart && TimeCurrent() < timeOpEnd;
   bool exitAll = TimeCurrent() > timeCloseAll;
   
//ATR Filters
   bool filtroATR = dataATR[1] > corteATR;
   
//Vela Filters
   bool filtroVelaSell = closePrice > ((highPrice - openPrice)/2 + openPrice);
   bool filtroVelaBuy = closePrice < ((lowPrice - openPrice)/2 + openPrice);
   
//Lot Size Primera Operación Filters
   if(PositionsTotal() == 0){
      if(closePrice > kcAvg20[1]){
         lotSize = lotsNo;
         if(EMA30[1] > EMA50[1] && EMA50[1] > EMA100[1] && EMA100[1] > EMA200[1]){
            lotSize = lotsOrden;
         }
         else if(EMA30[1] < EMA50[1] && EMA50[1] < EMA100[1] && EMA100[1] < EMA200[1]){
            lotSize = lotsInv;
         }
      }
      else if(closePrice < kcAvg20[1]){
         lotSize = lotsNo;
         if(EMA30[1] > EMA50[1] && EMA50[1] > EMA100[1] && EMA100[1] > EMA200[1]){
            lotSize = lotsInv;
         }
         else if(EMA30[1] < EMA50[1] && EMA50[1] < EMA100[1] && EMA100[1] < EMA200[1]){
            lotSize = lotsOrden;
         }
      }
   }
   
//Lot Size Posterior Filters
   double lotPosSize = lotSize;
   if(PositionsTotal() >= 1){
      lotPosSize = lotSize;
      
      if(PositionsTotal() >= 2){          //A partir de la tercera operación
         lotPosSize = lotPosSize + suma3aOperacion;        
   
         if(PositionsTotal() >= 3){          //4a y 5a operación
            lotPosSize = sizeLastOp;
         }
      }
   }
   
//No entrar después de SL Filter
   int highestBar = iHighest(forexPar,Timeframe,MODE_HIGH,20,0);
   double highestPrice = iHigh(forexPar,Timeframe,highestBar);
      
   int lowestBar = iLowest(forexPar,Timeframe,MODE_LOW,20,0);
   double lowestPrice = iLow(forexPar,Timeframe,lowestBar);
   
   bool filtroNewOp = 0;
   if(posType == POSITION_TYPE_SELL){
      filtroNewOp = posSL > highestPrice;
   }
   else if(posType == POSITION_TYPE_BUY){
      filtroNewOp = posSL < lowestPrice;
   }
   
//Cada cuanto entrar Filter
   double newEntries = kcDistance * kcMultiplier;
   newEntries = _Point * MathRound(newEntries / _Point);
   
//Simplificación Entradas Nuevas
   double multNewEntries = 1;
   
   if(PositionsTotal() == 2){
      multNewEntries = 2;
   }
   else if(PositionsTotal() == 3){
      multNewEntries = 3;
   }
   else if(PositionsTotal() == 4){
      multNewEntries = 4;
   }
   
   double distanciaNewEntries = newEntries * multNewEntries;
   distanciaNewEntries = _Point * MathRound(distanciaNewEntries / _Point);

//Vela +50% Range Filters
   bool filtroSellVelaGrande = (kcUp30[1] - kcLow30[1])/multRange50 > closePrice - openPrice;
   bool filtroBuyVelaGrande = (kcUp30[1] - kcLow30[1])/multRange50 > openPrice - closePrice;

   string breakTime = "Si";
   
   if(barsTotal < bars){  
      barsTotal = bars;
      
      if(!filtroSellVelaGrande){
         filtroVelaGrande = -1;
         breakTime = "No";
      }
      if(closePrice < kcAvg20[1] && openPrice > kcAvg20[1] && breakTime == "Si" && filtroVelaGrande == -1){
         filtroVelaGrande = 1;
      }
      
      if(!filtroBuyVelaGrande){
         filtroVelaGrande = 0;
         breakTime = "No";
      }  
      if(closePrice > kcAvg20[1] && openPrice < kcAvg20[1] && breakTime == "Si" && filtroVelaGrande == 0){
         filtroVelaGrande = 1;
      }
   
//Entrada Primera Operación
      if(entry1st && filtroATR && filtroVelaGrande == 1 && PositionsTotal() == 0){
//Sell      
         if(highPrice > kcUp20[1] && filtroVelaSell){ 
            trade.Sell(lotSize,forexPar,bid,distanceSellSL);
            
            posTicket = trade.ResultOrder();
         }
//Buy
         if(lowPrice < kcLow20[1] && filtroVelaBuy){ 
            trade.Buy(lotSize,forexPar,ask,distanceBuySL);
            
            posTicket = trade.ResultOrder();
         }
         
         PositionSelectByTicket(posTicket);
         posOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
         posType = PositionGetInteger(POSITION_TYPE);
         posSL = PositionGetDouble(POSITION_SL);
      }

//Entrada Todas Operaciones (A partir de SL ya no se compra más)
      if(entryAll && filtroATR && filtroNewOp && PositionsTotal() > 0 && PositionsTotal() <= 4){
//Sell
         if(closePrice > posOpenPrice + distanciaNewEntries && highPrice > kcUp20[1] && filtroVelaSell && posType == POSITION_TYPE_SELL && PositionsTotal() == 1){
            trade.Sell(lotPosSize,forexPar,bid,distanceSellSL);
            
            posTicketPosterior = trade.ResultOrder();
         }
         else if(closePrice > posOpenPrice + distanciaNewEntries && closePrice > posOpenPricePosterior + newEntries && highPrice > kcUp20[1] && filtroVelaSell && posType == POSITION_TYPE_SELL && PositionsTotal() >= 2){
            trade.Sell(lotPosSize,forexPar,bid,distanceSellSL);
            
            posTicketPosterior = trade.ResultOrder();
         }
//Buy    
         if(closePrice < posOpenPrice - distanciaNewEntries && lowPrice < kcLow20[1] && filtroVelaBuy && posType == POSITION_TYPE_BUY && PositionsTotal() == 1){
            trade.Buy(lotPosSize,forexPar,ask,distanceBuySL);
            
            posTicketPosterior = trade.ResultOrder();
         }
         else if(closePrice < posOpenPrice - distanciaNewEntries && closePrice < posOpenPricePosterior - newEntries && lowPrice < kcLow20[1] && filtroVelaBuy && posType == POSITION_TYPE_BUY && PositionsTotal() >= 2){
            trade.Buy(lotPosSize,forexPar,ask,distanceBuySL);
            
            posTicketPosterior = trade.ResultOrder();
         }
      }
      PositionSelectByTicket(posTicketPosterior);
      posOpenPricePosterior = PositionGetDouble(POSITION_PRICE_OPEN);
   }
   
//Salida Operaciones
   if(posType == POSITION_TYPE_SELL){
      if(closePrice < kcAvg20[1] || exitAll){
         for(int i = PositionsTotal()-1; i >= 0; i--){
            posTicket = PositionGetTicket(i);
            trade.PositionClose(posTicket);  
         }
      }
   }
   else if(posType == POSITION_TYPE_BUY){
      if(closePrice > kcAvg20[1] || exitAll){
         for(int i = PositionsTotal()-1; i >= 0; i--){
            posTicket = PositionGetTicket(i);
            trade.PositionClose(posTicket);  
         }
      }
   }
   
//En Pantalla
   string comment;
   comment = "KCAverage: "+DoubleToString(kcAvg20[0],_Digits);
   comment += "\nKCUp: "+DoubleToString(kcUp20[0],_Digits);
   comment += "\nKCLow: "+DoubleToString(kcLow20[0],_Digits);
   comment += "\nKCDistance: "+DoubleToString(kcDistance,_Digits);
   Comment(comment);    
}    
Files:
Captura4.PNG  20 kb
 

That's because the local trading environment is not update synchronously with the server. That means, as your code is executed on each tick, it can be executed again before PositionTotal() (and so all position information) is updated. Not always, it depends of the ticks and the server response to your request.

  1. Tick X : trade request send, server answer it's ok. But Positions are NOT UPDATED yet.
  2. Tick X+1 : Positions still not updated, your code send an other trade request => double trades
  3. The terminal receive the Positions update information.
  4. Tick X+2 : All is ok now, but you have a double trades.

Your code needs to deal with that.

 

And how can I solve that?

To clarify, I am not using a VPS or something similar.

 
Vikred888 #:

And how can I solve that?

With creativity. Or check for an existing solution on the forum or codebase, now you know what the problem is.

To clarify, I am not using a VPS or something similar.

It's not related to any computer or whatever, it's how MT5 works.

 
Alain Verleyen #:
With creativity. Or check for an existing solution on the forum or codebase, now you know what the problem is.

It's not related to any computer or whatever, it's how MT5 works.

Okay, thank you. I will try to find something. I found this article that could be interesting: https://www.mql5.com/es/articles/159

And also the second operation is executed exactly at the same milisecond, which is a bit suspicious. For me it is difficult to understand that the code can be executed twice and the tick can change more than once in exactly 1 milisecond.


Btw, the other problem, do you know why it is happening? Every time I open a position in the real/live account, it is closed automatically and I do not know why. I am completely lost :(

Gestor de evento "Nueva barra"
Gestor de evento "Nueva barra"
  • www.mql5.com
El lenguaje de programación MQL5 es capaz de resolver problemas a un nuevo nivel. Incluso aquellas tareas que ya tienen soluciones, gracias a la programación orientada a objetos pueden subir a un nivel superior. En este artículo veremos un sencillo ejemplo sobre la comprobación de una nueva barra en un gráfico, transformada en una herramienta más potente y versátil. ¿Qué herramienta? Lo veremos en este artículo.
 
Vikred888 #:

Okay, thank you. I will try to find something. I found this article that could be interesting: https://www.mql5.com/es/articles/159

If your logic can be executed on new bar only, for sure you will not have this problem.

And also the second operation is executed exactly at the same milisecond, which is a bit suspicious. For me it is difficult to understand that the code can be executed twice and the tick can change more than once in exactly 1 milisecond.

Yes you can have several ticks in the same milliseconds. Such code as yours in OnTick() is executed in microseconds, it's an other time scale.

Btw, the other problem, do you know why it is happening? Every time I open a position in the real/live account, it is closed automatically and I do not know why. I am completely lost :(

You will have to debug your code, sorry but I will not do it for you. If your position is closed, it's because your conditions to close triggered. Use the Debugger and/or print the needed values to understand in the log.

 

If somebody is curious, this is what I did:

To solve the first problem of the repeated operations, I wrote PositionsTotal() == 0 && OrdersTotal() == 0 at the beginning of the operations. I also included the CisNewBar class. I already had something very similar with (barsTotal < iBars), but anyway, I hope this is more reliable.

For the second problem, I am now going to debug it.

Reason: