MQL5: Multi Symbol EA doubled trades

 

I would like to get that EA to trade only one deal per signal but while testing this EA I noticed that sometimes it opens two trades at the same time or it opens a trade after signal and then another trade comes one bar later.

//+------------------------------------------------------------------+
//|                                                        RR EA.mq5 |
//|                        Copyright 2023, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\AccountInfo.mqh>
#include <Trade\DealInfo.mqh>
#include <Trade\OrderInfo.mqh>
#include <Expert\Money\MoneyFixedMargin.mqh>
CPositionInfo  m_position;                   // trade position object
CTrade         m_trade;                      // trading object
CSymbolInfo    m_symboll;
CSymbolInfo    m_symbol_1;                   // symbol info object
CSymbolInfo    m_symbol_2;                   // symbol info object
CSymbolInfo    m_symbol_3;                   // symbol info object
CAccountInfo   m_account;                    // account info wrapper
CDealInfo      m_deal;                       // deals object
COrderInfo     m_order;                      // pending orders object
CMoneyFixedMargin *m_money;
//--- input parameters
input string   s1="EURUSD+";                  // Symbol #1
input string   s2="AUDUSD+";                  // Symbol #2
input string   s3="GBPUSD+";                  // Symbol #3
//---
input ulong    m_magic=122665745;   // magic number
//---
ulong  m_slippage=10;               // slippage
int    handle_iRR_1;                // variable for storing the handle of the iRR indicator
int    handle_iRR_2;                // variable for storing the handle of the iRR indicator
int    handle_iRR_3;                // variable for storing the handle of the iRR indicator
double m_adjusted_point_1;          // point value adjusted for 3 or 5 points
double m_adjusted_point_2;          // point value adjusted for 3 or 5 points
double m_adjusted_point_3;          // point value adjusted for 3 or 5 points
bool   m_use_1;
bool   m_use_2;
bool   m_use_3;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   m_use_1=m_symbol_1.Name(s1);  // sets symbol #1 name
   if(m_use_1)
      RefreshRates(m_symbol_1);

   m_use_2=m_symbol_2.Name(s2);  // sets symbol #2 name
   if(m_use_2)
      RefreshRates(m_symbol_2);

   m_use_3=m_symbol_3.Name(s3);  // sets symbol #3 name
   if(m_use_3)
      RefreshRates(m_symbol_3);
//--- tuning for 3 or 5 digits
   int digits_adjust=1;
   if(m_use_1)
     {
      if(m_symbol_1.Digits()==3 || m_symbol_1.Digits()==5)
         digits_adjust=10;
      m_adjusted_point_1=m_symbol_1.Point()*digits_adjust;
     }

   digits_adjust=1;
   if(m_use_2)
     {
      if(m_symbol_2.Digits()==3 || m_symbol_2.Digits()==5)
         digits_adjust=10;
      m_adjusted_point_2=m_symbol_2.Point()*digits_adjust;
     }

   digits_adjust=1;
   if(m_use_3)
     {
      if(m_symbol_3.Digits()==3 || m_symbol_3.Digits()==5)
         digits_adjust=10;
      m_adjusted_point_3=m_symbol_3.Point()*digits_adjust;
     }
