English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Manuale MQL5: Expert Advisor multivaluta: approccio semplice, accurato e rapido

Manuale MQL5: Expert Advisor multivaluta: approccio semplice, accurato e rapido

MetaTrader 5Esempi | 11 gennaio 2022, 16:46
231 0
Anatoli Kazharski
Anatoli Kazharski

Introduzione

Questo articolo descriverà l'implementazione di un approccio semplice, adatto a un Expert Advisor multivaluta. Ciò significa che sarai in grado di impostare l'Expert Advisor per testare/tradare in condizioni identiche ma con parametri diversi per ogni simbolo. Ad esempio creeremo un pattern per due simboli ma in modo tale da poter aggiungere ulteriori simboli, se necessario, apportando piccole modifiche al codice.

Un modello multi-valuta può essere implementato in MQL5 in diversi modi:

  • Possiamo utilizzare uno schema in cui un Expert Advisor è guidato dal tempo, essendo in grado di eseguire controlli più accurati agli intervalli di tempo specificati nelle OnTimer().

  • In alternativa, come in tutti gli Expert Advisor introdotti nei precedenti articoli della serie, la verifica può essere effettuata nelle OnTick() nel qual caso l'Expert Advisor dipenderà dai tick per il simbolo corrente sul quale lavora. Quindi, se c'è una barra completata su un altro simbolo, mentre non c'è ancora un segno di spunta per il simbolo corrente, l'Expert Advisor eseguirà un controllo solo una volta che c'è un nuovo segno di spunta per il simbolo corrente.

  • C'è ancora un'altra opzione interessante suggerita dal suo autore Konstantin Gruzdev (Lizar). Impiega un modello di eventi: utilizzando le OnChartEvent(), un Expert Advisor ottiene eventi che vengono riprodotti da agenti indicatori situati sui grafici dei simboli coinvolti nel test/trading. Gli agenti indicatori possono riprodurre nuovi eventi barra e segno di spunta dei simboli a cui sono collegati. Questo tipo di indicatore (EventsSpy.mq5) può essere scaricato alla fine dell'articolo. Ne avremo bisogno per il funzionamento dell'Expert Advisor.


Sviluppo di Expert Advisor

L'Expert Advisor presentato nell'articolo "MQL5 Cookbook: Utilizzo degli indicatori per impostare le condizioni di trading in Expert Advisor" servirà come modello. Ho già cancellato da esso tutto ciò che aveva a che fare con il pannello informativo e ho anche semplificato la condizione di apertura della posizione come implementato nel precedente articolo intitolato "Ricettario MQL5: Sviluppo di un framework per un sistema di trading basato sulla strategia a triplo schermo". Poiché intendiamo creare un Expert Advisor per due simboli, ognuno di essi avrà bisogno del proprio set di parametri esterni:

//--- 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  int    IndicatorPeriod_01    = 5;        // |     Indicator period
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
input  double VolumeIncrease_01     = 0.1;      // |     Position volume increase
input  double VolumeIncreaseStep_01 = 10;       // |     Volume increase step
//---
sinput string delimeter_01=""; // --------------------------------
sinput string Symbol_02             = "NZDUSD"; // Symbol 2
input  int    IndicatorPeriod_02    = 5;        // |     Indicator period
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
input  double VolumeIncrease_02     = 0.1;      // |     Position volume increase
input  double VolumeIncreaseStep_02 = 10;       // |     Volume increase step

I parametri esterni verranno inseriti in array le cui dimensioni dipenderanno dal numero di simboli utilizzati. Il numero di simboli utilizzati nell'Expert Advisor sarà determinato dal valore della costante NUMBER_OF_SYMBOLS che dobbiamo creare all'inizio del file:

//--- Number of traded symbols
#define NUMBER_OF_SYMBOLS 2
//--- Name of the Expert Advisor
#define EXPERT_NAME MQL5InfoString(MQL5_PROGRAM_NAME)

Creiamo gli array che saranno necessari per memorizzare i parametri esterni:

//--- Arrays for storing external parameters
string Symbols[NUMBER_OF_SYMBOLS];            // Symbol
int    IndicatorPeriod[NUMBER_OF_SYMBOLS];    // Indicator period
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
double VolumeIncrease[NUMBER_OF_SYMBOLS];     // Position volume increase
double VolumeIncreaseStep[NUMBER_OF_SYMBOLS]; // Volume increase step

Le funzioni di inizializzazione dell'array verranno inserite nel file include InitArrays.mqh. Per inizializzare l'array Symbols[], creeremo la funzione GetSymbol(). Otterrà il nome del simbolo dai parametri esterni e se tale simbolo è disponibile nell'elenco dei simboli sul server, verrà selezionato nella finestra Market Watch. Oppure, se sul server non è possibile trovare il simbolo richiesto, la funzione restituirà una stringa vuota e il Journal of Expert Advisors verrà aggiornato di conseguenza.

Di seguito è riportato il codice della funzione GetSymbol():

//+------------------------------------------------------------------+
//| Adding the specified symbol to the Market Watch window           |
//+------------------------------------------------------------------+
string GetSymbolByName(string symbol)
  {
   string symbol_name="";   // Symbol name on the server
//--- If an empty string is passed, return the empty string
   if(symbol=="")
      return("");
//--- Iterate over the list of all symbols on the server
   for(int s=0; s<SymbolsTotal(false); s++)
     {
      //--- Get the symbol name
      symbol_name=SymbolName(s,false);
      //--- If the required symbol is available on the server
      if(symbol==symbol_name)
        {
         //--- Select it in the Market Watch window
         SymbolSelect(symbol,true);
         //--- Return the symbol name
         return(symbol);
        }
     }
//--- If the required symbol cannot be found, return the empty string
   Print("The "+symbol+" symbol could not be found on the server!");
   return("");
  }

L'array Symbols[] verrà inizializzato nella funzione GetSymbols():

//+------------------------------------------------------------------+
//| Filling the array of symbols                                     |
//+------------------------------------------------------------------+
void GetSymbols()
  {
   Symbols[0]=GetSymbolByName(Symbol_01);
   Symbols[1]=GetSymbolByName(Symbol_02);
  }

Inoltre, lo implementeremo in modo tale che un valore vuoto nei parametri esterni di un determinato simbolo indichi che il blocco corrispondente non sarà coinvolto nel test/negoziazione. Ciò è necessario per poter ottimizzare i parametri per ciascun simbolo separatamente, escludendo completamente il resto.

Tutti gli altri array di parametri esterni vengono inizializzati allo stesso modo. In altre parole, dobbiamo creare una funzione separata per ogni array. I codici di tutte queste funzioni sono forniti di seguito:

//+------------------------------------------------------------------+
//| Filling the indicator period array                               |
//+------------------------------------------------------------------+
void GetIndicatorPeriod()
  {
   IndicatorPeriod[0]=IndicatorPeriod_01;
   IndicatorPeriod[1]=IndicatorPeriod_02;
  }
//+------------------------------------------------------------------+
//| Filling the Take Profit array                                    |
//+------------------------------------------------------------------+
void GetTakeProfit()
  {
   TakeProfit[0]=TakeProfit_01;
   TakeProfit[1]=TakeProfit_02;
  }
//+------------------------------------------------------------------+
//| Filling the Stop Loss array                                      |
//+------------------------------------------------------------------+
void GetStopLoss()
  {
   StopLoss[0]=StopLoss_01;
   StopLoss[1]=StopLoss_02;
  }
//+------------------------------------------------------------------+
//| Filling the Trailing Stop array                                  |
//+------------------------------------------------------------------+
void GetTrailingStop()
  {
   TrailingStop[0]=TrailingStop_01;
   TrailingStop[1]=TrailingStop_02;
  }
//+------------------------------------------------------------------+
//| Filling the Reverse array                                        |
//+------------------------------------------------------------------+
void GetReverse()
  {
   Reverse[0]=Reverse_01;
   Reverse[1]=Reverse_02;
  }
//+------------------------------------------------------------------+
//| Filling the Lot array                                            |
//+------------------------------------------------------------------+
void GetLot()
  {
   Lot[0]=Lot_01;
   Lot[1]=Lot_02;
  }
