How to create email alerts when a trade is opened and closed

Hi, I need to add the email alert feature on my EA when a trade is opened and a trade is closed (by trailing stop/stop loss/take profit or just close the trade). I am aware of the function SendMail, but I have two issues that block my progress.

  1. How to locate the most recent trade when sending the email alert? I guess I could add SendEmail after OrderSend for each position opened, but how to do that for closed trades?

  2. How to set up email alerts so it would also send email alerts for closed trades via trailing stop/stop loss/take profit? I could insert SendMail after my close order condition, but that only covers one scenario, how should I deal with the rest?

My EA is as follows, please let me know how I can add the email alert feature to it, thanks very much!

#property version   "1.00"
#property strict

input bool   OpenBUY=True;
input bool   OpenSELL=True;
input bool   CloseBySignal=True;
input double StopLoss=0;
input double TakeProfit=0;
input double TrailingStop=0;
input int    RSIperiod=14;
input double BuyLevel=30;
input double SellLevel=70;

input int    MagicNumber=123;
input string Koment="RSIea";
input int    Slippage=10;
//---
int OrderBuy,OrderSell;
int ticket;
int LotDigits;
double Trail,iTrailingStop;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   return(0);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int deinit()
  {
   return(0);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
  {
   double stoplevel=MarketInfo(Symbol(),MODE_STOPLEVEL);
   OrderBuy=0;
   OrderSell=0;
   for(int cnt=0; cnt<OrdersTotal(); cnt++)
     {
      if(OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES))
         if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber && OrderComment()==Koment)
           {
            if(OrderType()==OP_BUY) OrderBuy++;
            if(OrderType()==OP_SELL) OrderSell++;
            if(TrailingStop>0)
              {
               iTrailingStop=TrailingStop;
               if(TrailingStop<stoplevel) iTrailingStop=stoplevel;
               Trail=iTrailingStop*Point;
               double tsbuy=NormalizeDouble(Bid-Trail,Digits);
               double tssell=NormalizeDouble(Ask+Trail,Digits);
               if(OrderType()==OP_BUY && Bid-OrderOpenPrice()>Trail && Bid-OrderStopLoss()>Trail)
                 {
                  ticket=OrderModify(OrderTicket(),OrderOpenPrice(),tsbuy,OrderTakeProfit(),0,Blue);
                 }
               if(OrderType()==OP_SELL && OrderOpenPrice()-Ask>Trail && (OrderStopLoss()-Ask>Trail || OrderStopLoss()==0))
                 {
                  ticket=OrderModify(OrderTicket(),OrderOpenPrice(),tssell,OrderTakeProfit(),0,Blue);
                 }
              }
           }
     }
   double rsim1=iRSI(Symbol(),1,RSIperiod,PRICE_CLOSE,1);
   double rsim5=iRSI(Symbol(),5,RSIperiod,PRICE_CLOSE,1);
   double rsim15=iRSI(Symbol(),15,RSIperiod,PRICE_CLOSE,1);
   double rsim30=iRSI(Symbol(),30,RSIperiod,PRICE_CLOSE,1);
   double rsim60=iRSI(Symbol(),60,RSIperiod,PRICE_CLOSE,1);
   double rsid1=iRSI(Symbol(),1440,RSIperiod,PRICE_CLOSE,1);

//--- open position
   if(OpenSELL && OrderSell<1 && rsim30>=SellLevel && rsim60>=SellLevel && rsid1<50) OPSELL();
   if(OpenBUY && OrderBuy<1 && rsim30<=BuyLevel && rsim60<=BuyLevel && rsid1>50) OPBUY();
//--- close position by signal
   if(CloseBySignal)
     {
      if(OrderBuy>0 && rsim30>=SellLevel && rsim60>=SellLevel) CloseBuy();
      if(OrderSell>0 && rsim30<=BuyLevel && rsim60<=BuyLevel) CloseSell();
     }
//---
   return(0);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OPBUY()
  {
   double StopLossLevel;
   double TakeProfitLevel;
   if(StopLoss>0) StopLossLevel=Bid-StopLoss*Point; else StopLossLevel=0.0;
   if(TakeProfit>0) TakeProfitLevel=Ask+TakeProfit*Point; else TakeProfitLevel=0.0;

   ticket=OrderSend(Symbol(),OP_BUY,1,Ask,Slippage,StopLossLevel,TakeProfitLevel,Koment,MagicNumber,0,DodgerBlue);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OPSELL()
  {
   double StopLossLevel;
   double TakeProfitLevel;
   if(StopLoss>0) StopLossLevel=Ask+StopLoss*Point; else StopLossLevel=0.0;
   if(TakeProfit>0) TakeProfitLevel=Bid-TakeProfit*Point; else TakeProfitLevel=0.0;
//---
   ticket=OrderSend(Symbol(),OP_SELL,1,Bid,Slippage,StopLossLevel,TakeProfitLevel,Koment,MagicNumber,0,DeepPink);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CloseSell()
  {
   int  total=OrdersTotal();
   for(int y=OrdersTotal()-1; y>=0; y--)
     {
      if(OrderSelect(y,SELECT_BY_POS,MODE_TRADES))
         if(OrderSymbol()==Symbol() && OrderType()==OP_SELL && OrderMagicNumber()==MagicNumber)
           {
            ticket=OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),5,Black);
           }
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CloseBuy()
  {
   int  total=OrdersTotal();
   for(int y=OrdersTotal()-1; y>=0; y--)
     {
      if(OrderSelect(y,SELECT_BY_POS,MODE_TRADES))
         if(OrderSymbol()==Symbol() && OrderType()==OP_BUY && OrderMagicNumber()==MagicNumber)
           {
            ticket=OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),5,Black);
           }
     }
  }
//+------------------------------------------------------------------+

There’s many ways to go about it, but what I would do is initialize a set with all the existing ticket numbers in the history pool. Then on each tick I’d check to see if there are any new tickets in the history pool, and if there are then send an alert.

#property strict
#define MAGIC_NUMBA 6660666
#include <Arrays\ArrayInt.mqh>

class SetInt : public CArrayInt
{
   public: bool Add(const int element){
      this.Sort();
      if(this.Search(element) >= 0)
         return false;
      return CArrayInt::InsertSort(element);
   }
};

SetInt history_ticket_set;

int OnInit()
{
   for(int i=OrdersHistoryTotal()-1; i>=0; --i)
      if(select_history(i))
         history_ticket_set.Add(OrderTicket());
   return INIT_SUCCEEDED;
}

void OnTick()
{
   for(int i=OrdersHistoryTotal()-1; i>=0; --i)
      if(select_history(i))
         if(history_ticket_set.Add(OrderTicket()))
            SendMail("Newly closed order", _Symbol);
}

bool select_history(const int index)
{
   return (
      OrderSelect(index, SELECT_BY_POS, MODE_HISTORY)
      && OrderSymbol() == _Symbol
      && OrderMagicNumber() == MAGIC_NUMBA
   );
}

Thanks, I am new to MQL coding, so forgive me if there are some basics I may not fully understand. I have read your code, so it does send me the email alert when an order is closed, correct? Can I simply integrate your code into mine? Or would there be a logic conflict (e.g. int OnInit() in your code with my int Start(), and I have already a MagicNumber=123 in my code, do I still need to define a MAGIC_NUMBA?) ? If there is, could you instruct me on how to amalgamate them? Thanks very much!

//+------------------------------------------------------------------+
//|                                          TradeCloseAlertTest.mq4 |
//|                                                      nicholishen |
//|                         https://www.forexfactory.com/nicholishen |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property link      "https://www.forexfactory.com/nicholishen"
#property version   "1.00"
#property strict
#define MAGIC 1010101

/* Save the trade_close_alerter.mqh file to the root of the 
   ...\\MQL4\\Include directory. 
   Integrate into the EA as follows.
!!!Note: Make sure the file name is exactly "trade_close_alerter.mqh"

*/ 
#include <trade_close_alerter.mqh>

// Create a global instance of this class
TradeCloseAlerter g_close_alerter;
//+------------------------------------------------------------------+
int OnInit()
{
   g_close_alerter.on_init(_Symbol, MAGIC); // Initialize the alerter in OnInit
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnTick()
{
   //logic here 
   
   // Call the alerter in OnTick (best after all logic)
   // or call in OnTimer as well. 
   g_close_alerter.on_tick(); 
}
//+------------------------------------------------------------------+

Files: trade_close_alerter.mqh 3 kb

Hi, thanks for the help! Just to clarify, when you type “logic here” in void OnTick(), what logic were you referring to? My close trade logic or was it SendMail logic? Then I just need to put your code into my EA, right? Sorry for being a slow learner, I appreciate your help immensely!

It honestly doesn’t matter where it goes, but I would put it at the end of the function

So just to clarify, I should put all your codes at the end of my EA, right? And “logic here” in void OnTick() refers to what, sir? SendMail function cannot be backtested, so I am worried that it may take overdue long to find out if it is functioning as I intended it to. Thanks for your patience and help!