//--- create handles
   if(m_use_1)
     {
      handle_iRR_1=iCustom(m_symbol_1.Name(),PERIOD_CURRENT,"Indicator_S.ex5",0,0);
      //--- if the handle is not created
      if(handle_iRR_1==INVALID_HANDLE)
        {
         PrintFormat("Failed to create handle of the iRR indicator for the symbol %s/%s, error code %d",
                     m_symbol_1.Name(),
                     EnumToString(Period()),
                     GetLastError());
         return(INIT_FAILED);
        }
     }
   if(m_use_2)
     {
      handle_iRR_2=iCustom(m_symbol_2.Name(),PERIOD_CURRENT,"Indicator_S.ex5",0,0);
      //--- if the handle is not created
      if(handle_iRR_2==INVALID_HANDLE)
        {
         PrintFormat("Failed to create handle of the iRR indicator for the symbol %s/%s, error code %d",
                     m_symbol_2.Name(),
                     EnumToString(Period()),
                     GetLastError());
         return(INIT_FAILED);
        }
     }
   if(m_use_3)
     {
      handle_iRR_3=iCustom(m_symbol_3.Name(),PERIOD_CURRENT,"Indicator_S.ex5",0,0);
      //--- if the handle is not created
      if(handle_iRR_3==INVALID_HANDLE)
        {
         PrintFormat("Failed to create handle of the iRR indicator for the symbol %s/%s, error code %d",
                     m_symbol_3.Name(),
                     EnumToString(Period()),
                     GetLastError());
         return(INIT_FAILED);
        }
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- we work only at the time of the birth of new bar
   static datetime PrevBars_1=0;
   static datetime PrevBars_2=0;
   static datetime PrevBars_3=0;

   datetime time_1=0;
   datetime time_2=0;
   datetime time_3=0;

   if(m_use_1)
      time_1=iTime(m_symbol_1.Name(),PERIOD_CURRENT,0);

   if(m_use_2)
      time_2=iTime(m_symbol_2.Name(),PERIOD_CURRENT,0);

   if(m_use_3)
      time_3=iTime(m_symbol_3.Name(),PERIOD_CURRENT,0);

   if(((m_use_1 && time_1==PrevBars_1) || !m_use_1) &&
      ((m_use_2 && time_2==PrevBars_2) || !m_use_2) &&
      ((m_use_3 && time_3==PrevBars_3) || !m_use_3))
      return;
   PrevBars_1=time_1;
   PrevBars_2=time_2;
   PrevBars_3=time_3;

//--- check symbol #1
   if(m_use_1)
     {
      double up_1_array[];
      ArraySetAsSeries(up_1_array,true);
      int buffer=0,start_pos=0,count=2;
      if(!iGetArray(handle_iRR_1,0,start_pos,count,up_1_array))
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }
      double freeze_level,stop_level;
      //---
      if(!RefreshRates(m_symbol_1))
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }
      freeze_level=m_symbol_1.FreezeLevel()*m_symbol_1.Point();
      if(freeze_level==0.0)
         freeze_level=(m_symbol_1.Ask()-m_symbol_1.Bid())*3.0;
      freeze_level*=1.1;

      stop_level=m_symbol_1.StopsLevel()*m_symbol_1.Point();
      if(stop_level==0.0)
         stop_level=(m_symbol_1.Ask()-m_symbol_1.Bid())*3.0;
      stop_level*=1.1;

      if(freeze_level<=0.0 || stop_level<=0.0)
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }

      //---BUY
      if(up_1_array[1]!=EMPTY_VALUE)
        {
         if(IsNewCandle())
            OpenBuy(m_symbol_1,m_symbol_1.LotsMin(),0,0);
        }
     }
//--- check symbol #2
   if(m_use_2)
     {
      double up_2_array[];
      ArraySetAsSeries(up_2_array,true);
      int buffer=0,start_pos=0,count=2;
      if(!iGetArray(handle_iRR_2,0,start_pos,count,up_2_array))
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }
      double freeze_level,stop_level;
      //---
      if(!RefreshRates(m_symbol_2))
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }
      freeze_level=m_symbol_2.FreezeLevel()*m_symbol_2.Point();
      if(freeze_level==0.0)
         freeze_level=(m_symbol_2.Ask()-m_symbol_2.Bid())*3.0;
      freeze_level*=1.1;

      stop_level=m_symbol_2.StopsLevel()*m_symbol_2.Point();
      if(stop_level==0.0)
         stop_level=(m_symbol_2.Ask()-m_symbol_2.Bid())*3.0;
      stop_level*=1.1;

      if(freeze_level<=0.0 || stop_level<=0.0)
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }

      //---BUY
      if(up_2_array[1]!=EMPTY_VALUE)
        {
         if(IsNewCandle())
            OpenBuy(m_symbol_2,m_symbol_2.LotsMin(),0,0);
        }
     }
//--- check symbol #3
   if(m_use_3)
     {
      double up_3_array[];
      ArraySetAsSeries(up_3_array,true);
      int buffer=0,start_pos=0,count=2;
      if(!iGetArray(handle_iRR_3,0,start_pos,count,up_3_array))
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }
      double freeze_level,stop_level;
      //---
      if(!RefreshRates(m_symbol_3))
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }
      freeze_level=m_symbol_3.FreezeLevel()*m_symbol_3.Point();
      if(freeze_level==0.0)
         freeze_level=(m_symbol_3.Ask()-m_symbol_3.Bid())*3.0;
      freeze_level*=1.1;

      stop_level=m_symbol_3.StopsLevel()*m_symbol_3.Point();
      if(stop_level==0.0)
         stop_level=(m_symbol_3.Ask()-m_symbol_3.Bid())*3.0;
      stop_level*=1.1;

      if(freeze_level<=0.0 || stop_level<=0.0)
        {
         PrevBars_1=0;
         PrevBars_2=0;
         PrevBars_3=0;
         return;
        }

      //---BUY
      if(up_3_array[1]!=EMPTY_VALUE)
        {
         if(IsNewCandle())
            OpenBuy(m_symbol_3,m_symbol_3.LotsMin(),0,0);
        }
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsNewCandle(void)
  {
   static datetime t_bar=iTime(_Symbol,PERIOD_CURRENT,0);
   datetime time=iTime(_Symbol,PERIOD_CURRENT,0);
//---
   if(t_bar==time)
      return false;
   t_bar=time;
//---
   return true;
  }