//+------------------------------------------------------------------+
//| Filling the VolumeIncrease array                                 |
//+------------------------------------------------------------------+
void GetVolumeIncrease()
  {
   VolumeIncrease[0]=VolumeIncrease_01;
   VolumeIncrease[1]=VolumeIncrease_02;
  }
//+------------------------------------------------------------------+
//| Filling the VolumeIncreaseStep array                             |
//+------------------------------------------------------------------+
void GetVolumeIncreaseStep()
  {
   VolumeIncreaseStep[0]=VolumeIncreaseStep_01;
   VolumeIncreaseStep[1]=VolumeIncreaseStep_02;
  }

Creiamo ora una funzione che ci aiuterà a inizializzare comodamente tutti gli array di parametri esterni contemporaneamente: la funzione InitializeInputParameters():

//+------------------------------------------------------------------+
//| Initializing external parameter arrays                           |
//+------------------------------------------------------------------+
void InitializeInputParameters()
  {
   GetSymbols();
   GetIndicatorPeriod();
   GetTakeProfit();
   GetStopLoss();
   GetTrailingStop();
   GetReverse();
   GetLot();
   GetVolumeIncrease();
   GetVolumeIncreaseStep();
  }

Dopo l'inizializzazione degli array di parametri esterni, possiamo procedere alla parte principale. Alcune procedure come ottenere le maniglie degli indicatori, i loro valori e le informazioni sui prezzi, così come il controllo della nuova barra, ecc., verranno eseguite in loop consecutivamente per ciascun simbolo. Questo è il motivo per cui i valori dei parametri esterni sono stati organizzati in array. Quindi tutto sarà fatto nei loop come segue:

//--- Iterate over all symbols
for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
  {
//--- If trading for this symbol is allowed
   if(Symbols[s]!="")
     {
      //--- The rest of the code
     }
  }

Ma prima di iniziare a modificare le funzioni esistenti e a crearne di nuove, creiamo anche gli array che saranno richiesti in quel modello.

Avremo bisogno di due array per gli handle degli indicatori:

//--- Array of indicator agent handles
int spy_indicator_handles[NUMBER_OF_SYMBOLS];
//--- Array of signal indicator handles
int signal_indicator_handles[NUMBER_OF_SYMBOLS];

Questi due array verranno prima inizializzati su valori non validi:

//+------------------------------------------------------------------+
//| Initializing arrays of indicator handles                         |
//+------------------------------------------------------------------+
void InitializeArrayHandles()
  {
   ArrayInitialize(spy_indicator_handles,INVALID_HANDLE);
   ArrayInitialize(signal_indicator_handles,INVALID_HANDLE);
  }

Ora sarà possibile accedere a matrici di dati sui prezzi e valori degli indicatori utilizzando Strutture e :

//--- Data arrays for checking trading conditions
struct PriceData
  {
   double            value[];
  };
PriceData open[NUMBER_OF_SYMBOLS];      // Opening price of the bar
PriceData high[NUMBER_OF_SYMBOLS];      // High price of the bar
PriceData low[NUMBER_OF_SYMBOLS];       // Low price of the bar
PriceData close[NUMBER_OF_SYMBOLS];     // Closing price of the bar
PriceData indicator[NUMBER_OF_SYMBOLS]; // Array of indicator values

Ora, se hai bisogno di ottenere il valore dell'indicatore sull'ultima barra completata del primo simbolo nell'elenco, dovresti scrivere qualcosa del genere:

double indicator_value=indicator[0].value[1];

Abbiamo anche bisogno di creare array invece delle variabili che sono state precedentemente utilizzate nella funzione CheckNewBar():

//--- Arrays for getting the opening time of the current bar
struct Datetime
  {
   datetime          time[];
  };
Datetime lastbar_time[NUMBER_OF_SYMBOLS];
//--- Array for checking the new bar for each symbol
datetime new_bar[NUMBER_OF_SYMBOLS];

Quindi abbiamo organizzato gli array. Ora dobbiamo modificare una serie di funzioni in base alle modifiche apportate sopra. Iniziamo con la funzione GetIndicatorHandles():

//+------------------------------------------------------------------+
//| Getting indicator handles                                        |
//+------------------------------------------------------------------+
void GetIndicatorHandles()
  {
//--- Iterate over all symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading for this symbol is allowed
      if(Symbols[s]!="")
        {
         //--- If the handle is yet to be obtained
         if(signal_indicator_handles[s]==INVALID_HANDLE)
           {
            //--- Get the indicator handle
            signal_indicator_handles[s]=iMA(Symbols[s],_Period,IndicatorPeriod[s],0,MODE_SMA,PRICE_CLOSE);
            //--- If the indicator handle could not be obtained
            if(signal_indicator_handles[s]==INVALID_HANDLE)
               Print("Failed to get the indicator handle for the symbol "+Symbols[s]+"!");
           }
        }
     }
  }

Ora, indipendentemente dal numero di simboli utilizzati nei test/trading, il codice della funzione rimarrà lo stesso.

Allo stesso modo, creeremo un'altra funzione, GetSpyHandles(), per ottenere handle di agenti indicatori che trasmetteranno tick da altri simboli. Ma prima, aggiungeremo un'ulteriore enumerazione di tutti gli eventi per simbolo, ENUM_CHART_EVENT_SYMBOL, disposti come flag nel file Enums.mqh:

//+------------------------------------------------------------------+
//| New bar and tick events from all symbols and time frames         |
//+------------------------------------------------------------------+
enum ENUM_CHART_EVENT_SYMBOL
  {
   CHARTEVENT_NO         = 0,          // Events are disabled - 0
   CHARTEVENT_INIT       = 0,          // Initialization event - 0
   //---
   CHARTEVENT_NEWBAR_M1  = 0x00000001, // New bar event on a minute chart (1)
   CHARTEVENT_NEWBAR_M2  = 0x00000002, // New bar event on a 2-minute chart (2)
   CHARTEVENT_NEWBAR_M3  = 0x00000004, // New bar event on a 3-minute chart (4)
   CHARTEVENT_NEWBAR_M4  = 0x00000008, // New bar event on a 4-minute chart (8)
   //---
   CHARTEVENT_NEWBAR_M5  = 0x00000010, // New bar event on a 5-minute chart (16)
   CHARTEVENT_NEWBAR_M6  = 0x00000020, // New bar event on a 6-minute chart (32)
   CHARTEVENT_NEWBAR_M10 = 0x00000040, // New bar event on a 10-minute chart (64)
   CHARTEVENT_NEWBAR_M12 = 0x00000080, // New bar event on a 12-minute chart (128)
   //---
   CHARTEVENT_NEWBAR_M15 = 0x00000100, // New bar event on a 15-minute chart (256)
   CHARTEVENT_NEWBAR_M20 = 0x00000200, // New bar event on a 20-minute chart (512)
   CHARTEVENT_NEWBAR_M30 = 0x00000400, // New bar event on a 30-minute chart (1024)
   CHARTEVENT_NEWBAR_H1  = 0x00000800, // New bar event on an hour chart (2048)
   //---
   CHARTEVENT_NEWBAR_H2  = 0x00001000, // New bar event on a 2-hour chart (4096)
   CHARTEVENT_NEWBAR_H3  = 0x00002000, // New bar event on a 3-hour chart (8192)
   CHARTEVENT_NEWBAR_H4  = 0x00004000, // New bar event on a 4-hour chart (16384)
   CHARTEVENT_NEWBAR_H6  = 0x00008000, // New bar event on a 6-hour chart (32768)
   //---
   CHARTEVENT_NEWBAR_H8  = 0x00010000, // New bar event on a 8-hour chart (65536)
   CHARTEVENT_NEWBAR_H12 = 0x00020000, // New bar event on a 12-hour chart (131072)
   CHARTEVENT_NEWBAR_D1  = 0x00040000, // New bar event on a daily chart (262144)
   CHARTEVENT_NEWBAR_W1  = 0x00080000, // New bar event on a weekly chart (524288)
   //---
   CHARTEVENT_NEWBAR_MN1 = 0x00100000, // New bar event on a monthly chart (1048576)
   CHARTEVENT_TICK       = 0x00200000, // New tick event (2097152)
   //---
   CHARTEVENT_ALL        = 0xFFFFFFFF  // All events are enabled (-1)
  };

