English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
MQL5 Cookbook - Consulente esperto multi-valuta e il lavoro con ordini in sospeso in MQL5

MQL5 Cookbook - Consulente esperto multi-valuta e il lavoro con ordini in sospeso in MQL5

MetaTrader 5Esempi | 12 gennaio 2022, 09:59
158 0
Anatoli Kazharski
Anatoli Kazharski

Introduzione

Questa volta creeremo un Expert Advisor multi-valuta con un algoritmo di trading basato sul lavoro con gli ordini in sospeso Buy Stop e Sell Stop. Il modello che creeremo sarà progettato per gli scambi/test infragiornalieri. L'articolo prende in considerazione i seguenti argomenti:

  • Trading in un intervallo di tempo specificato. Creiamo una funzionalità che ci permetterà di impostare l'ora di inizio e fine trading. Ad esempio, può essere l'ora delle sessioni di trading europee o americane. Sicuramente ci sarà l'opportunità di trovare l'intervallo di tempo più adatto durante l'ottimizzazione dei parametri dell'Expert Advisor.
  • Inserimento/modifica/cancellazione ordini in sospeso.
  • Elaborazione degli eventi commerciali: verifica se l'ultima posizione è stata chiusa a Take Profit o Stop Loss e controllo sullo storico delle transazioni per ogni simbolo.


Sviluppo di consulenti esperti

Utilizzeremo il codice dell'articolo MQL5 Cookbook: Consulente esperto multivaluta: approccio semplice, accurato e rapido come modello. Sebbene la struttura essenziale del modello rimarrà la stessa, verranno introdotti alcuni cambiamenti significativi. L'Expert Advisor sarà progettato per il commercio infragiornaliero, tuttavia, questa modalità potrebbe essere disattivata in caso di necessità. Gli ordini pendenti, in tal caso, verranno sempre piazzati immediatamente (su evento New Bar) se una posizione è stata chiusa.

Cominciamo con i parametri esterni dell'expert advisor. Inizialmente creeremo una nuova enumerazione ENUM_HOURS nel file include Enums.mqh. Il numero di identificatori in questa enumerazione è uguale al numero di ore in un giorno:

//--- Hours Enumeration
enum ENUM_HOURS
  {
   h00 = 0,  // 00 : 00
   h01 = 1,  // 01 : 00
   h02 = 2,  // 02 : 00
   h03 = 3,  // 03 : 00
   h04 = 4,  // 04 : 00
   h05 = 5,  // 05 : 00
   h06 = 6,  // 06 : 00
   h07 = 7,  // 07 : 00
   h08 = 8,  // 08 : 00
   h09 = 9,  // 09 : 00
   h10 = 10, // 10 : 00
   h11 = 11, // 11 : 00
   h12 = 12, // 12 : 00
   h13 = 13, // 13 : 00
   h14 = 14, // 14 : 00
   h15 = 15, // 15 : 00
   h16 = 16, // 16 : 00
   h17 = 17, // 17 : 00
   h18 = 18, // 18 : 00
   h19 = 19, // 19 : 00
   h20 = 20, // 20 : 00
   h21 = 21, // 21 : 00
   h22 = 22, // 22 : 00
   h23 = 23  // 23 : 00
  };

Quindi nell'elenco dei parametri esterni creeremo quattro parametri relativi al trading in un intervallo di tempo:

  • TradeInTimeRange - abilita/disabilita la modalità. Come già accennato, renderemo possibile il lavoro dell'Expert Advisor non solo entro un certo intervallo di tempo ma anche 24 ore su 24, cioè in modalità continua.
  • StartTrade - l'ora in cui inizia una sessione di trading. Non appena il tempo del server sarà pari a questo valore, l'Expert Advisor effettuerà ordini in sospeso, a condizione che la modalità TradeInTimeRange sia attiva.
  • StopOpenOrders - l'ora della fine dell'immissione degli ordini. Quando il tempo del server è uguale a questo valore, l'Expert Advisor smetterà di piazzare ordini in sospeso se una posizione viene chiusa.
  • EndTrade - l'ora in cui si interrompe una sessione di trading. Una volta che il tempo del server è uguale a questo valore, l'Expert Advisor interrompe il trading. Una posizione aperta per il simbolo specificato verrà chiusa e gli ordini in sospeso verranno eliminati.

L'elenco dei parametri esterni apparirà come mostrato di seguito. L'esempio fornito è per due simboli. Nel parametro PendingOrder impostiamo una distanza in punti dal prezzo corrente.

//--- External parameters of the Expert Advisor 
sinput long       MagicNumber       = 777;      // Magic number
sinput int        Deviation         = 10;       // Slippage
//---
sinput string delimeter_00=""; // --------------------------------
sinput string     Symbol_01            ="EURUSD";  // Symbol 1
input  bool       TradeInTimeRange_01  =true;      // |     Trading in a time range
input  ENUM_HOURS StartTrade_01        = h10;      // |     The hour of the beginning of a trading session
input  ENUM_HOURS StopOpenOrders_01    = h17;      // |     The hour  of the end of placing orders
input  ENUM_HOURS EndTrade_01          = h22;      // |     The hour of the end of a trading session
input  double     PendingOrder_01      = 50;       // |     Pending order
input  double     TakeProfit_01        = 100;      // |     Take Profit
input  double     StopLoss_01          = 50;       // |     Stop Loss
input  double     TrailingStop_01      = 10;       // |     Trailing Stop
input  bool       Reverse_01           = true;     // |     Position reversal
input  double     Lot_01               = 0.1;      // |     Lot
//---
sinput string delimeter_01=""; // --------------------------------
sinput string     Symbol_02            ="AUDUSD";  // Symbol 2
input  bool       TradeInTimeRange_02  =true;      // |     Trading in a time range
input  ENUM_HOURS StartTrade_02        = h10;      // |     The hour of the beginning of a trading session
input  ENUM_HOURS StopOpenOrders_02    = h17;      // |     The hour  of the end of placing orders
input  ENUM_HOURS EndTrade_02          = h22;      // |     The hour of the end of a trading session
input  double     PendingOrder_02      = 50;       // |     Pending order
input  double     TakeProfit_02        = 100;      // |     Take Profit
input  double     StopLoss_02          = 50;       // |     Stop Loss
input  double     TrailingStop_02      = 10;       // |     Trailing Stop
input  bool       Reverse_02           = true;     // |     Position reversal
input  double     Lot_02               = 0.1;      // |     Lot

Anche le modifiche corrispondenti devono essere apportate nell'elenco degli array che verranno riempiti con i valori dei parametri esterni:

