//+------------------------------------------------------------------+
//|                                                   HedgeEA_V1.mq4 |
//|                        Copyright 2021, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
enum ENUM_OPEN_TYPE    {TYPE1,      //Type1: Instant Hedging
                        TYPE2       //Type2: Price Entry
                           };  
enum ENUM_TRADE_DIRECTION   {BUY,SELL,BOTH};                           
//+------------------------------------------------------------------+
//| Trades                                                           |
//+------------------------------------------------------------------+
extern string               Trades="<<=== TRADES ===>>";                //.
extern int                  Magic=54126548;              //Magic Number 
extern string               TradeComment="VtranscriptionEA";   //Trade Comment
extern ENUM_TRADE_DIRECTION Direction=BOTH;               //Trade Direction
extern ENUM_OPEN_TYPE       OpenType=TYPE1;               //Trade Open Type  
extern double               EntryPrice=1.20000;           //Enter Price  
extern double               MaxSpread=3;                  //Max Spread(Pips) 
extern string               Vols="<<=== Lot Size ===>>";  
extern double               FixedLotSize=0.1;             //Fixed Lot Size [0:Auto]
extern double               LotRisk=1;                    //Risk % of Equity
extern string               Param="<<=== SETTINGS ===>>"; //.
extern double               UserSL=20;                    //Stop Loss(Pips)  [0:off]
extern double               UserTP=20;                    //Take Profit(Pips)[0:off]
extern double               Distance=10;                  //Distance(Pips)[0:off]
//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
int Bar=0; 
int DBar=0;
string Description;
double Pip=Point()*10;
datetime TimeIndex=0;
//+------------------------------------------------------------------+
//| Structer                                                         |
//+------------------------------------------------------------------+
struct OrderStr
  {
  int       Step;
   double   Lot;
   string   Start;
   double   TP_Price;
   double   SL_Price;
  };
//---
OrderStr Buy,Sell,Order;                 
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create timer
   EventSetTimer(60);
//---
 if(OpenType==TYPE1)
   Take_Instant_Positions(); 

 if(OpenType==TYPE2)
   Place_Initial_Pending();    
     
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- destroy timer
   EventKillTimer();
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
  
   //  datetime NY=D'2021.12.15';
   //if(TimeCurrent()>=NY )
   //   {
   //   Comment("Product Expired,Contact the Author");
   //   ExpertRemove();
   //   } 
      
//---
  if(Distance==0)
   {
   if(Is_Hit_SL())
     Take_Another_Positions();
   //---
   if(Is_Hit_TP())
     Take_Another_Positions(); 
   }

  if(Distance>0)
   {   
   Place_Pending();
     
   }
//---

  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Taking Positions                                                 |