Questa enumerazione è necessaria per lavorare con l'indicatore personalizzato EventsSpy.mq5 (il file è allegato all'articolo) nella funzione GetSpyHandles() il cui codice è fornito di seguito:

//+------------------------------------------------------------------+
//| Getting agent handles by the specified symbols                   |
//+------------------------------------------------------------------+
void GetSpyHandles()
  {
//--- Iterate over all symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading for this symbol is allowed
      if(Symbols[s]!="")
        {
         //--- If the handle is yet to be obtained
         if(spy_indicator_handles[s]==INVALID_HANDLE)
           {
            //--- Get the indicator handle
            spy_indicator_handles[s]=iCustom(Symbols[s],_Period,"EventsSpy.ex5",ChartID(),0,CHARTEVENT_TICK);
            //--- If the indicator handle could not be obtained
            if(spy_indicator_handles[s]==INVALID_HANDLE)
               Print("Failed to install the agent on "+Symbols[s]+"");
           }
        }
     }
  }

Si prega di notare l'ultimo parametro nella funzione iCustom(): in questo caso, l'identificatore CHARTEVENT_TICK è stato utilizzato per ottenere eventi tick. Ma se è necessario, può essere modificato per ottenere i nuovi eventi della barra. Ad esempio, se utilizzi la riga come mostrato di seguito, Expert Advisor otterrà nuovi eventi barra su intervalli di tempo di un minuto (M1) e un'ora (H1):

handle_event_indicator[s]=iCustom(Symbols[s],_Period,"EventsSpy.ex5",ChartID(),0,CHARTEVENT_NEWBAR_M1|CHARTEVENT_NEWBAR_H1);

Per ottenere tutti gli eventi (tick e bar events su tutti i time frame), è necessario specificare l'identificatore CHARTEVENT_ALL.

Tutti gli array sono inizializzati nelle OnInit():

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- Initialization of arrays of external parameters
   InitializeInputParameters();
//--- Initialization of arrays of indicator handles
   InitializeArrayHandles();
//--- Get agent handles
   GetSpyHandles();
//--- Get indicator handles
   GetIndicatorHandles();
//--- Initialize the new bar
   InitializeArrayNewBar();
  }

Come già accennato all'inizio dell'articolo, gli eventi dagli agenti indicatori vengono ricevuti nelle OnChartEvent(). Di seguito è riportato il codice che verrà utilizzato in questa funzione:

//+------------------------------------------------------------------+
//| Chart events handler                                             |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // Event identifier
                  const long &lparam,   // Long type event parameter
                  const double &dparam, // Double type event parameter
                  const string &sparam) // String type event parameter
  {
//--- If this is a custom event
   if(id>=CHARTEVENT_CUSTOM)
     {
      //--- Exit if trading is not allowed
      if(CheckTradingPermission()>0)
         return;
      //--- If there was a tick event
      if(lparam==CHARTEVENT_TICK)
        {
         //--- Check signals and trade on them
         CheckSignalsAndTrade();
         return;
        }
     }
  }

Nella funzione CheckSignalAndTrade() (la riga evidenziata nel codice sopra), avremo un ciclo in cui tutti i simboli verranno alternativamente controllati per il nuovo evento bar e segnali di trading come implementato in precedenza nelle OnTick() funzione:

//+------------------------------------------------------------------+
//| Checking signals and trading based on the new bar event          |
//+------------------------------------------------------------------+
void CheckSignalsAndTrade()
  {
//--- Iterate over all specified symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading for this symbol is allowed
      if(Symbols[s]!="")
        {
         //--- If the bar is not new, proceed to the next symbol
         if(!CheckNewBar(s))
            continue;
         //--- If there is a new bar
         else
           {
            //--- Get indicator data. If there is no data, proceed to the next symbol
            if(!GetIndicatorsData(s))
               continue;
            //--- Get bar data               
            GetBarsData(s);
            //--- Check the conditions and trade
            TradingBlock(s);
            //--- Trailing Stop
            ModifyTrailingStop(s);
           }
        }
     }
  }

Tutte le funzioni che utilizzavano i parametri esterni, così come i dati dei simboli e degli indicatori, devono essere modificate in conformità con tutte le modifiche di cui sopra. A tal fine, dovremmo aggiungere il numero del simbolo come primo parametro e sostituire tutte le variabili e gli array all'interno della funzione con i nuovi array descritti sopra.

A titolo illustrativo, di seguito sono forniti i codici rivisti delle funzioni CheckNewBar(), TradingBlock() e OpenPosition().

Il codice della funzione CheckNewBar():

//+------------------------------------------------------------------+
//| Checking for the new bar                                         |
//+------------------------------------------------------------------+
bool CheckNewBar(int number_symbol)
  {
//--- Get the opening time of the current bar
//    If an error occurred when getting the time, print the relevant message
   if(CopyTime(Symbols[number_symbol],Period(),0,1,lastbar_time[number_symbol].time)==-1)
      Print(__FUNCTION__,": Error copying the opening time of the bar: "+IntegerToString(GetLastError()));
//--- If this is a first function call
   if(new_bar[number_symbol]==NULL)
     {
      //--- Set the time
      new_bar[number_symbol]=lastbar_time[number_symbol].time[0];
      Print(__FUNCTION__,": Initialization ["+Symbols[number_symbol]+"][TF: "+TimeframeToString(Period())+"]["
            +TimeToString(lastbar_time[number_symbol].time[0],TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"]");
      return(false);
     }
//--- If the time is different
   if(new_bar[number_symbol]!=lastbar_time[number_symbol].time[0])
     {
      //--- Set the time and exit
      new_bar[number_symbol]=lastbar_time[number_symbol].time[0];
      return(true);
     }
//--- If we have reached this line, then the bar is not new, so return false
   return(false);
  }

Il codice della funzione TradingBlock():

//+------------------------------------------------------------------+
//| Trading block                                                    |
//+------------------------------------------------------------------+
void TradingBlock(int symbol_number)
  {
   ENUM_ORDER_TYPE      signal=WRONG_VALUE;                 // Variable for getting a signal
   string               comment="hello :)";                 // Position comment
   double               tp=0.0;                             // Take Profit
   double               sl=0.0;                             // Stop Loss
   double               lot=0.0;                            // Volume for position calculation in case of position reversal
   double               position_open_price=0.0;            // Position opening price
   ENUM_ORDER_TYPE      order_type=WRONG_VALUE;             // Order type for opening a position
   ENUM_POSITION_TYPE   opposite_position_type=WRONG_VALUE; // Opposite position type
//--- Find out if there is a position
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- Get the signal
   signal=GetTradingSignal(symbol_number);
//--- If there is no signal, exit
   if(signal==WRONG_VALUE)
      return;
//--- Get symbol properties
   GetSymbolProperties(symbol_number,S_ALL);
//--- Determine values for trade variables
   switch(signal)
     {
      //--- Assign values to variables for a BUY
      case ORDER_TYPE_BUY  :
         position_open_price=symb.ask;
         order_type=ORDER_TYPE_BUY;
         opposite_position_type=POSITION_TYPE_SELL;
         break;
         //--- Assign values to variables for a SELL
      case ORDER_TYPE_SELL :
         position_open_price=symb.bid;
         order_type=ORDER_TYPE_SELL;
         opposite_position_type=POSITION_TYPE_BUY;
         break;
     }
//--- Get the Take Profit and Stop Loss levels
   sl=CalculateStopLoss(symbol_number,order_type);
   tp=CalculateTakeProfit(symbol_number,order_type);
//--- If there is no position
   if(!pos.exists)
     {
      //--- Adjust the volume
      lot=CalculateLot(symbol_number,Lot[symbol_number]);
      //--- Open a position
      OpenPosition(symbol_number,lot,order_type,position_open_price,sl,tp,comment);
     }
//--- If the position exists
   else
     {
      //--- Get the position type
      GetPositionProperties(symbol_number,P_TYPE);
      //--- If the position is opposite to the signal and the position reversal is enabled
      if(pos.type==opposite_position_type && Reverse[symbol_number])
        {
         //--- Get the position volume
         GetPositionProperties(symbol_number,P_VOLUME);
         //--- Adjust the volume
         lot=pos.volume+CalculateLot(symbol_number,Lot[symbol_number]);
         //--- Reverse the position
         OpenPosition(symbol_number,lot,order_type,position_open_price,sl,tp,comment);
         return;
        }
      //--- If the signal is in the direction of the position and the volume increase is enabled, increase the position volume
      if(!(pos.type==opposite_position_type) && VolumeIncrease[symbol_number]>0)
        {
         //--- Get the Stop Loss of the current position
         GetPositionProperties(symbol_number,P_SL);
         //--- Get the Take Profit of the current position
         GetPositionProperties(symbol_number,P_TP);
         //--- Adjust the volume
         lot=CalculateLot(symbol_number,VolumeIncrease[symbol_number]);
         //--- Increase the position volume
         OpenPosition(symbol_number,lot,order_type,position_open_price,pos.sl,pos.tp,comment);
         return;
        }
     }
  }

Il codice della funzione OpenPosition():

//+------------------------------------------------------------------+
//| Opening a position                                               |
//+------------------------------------------------------------------+
void OpenPosition(int symbol_number,
                  double lot,
                  ENUM_ORDER_TYPE order_type,
                  double price,
                  double sl,
                  double tp,
                  string comment)
  {
//--- Set the magic number in the trading structure
   trade.SetExpertMagicNumber(MagicNumber);
//--- Set the slippage in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation));
//--- Instant Execution and Market Execution mode
//    *** Starting with build 803, Stop Loss and Take Profit ***
//    *** can be set upon opening a position in the SYMBOL_TRADE_EXECUTION_MARKET mode ***
   if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT ||
      symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET)
     {
      //--- If the position failed to open, print the relevant message
      if(!trade.PositionOpen(Symbols[symbol_number],order_type,lot,price,sl,tp,comment))
         Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
     }
  }

Quindi, ogni funzione ora riceve il numero del simbolo (symbol_number). Si prega di notare anche la modifica introdotta nella build 803:

A partire dalla build 803, Stop Loss e Take Profit possono essere impostati all'apertura di una posizione nella modalità SYMBOL_TRADE_EXECUTION_MARKET.

I codici rivisti delle altre funzioni si trovano nei file allegati. Tutto ciò che dobbiamo fare ora è ottimizzare i parametri ed eseguire i test.


Ottimizzazione dei parametri e test Expert Advisor

Ottimizzeremo prima i parametri per il primo simbolo e poi per il secondo. Cominciamo con EURUSD.

Di seguito sono riportate le impostazioni dello Strategy Tester:

Fig. 1. Impostazioni del tester di strategia

Fig. 1. Impostazioni del tester di strategia.

Le impostazioni dell'Expert Advisor devono essere effettuate come mostrato di seguito (per comodità, i file .set contenenti le impostazioni per ogni simbolo sono allegati all'articolo). Per escludere un determinato simbolo dall'ottimizzazione, è sufficiente lasciare vuoto il campo del parametro del nome del simbolo. Anche l'ottimizzazione dei parametri eseguita separatamente per ciascun simbolo accelererà il processo di ottimizzazione.

Fig. 2. Impostazioni Expert Advisor per l'ottimizzazione dei parametri: EURUSD

Fig. 2. Impostazioni Expert Advisor per l'ottimizzazione dei parametri: EURUSD.

L'ottimizzazione richiederà circa un'ora su un processore dual-core. I risultati del test del fattore di recupero massimo sono mostrati di seguito:

Fig. 3. Risultati del test del fattore di recupero massimo per EURUSD

Fig. 3. Risultati del test del fattore di recupero massimo per EURUSD.

Ora imposta NZDUSD come secondo simbolo. Per l'ottimizzazione, lasciare vuota la riga con il nome del simbolo per il primo blocco di parametri.

In alternativa, puoi semplicemente aggiungere un trattino alla fine del nome del simbolo. L'Expert Advisor non troverà il simbolo con tale nome nell'elenco dei simboli e inizializzerà l'indice dell'array con una stringa vuota.

I risultati per NZDUSD sono sembrati essere i seguenti:

Fig. 4. Risultati del test del fattore di recupero massimo per NZDUSD

Fig. 4. Risultati del test del fattore di recupero massimo per NZDUSD.

Ora possiamo testare due simboli insieme. Nelle impostazioni di Strategy Tester, puoi impostare qualsiasi simbolo su cui viene lanciato l'Expert Advisor poiché i risultati saranno identici. Può anche essere un simbolo che non è coinvolto nel trading/test.

Di seguito sono riportati i risultati per due simboli testati insieme:

Fig. 5. Risultati del test per due simboli: EURUSD e NZDUSD

Fig. 5. Risultati del test per due simboli: EURUSD e NZDUSD.


Conclusione

Questo è tutto. I codici sorgente sono allegati di seguito e possono essere scaricati per uno studio più dettagliato di quanto sopra. Per fare pratica, prova a selezionare uno o più simboli o cambia le condizioni di apertura della posizione usando altri indicatori.

Dopo aver estratto i file dall'archivio, posizionare la cartella MultiSymbolExpert nella directory MetaTrader 5\MQL5\Experts. Inoltre, l'indicatore EventsSpy.mq5 deve essere inserito nella directory MetaTrader 5\MQL5\Indicators.

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

Manuale MQL5: Sviluppo di un Expert Advisor multi-valuta con un numero illimitato di parametri Manuale MQL5: Sviluppo di un Expert Advisor multi-valuta con un numero illimitato di parametri
In questo articolo, creeremo un modello che utilizza un singolo set di parametri per l'ottimizzazione di un sistema di trading, consentendo al contempo un numero illimitato di parametri. L'elenco dei simboli verrà creato in un file di testo standard (*.txt). Anche i parametri di input per ciascun simbolo verranno memorizzati nei file. In questo modo saremo in grado di aggirare la restrizione del terminale sul numero di parametri di input di un Expert Advisor.
Manuale MQL5: Sviluppo di un framework per un sistema di trading basato sulla strategia a triplo schermo Manuale MQL5: Sviluppo di un framework per un sistema di trading basato sulla strategia a triplo schermo
In questo articolo, svilupperemo un framework per un sistema di trading basato sulla strategia Triple Screen in MQL5. L'Expert Advisor non sarà sviluppato da zero. Invece, modificheremo semplicemente il programma dal precedente articolo "Manuale MQL5: Utilizzo di indicatori per impostare le condizioni di trading in Expert Advisors" che già sostanzialmente serve al nostro scopo. Quindi l'articolo dimostrerà anche come è possibile modificare facilmente i modelli di programmi già pronti.
Manuale MQL5: Scrittura della cronologia delle offerte in un file e creazione di grafici di bilanciamento per ogni simbolo in Excel Manuale MQL5: Scrittura della cronologia delle offerte in un file e creazione di grafici di bilanciamento per ogni simbolo in Excel
Quando comunicavo in vari forum, usavo spesso esempi dei risultati dei miei test visualizzati come schermate di grafici di Microsoft Excel. Molte volte mi è stato chiesto di spiegare come tali grafici possono essere creati. Infine, ora ho un po 'di tempo per spiegare tutto in questo articolo.
L'indicatore ZigZag: Nuovo approccio e nuove soluzioni L'indicatore ZigZag: Nuovo approccio e nuove soluzioni
L'articolo esamina la possibilità di creare un indicatore ZigZag avanzato. L'idea di identificare i nodi si basa sull'uso dell'indicatore Inviluppo (Envelopes)a. Supponiamo di poter trovare una certa combinazione di parametri di input per una serie di inviluppo, per cui tutti i nodi ZigZag si trovano all'interno dei confini delle bande di Envelopes (inviluppo). Di conseguenza, possiamo provare a prevedere le coordinate del nuovo nodo.