//--- Arrays for storing external parameters
string     Symbols[NUMBER_OF_SYMBOLS];          // Symbol
bool       TradeInTimeRange[NUMBER_OF_SYMBOLS]; // Trading in a time range
ENUM_HOURS StartTrade[NUMBER_OF_SYMBOLS];       // The hour of the beginning of a trading session
ENUM_HOURS StopOpenOrders[NUMBER_OF_SYMBOLS];   // The hour  of the end of placing orders
ENUM_HOURS EndTrade[NUMBER_OF_SYMBOLS];         // The hour of the end of a trading session
double     PendingOrder[NUMBER_OF_SYMBOLS];     // Pending order
double     TakeProfit[NUMBER_OF_SYMBOLS];       // Take Profit
double     StopLoss[NUMBER_OF_SYMBOLS];         // Stop Loss
double     TrailingStop[NUMBER_OF_SYMBOLS];     // Trailing Stop
bool       Reverse[NUMBER_OF_SYMBOLS];          // Position Reversal
double     Lot[NUMBER_OF_SYMBOLS];              // Lot

Ora faremo in modo che nella modalità di inversione (il valore del parametro Reverse è vero) l'ordine in sospeso opposto venga eliminato e inserito di nuovo, quando viene attivato uno degli ordini in sospeso. Non possiamo modificare il volume dell'ordine in sospeso come faremmo in caso di modifica dei suoi livelli di prezzo (prezzo dell'ordine, Stop Loss, Take Profit). Pertanto, dobbiamo eliminarlo e inserire un nuovo ordine in sospeso con il volume richiesto.

Inoltre, se la modalità di inversione è abilitata e contemporaneamente è impostato il livello di Trailing Stop, l'ordine in sospeso seguirà il prezzo. Se, oltre a ciò, viene posizionato lo Stop Loss, il suo valore di prezzo verrà calcolato e specificato in base all'ordine in sospeso.

Nell'ambito globale creiamo due variabili stringa per i commenti dell'ordine in sospeso:

//--- Pending order comments 
string comment_top_order    ="top_order";
string comment_bottom_order ="bottom_order";

All'inizializzazione nella funzione OnInit() durante il caricamento di Expert Advisor, verificheremo la correttezza dei parametri esterni. I criteri per la valutazione sono i seguenti. Quando la modalità TradeInTimeRange è abilitata, l'ora di inizio di una sessione di negoziazione non deve essere inferiore di un'ora all'ora di fine dell'immissione degli ordini in sospeso. L'ora di fine dell'immissione degli ordini pendenti, a sua volta, non deve essere inferiore di un'ora all'ora di fine di una sessione di negoziazione. Scriviamo la funzione CheckInputParameters() che effettuerà tale controllo:

//+------------------------------------------------------------------+
//| Checks external parameters                                       |
//+------------------------------------------------------------------+
bool CheckInputParameters()
  {
//--- Loop through the specified symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If there is no symbol and the TradeInTimeRange mode is disabled, move on to the following symbol.
      if(Symbols[s]=="" || !TradeInTimeRange[s])
         continue;
      //--- Check the accuracy of the start and the end of a trade session time
      if(StartTrade[s]>=EndTrade[s])
        {
         Print(Symbols[s],
               ": The hour of the beginning of a trade session("+IntegerToString(StartTrade[s])+") "
               "must be less than the hour of the end of a trade session"("+IntegerToString(EndTrade[s])+")!");
         return(false);
        }
      //--- A trading session is to start no later that one hour before the hour of placing pending orders.
      //    Pending orders are to be placed no later than one hour before the hour of the end  of a trading session.
      if(StopOpenOrders[s]>=EndTrade[s] ||
         StopOpenOrders[s]<=StartTrade[s])
        {
         Print(Symbols[s],
               ": The hour of the end of placing orders ("+IntegerToString(StopOpenOrders[s])+") "
               "is to be less than the hour of the end ("+IntegerToString(EndTrade[s])+") and "
               "greater than the hour of the beginning of a trading session  ("+IntegerToString(StartTrade[s])+")!");
         return(false);
        }
     }
//--- Parameters are correct
   return(true);
  }

Per implementare questo modello avremo bisogno delle funzioni che effettueranno i controlli per rimanere entro gli intervalli di tempo specificati per il commercio e l'immissione di ordini in sospeso. Chiameremo queste funzioni IsInTradeTimeRange() e IsInOpenOrdersTimeRange(). Funzionano entrambi allo stesso modo, l'unica differenza è nel limite superiore dell'intervallo sotto controllo. Più avanti vedremo dove verranno utilizzate queste funzioni.

//+------------------------------------------------------------------+
//| Checks if we are within the time range for trade                 |
//+------------------------------------------------------------------+
bool IsInTradeTimeRange(int symbol_number)
  {
//--- If TradeInTimeRange mode is enabled
   if(TradeInTimeRange[symbol_number])
     {
      //--- Structure of the date and time
      MqlDateTime last_date;
      //--- Get the last value of the date and time data set
      TimeTradeServer(last_date);
      //--- Outside of the allowed time range
      if(last_date.hour<StartTrade[symbol_number] ||
         last_date.hour>=EndTrade[symbol_number])
         return(false);
     }
//--- Within the allowed time range
   return(true);
  }
//+------------------------------------------------------------------+
//| Checks if we are within the time range for placing orders        |
//+------------------------------------------------------------------+
bool IsInOpenOrdersTimeRange(int symbol_number)
  {
//--- If the TradeInTimeRange mode if enabled
   if(TradeInTimeRange[symbol_number])
     {
      //--- Structure of the date and time
      MqlDateTime last_date; 
      //--- Get the last value of the date and time data set
      TimeTradeServer(last_date);
      //--- Outside the allowed time range
      if(last_date.hour<StartTrade[symbol_number] ||
         last_date.hour>=StopOpenOrders[symbol_number])
         return(false);
     }
//--- Within the allowed time range
   return(true);
  }

Gli articoli precedenti hanno già considerato le funzioni per la ricezione delle proprietà di posizione, simbolo e storia dei deal. In questo articolo avremo bisogno di una funzione simile per ottenere le proprietà di un ordine in sospeso. Nel file include Enums.mqh creeremo un'enumerazione con le proprietà di un ordine in sospeso:

//--- Enumeration of the properties of a pending order 
enum ENUM_ORDER_PROPERTIES
  {
   O_SYMBOL          = 0,
   O_MAGIC           = 1,
   O_COMMENT         = 2,
   O_PRICE_OPEN      = 3,
   O_PRICE_CURRENT   = 4,
   O_PRICE_STOPLIMIT = 5,
   O_VOLUME_INITIAL  = 6,
   O_VOLUME_CURRENT  = 7,
   O_SL              = 8,
   O_TP              = 9,
   O_TIME_SETUP      = 10,
   O_TIME_EXPIRATION = 11,
   O_TIME_SETUP_MSC  = 12,
   O_TYPE_TIME       = 13,
   O_TYPE            = 14,
   O_ALL             = 15
  };

Quindi nel file include TradeFunctions.mqh dobbiamo scrivere una struttura con le proprietà di un ordine in sospeso e quindi istanziarlo:

//-- Properties of a pending order
struct pending_order_properties
  {
   string            symbol;          // Symbol
   long              magic;           // Magic number
   string            comment;         // Comment
   double            price_open;      // Price specified in the order
   double            price_current;   // Current price of the order symbol
   double            price_stoplimit; // Limit order price for the Stop Limit order
   double            volume_initial;  // Initial order volume
   double            volume_current;  // Current order volume
   double            sl;              // Stop Loss level
   double            tp;              // Take Profit level
   datetime          time_setup;      // Order placement time
   datetime          time_expiration; // Order expiration time
   datetime          time_setup_msc;  // The time of placing an order for execution in milliseconds since 01.01.1970
   datetime          type_time;       // Order lifetime
   ENUM_ORDER_TYPE   type;            // Position type
  };
//--- Variable of the order features
pending_order_properties ord;

Per ottenere una proprietà o anche tutte le proprietà di un ordine in sospeso, scriveremo la funzione GetPendingOrderProperties(). Dopo che l'ordine in sospeso è stato selezionato, possiamo utilizzare questa funzione per recuperare le proprietà dell'ordine. Il modo per farlo sarà descritto più avanti.

//+------------------------------------------------------------------+
//| Retrieves the properties of the previously selected pending order|
//+------------------------------------------------------------------+
void GetPendingOrderProperties(ENUM_ORDER_PROPERTIES order_property)
  {
   switch(order_property)
     {
      case O_SYMBOL          : ord.symbol=OrderGetString(ORDER_SYMBOL);                              break;
      case O_MAGIC           : ord.magic=OrderGetInteger(ORDER_MAGIC);                               break;
      case O_COMMENT         : ord.comment=OrderGetString(ORDER_COMMENT);                            break;
      case O_PRICE_OPEN      : ord.price_open=OrderGetDouble(ORDER_PRICE_OPEN);                      break;
      case O_PRICE_CURRENT   : ord.price_current=OrderGetDouble(ORDER_PRICE_CURRENT);                break;
      case O_PRICE_STOPLIMIT : ord.price_stoplimit=OrderGetDouble(ORDER_PRICE_STOPLIMIT);            break;
      case O_VOLUME_INITIAL  : ord.volume_initial=OrderGetDouble(ORDER_VOLUME_INITIAL);              break;
      case O_VOLUME_CURRENT  : ord.volume_current=OrderGetDouble(ORDER_VOLUME_CURRENT);              break;
      case O_SL              : ord.sl=OrderGetDouble(ORDER_SL);                                      break;
      case O_TP              : ord.tp=OrderGetDouble(ORDER_TP);                                      break;
      case O_TIME_SETUP      : ord.time_setup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);           break;
      case O_TIME_EXPIRATION : ord.time_expiration=(datetime)OrderGetInteger(ORDER_TIME_EXPIRATION); break;
      case O_TIME_SETUP_MSC  : ord.time_setup_msc=(datetime)OrderGetInteger(ORDER_TIME_SETUP_MSC);   break;
      case O_TYPE_TIME       : ord.type_time=(datetime)OrderGetInteger(ORDER_TYPE_TIME);             break;
      case O_TYPE            : ord.type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);                break;
      case O_ALL             :
         ord.symbol=OrderGetString(ORDER_SYMBOL);
         ord.magic=OrderGetInteger(ORDER_MAGIC);
         ord.comment=OrderGetString(ORDER_COMMENT);
         ord.price_open=OrderGetDouble(ORDER_PRICE_OPEN);
         ord.price_current=OrderGetDouble(ORDER_PRICE_CURRENT);
         ord.price_stoplimit=OrderGetDouble(ORDER_PRICE_STOPLIMIT);
         ord.volume_initial=OrderGetDouble(ORDER_VOLUME_INITIAL);
         ord.volume_current=OrderGetDouble(ORDER_VOLUME_CURRENT);
         ord.sl=OrderGetDouble(ORDER_SL);
         ord.tp=OrderGetDouble(ORDER_TP);
         ord.time_setup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);
         ord.time_expiration=(datetime)OrderGetInteger(ORDER_TIME_EXPIRATION);
         ord.time_setup_msc=(datetime)OrderGetInteger(ORDER_TIME_SETUP_MSC);
         ord.type_time=(datetime)OrderGetInteger(ORDER_TYPE_TIME);
         ord.type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);                                      break;
         //---
     default: Print("Retrieved feature of the pending order was not taken into account in the enumeration "); return;
     }
  }

Ora scriveremo le funzioni di base per inserire, modificare ed eliminare gli ordini in sospeso. La funzione SetPendingOrder() inserisce un ordine in sospeso. Se l'ordine in sospeso non è stato effettuato, la funzione menzionata effettuerà una registrazione nel giornale con un codice di errore e la sua descrizione:

//+------------------------------------------------------------------+
//| Places a pending order                                           |
//+------------------------------------------------------------------+
void SetPendingOrder(int                  symbol_number,   // Symbol number
                     ENUM_ORDER_TYPE      order_type,      // Order type
                     double               lot,             // Volume
                     double               stoplimit_price, // Level of the StopLimit order 
                     double               price,           // Price
                     double               sl,              // Stop Loss
                     double               tp,              // Take Profit
                     ENUM_ORDER_TYPE_TIME type_time,       // Order Expiration
                     string               comment)         // Comment
//--- Set magic number in the trade structure
   trade.SetExpertMagicNumber(MagicNumber);
//--- If a pending order failed to be placed, print an error message  
   if(!trade.OrderOpen(Symbols[symbol_number],
                       order_type,lot,stoplimit_price,price,sl,tp,type_time,0,comment))
      Print("Error when placing a pending order: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }

La funzione ModifyPendingOrder() modifica un ordine in sospeso. Ci organizzeremo in modo da poter modificare non solo il prezzo dell'ordine ma anche il suo volume e passarlo come ultimo parametro della funzione. Se il valore del volume passato è maggiore di zero, significa che l'ordine in sospeso deve essere cancellato e deve essere inserito uno nuovo con un valore del volume richiesto. In tutti gli altri casi modifichiamo semplicemente l'ordine esistente modificando il valore del prezzo.

//+------------------------------------------------------------------+
//| Modifies a pending order                                         |
//+------------------------------------------------------------------+
void ModifyPendingOrder(int                  symbol_number,   //Symbol number
                        ulong                ticket,          // Order ticket
                        ENUM_ORDER_TYPE      type,            // Order type
                        double               price,           // Order price
                        double               sl,              // Stop Loss of the order
                        double               tp,              // Take Profit of the order
                        ENUM_ORDER_TYPE_TIME type_time,       // Order expiration
                        datetime             time_expiration, // Order expiration time
                        double               stoplimit_price, // Price
                        string               comment,         // Comment
                        double               volume)          // Volume
  {
//--- If the passed volume value is non-zero, delete the order and place it again
   if(volume>0)
     {
      //--- If the order failed to be deleted, exit
      if(!DeletePendingOrder(ticket))
         return;
      //--- Place a pending order
      SetPendingOrder(symbol_number,type,volume,0,price,sl,tp,type_time,comment);
      //--- Adjust Stop Loss of position as related to the order
      CorrectStopLossByOrder(symbol_number,price,type);
     }
//--- If the passed volume value is zero, modify the order
   else
     {
      //--- If the pending order failed to be modified, print a relevant message
      if(!trade.OrderModify(ticket,price,sl,tp,type_time,time_expiration,stoplimit_price))
         Print("Error when modifying the pending order price: ",
         GetLastError()," - ",ErrorDescription(GetLastError()));
      //--- Otherwise adjust Stop Loss of position as related to the order
      else
         CorrectStopLossByOrder(symbol_number,price,type);
     }
  }

Nel codice sopra evidenziato ci sono due nuove funzioni DeletePendingOrder() e CorrectStopLossByOrder(). Il primo elimina un ordine in sospeso e il secondo regola lo Stop Loss della posizione in relazione all'ordine in sospeso.

//+------------------------------------------------------------------+
//| Deletes a pending order                                          | 
//+------------------------------------------------------------------+
bool DeletePendingOrder(ulong ticket)
  {

//--- If a pending order failed to get deleted, print a relevant message
   if(!trade.OrderDelete(ticket))
     {
      Print("Error when deleting a pending order: ",GetLastError()," - ",ErrorDescription(GetLastError()));
      return(false);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Modifies StopLoss of the position as related to the pending order|
//+------------------------------------------------------------------+
void CorrectStopLossByOrder(int             symbol_number, // Symbol number
                            double          price,         // Order Price
                            ENUM_ORDER_TYPE type)          // Order Type
  {
//--- If Stop Loss disabled, exit
   if(StopLoss[symbol_number]==0)
      return;
//--- If Stop Loss enabled
   double new_sl=0.0; // New Stop Loss value
//--- Get a Point value
   GetSymbolProperties(symbol_number,S_POINT);
//--- Number of decimal places
   GetSymbolProperties(symbol_number,S_DIGITS);
//--- Get Take Profit of position
   GetPositionProperties(symbol_number,P_TP);
//--- Calculate as related to the order type
   switch(type)
     {
      case ORDER_TYPE_BUY_STOP  :
         new_sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         break;
      case ORDER_TYPE_SELL_STOP :
         new_sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         break;
     }
//--- Modify the position
   if(!trade.PositionModify(Symbols[symbol_number],new_sl,pos.tp))
      Print("Error when modifying position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }

Prima di effettuare un ordine in sospeso, è inoltre necessario verificare se esiste già un ordine in sospeso con gli stessi commenti. Come menzionato all'inizio di questo articolo, inseriremo l'ordine Buy Stop principale con un commento "top_order" e l'ordine Sell Stop con un commento "bottom_order". Per facilitare tale controllo scriviamo una funzione chiamata CheckPendingOrderByComment():

//+------------------------------------------------------------------+
//| Checks existence of a pending order by a comment                 |
//+------------------------------------------------------------------+
bool CheckPendingOrderByComment(int symbol_number,string comment)
  {
   int    total_orders  =0;  // Total number of pending orders
   string order_symbol  =""; // Order Symbol
   string order_comment =""; // Order Comment
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //---Select the order by the ticket
      if(OrderGetTicket(i)>0)
        {
         //--- Get the symbol name
         order_symbol=OrderGetString(ORDER_SYMBOL);
         //--- If the symbols are equal
         if(order_symbol==Symbols[symbol_number])
           {
            //--- Get the order comment
            order_comment=OrderGetString(ORDER_COMMENT);
            //--- If the comments are equal
            if(order_comment==comment)
               return(true);
           }
        }
     }
//--- Order with a specified comment not found
   return(false);
  }

Il codice sopra mostra che il numero totale di ordini può essere ottenuto utilizzando la funzione di sistema OrdersTotal(). Tuttavia, per ottenere il numero totale di ordini in sospeso per un simbolo specificato, scriveremo una funzione definita dall'utente. Lo chiameremo OrdersTotalBySymbol():

//+------------------------------------------------------------------+
//| Returns the total number of orders for the specified symbol      |
//+------------------------------------------------------------------+
int OrdersTotalBySymbol(string symbol)
  {
   int   count        =0; // Order counter
   int   total_orders =0; // Total number of pending orders
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total number of orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If an order has been selected
      if(OrderGetTicket(i)>0)
        {
         //--- Get the order symbol
         GetOrderProperties(O_SYMBOL);
         //--- If the order symbol and the specified symbol are equal
         if(ord.symbol==symbol)
            //--- Increase the counter
            count++;
        }
     }
//--- Return the total number of orders
   return(count);
  }

Prima di piazzare un ordine in sospeso è necessario calcolare un prezzo per esso, nonché i livelli di Stop Loss e Take Profit, se necessario. Se la modalità di inversione è abilitata, avremo bisogno di funzioni definite dall'utente separate per ricalcolare e modificare i livelli di Trailing Stop.

Per calcolare il prezzo di un ordine in sospeso scriviamo la funzione CalculatePendingOrder():

//+------------------------------------------------------------------+
//| Calculates the pending order level(price)                        |
//+------------------------------------------------------------------+
double CalculatePendingOrder(int symbol_number,ENUM_ORDER_TYPE order_type)
  {
//--- For the calculated pending order value
   double price=0.0;
//--- If the value for SELL STOP order is to be calculated
   if(order_type==ORDER_TYPE_SELL_STOP)
     {
      //--- Calculate level
      price=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- Return calculated value if it is less than the lower limit of Stops level
      //    If the value is equal or greater, return the adjusted value
      return(price<symb.down_level ? price : symb.down_level-symb.offset);
     }
//--- If the value for BUY STOP order is to be calculated
   if(order_type==ORDER_TYPE_BUY_STOP)
     {
      //--- Calculate level
      price=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- Return the calculated value if it is greater than the upper limit of Stops level
      //    If the value is equal or less, return the adjusted value
      return(price>symb.up_level ? price : symb.up_level+symb.offset);
     }
//---
   return(0.0);
  }

Di seguito è riportato il codice funzione per il calcolo dei livelli di Stop Loss e Take Profit in un ordine in sospeso.

//+------------------------------------------------------------------+
//| Calculates Stop Loss level for a pending order                   |
//+------------------------------------------------------------------+
double CalculatePendingOrderStopLoss(int symbol_number,ENUM_ORDER_TYPE order_type,double price)
  {
//--- If Stop Loss is required
   if(StopLoss[symbol_number]>0)
     {
      double sl         =0.0; // For the Stop Loss calculated value
      double up_level   =0.0; // Upper limit of Stop Levels
      double down_level =0.0; // Lower limit of Stop Levels
      //--- If the value for BUY STOP order is to be calculated
      if(order_type==ORDER_TYPE_BUY_STOP)
        {
         //--- Define lower threshold
         down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits);
         //--- Calculate level
         sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is less than the lower limit of Stop level
         //    If the value is equal or greater, return the adjusted value
         return(sl<down_level ? sl : NormalizeDouble(down_level-symb.offset,symb.digits));
        }
      //--- If the value for the SELL STOP order is to be calculated
      if(order_type==ORDER_TYPE_SELL_STOP)
        {
         //--- Define the upper threshold
         up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is greater than the upper limit of the Stops level
         //    If the value is less or equal, return the adjusted value.
         return(sl>up_level ? sl : NormalizeDouble(up_level+symb.offset,symb.digits));
        }
     }
//---
   return(0.0);
  }
//+------------------------------------------------------------------+
//| Calculates the Take Profit level for a pending order             |
//+------------------------------------------------------------------+
double CalculatePendingOrderTakeProfit(int symbol_number,ENUM_ORDER_TYPE order_type,double price)
  {
//--- If Take Profit is required
   if(TakeProfit[symbol_number]>0)
     {
      double tp         =0.0; // For the calculated Take Profit value
      double up_level   =0.0; // Upper limit of Stop Levels
      double down_level =0.0; // Lower limit of Stop Levels
      //--- If the value for SELL STOP order is to be calculated
      if(order_type==ORDER_TYPE_SELL_STOP)
        {
         //--- Define lower threshold
         down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         tp=NormalizeDouble(price-CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is less than the below limit of the Stops level
         //    If the value is greater or equal, return the adjusted value
         return(tp<down_level ? tp : NormalizeDouble(down_level-symb.offset,symb.digits));
        }
      //--- If the value for the BUY STOP order is to be calculated
      if(order_type==ORDER_TYPE_BUY_STOP)
        {
         //--- Define the upper threshold
         up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         tp=NormalizeDouble(price+CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is greater than the upper limit of the Stops level
         //    If the value is less or equal, return the adjusted value
         return(tp>up_level ? tp : NormalizeDouble(up_level+symb.offset,symb.digits));
        }
     }
//---
   return(0.0);
  }

Per calcolare il livello di Stop (prezzo) di un ordine pendente invertito e tirarlo su, scriveremo le seguenti funzioni CalculateReverseOrderTrailingStop() e ModifyPendingOrderTrailingStop(). Di seguito puoi trovare i codici delle funzioni.

Il codice della funzione CalculateReverseOrderTrailingStop():

//+----------------------------------------------------------------------------+
//| Calculates the Trailing Stop level for the reversed order                  |
//+----------------------------------------------------------------------------+
double CalculateReverseOrderTrailingStop(int symbol_number,ENUM_POSITION_TYPE position_type)
  {
//--- Variables for calculation
   double    level       =0.0;
   double    buy_point   =low[symbol_number].value[1];  // Low value for Buy
   double    sell_point  =high[symbol_number].value[1]; // High value for Sell
//--- Calculate the level for the BUY position
   if(position_type==POSITION_TYPE_BUY)
     {
      //--- Bar's low minus the specified number of points
      level=NormalizeDouble(buy_point-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //---  If the calculated level is lower than the lower limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level<symb.down_level)
         return(level);
      //--- If it is not lower, try to calculate based on the bid price
      else
        {
         level=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
         //--- If the calculated level is lower than the limit, return the current value of the level
         //    otherwise set the nearest possible value
         return(level<symb.down_level ? level : symb.down_level-symb.offset);
        }
     }
//--- Calculate the level for the SELL position
   if(position_type==POSITION_TYPE_SELL)
     {
      // Bar's high plus the specified number of points
      level=NormalizeDouble(sell_point+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- If the calculated level is higher than the upper limit of the Stops level, 
      //    then the calculation is complete, return the current value of the level
      if(level>symb.up_level)
         return(level);
      //--- If it is not higher, try to calculate based on the ask price
      else
        {
         level=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
         //--- If the calculated level is higher than the limit, return the current value of the level
         //    Otherwise set the nearest possible value
         return(level>symb.up_level ? level : symb.up_level+symb.offset);
        }
     }
//---
   return(0.0);
  }

Il codice della funzione ModifyPendingOrderTrailingStop():

//+------------------------------------------------------------------+
//| Modifying the Trailing Stop level for a pending order            |
//+------------------------------------------------------------------+
void ModifyPendingOrderTrailingStop(int symbol_number)
  {
//--- Exit, if the reverse position mode is disabled and Trailing Stop is not set
   if(!Reverse[symbol_number] || TrailingStop[symbol_number]==0)
      return;
//--- 
   double          new_level              =0.0;         // For calculating a new level for a pending order
   bool            condition              =false;       // For checking the modification condition
   int             total_orders           =0;           // Total number of pending orders
   ulong           order_ticket           =0;           // Order ticket
   string          opposite_order_comment ="";          // Opposite order comment
   ENUM_ORDER_TYPE opposite_order_type    =WRONG_VALUE; // Order type

//--- Get the flag of presence/absence of a position
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If a position is absent
   if(!pos.exists)
      return;
//--- Get a total number of pending orders
   total_orders=OrdersTotal();
//--- Get the symbol properties
   GetSymbolProperties(symbol_number,S_ALL);
//--- Get the position properties
   GetPositionProperties(symbol_number,P_ALL);
//--- Get the level for Stop Loss
   new_level=CalculateReverseOrderTrailingStop(symbol_number,pos.type);
//--- Loop through the orders from the last to the first one
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If the order selected
      if((order_ticket=OrderGetTicket(i))>0)
        {
         //--- Get the order symbol
         GetPendingOrderProperties(O_SYMBOL);
         //--- Get the order comment
         GetPendingOrderProperties(O_COMMENT);
         //--- Get the order price
         GetPendingOrderProperties(O_PRICE_OPEN);
         //--- Depending on the position type, check the relevant condition for the Trailing Stop modification
         switch(pos.type)
           {
            case POSITION_TYPE_BUY  :
               //---If the new order value is greater than the current value plus set step then condition fulfilled 
               condition=new_level>ord.price_open+CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point);
               //--- Define the type and comment of the reversed pending order for check.
               opposite_order_type    =ORDER_TYPE_SELL_STOP;
               opposite_order_comment =comment_bottom_order;
               break;
            case POSITION_TYPE_SELL :
               //--- If the new value for the order if less than the current value minus a set step then condition fulfilled
               condition=new_level<ord.price_open-CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point);
               //--- Define the type and comment of the reversed pending order for check
               opposite_order_type    =ORDER_TYPE_BUY_STOP;
               opposite_order_comment =comment_top_order;
               break;
           }
         //--- If condition fulfilled, the order symbol and positions are equal
         //    and order comment and the reversed order comment are equal
         if(condition && 
            ord.symbol==Symbols[symbol_number] && 
            ord.comment==opposite_order_comment)
           {
            double sl=0.0; // Stop Loss
            double tp=0.0; // Take Profit
            //--- Get Take Profit and Stop Loss levels
            sl=CalculatePendingOrderStopLoss(symbol_number,opposite_order_type,new_level);
            tp=CalculatePendingOrderTakeProfit(symbol_number,opposite_order_type,new_level);
            //--- Modify order
            ModifyPendingOrder(symbol_number,order_ticket,opposite_order_type,new_level,sl,tp,
                               ORDER_TIME_GTC,ord.time_expiration,ord.price_stoplimit,ord.comment,0);
            return;
           }
        }
     }
  }

A volte può essere necessario scoprire se una posizione è stata chiusa allo Stop Loss o Take Profit. In questo caso particolare ci imbatteremo in tale requisito. Quindi scriviamo le funzioni che identificheranno questo evento dall'ultimo commento dell'affare. Per recuperare l'ultimo commento sull'operazione per un simbolo specificato, scriveremo una funzione separata denominata GetLastDealComment():

//+------------------------------------------------------------------+
//| Returns a the last deal comment for a specified symbol           |
//+------------------------------------------------------------------+
string GetLastDealComment(int symbol_number)
  {
   int    total_deals  =0;  // Total number of deals in the selected history
   string deal_symbol  =""; // Deal symbol 
   string deal_comment =""; // Deal comment
//--- If the deals history retrieved
   if(HistorySelect(0,TimeCurrent()))
     {
      //--- Receive the number of deals in the retrieved list
      total_deals=HistoryDealsTotal();
      //--- Loop though the total number of deals in the retrieved list from the last deal to the first one.
      for(int i=total_deals-1; i>=0; i--)
        {
         //--- Receive the deal comment
         deal_comment=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_COMMENT);
         //--- Receive the deal symbol
         deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL);
         //--- If the deal symbol and the current symbol are equal, stop the loop
         if(deal_symbol==Symbols[symbol_number])
            break;
        }
     }
//---
   return(deal_comment);
  }

Ora è facile scrivere funzioni che determineranno il motivo della chiusura dell'ultima posizione per il simbolo specificato. Di seguito i codici delle funzioni IsClosedByTakeProfit() e IsClosedByStopLoss():

//+------------------------------------------------------------------+
//| Returns the reason for closing position at Take Profit           |
//+------------------------------------------------------------------+
bool IsClosedByTakeProfit(int symbol_number)
  {
   string last_comment="";
//--- Get the last deal comment for the specified symbol
   last_comment=GetLastDealComment(symbol_number);
//--- If the comment contain a string "tp"
   if(StringFind(last_comment,"tp",0)>-1)
      return(true);
//--- If the comment does not contain a string "tp"
   return(false);
  }
//+------------------------------------------------------------------+
//| Returns the reason for closing position at Stop Loss             |
//+------------------------------------------------------------------+
bool IsClosedByStopLoss(int symbol_number)
  {
   string last_comment="";
//--- Get the last deal comment for the specified symbol
   last_comment=GetLastDealComment(symbol_number);
//--- If the comment contains the string "sl"
   if(StringFind(last_comment,"sl",0)>-1)
      return(true);
//--- If the comment does not contain the string "sl"
   return(false);
  }

Effettueremo un altro controllo per determinare se l'ultimo affare nella cronologia è veramente un affare per il simbolo specificato. Vogliamo mantenere in memoria il biglietto dell'ultimo affare. Per ottenere ciò, aggiungeremo un array sull'ambito globale:

//--- Array for checking the ticket of the last deal for each symbol.
ulong last_deal_ticket[NUMBER_OF_SYMBOLS];

La funzione IsLastDealTicket() per il controllo dell'ultimo ticket di vendita apparirà come mostrato nel codice seguente:

//+------------------------------------------------------------------+
//| Returns the event of the last deal for the specified symbol      |
//+------------------------------------------------------------------+
bool IsLastDealTicket(int symbol_number)
  {
   int    total_deals =0;  // Total number of deals in the selected history list
   string deal_symbol =""; // Deal symbol
   ulong  deal_ticket =0;  // Deal ticket
//--- If the deal history was received
   if(HistorySelect(0,TimeCurrent()))
     {
      //--- Get the total number of deals in the received list
      total_deals=HistoryDealsTotal();
      //--- Loop through the total number of deals from the last deal to the first one
      for(int i=total_deals-1; i>=0; i--)
        {
         //--- Get deal ticket
         deal_ticket=HistoryDealGetTicket(i);
         //--- Get deal symbol
         deal_symbol=HistoryDealGetString(deal_ticket,DEAL_SYMBOL);
         //--- If deal symbol and the current one are equal, stop the loop
         if(deal_symbol==Symbols[symbol_number])
           {
            //--- If the tickets are equal, exit
            if(deal_ticket==last_deal_ticket[symbol_number])
               return(false);
            //--- If the tickets are not equal report it
            else
              {
               //--- Save the last deal ticket
               last_deal_ticket[symbol_number]=deal_ticket;
               return(true);
              }
           }
        }
     }
//---
   return(false);
  }

Se l'ora corrente è al di fuori dell'intervallo di negoziazione specificato, la posizione sarà forzata a chiudersi, indipendentemente dal fatto che sia in perdita o in profitto. Scriviamo la funzione ClosePosition() per chiudere una posizione:

//+------------------------------------------------------------------+
//| Closes position                                                  |
//+------------------------------------------------------------------+
void ClosePosition(int symbol_number)
  {
//--- Check if position exists  
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If there is no position, exit
   if(!pos.exists)
      return;
//--- Set the slippage value in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation));
//--- If the position was not closed, print the relevant message
   if(!trade.PositionClose(Symbols[symbol_number]))
      Print("Error when closing position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }

Quando una posizione viene chiusa al di fuori dell'intervallo di tempo di negoziazione, tutti gli ordini in sospeso devono essere eliminati. La funzione DeleteAllPendingOrders() che stiamo per scrivere cancellerà tutti gli ordini in sospeso per il simbolo specificato:

//+------------------------------------------------------------------+
//| Deletes all pending orders                                       |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders(int symbol_number)
  {
   int   total_orders =0; // Total number of pending orders
   ulong order_ticket =0; // Order ticket
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total number of pending orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If the order selected
      if((order_ticket=OrderGetTicket(i))>0)
        {
         //--- Get the order symbol
         GetOrderProperties(O_SYMBOL);
         //--- If the order symbol and the current symbol are equal
         if(ord.symbol==Symbols[symbol_number])
            //--- Delete the order
            DeletePendingOrder(order_ticket);
        }
     }
  }

Quindi ora abbiamo tutte le funzioni necessarie per lo schema strutturale. Diamo un'occhiata alla familiare funzione TradingBlock(), che ha subito alcune modifiche significative e una nuova per la gestione degli ordini in sospeso ManagePendingOrders(). In esso verrà effettuato il pieno controllo della situazione attuale relativa agli ordini in sospeso.

La funzione TradingBlock() per il modello corrente ha il seguente aspetto:

//+------------------------------------------------------------------+
//| Trade block                                                      |
//+------------------------------------------------------------------+
void TradingBlock(int symbol_number)
  {
   double          tp=0.0;                 // Take Profit
   double          sl=0.0;                 // Stop Loss
   double          lot=0.0;                // Volume for position calculation in case of reversed position
   double          order_price=0.0;        // Price for placing the order
   ENUM_ORDER_TYPE order_type=WRONG_VALUE; // Order type for opening position
//--- If outside of the time range for placing pending orders
   if(!IsInOpenOrdersTimeRange(symbol_number))
      return;
//--- Find out if there is an open position for the symbol
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If there is no position
   if(!pos.exists)
     {
      //--- Get symbol properties
      GetSymbolProperties(symbol_number,S_ALL);
      //--- Adjust the volume
      lot=CalculateLot(symbol_number,Lot[symbol_number]);
      //--- If there is no upper pending order
      if(!CheckPendingOrderByComment(symbol_number,comment_top_order))
        {
         //--- Get the price for placing a pending order
         order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_BUY_STOP);
         //--- Get Take Profit and Stop Loss levels
         sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_BUY_STOP,order_price);
         tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_BUY_STOP,order_price);
         //--- Place a pending order
         SetPendingOrder(symbol_number,ORDER_TYPE_BUY_STOP,lot,0,order_price,sl,tp,ORDER_TIME_GTC,comment_top_order);
        }
      //--- If there is no lower pending order
      if(!CheckPendingOrderByComment(symbol_number,comment_bottom_order))
        {
         //--- Get the price for placing the pending order
         order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_SELL_STOP);
         //--- Get Take Profit and Stop Loss levels
         sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_SELL_STOP,order_price);
         tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_SELL_STOP,order_price);
         //--- Place a pending order
         SetPendingOrder(symbol_number,ORDER_TYPE_SELL_STOP,lot,0,order_price,sl,tp,ORDER_TIME_GTC,comment_bottom_order);
        }
     }
  }

Codice della funzione ManagePendingOrders() per la gestione degli ordini pendenti:

//+------------------------------------------------------------------+
//| Manages pending orders                                           |
//+------------------------------------------------------------------+
void ManagePendingOrders()
  {
//--- Loop through the total number of symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading this symbol is forbidden, go to the following one
      if(Symbols[s]=="")
         continue;
      //--- Find out if there is an open position for the symbol
      pos.exists=PositionSelect(Symbols[s]);
      //--- If there is no position
      if(!pos.exists)
        {
         //--- If the last deal on current symbol and
         //    position  was exited on Take Profit or Stop Loss
         if(IsLastDealTicket(s) && 
            (IsClosedByStopLoss(s) || IsClosedByTakeProfit(s)))
            //--- Delete all pending orders for the symbol
            DeleteAllPendingOrders(s);
         //--- Go to the following symbol
         continue;
        }
      //--- If there is a position
      ulong           order_ticket           =0;           // Order ticket
      int             total_orders           =0;           // Total number of pending orders
      int             symbol_total_orders    =0;           // Number of pending orders for the specified symbol
      string          opposite_order_comment ="";          // Opposite order comment
      ENUM_ORDER_TYPE opposite_order_type    =WRONG_VALUE; // Order type
      //--- Get the total number of pending orders
      total_orders=OrdersTotal();
      //--- Get the total number of pending orders for the specified symbol
      symbol_total_orders=OrdersTotalBySymbol(Symbols[s]);
      //--- Get symbol properties
      GetSymbolProperties(s,S_ASK);
      GetSymbolProperties(s,S_BID);
      //--- Get the comment for the selected position
      GetPositionProperties(s,P_COMMENT);
      //--- If the position comment belongs to the upper order,
      //    then the lower order is to be deleted, modified/placed
      if(pos.comment==comment_top_order)
        {
         opposite_order_type    =ORDER_TYPE_SELL_STOP;
         opposite_order_comment =comment_bottom_order;
        }
      //--- If the position comment belongs to the lower order,
      //    then the upper order is to be deleted/modified/placed
      if(pos.comment==comment_bottom_order)
        {
         opposite_order_type    =ORDER_TYPE_BUY_STOP;
         opposite_order_comment =comment_top_order;
        }
      //--- If there are no pending orders for the specified symbol
      if(symbol_total_orders==0)
        {
         //--- If the position reversal is enabled, place a reversed order
         if(Reverse[s])
           {
            double tp=0.0;          // Take Profit
            double sl=0.0;          // Stop Loss
            double lot=0.0;         // Volume for position calculation in case of reversed positio
            double order_price=0.0; // Price for placing the order
            //--- Get the price for placing a pending order
            order_price=CalculatePendingOrder(s,opposite_order_type);
            //---Get Take Profit и Stop Loss levels
            sl=CalculatePendingOrderStopLoss(s,opposite_order_type,order_price);
            tp=CalculatePendingOrderTakeProfit(s,opposite_order_type,order_price);
            //--- Calculate double volume
            lot=CalculateLot(s,pos.volume*2);
            //--- Place the pending order
            SetPendingOrder(s,opposite_order_type,lot,0,order_price,sl,tp,ORDER_TIME_GTC,opposite_order_comment);
            //--- Adjust Stop Loss as related to the order
            CorrectStopLossByOrder(s,order_price,opposite_order_type);
           }
         return;
        }
      //--- If there are pending orders for this symbol, then depending on the circumstances delete or
      //    modify the reversed order
      if(symbol_total_orders>0)
        {
         //--- Loop through the total number of orders from the last one to the first one
         for(int i=total_orders-1; i>=0; i--)
           {
            //--- If the order chosen
            if((order_ticket=OrderGetTicket(i))>0)
              {
               //--- Get the order symbol
               GetPendingOrderProperties(O_SYMBOL);
               //--- Get the order comment
               GetPendingOrderProperties(O_COMMENT);
               //--- If order symbol and position symbol are equal,
               //    and order comment and the reversed order comment are equal
               if(ord.symbol==Symbols[s] && 
                  ord.comment==opposite_order_comment)
                 {
                  //--- If position reversal is disabled
                  if(!Reverse[s])
                     //--- Delete order
                     DeletePendingOrder(order_ticket);
                  //--- If position reversal is enabled
                  else
                    {
                     double lot=0.0;
                     //--- Get the current order properties
                     GetPendingOrderProperties(O_ALL);
                     //--- Get the current position volume
                     GetPositionProperties(s,P_VOLUME);
                     //--- If the order has been modified already, exit the loop.
                     if(ord.volume_initial>pos.volume)
                        break;
                     //--- Calculate double volume
                     lot=CalculateLot(s,pos.volume*2);
                     //--- Modify (delete and place again) the order
                     ModifyPendingOrder(s,order_ticket,opposite_order_type,
                                        ord.price_open,ord.sl,ord.tp,
                                        ORDER_TIME_GTC,ord.time_expiration,
                                        ord.price_stoplimit,opposite_order_comment,lot);
                    }
                 }
              }
           }
        }
     }
  }

Ora dobbiamo solo apportare piccole modifiche al file del programma principale. Aggiungeremo il gestore di eventi commerciali OnTrade(). In tale funzione verrà effettuata la valutazione della situazione attuale in relazione agli ordini pendenti a fronte dell'evento di negoziazione.

//+------------------------------------------------------------------+
//| Processing of trade events                                       |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Check the state of pending orders
   ManagePendingOrders();
  }

La funzione ManagePendingOrders() verrà utilizzata anche nel gestore eventi utente OnChartEvent():

//+------------------------------------------------------------------+
//| User events and chart events handler                             |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // Event identifier
                  const long &lparam,   // Parameter of long event type
                  const double &dparam, // Parameter of double event type
                  const string &sparam) // Parameter of string event type
  {
//--- If it is a user event
   if(id>=CHARTEVENT_CUSTOM)
     {
      //--- Exit, if trade is prohibited
      if(CheckTradingPermission()>0)
         return;
      //--- If it is a tick event
      if(lparam==CHARTEVENT_TICK)
        {
         //--- Check the state of pending orders
         ManagePendingOrders();
         //--- Check signals and trade according to them
         CheckSignalsAndTrade();
         return;
        }
     }
  }

Sono state apportate alcune modifiche anche alla funzione CheckSignalsAndTrade(). Nel codice sottostante sono evidenziate le stringhe con le nuove funzioni considerate in questo articolo.

//+------------------------------------------------------------------+
//| Checks signals and trades based on New Bar event                 |
//+------------------------------------------------------------------+
void CheckSignalsAndTrade()
  {
//--- Loop through all specified signals
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading this symbol is prohibited, exit
      if(Symbols[s]=="")
         continue;
      //--- If the bar is not new, move on to the following symbol
      if(!CheckNewBar(s))
         continue;
      //--- If there is a new bar
      else
        {
         //--- If outside the time range
         if(!IsInTradeTimeRange(s))
           {
            //--- Close position
            ClosePosition(s);
            //--- Delete all pending orders
            DeleteAllPendingOrders(s);
            //--- Move on to the following symbol
            continue;
           }
         //--- Get bars data
         GetBarsData(s);
         //--- Check conditions and trade
         TradingBlock(s);
         //--- If position reversal if enabled
         if(Reverse[s])
            //--- Pull up Stop Loss for pending order
            ModifyPendingOrderTrailingStop(s);
         //--- If position reversal is disabled
         else
         //--- Pull up Stop Loss
            ModifyTrailingStop(s);
        }
     }
  

Ora è tutto pronto e possiamo provare ad ottimizzare i parametri di questo Expert Advisor multivaluta Impostiamo il Tester di strategia come mostrato di seguito:

Fig. 1 - Impostazioni del tester per l'ottimizzazione dei parametri.

Fig. 1 - Impostazioni del tester per l'ottimizzazione dei parametri.

Prima ottimizzeremo i parametri per la coppia di valute EURUSD e poi per AUDUSD. La schermata seguente mostra quali parametri selezioneremo per l'ottimizzazione di EURUSD:

Fig. 2 - Impostazione dei parametri per l'ottimizzazione di Expert Advisor multivaluta

Fig. 2 - Impostazione dei parametri per l'ottimizzazione di Expert Advisor multivaluta

Dopo che i parametri della coppia di valute EURUSD sono stati ottimizzati, gli stessi parametri dovrebbero essere ottimizzati per AUDUSD. Di seguito è riportato il risultato per entrambi i simboli testati insieme. I risultati sono stati selezionati dal fattore di recupero massimo. Per il test, il valore del lotto è stato impostato su 1 per entrambi i simboli.

Fig. 3 - risultato del test per i due simboli insieme.

Fig. 3 - risultato del test per i due simboli insieme.


Conclusione

Questo è praticamente tutto. Con le funzioni pronte a portata di mano, puoi concentrarti sullo sviluppo dell'idea di prendere decisioni commerciali. In questo caso le modifiche dovranno essere implementate nelle funzioni TradingBlock() e ManagePendingOrders(). Per coloro che hanno iniziato a imparare MQL5 di recente, consigliamo di esercitarsi nell'aggiunta di più simboli e di modificare lo schema dell'algoritmo commerciale.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/755

File allegati |
755.set (1.59 KB)
Indicatore per la creazione di grafici Renko Indicatore per la creazione di grafici Renko
L'articolo descrive un esempio di creazione di grafici Renko e la sua implementazione in MQL5 come indicatore. Le modifiche di questo indicatore lo distinguono da un grafico classico. Può essere costruito sia nella finestra dell'indicatore che sul grafico principale. Inoltre, c'è l'indicatore ZigZag. Puoi trovare alcuni esempi dell'implementazione del grafico.
Manuale MQL5: Sviluppo di un indicatore multi-simbolo per l’analisi della divergenza dei prezzi Manuale MQL5: Sviluppo di un indicatore multi-simbolo per l’analisi della divergenza dei prezzi
In questo articolo, considereremo lo sviluppo di un indicatore multi-simbolo per analizzare la divergenza dei prezzi in un determinato periodo di tempo. Gli argomenti principali sono già stati discussi nel precedente articolo sulla programmazione degli indicatori multi-valuta "MQL5 Cookbook: Sviluppo di un indicatore di volatilità multi-simbolo in MQL5". Quindi questa volta ci soffermeremo solo su quelle nuove caratteristiche e funzioni che sono state cambiate radicalmente. Se sei un neofita della programmazione di indicatori multi-valuta, ti consiglio di leggere prima l'articolo precedente.
Lavorare con il modem GSM da un Expert Advisor MQL5 Lavorare con il modem GSM da un Expert Advisor MQL5
Attualmente esistono un discreto numero di mezzi per un comodo monitoraggio remoto di un conto di trading: terminali mobili, notifiche push, lavoro con ICQ. Ma tutte richiedono una connessione a Internet. Questo articolo descrive il processo di creazione di un Expert Advisor che ti consentirà di rimanere in contatto con il tuo terminale di trading anche quando Internet mobile non è disponibile, tramite chiamate e messaggi di testo.
Forum sulla programmazione MQL5 Liste Forum sulla programmazione MQL5 Liste
La nuova versione del linguaggio di programmazione per lo sviluppo di strategie di trading, MQL [MQL5], fornisce funzionalità più potenti ed efficaci rispetto alla versione precedente [MQL4]. Il vantaggio risiede essenzialmente nelle funzionalità di programmazione orientata agli oggetti. Questo articolo esamina la possibilità di utilizzare tipi di dati personalizzati complessi, come nodi ed elenchi. Fornisce inoltre un esempio di utilizzo delle liste nella programmazione pratica in MQL5.