//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
  {
//--- get transaction type as enumeration value
  }
//+------------------------------------------------------------------+
//| Refreshes the symbol quotes data                                 |
//+------------------------------------------------------------------+
bool RefreshRates(CSymbolInfo &m_symbol)
  {
//--- refresh rates
   if(!m_symbol.RefreshRates())
     {
      Print("RefreshRates error");
      return(false);
     }
//--- protection against the return value of "zero"
   if(m_symbol.Ask()==0 || m_symbol.Bid()==0)
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Get value of buffers                                             |
//+------------------------------------------------------------------+
double iGetArray(const int handle,const int buffer,const int start_pos,const int count,double &arr_buffer[])
  {
   bool result=true;
   if(!ArrayIsDynamic(arr_buffer))
     {
      Print("This a no dynamic array!");
      return(false);
     }
   ArrayFree(arr_buffer);
   ResetLastError();
   int copied=CopyBuffer(handle,buffer,start_pos,count,arr_buffer);
   if(copied!=count)
     {
      PrintFormat("Failed to copy data from the indicator, error code %d",GetLastError());
      return(false);
     }
   return(result);
  }
//+------------------------------------------------------------------+
//| Open Buy position                                                |
//+------------------------------------------------------------------+
void OpenBuy(CSymbolInfo &m_symbol,double lot,double sl,double tp)
  {
   sl=m_symbol.NormalizePrice(sl);
   tp=m_symbol.NormalizePrice(tp);

   double long_lot=lot;
//--- check volume before OrderSend to avoid "not enough money" error (CTrade)
   double free_margin_check= m_account.FreeMarginCheck(m_symbol.Name(),ORDER_TYPE_BUY,long_lot,m_symbol.Ask());
   double margin_check     = m_account.MarginCheck(m_symbol.Name(),ORDER_TYPE_SELL,long_lot,m_symbol.Bid());
   if(free_margin_check>margin_check)
     {
      if(m_trade.Buy(long_lot,m_symbol.Name(),m_symbol.Ask(),sl,tp))
        {
         if(m_trade.ResultDeal()==0)
           {
            Print("#1 Buy -> false. Result Retcode: ",m_trade.ResultRetcode(),
                  ", description of result: ",m_trade.ResultRetcodeDescription());
            PrintResultTrade(m_trade,m_symbol);
           }
         else
           {
            Print("#2 Buy -> true. Result Retcode: ",m_trade.ResultRetcode(),
                  ", description of result: ",m_trade.ResultRetcodeDescription());
            PrintResultTrade(m_trade,m_symbol);
           }
        }
      else
        {
         Print("#3 Buy -> false. Result Retcode: ",m_trade.ResultRetcode(),
               ", description of result: ",m_trade.ResultRetcodeDescription());
         PrintResultTrade(m_trade,m_symbol);
        }
     }
   else
     {
      Print(__FUNCTION__,", ERROR: method CAccountInfo::FreeMarginCheck returned the value ",DoubleToString(free_margin_check,2));
      return;
     }
//---
  }
//+------------------------------------------------------------------+
//| Open Sell position                                               |
//+------------------------------------------------------------------+
void OpenSell(CSymbolInfo &m_symbol,double lot,double sl,double tp)
  {
   sl=m_symbol.NormalizePrice(sl);
   tp=m_symbol.NormalizePrice(tp);

   double short_lot=lot;
//--- check volume before OrderSend to avoid "not enough money" error (CTrade)
   double free_margin_check= m_account.FreeMarginCheck(m_symbol.Name(),ORDER_TYPE_SELL,short_lot,m_symbol.Bid());
   double margin_check     = m_account.MarginCheck(m_symbol.Name(),ORDER_TYPE_SELL,short_lot,m_symbol.Bid());
   if(free_margin_check>margin_check)
     {
      if(m_trade.Sell(short_lot,m_symbol.Name(),m_symbol.Bid(),sl,tp))
        {
         if(m_trade.ResultDeal()==0)
           {
            Print("#1 Sell -> false. Result Retcode: ",m_trade.ResultRetcode(),
                  ", description of result: ",m_trade.ResultRetcodeDescription());
            PrintResultTrade(m_trade,m_symbol);
           }
         else
           {
            Print("#2 Sell -> true. Result Retcode: ",m_trade.ResultRetcode(),
                  ", description of result: ",m_trade.ResultRetcodeDescription());
            PrintResultTrade(m_trade,m_symbol);
           }
        }
      else
        {
         Print("#3 Sell -> false. Result Retcode: ",m_trade.ResultRetcode(),
               ", description of result: ",m_trade.ResultRetcodeDescription());
         PrintResultTrade(m_trade,m_symbol);
        }
     }
   else
     {
      Print(__FUNCTION__,", ERROR: method CAccountInfo::FreeMarginCheck returned the value ",DoubleToString(free_margin_check,2));
      return;
     }
//---
  }
//+------------------------------------------------------------------+
//| Print CTrade result                                              |
//+------------------------------------------------------------------+
void PrintResultTrade(CTrade &trade,CSymbolInfo &symbol)
  {
   Print("File: ",__FILE__,", symbol: ",symbol.Name());
   Print("Code of request result: "+IntegerToString(trade.ResultRetcode()));
   Print("code of request result as a string: "+trade.ResultRetcodeDescription());
   Print("Deal ticket: "+IntegerToString(trade.ResultDeal()));
   Print("Order ticket: "+IntegerToString(trade.ResultOrder()));
   Print("Volume of deal or order: "+DoubleToString(trade.ResultVolume(),2));
   Print("Price, confirmed by broker: "+DoubleToString(trade.ResultPrice(),symbol.Digits()));
   Print("Current bid price: "+DoubleToString(symbol.Bid(),symbol.Digits())+" (the requote): "+DoubleToString(trade.ResultBid(),symbol.Digits()));
   Print("Current ask price: "+DoubleToString(symbol.Ask(),symbol.Digits())+" (the requote): "+DoubleToString(trade.ResultAsk(),symbol.Digits()));
   Print("Broker comment: "+trade.ResultComment());
  }
 
timmytradeI would like to get that EA to trade only one deal per signal but while testing this EA I noticed that sometimes it opens two trades at the same time or it opens a trade after signal and then another trade comes one bar later.

Add a trade count and tell the buy/sell function to stop sending buy/sell orders when more than 1 trade is open.

I assume that if you have made the EA you will know how to do this. ;-)

 
Miguel Angel Vico Alba #:

Add a trade count and tell the buy/sell function to stop sending buy/sell orders when more than 1 trade is open.

I assume that if you have made the EA you will know how to do this. ;-)

Code is from codebase.

Trade count wont help because I need all trades to be open what comes from signal. 

 
timmytrade #:

Code is from codebase.

Trade count wont help because I need all trades to be open what comes from signal.

I understand... however there are several ways to solve it, one of them as I have indicated (even if the EA is multi-symbol and you want it to continue working in other pairs).

As you may have seen on other occasions:

  • People who can't code usually don't get free help on this forum, although it might happen if you are lucky. Be patient.
  • If you show your attempts and describe your specific problem clearly, you will probably receive a response from the community.
  • If you don't want to learn to code, that's not a problem. You can look at the CodeBase section where you will find free and open source code, or at the Market for paid products (also sometimes free). Finally, you also have the option of hiring a programmer in the Freelance section.
 

Found solution

if(iTime(m_symbol.Name(),m_working_period,0)==m_last_deal_in) // on one bar - only one deal
      return(true);

and OnTradeTransaction function

 
         if(IsNewCandle())
⋮
         if(IsNewCandle())
⋮
         if(IsNewCandle())

You can't know when a candle closes. Only when a new tick arrives that starts a new bar is the old bar closed, and that tick could arrive almost at the end of a bar's duration.

For a new bar test, Bars is unreliable (a refresh/reconnect can change number of bars on chart), volume is unreliable (miss ticks), Price is unreliable (duplicate prices and The == operand. - MQL4 programming forum.) Always use time.
          MT4: New candle - MQL4 programming forum #3 (2014)
          MT5: Accessing variables - MQL4 programming forum #3 (2022)

I disagree with making a new bar function, because it can only be called once per tick (second call returns false). A variable can be tested multiple times.
          Running EA once at the start of each bar - MQL4 programming forum (2011)

Reason: