Trouble with OnTradeTransaction function in MQL5

 

Dear developers, i am trying to write a hedging expert advisor and i have some strange issues with making it work properly,

first let me explain the whole idea and then mention the issue:

suppose LotMax = 1.0 (input box variable)

it is petty much a bot that when it hits its TP it applies some of that profit to its losing positions. the EA opens a random buy or sell position with some simple logics at first, let's imagine it is a buy(lot=LotMax at first run), then it place a sellstop(same lot, =LotMax) 20pips below the buy entry immediately, all positions have no sl and tp is 40 pips for example, if the buy hits tp and sell stop is not triggered it deletes the sell stop and does the previous step again.

But if the sellstop is filled, when buy hits tp, it read the last tp profit (for example 1.0 lot + 40pips == +400$) and decide to use some of it for example +300$ (TT value in my code) for closing the  furthest opposite running trade which is in loss, here the 1.0 lot sell is in -60pips loss (about -600$), so if the trade loss is less than TT value it closes it all, if not like here, it does partial exit, so the EA close about half size of sell trade in loss and makes it Sell (0.5 lot)

in this point because the EA has a sell trade, at the closure point of previous trade, it opens a sell (lot= LotMax- summation of sell basket so lot = 1.0 - 0.5 = 0.5) and hedge it with a buy stop 20 pips above with lot size = lotMax- summation of buy basket lots= 1.0 - 0 = 1.0

and so on...

the problem is, when a trade hits tp and it calculates the (TT) value for partial closing of the opposite position, instead of doing it once, it does it multiple of time until the loss position will be closed completely!!!! 

i have used OnTradeTransaction function to call close/Partial close function, and i have written the code in many different states, but the issue remains the same, i will attach my code and waiting for your help.

( i deleted some part of the code like position counter and ... in order to not exceed tha max characters for posting here.)

Thanks in advance.

#property version   "1.00"
#property strict

// Trade Class
#include <Trade/Trade.mqh>
CTrade   Trade;

// Deal Info Class (used in OnTradeTransaction function)
#include <Trade\DealInfo.mqh>
CDealInfo   m_deal;

// EA General Settings
input string               eHeader1                      =  "-=-=-=-=-=-=-=-=-=-=-=-=-=-";               // -=-=-= EA General Settings =-=-=-
input int                  MagicNumber                   =  123789;                                      // Expert Magic Number
input string               TradeComment                  =  "Hedging EA";             // Expert Trades Comment
input int                  MaxSlippage                   =  20;                                          // Max. Slippage (Points)
input double               MaxLot                        =  1.00;                                        // Max. Lot Size
input int                  TPPips                        =  40;                                          // Take Profit (Pips)
input int                  GridDistance                  =  20;                                          // Hedge Grid Distance (Pips)

// Indicator Variables
string   Prefix               =  "GGP_HEA_";
int      Space                =  20;
double   TT                   =  0.0;
long     DealType             =  -1;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Initialize Expert Common Parameters
   Trade.SetExpertMagicNumber(MagicNumber);
   Trade.SetDeviationInPoints(MaxSlippage);
   
//---
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {  
   // Delete unfilled orders
   if(PositionCounter(2) == 0 && OrderCounter(2) != 0)
     {
      DeletePendingOrders(2);
     }
   
   // Step 4
   if(DealType == 0 && PositionCounter(0) > 0 && OrderCounter(1) == 0)
     {
      TakePosition(0);
      DealType =  -1;
     }
   
   if(DealType == 1 && PositionCounter(1) > 0 && OrderCounter(0) == 0)
     {
      TakePosition(1);
      DealType =  -1;
     }
     
   // First Entry
   if(PositionCounter(2) == 0 && OrderCounter(2) == 0)
     {
      if(RandomEntry(1) == 0)
        {
         TakePosition(0);
        }
      if(RandomEntry(1) == 1)
        {
         TakePosition(1);
        }
     }
  }

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
   // Get transaction type as enumeration value
   ENUM_TRADE_TRANSACTION_TYPE   type  =  trans.type;
   
   // If transaction is result of addition of the transaction in history
   if(type == TRADE_TRANSACTION_DEAL_ADD)
     {
      ResetLastError();
      if(HistoryDealSelect(trans.deal))
         m_deal.Ticket(trans.deal);

      else
        {
         Print(__FILE__, " ", __FUNCTION__, ", ERROR: ", "HistoryDealSelect(", trans.deal, ") error: ", GetLastError());
         return;
        }

      if(m_deal.DealType() == DEAL_TYPE_BUY || m_deal.DealType() == DEAL_TYPE_SELL)
        {
         if(m_deal.Entry() == DEAL_ENTRY_OUT)
           {
            long position_id  =  m_deal.PositionId();
            if(HistorySelectByPosition(position_id))
              {
               uint     total    = HistoryDealsTotal();
               ulong    ticket   = 0;
               
               // For all deals
               for(uint i=0; i<MathMax(total, 10); i++)
                 {
                  // Try to get deals ticket
                  if((ticket=HistoryDealGetTicket(i))>0)
                    {
                     if(HistoryDealGetString(ticket, DEAL_SYMBOL) == Symbol() && HistoryDealGetInteger(ticket, DEAL_MAGIC) == MagicNumber && HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT)
                       {
                        if(HistoryDealGetDouble(ticket, DEAL_PROFIT) > 0)
                          {
                           TT             =  HistoryDealGetDouble(ticket, DEAL_PROFIT)- HistoryDealGetDouble(ticket, DEAL_VOLUME)* 100;
                           DealType       =  HistoryDealGetInteger(ticket, DEAL_TYPE);
                           ApplyTT(TT, DealType);
                          }
                       }
                    }
                 }
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Random Entry                                                     |
//+------------------------------------------------------------------+
int RandomEntry(int bar)
   {
    // Random buy signal
    if(iClose(Symbol(), Period(), bar) > iOpen(Symbol(), Period(), bar))
       return (0);
    
    // Random sell signal
    else if(iClose(Symbol(), Period(), bar) < iOpen(Symbol(), Period(), bar))
       return (1);
    
    // No signal
    return (-1);
   }

//+------------------------------------------------------------------+
//| Order Basket Lot                                                 |
//+------------------------------------------------------------------+
double OrderBasketLot(int type)
   {
    double  basketLot   =  0.0;

    // Long trades
    if(type == 0)
      {
       // Sum buy orders lots
       for(int i=OrdersTotal()-1; i>=0; i--)
         {
          ulong   _ticket  =  OrderGetTicket(i);
          if(_ticket > 0)
            {
             if(OrderGetString(ORDER_SYMBOL) == Symbol() && OrderGetInteger(ORDER_MAGIC) == MagicNumber)
               {
                if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_LIMIT || OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_BUY_STOP)
                   basketLot  += OrderGetDouble(ORDER_VOLUME_CURRENT);
               }
            }
         }
      }

    // Short trades          
    if(type == 1)
      {
       // Sum sell positions lots
       for(int i=OrdersTotal()-1; i>=0; i--)
         {
          ulong   _ticket  =  OrderGetTicket(i);
          if(_ticket > 0)
            {
             if(OrderGetString(ORDER_SYMBOL) == Symbol() && OrderGetInteger(ORDER_MAGIC) == MagicNumber)
               {
                if(OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_LIMIT || OrderGetInteger(ORDER_TYPE) == ORDER_TYPE_SELL_STOP)
                   basketLot  += OrderGetDouble(ORDER_VOLUME_CURRENT);
               }
            }
         }       
      }
    
    NormalizeDouble(basketLot, 2);
    return basketLot;
   }

//+------------------------------------------------------------------+
//| Open Position Basket Lot                                         |
//+------------------------------------------------------------------+
double BasketLot(int type)
   {
    double  basketLot   =  0.0;

    // Long trades
    if(type == 0)
      {
       // Sum buy positions lots
       for(int i=PositionsTotal()-1; i>=0; i--)
         {
          ulong   _ticket  =  PositionGetTicket(i);
          if(_ticket > 0)
            {
             if(PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
               {
                if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                   basketLot  += PositionGetDouble(POSITION_VOLUME);
               }
            }
         }
      }

    // Short trades          
    if(type == 1)
      {
       // Sum sell positions lots
       for(int i=PositionsTotal()-1; i>=0; i--)
         {
          ulong   _ticket  =  PositionGetTicket(i);
          if(_ticket > 0)
            {
             if(PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
               {
                if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                   basketLot  += PositionGetDouble(POSITION_VOLUME);
               }
            }
         }
      }
    
    NormalizeDouble(basketLot, 2);
    return basketLot;
   }

//+------------------------------------------------------------------+
//| Take Position                                                    |
//+------------------------------------------------------------------+
void TakePosition(int type)
   {
    // Delete not filled orders
    DeletePendingOrders(2);

    // Setting long positions
    if(type == 0)
      {
       double     _price   =  SymbolInfoDouble(Symbol(), SYMBOL_ASK);
       double     _sl      =  0;
       double     _tp      =  NormalizeDouble(_price+ PipsToDouble(TPPips, Symbol()), Digits());
       double     _lot     =  NormalizeDouble(MaxLot- BasketLot(0), 2);
              
       Trade.Buy(_lot, Symbol(), _price, _sl, _tp, TradeComment);
       
       // Hedge buy
       double     hPrice   =  SymbolInfoDouble(Symbol(), SYMBOL_ASK)- PipsToDouble(GridDistance, Symbol());
       double     hSl      =  0;
       double     hTp      =  NormalizeDouble(hPrice- PipsToDouble(TPPips, Symbol()), Digits());
       double     hLot     =  NormalizeDouble(MaxLot- BasketLot(1), 2);

       Trade.SellStop(hLot, hPrice, Symbol(), hSl, hTp, ORDER_TIME_GTC, 0, TradeComment);
      }

    // Setting short positions
    if(type == 1)
      {       
       double     _price   =  SymbolInfoDouble(Symbol(), SYMBOL_BID);
       double     _sl      =  0;
       double     _tp      =  NormalizeDouble(_price- PipsToDouble(TPPips, Symbol()), Digits());
       double     _lot     =  NormalizeDouble(MaxLot- BasketLot(1), 2);
       
       Trade.Sell(_lot, Symbol(), _price, _sl, _tp, TradeComment);

       // Hedge sell
       double     hPrice   =  SymbolInfoDouble(Symbol(), SYMBOL_BID)+ PipsToDouble(GridDistance, Symbol());
       double     hSl      =  0;
       double     hTp      =  NormalizeDouble(hPrice+ PipsToDouble(TPPips, Symbol()), Digits());
       double     hLot     =  NormalizeDouble(MaxLot- BasketLot(0), 2);

       Trade.BuyStop(hLot, hPrice, Symbol(), hSl, hTp, ORDER_TIME_GTC, 0, TradeComment);
      }
   }

//+------------------------------------------------------------------+
//| Find Furthest Position                                           |
//+------------------------------------------------------------------+
ulong Furthest(int type)
   {
    ulong   furthestTicket       =  0;
    double  furthestOpenPrice    =  0.0;
    
    // Furthest buy
    if(type == 0)
      {
       for(int i=PositionsTotal()-1; i>=0; i--)
         {
          ulong positionTicket   = PositionGetTicket(i);
          if(positionTicket > 0)
            {
             if(PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
               {
                if(PositionGetDouble(POSITION_PRICE_OPEN) > furthestOpenPrice)
                  {
                   furthestOpenPrice   =  PositionGetDouble(POSITION_PRICE_OPEN);
                   furthestTicket      =  positionTicket;
                  }
               }
            }
         }
      }
    
    // Furthest sell
    if(type == 1)
      {
       furthestOpenPrice         =  9999999999;
       
       for(int i=PositionsTotal()-1; i>=0; i--)
         {
          ulong positionTicket   = PositionGetTicket(i);
          if(positionTicket > 0)
            {
             if(PositionGetString(POSITION_SYMBOL) == Symbol() && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
               {
                if(PositionGetDouble(POSITION_PRICE_OPEN) < furthestOpenPrice)
                  {
                   furthestOpenPrice   =  PositionGetDouble(POSITION_PRICE_OPEN);
                   furthestTicket      =  positionTicket;
                  }
               }
            }
         }
      }
    
    // Furthest position ticket
    return (furthestTicket);
   }

//+------------------------------------------------------------------+
//| Apply TT                                                         |
//+------------------------------------------------------------------+
void ApplyTT(double tt, long dealType)
   {
    double  remainedTT        =  tt;
    ulong   furthestTicket    =  0;
    double  furthestProfit    =  0.0;

    // If no opposite position is running
    if(dealType == 0 && PositionCounter(0) == 0)
       return;

    if(dealType == 1 && PositionCounter(1) == 0)
       return;
    
    // If buy position hits tp, apply TT to sell positions
    if(dealType == 1)
      {
       furthestTicket         =  Furthest(1);
       if(PositionSelectByTicket(furthestTicket))
          furthestProfit      =  PositionGetDouble(POSITION_PROFIT)+ PositionGetDouble(POSITION_SWAP);
       
       while(remainedTT+ furthestProfit > 0)
         {
          remainedTT         +=  furthestProfit;
          Trade.PositionClose(furthestTicket);
          
          // Go for next run
          furthestTicket      =  Furthest(1);
          
          if(furthestTicket == 0)
             break;
          
          if(PositionSelectByTicket(furthestTicket))
             furthestProfit   =  PositionGetDouble(POSITION_PROFIT)+ PositionGetDouble(POSITION_SWAP);
         }
       
       // Partial close
       if(remainedTT > 0)
         {
          furthestTicket      =  Furthest(1);
          if(PositionSelectByTicket(furthestTicket))
             furthestProfit   =  PositionGetDouble(POSITION_PROFIT)+ PositionGetDouble(POSITION_SWAP);
          
          if(furthestProfit == 0.0)
             return;

          double  exitLot     =  remainedTT* PositionGetDouble(POSITION_VOLUME)/ MathAbs(furthestProfit);
          exitLot             =  NormalizeDouble(exitLot, 2);
          remainedTT         +=  furthestProfit;
          Trade.PositionClosePartial(furthestTicket, exitLot);
         }       
      }
    
    // If sell position hits tp, apply TT to buy positions
    if(dealType == 0)
      {
       furthestTicket         =  Furthest(0);
       if(PositionSelectByTicket(furthestTicket))
          furthestProfit      =  PositionGetDouble(POSITION_PROFIT)+ PositionGetDouble(POSITION_SWAP);
       
       while(remainedTT+ furthestProfit > 0)
         {
          remainedTT         +=  furthestProfit;
          Trade.PositionClose(furthestTicket);
          
          // Go for next run
          furthestTicket      =  Furthest(0);
          
          if(furthestTicket == 0)
             break;
          
          if(PositionSelectByTicket(furthestTicket))
             furthestProfit   =  PositionGetDouble(POSITION_PROFIT)+ PositionGetDouble(POSITION_SWAP);
         }
       
       // Partial close
       if(remainedTT > 0)
         {
          furthestTicket      =  Furthest(0);
          if(PositionSelectByTicket(furthestTicket))
             furthestProfit   =  PositionGetDouble(POSITION_PROFIT)+ PositionGetDouble(POSITION_SWAP);
          
          if(furthestProfit == 0.0)
             return;

          double  exitLot     =  remainedTT* PositionGetDouble(POSITION_VOLUME)/ MathAbs(furthestProfit);
          exitLot             =  NormalizeDouble(exitLot, 2);
          remainedTT         +=  furthestProfit;
          Trade.PositionClosePartial(furthestTicket, exitLot);
         }       
      }

    TT   =  0.0;
   }
 
Mohammadmahmood Pirayeh:

the problem is, when a trade hits tp and it calculates the (TT) value for partial closing of the opposite position, instead of doing it once, it does it multiple of time until the loss position will be closed completely!!!! 

There is a lot of info here so I will focus on the problem statement (apologies if I have missed some detail)

If you set a breakpoint in OnTradeTransaction() I think you will find it is called many times when closing a ticket - you need some way of tracking if you have hedged it already. 

There could be a better/standard way, but in my ignorance I have written mechanisms to do this.

For example, you could capture the posID from request.position and add it to a global list and check whether it has been hedged before attempting a new hedge - of course if your EA restarts you would need a way of reconstructing that list - maybe writing to a file could be a simple way to preserve it.

I would also suggest you read this section of the help which may give you some ideas on how you want to approach it

https://www.mql5.com/en/docs/event_handlers/ontradetransaction

Documentation on MQL5: Constants, Enumerations and Structures / Data Structures / Trade Transaction Structure
Documentation on MQL5: Constants, Enumerations and Structures / Data Structures / Trade Transaction Structure
  • www.mql5.com
Trade Transaction Structure - Data Structures - Constants, Enumerations and Structures - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
R4tna C #:

There is a lot of info here so I will focus on the problem statement (apologies if I have missed some detail)

If you set a breakpoint in OnTradeTransaction() I think you will find it is called many times when closing a ticket - you need some way of tracking if you have hedged it already. 

There could be a better/standard way, but in my ignorance I have written mechanisms to do this.

For example, you could capture the posID from request.position and add it to a global list and check whether it has been hedged before attempting a new hedge - of course if your EA restarts you would need a way of reconstructing that list - maybe writing to a file could be a simple way to preserve it.

I would also suggest you read this section of the help which may give you some ideas on how you want to approach it

https://www.mql5.com/en/docs/event_handlers/ontradetransaction

Thanks so much for replying.

i tried to do that with a bool variable, and i didn't get what i expected. i will use your idea and check that with position ID, and will share the result here.

thanks again for your time.

 
Mohammadmahmood Pirayeh #:

Thanks so much for replying.

i tried to do that with a bool variable, and i didn't get what i expected. i will use your idea and check that with position ID, and will share the result here.

thanks again for your time.

Sure - good luck

 
R4tna C #:

Sure - good luck

hi  R4tna

i couldn't find any way to do that with position ID filter and OnTransaction Function, so i decided to write the code differently with OnTick function and i made it work, however that was not the logical solution for that, i continue researching to find the right solution and share it here for others who may have same issue.

thanks anyway

 
Mohammadmahmood Pirayeh #:

hi  R4tna

i couldn't find any way to do that with position ID filter and OnTransaction Function, so i decided to write the code differently with OnTick function and i made it work, however that was not the logical solution for that, i continue researching to find the right solution and share it here for others who may have same issue.

thanks anyway

sure - if that suits you better then it may be the better way

Did you try creating a global variable/array to store the posID when hedging from OnTradeTransaction()?

 
R4tna C #:

sure - if that suits you better then it may be the better way

Did you try creating a global variable/array to store the posID when hedging from OnTradeTransaction()?

yes, i defined a global array to store the position ID of the opposite position which was supposed to be closed or partially closed, and at the end of that function i added the position ID to the array, then every time before calling the function, i checked the position id with the array data, but that didn't work for me or perhaps i wrote it wrongly !!