//+------------------------------------------------------------------+
void Take_Instant_Positions()
{
double spread=MarketInfo(_Symbol,MODE_SPREAD);

if(spread<=MaxSpread*10)
if(Direction==BUY || Direction==BOTH)
 if(Positions_Buy_Num()==0)  
  {
   Order.Step=1;
   //---
   Buy.Lot=LotSize();
   //---
   Buy.Lot=NormalizeLots(Buy.Lot,Symbol());
   //---
   int BuyTicket=0;
   //---
   if(CheckMoneyForTrade(_Symbol,Buy.Lot,OP_BUY))
      if(CheckVolumeValue(Buy.Lot,Description))
          BuyTicket=OrderSend(_Symbol,OP_BUY,Buy.Lot,Ask,10,0,0,TradeComment,Magic,0,clrBlue);
   //---
    Order.Start="Begin";
    //---
    if(UserTP>0)
      Buy.TP_Price=Ask+UserTP*Pip;
    else
      Buy.TP_Price=0;
    //---
    if(UserSL>0)
      Buy.SL_Price=Ask-UserSL*Pip; 
    else
      Buy.SL_Price=0;
    //---
    Order_SLTP_Modification(Buy.SL_Price,Buy.TP_Price,BuyTicket);
    //---
    TimeIndex=TimeCurrent();
    }
//---
if(spread<=MaxSpread*10)
if(Direction==SELL|| Direction==BOTH)  
 if(Positions_Sell_Num()==0) 
   {
   Order.Step=1; 
   //---
   Sell.Lot=LotSize();
   //---
   Sell.Lot=NormalizeLots(Sell.Lot,Symbol());
   //---
   int  SellTicket=0;
   if(CheckMoneyForTrade(_Symbol,Sell.Lot,OP_SELL))
     if(CheckVolumeValue(Sell.Lot,Description))
         SellTicket=OrderSend(_Symbol,OP_SELL,Sell.Lot,Bid,10,0,0,TradeComment,Magic,0,clrRed);
   //---
   Order.Start="Begin";
   //---
    if(UserTP>0)
       Sell.TP_Price=Bid-UserTP*Pip;
    else 
       Sell.TP_Price=0;
    //---
    if(UserSL>0)
      Sell.SL_Price=Bid+UserSL*Pip;  
    else
      Sell.SL_Price=0; 
    //---
    Order_SLTP_Modification(Sell.SL_Price,Sell.TP_Price,SellTicket);
    //---
    TimeIndex=TimeCurrent();
   } 
   
 //---
 
   
}
//+------------------------------------------------------------------+
//| Taking Positions                                                 |
//+------------------------------------------------------------------+
void Take_Another_Positions()
{
double spread=MarketInfo(_Symbol,MODE_SPREAD);

if(spread<=MaxSpread*10)
if(Direction==BUY || Direction==BOTH)
  {
   Order.Step++;
   //---
   Buy.Lot=LotSize();
   //---
   Buy.Lot=NormalizeLots(Buy.Lot,Symbol());
   //---
   int BuyTicket=0;
   //---
   if(CheckMoneyForTrade(_Symbol,Buy.Lot,OP_BUY))
      if(CheckVolumeValue(Buy.Lot,Description))
          BuyTicket=OrderSend(_Symbol,OP_BUY,Buy.Lot,Ask,10,0,0,TradeComment,Magic,0,clrBlue);
   //---
    Order.Start="Begin";
    //---
    if(UserTP>0)
      Buy.TP_Price=Ask+UserTP*Pip;
    else
      Buy.TP_Price=0;
    //---
    if(UserSL>0)
      Buy.SL_Price=Ask-UserSL*Pip; 
    else
      Buy.SL_Price=0;
    //---
    Order_SLTP_Modification(Buy.SL_Price,Buy.TP_Price,BuyTicket);
        //---
    TimeIndex=TimeCurrent();
    }
//---
if(spread<=MaxSpread*10)
if(Direction==SELL|| Direction==BOTH)  
   {
   Order.Step=1; 
   //---
   Sell.Lot=LotSize();
   //---
   Sell.Lot=NormalizeLots(Sell.Lot,Symbol());
   //---
   int  SellTicket=0;
   if(CheckMoneyForTrade(_Symbol,Sell.Lot,OP_SELL))
     if(CheckVolumeValue(Sell.Lot,Description))
         SellTicket=OrderSend(_Symbol,OP_SELL,Sell.Lot,Bid,10,0,0,TradeComment,Magic,0,clrRed);
   //---
   Order.Start="Begin";
   //---
    if(UserTP>0)
       Sell.TP_Price=Bid-UserTP*Pip;
    else 
       Sell.TP_Price=0;
    //---
    if(UserSL>0)
      Sell.SL_Price=Bid+UserSL*Pip;  
    else
      Sell.SL_Price=0; 
    //---
    Order_SLTP_Modification(Sell.SL_Price,Sell.TP_Price,SellTicket);
        //---
    TimeIndex=TimeCurrent();
   } 
   
 //---
 
   
}
//+------------------------------------------------------------------+
//| Place Initial Pending Order                                      |
//+------------------------------------------------------------------+
void Place_Initial_Pending()
{

if(Direction==BUY || Direction==BOTH)
 if(Order_Buy_Num()==0)  
  {
   int BuyTicket=0;
   if(Ask>EntryPrice)   
      BuyTicket=OrderSend(_Symbol,OP_BUYLIMIT,LotSize(),EntryPrice,5,0,0,TradeComment,Magic,0,clrBlue);      
   if(Ask<EntryPrice)  
        BuyTicket=OrderSend(_Symbol,OP_BUYSTOP,LotSize(),EntryPrice,5,0,0,TradeComment,Magic,0,clrBlue);   
    
    //---
    if(UserTP>0)
      Buy.TP_Price=EntryPrice+UserTP*Pip;
    else
      Buy.TP_Price=0;
    //---
    if(UserSL>0)
      Buy.SL_Price=EntryPrice-UserSL*Pip; 
    else
      Buy.SL_Price=0;
    //---
    Order_SLTP_Modification(Buy.SL_Price,Buy.TP_Price,BuyTicket);       
  }
   //---

if(Direction==SELL || Direction==BOTH)
 if(Order_Sell_Num()==0)  
  {
   int SellTicket=0;
   if(Bid>EntryPrice)
       SellTicket=OrderSend(_Symbol,OP_SELLSTOP,LotSize(),EntryPrice,5,0,0,TradeComment,Magic,0,clrRed);      
   if(Bid<EntryPrice)
       SellTicket=OrderSend(_Symbol,OP_SELLLIMIT,LotSize(),EntryPrice,5,0,0,TradeComment,Magic,0,clrRed); 

   //---
    if(UserTP>0)
       Sell.TP_Price=EntryPrice-UserTP*Pip;
    else 
       Sell.TP_Price=0;
    //---
    if(UserSL>0)
      Sell.SL_Price=EntryPrice+UserSL*Pip;  
    else
      Sell.SL_Price=0; 
    //---
    Order_SLTP_Modification(Sell.SL_Price,Sell.TP_Price,SellTicket);

         
   }

   
}
//+------------------------------------------------------------------+
//| Place Pending Order                                              |
//+------------------------------------------------------------------+
void Place_Pending()
{


if(Direction==BUY || Direction==BOTH)
 {
 if(Order_Num(OP_BUYSTOP)==0)  
  {
    int BuyTicket=0;
    //---
    double Price=Ask+Distance*Point()*10;  
    BuyTicket=OrderSend(_Symbol,OP_BUYSTOP,LotSize(),Price,5,0,0,TradeComment,Magic,0,clrBlue);    
    //---
    if(UserTP>0)
      Buy.TP_Price=Price+UserTP*Pip;
    else
      Buy.TP_Price=0;
    //---
    if(UserSL>0)
      Buy.SL_Price=Price-UserSL*Pip; 
    else
      Buy.SL_Price=0;
    //---
    Order_SLTP_Modification(Buy.SL_Price,Buy.TP_Price,BuyTicket);   
   }
 //---
 if(Order_Num(OP_BUYLIMIT)==0)   
   {
    double Price=Ask-Distance*Point()*10;   
    int BuyTicket=OrderSend(_Symbol,OP_BUYLIMIT,LotSize(),Price,5,0,0,TradeComment,Magic,0,clrBlue);    
    //---
    if(UserTP>0)
      Buy.TP_Price=Price+UserTP*Pip;
    else
      Buy.TP_Price=0;
    //---
    if(UserSL>0)
      Buy.SL_Price=Price-UserSL*Pip; 
    else
      Buy.SL_Price=0;
    //---
    Order_SLTP_Modification(Buy.SL_Price,Buy.TP_Price,BuyTicket);  
    } 
  }
   //---

if(Direction==SELL || Direction==BOTH)
 {
 if(Order_Num(OP_SELLLIMIT)==0)  
  {
   int SellTicket=0;
   //---
   double Price=Bid+Distance*Point()*10;
   SellTicket=OrderSend(_Symbol,OP_SELLLIMIT,LotSize(),Price,5,0,0,TradeComment,Magic,0,clrRed);      
   //---
    if(UserTP>0)
       Sell.TP_Price=Price-UserTP*Pip;
    else 
       Sell.TP_Price=0;
    //---
    if(UserSL>0)
      Sell.SL_Price=Price+UserSL*Pip;  
    else
      Sell.SL_Price=0; 
    //---
    Order_SLTP_Modification(Sell.SL_Price,Sell.TP_Price,SellTicket);
   } 
 //---
 if(Order_Num(OP_SELLSTOP)==0)  
  {
    double Price=Bid-Distance*Point()*10;
    int SellTicket=OrderSend(_Symbol,OP_SELLSTOP,LotSize(),Price,5,0,0,TradeComment,Magic,0,clrRed);  
    //---
   if(UserTP>0)
       Sell.TP_Price=Price-UserTP*Pip;
    else 
       Sell.TP_Price=0;
    //---
    if(UserSL>0)
      Sell.SL_Price=Price+UserSL*Pip;  
    else
      Sell.SL_Price=0; 
    //---
    Order_SLTP_Modification(Sell.SL_Price,Sell.TP_Price,SellTicket);     
   }
 }
   
}
//+------------------------------------------------------------------+
//|  Position Number                                                 |
//+------------------------------------------------------------------+
int Positions_Num()
{int Num=0;
   for(int i=OrdersTotal()-1;i>=0;i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        if(OrderMagicNumber()==Magic && OrderSymbol()==_Symbol) 
         if(OrderType()==OP_BUY || OrderType()==OP_SELL)
        Num++;

     }
return(Num);
}  
//+------------------------------------------------------------------+
//|  Position Number                                                 |
//+------------------------------------------------------------------+
int Positions_Sell_Num()
{int Num=0;
   for(int i=OrdersTotal()-1;i>=0;i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        if(OrderMagicNumber()==Magic && OrderSymbol()==_Symbol) 
         if(OrderType()==OP_SELL)
        Num++;

     }
return(Num);
} 
//+------------------------------------------------------------------+
//|  Position Buy Number                                             |
//+------------------------------------------------------------------+
int Positions_Buy_Num()
{int Num=0;
   for(int i=OrdersTotal()-1;i>=0;i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        if(OrderMagicNumber()==Magic && OrderSymbol()==_Symbol) 
         if(OrderType()==OP_BUY)
        Num++;

     }
return(Num);
}
//+------------------------------------------------------------------+
//|  Position Number                                                 |
//+------------------------------------------------------------------+
int Order_Sell_Num()
{
int Num=0;
   for(int i=OrdersTotal()-1;i>=0;i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        if(OrderMagicNumber()==Magic && OrderSymbol()==_Symbol) 
         if(OrderType()==OP_SELLLIMIT || OrderType()==OP_SELLSTOP)
        Num++;

     }
return(Num);
} 
//+------------------------------------------------------------------+
//|  Position Buy Number                                             |
//+------------------------------------------------------------------+
int Order_Buy_Num()
{int Num=0;
   for(int i=OrdersTotal()-1;i>=0;i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        if(OrderMagicNumber()==Magic && OrderSymbol()==_Symbol) 
         if(OrderType()==OP_BUYLIMIT || OrderType()==OP_BUYSTOP)
        Num++;

     }
return(Num);
}
//+------------------------------------------------------------------+
//|  Order Numbers By Type                                            |
//+------------------------------------------------------------------+
int Order_Num(ENUM_ORDER_TYPE Type)
{int Num=0;
   for(int i=OrdersTotal()-1;i>=0;i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        if(OrderMagicNumber()==Magic && OrderSymbol()==_Symbol) 
         if(OrderType()==Type)
          Num++;

     }
return(Num);
}
//+------------------------------------------------------------------+
//|  Normalize                                                       |
//+------------------------------------------------------------------+
double NormalizeLots(double lots, string pair="")
{
    if (pair == "") pair = Symbol();
    double  lotStep     = MarketInfo(pair, MODE_LOTSTEP),
            minLot      = MarketInfo(pair, MODE_MINLOT);
    lots            = MathRound(lots/lotStep) * lotStep;
    if (lots < minLot) lots = minLot;    // or minLot
    return(lots);
}
//+------------------------------------------------------------------+
//| Check if there is enough money                                   |
//+------------------------------------------------------------------+
bool CheckMoneyForTrade(string symb, double lots,int type)
  {
   double free_margin=AccountFreeMarginCheck(symb,type, lots);
//-- if there is not enough money
   if(free_margin<0)
     {
      string oper=(type==OP_BUY)? "Buy":"Sell";
      Print("Not enough money for ", oper," ",lots, " ", symb, " Error code=",GetLastError());
      return(false);
     }
//--- checking successful
   return(true);
  }
  
//+------------------------------------------------------------------+
//| Check the correctness of the order volume                        |
//+------------------------------------------------------------------+
bool CheckVolumeValue(double volume,string &description)
  {
//--- minimal allowed volume for trade operations
   double min_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   if(volume<min_volume)
     {
      description=StringFormat("Volume is less than the minimal allowed SYMBOL_VOLUME_MIN=%.2f",min_volume);
      return(false);
     }

//--- maximal allowed volume of trade operations
   double max_volume=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   if(volume>max_volume)
     {
      description=StringFormat("Volume is greater than the maximal allowed SYMBOL_VOLUME_MAX=%.2f",max_volume);
      return(false);
     }

//--- get minimal step of volume changing
   double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);

   int ratio=(int)MathRound(volume/volume_step);
   if(MathAbs(ratio*volume_step-volume)>0.0000001)
     {
      description=StringFormat("Volume is not a multiple of the minimal step SYMBOL_VOLUME_STEP=%.2f, the closest correct volume is %.2f",
                               volume_step,ratio*volume_step);
      return(false);
     }
   description="Correct volume value";
   return(true);
}   
//+------------------------------------------------------------------+
//| LotSize Calculator                                               |
//+------------------------------------------------------------------+
double LotSize()
  {
  if(FixedLotSize>0)
    return(FixedLotSize);
  //---
  if(FixedLotSize==0 && UserSL>0)
   {
   double Current_PointValue= (MarketInfo(Symbol(),MODE_TICKSIZE)!=0) ? MarketInfo(Symbol(),MODE_TICKVALUE)*MarketInfo(Symbol(),MODE_POINT)/MarketInfo(Symbol(),MODE_TICKSIZE)*1 : 0;
   double Target_PointValue= (UserSL != 0) ? (AccountEquity()*LotRisk/100)/ (UserSL*10) : 0 ;
   double Vol= (Current_PointValue!=0) ? Target_PointValue/Current_PointValue : 0;
  // Print("Sym: ",Sym," Vol: ",Vol," Target_PointValue: ",Target_PointValue," Current_PointValue: ",Current_PointValue);
   if(MarketInfo(Symbol(),MODE_MINLOT)==0.1)
      Vol=NormalizeDouble(Vol,1);
   else if(MarketInfo(Symbol(),MODE_MINLOT)==0.01)  
      Vol=NormalizeDouble(Vol,2);
   if(Vol<MarketInfo(Symbol(),MODE_MINLOT)) Vol=MarketInfo(Symbol(),MODE_MINLOT);   
   if(Vol>MarketInfo(Symbol(),MODE_MAXLOT)) Vol=MarketInfo(Symbol(),MODE_MAXLOT);   
   return(Vol);
    }
  return 0;  
  } 
//+------------------------------------------------------------------+
//| Order SL & TP Modification for ECN Accounts                      |
//+------------------------------------------------------------------+
void Order_SLTP_Modification(double StopLoss,double TakeProfit,int Ticket)
  {

   if(OrderSelect(Ticket,SELECT_BY_TICKET,MODE_TRADES))   
     {
     bool Modify_Order=OrderModify(OrderTicket(),OrderOpenPrice(),StopLoss,TakeProfit,0,clrNONE);    
     if(!Modify_Order)
         Print("Cannot Modify Order. Error: ",GetLastError()," SL: ",StopLoss," TP: ",TakeProfit," Type: ",OrderType());      
     }

  }     
//+------------------------------------------------------------------+
//|  Check if trade hit TP                                           |
//+------------------------------------------------------------------+
bool Is_Hit_TP()
  {
   for(int i=OrdersHistoryTotal()-1; i>=0; i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY))
         if(OrderSymbol()==Symbol())
           if(OrderMagicNumber()==Magic)
             if(OrderCloseTime()>TimeIndex)
               if(StringSubstr(OrderComment(),StringLen(OrderComment())-4,4)=="[tp]") 
                  {
                  TimeIndex=TimeCurrent();
                  return(true);
                  }

     }
   return(false);
  }    

//+------------------------------------------------------------------+
//|  Check if trade hit SL                                           |
//+------------------------------------------------------------------+
bool Is_Hit_SL()
  {
   for(int i=OrdersHistoryTotal()-1; i>=0; i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY))
        if(OrderSymbol()==Symbol())
          if(OrderMagicNumber()==Magic)
            if(OrderCloseTime()>TimeIndex)
              if(StringSubstr(OrderComment(),StringLen(OrderComment())-4,4)=="[sl]") 
                  {
                  TimeIndex=TimeCurrent();
                  return(true);
                  }

     }
   return(false);
  }     