English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Controllo dello Slope della Curva di Saldo Durante il Lavoro di un Expert Advisor

Controllo dello Slope della Curva di Saldo Durante il Lavoro di un Expert Advisor

MetaTrader 5Trading | 16 dicembre 2021, 11:00
90 0
Dmitriy Skub
Dmitriy Skub


Introduzione

Questo articolo descrive uno degli approcci che consente di migliorare le prestazioni degli Expert Advisor attraverso la creazione di un feedback. In questo caso, il feedback si baserà sulla misurazione della pendenza della curva di bilanciamento. Il controllo della pendenza viene eseguito automaticamente regolando il volume di lavoro. Un Expert Advisor può negoziare nelle seguenti modalità: con un volume tagliato, con la quantità di lavoro di lotti (in base a quello inizialmente regolato) e con un volume intermedio. La modalità di lavoro viene commutata automaticamente.

Diverse caratteristiche di regolazione sono utilizzate nella catena di feedback: a gradini, a gradini con isteresi, lineari. Permette di regolare il sistema di controllo dello slope della curva di equilibrio alle caratteristiche di un determinato sistema.

L'idea principale è quella di automatizzare il processo di prendere decisioni per un trader monitorando il proprio sistema di trading. È ragionevole ridurre i rischi durante i periodi sfavorevoli del suo funzionamento. Ritornando alla normale modalità di lavoro i rischi possono essere ripristinati al livello iniziale.

Naturalmente, questo sistema non è una panacea e non trasformerà un Expert Advisor perdente in uno redditizio. In qualche modo, questa è un'aggiunta al MM (gestione del denaro) di Expert Advisor che gli impedisce di ottenere perdite considerevoli su un conto.

L'articolo include una libreria che consente di incorporare questa funzione nel codice di qualsiasi Expert Advisor.


Principio di Operazione

Diamo un'occhiata al principio di operazione del sistema, che controlla lo slope della curva di equilibrio. Supponiamo di avere un Expert Advisor di trading. La sua ipotetica curva di equilibrio appare come segue:

Principio di operazione del sistema che controlla la pendenza della curva di equilibrio

Figura 1. Principio di funzionamento del sistema che controlla la pendenza della curva di bilanciamento

La curva iniziale di equilibrio per l'Expert Advisor che utilizza un volume costante di operazioni di trading è mostrata sopra. I trade chiusi sono mostrati con i punti rossi. Colleghiamo quei punti con una linea di curva, che rappresenta il cambiamento di equilibrio dell'Expert Advisor durante il trading (linea nera spessa).

Ora tracceremo continuamente l'angolo di pendenza di questa linea rispetto all'asse del tempo (mostrato con sottili linee blu). O per essere più precisi, prima di aprire ogni operazione con un segnale, calcoleremo l'angolo dello slope di due operazioni precedentemente chiuse (o di due operazioni, affinché la descrizione sia più semplice). Se l'angolo dello slope diventa inferiore al valore specificato, il nostro sistema di controllo inizia a funzionare; diminuisce il volume in base al valore calcolato dell'angolo e alla funzione di regolazione specificata.

In tal modo, se il trading entra in un periodo senza successo, il volume diminuisce da Vmax. a Vmin. all'interno del Т3...Т5 periodo di trading. Dopo che il trading di punti Т5 viene eseguito con un volume minimo specificato - nella modalità di rifiuto del volume degli scambi. Una volta ripristinata la redditività dell'Expert Advisor e l'angolo dello slope della curva di bilanciamento sale al di sopra del valore specificato, il volume inizia ad aumentare. Questo accade all'interno dell’intervalloТ8...Т10. Dopo il punto Т10 il volume delle operazioni commerciali viene ripristinato allo stato iniziale Vmax.

La curva di equilibrio formata a seguito di tale regolazione è mostrata nella parte inferiore della fig. 1. Puoi vedere che il drawdown iniziale da B1 a B2 è diminuito ed è diventato da B1 a B2*. Puoi anche osservare che il profitto è leggermente diminuito entro il periodo di ripristino del volume massimo Т8 ... Т10 - questo è il rovescio della medaglia.

Il colore verde evidenzia la parte della curva di bilanciamento quando il trading è stato eseguito con un volume minimo specificato. Il colore giallo rappresenta le parti di transizione dal volume massimo a quello minimo e ritorno. Sono possibili diverse varianti di transizione qui:

  • a gradini - variazioni di volume in passi discreti dal volume massimo al minimo e ritorno;
  • lineare - il volume viene modificato linearmente a seconda dell'angolo di pendenza della curva di equilibrio all'interno dell'intervallo regolato;
  • a gradini con isteresi - la transizione dal volume massimo al minimo e il ritorno viene eseguita a valori di differenza dell'angolo di pendenza;

Illustriamolo in immagini:

Tipi di caratteristiche di regolazione

Figura 2. Tipi di caratteristiche di regolazione

Le caratteristiche di regolazione influenzano i tassi del sistema di controllo - il ritardo di abilitazione / disabilitazione, il processo di transizione dal volume massimo al minimo e ritorno. Si consiglia di scegliere una caratteristica su base sperimentale quando si raggiungono i migliori risultati dei test.

Pertanto, miglioriamo il sistema di trading con il feedback basato sull'angolo dello slope della curva di bilanciamento. Si noti che tale regolazione del volume è adatta solo per quei sistemi che non hanno il volume come parte del sistema di trading stesso. Ad esempio, se si utilizza il principio Martingale, non è possibile utilizzare questo sistema direttamente senza modifiche nell'Expert Advisor iniziale.

Inoltre, dobbiamo attirare la nostra attenzione sui seguenti punti importanti:

  • l'efficacia della gestione dello slope della linea di bilanciamento dipende direttamente dal rapporto tra il volume di lavoro in modalità normale di funzionamento e il volume nella modalità di rifiuto del volume. Maggiore è questo rapporto, più efficace è la gestione. Ecco perché il volume di lavoro iniziale dovrebbe essere considerevolmente maggiore di quello minimo possibile.
  • il periodo medio di alterazione degli aumenti e delle cadute del saldo di Expert Advisor dovrebbe essere considerevolmente superiore al tempo di reazione del sistema di controllo. In caso contrario, il sistema non riuscirà a regolare la pendenza della curva di bilanciamento. Maggiore è il rapporto tra il periodo medio e il tempo di reazione, più efficace è il sistema. Questo requisito riguarda quasi tutti i sistemi di regolazione automatica.

Implementazione in MQL5 Utilizzando la Programmazione Orientata agli Oggetti

Scriviamo una libreria che realizzi l'approccio sopra descritto. Per farlo, usiamo la nuova funzionalità di MQL5 - approccio orientato agli oggetti. Questo approccio consente di sviluppare ed espandere facilmente la nostra libreria in futuro senza riscrivere grandi parti del codice da zero.


Classe TradeSymbol

Poiché il test multi-valuta è implementato nella nuova piattaforma MetaTrader 5, abbiamo bisogno di una classe, che incapsula in sé l'intero lavoro con qualsiasi simbolo di lavoro. Permette di utilizzare questa libreria in Expert Advisor multi-valuta. Questa classe non riguarda direttamente il sistema di controllo, è ausiliaria. Pertanto, verrà utilizzata per le operazioni con il simbolo di lavoro.

//---------------------------------------------------------------------
//  Operations with work symbol:
//---------------------------------------------------------------------
class TradeSymbol
{
private:
  string  trade_symbol;                          // work symbol

private:
  double  min_trade_volume;                      // minimum allowed volume for trade operations
  double  max_trade_volume;                      // maximum allowed volume for trade operations
  double  min_trade_volume_step;                 // minimum change of volume
  double  max_total_volume;                      // maximum change of volume
  double  symbol_point;                          // size of one point
  double  symbol_tick_size;                      // minimum change of price
  int     symbol_digits;                        // number of digits after decimal point

protected:

public:
  void    RefreshSymbolInfo( );                  // refresh market information about the work symbol
  void    SetTradeSymbol( string _symbol );      // set/change work symbol
  string  GetTradeSymbol( );                     // get work symbol
  double  GetMaxTotalLots( );                    // get maximum cumulative volume
  double  GetPoints( double _delta );            // get change of price in points

public:
  double  NormalizeLots( double _requied_lot );  // get normalized trade volume
  double  NormalizePrice( double _org_price );   // get normalized price with consideration of step of change of quote

public:
  void    TradeSymbol( );                       // constructor
  void    ~TradeSymbol( );                      // destructor
};

La struttura della classe è molto semplice. Lo scopo è ottenere, archiviare ed elaborare le informazioni di mercato correnti con un simbolo specificato. I metodi principali sono TradeSymbol::RefreshSymbolInfo, TradeSymbol::NormalizeLots, TradeSymbol::NormalizePrice. Consideriamoli uno per uno.


Il metodo TradeSymbol::RefreshSymbolInfo ha lo scopo di aggiornare le informazioni di mercato con il simbolo di lavoro.

//---------------------------------------------------------------------
//  Refresh market information by work symbol:
//---------------------------------------------------------------------
void
TradeSymbol::RefreshSymbolInfo( )
{
//  If a work symbol is not set, don't do anything:
  if( GetTradeSymbol( ) == NULL )
  {
    return;
  }

//  Calculate parameters necessary for normalization of volume:
  min_trade_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_MIN );
  max_trade_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_MAX );
  min_trade_volume_step = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_STEP );

  max_total_volume = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_VOLUME_LIMIT );

  symbol_point = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_POINT );
  symbol_tick_size = SymbolInfoDouble( GetTradeSymbol( ), SYMBOL_TRADE_TICK_SIZE );
  symbol_digits = ( int )SymbolInfoInteger( GetTradeSymbol( ), SYMBOL_DIGITS );
}

Presta attenzione a un punto importante che viene utilizzato in diversi metodi. Poiché l'attuale realizzazione di MQL5 non consente l'utilizzo di un costruttore con parametri, è necessario chiamare il seguente metodo per l'impostazione primaria dei simboli di lavoro:

void    SetTradeSymbol( string _symbol );      // set/change work symbol


Il metodo TradeSymbol::NormalizeLots viene utilizzato per ottenere un volume corretto e normalizzato. Sappiamo che la dimensione di una posizione non può essere inferiore al valore minimo possibile consentito dal broker. Il passaggio minimo di cambiamento di una posizione è determinato anche dal broker e può differire. Questo metodo restituisce il valore di volume più vicino dal basso.

Controlla anche se il volume della presunta posizione supera il valore massimo consentito dal broker.

//---------------------------------------------------------------------
//  Get normalized trade volume:
//---------------------------------------------------------------------
//  - input necessary volume;
//  - output is normalized volume;
//---------------------------------------------------------------------
double
TradeSymbol::NormalizeLots( double _requied_lots )
{
  double   lots, koeff;
  int      nmbr;

//  If a work symbol is not set, don't do anything:
  if( GetTradeSymbol( ) == NULL )
  {
    return( 0.0 );
  }

  if( this.min_trade_volume_step > 0.0 )
  {
    koeff = 1.0 / min_trade_volume_step;
    nmbr = ( int )MathLog10( koeff );
  }
  else
  {
    koeff = 1.0 / min_trade_volume;
    nmbr = 2;
  }
  lots = MathFloor( _requied_lots * koeff ) / koeff;

//  Lower limit of volume:
  if( lots < min_trade_volume )
  {
    lots = min_trade_volume;
  }

//  Upper limit of volume:
  if( lots > max_trade_volume )
  {
    lots = max_trade_volume;
  }

  lots = NormalizeDouble( lots, nmbr );
  return( lots );
}


Il metodo TradeSymbol::NormalizePrice viene utilizzato per ottenere un prezzo corretto e normalizzato. Poiché il numero di cifre significative dopo il punto decimale (precisione del prezzo) deve essere determinato per un determinato simbolo, è necessario troncare il prezzo. Oltre ad esso, alcuni simboli (ad esempio, i future) hanno un passo minimo di variazione del prezzo maggiore di un punto. Ecco perché dobbiamo fare in modo che i valori del prezzo siano multipli della discrepanza minima.

//---------------------------------------------------------------------
//  Normalization of price with consideration of step of price change:
//---------------------------------------------------------------------
double
TradeSymbol::NormalizePrice( double _org_price )
{
//  Minimal step of quote change in points:
  double  min_price_step = NormalizeDouble( symbol_tick_size / symbol_point, 0 );

  double  norm_price = NormalizeDouble( NormalizeDouble(( NormalizeDouble( _org_price / symbol_point, 0 )) / min_price_step, 0 ) * min_price_step * symbol_point, symbol_digits );
  return( norm_price );
}

Il prezzo non normalizzato necessario viene inserito nella funzione. E restituisce il prezzo normalizzato che è il più vicino a quello necessario.

Lo scopo degli altri metodi è chiaramente descritto nei commenti; non richiede ulteriori descrizioni.


Classe TBalanceHistory

Questa classe, è destinata ad operare con la storia del saldo di un conto che è chiaro per il suo nome. È anche una classe base per diverse classi descritte di seguito. Lo scopo principale di questa classe è l'accesso alla storia commerciale di un Expert Advisor. Inoltre, è possibile filtrare la cronologia per simbolo di lavoro, per "numero magico", per data di inizio del monitoraggio dell'Expert Advisor o per tutti e tre gli elementi contemporaneamente.

//---------------------------------------------------------------------
//  Operations with balance history:
//---------------------------------------------------------------------
class TBalanceHistory
{
private:
  long      current_magic;            // value of "magic number" when accessing the history of deals ( 0 - any number )
  long      current_type;             // type of deals ( -1 - all )
  int       current_limit_history;   // limit of depth of history ( 0 - all history )
  datetime   monitoring_begin_date;   // date of start of monitoring history of deals
  int       real_trades;             // number of actual trades already performed

protected:
  TradeSymbol  trade_symbol;          // operations with work symbol

protected:
//  "Raw" arrays:
  double    org_datetime_array[ ];    // date/time of trade
  double    org_result_array[ ];      // result of trade

//  Arrays with data grouped by time:
  double    group_datetime_array[ ];  // date/time of trade
  double    group_result_array[ ];    // result of trade

  double    last_result_array[ ];     // array for storing results of last trades ( points on the Y axis )
  double    last_datetime_array[ ];   // array for storing time of last trades ( points on the X axis )

private:
  void      SortMasterSlaveArray( double& _m[ ], double& _s[ ] );  // synchronous ascending sorting of two arrays

public:
  void      SetTradeSymbol( string _symbol );                      // set/change work symbol
  string    GetTradeSymbol( );                                    // get work symbol
  void      RefreshSymbolInfo( );                                 // refresh market information by work symbol
  void      SetMonitoringBeginDate( datetime _dt );                // set date of start of monitoring
  datetime  GetMonitoringBeginDate( );                            // get date of start of monitoring
  void      SetFiltrParams( long _magic, long _type = -1, int _limit = 0 );// set parameters of filtration of deals

public:
// Get results of last trades:
  int       GetTradeResultsArray( int _max_trades );

public:
  void      TBalanceHistory( );       // constructor
  void      ~TBalanceHistory( );      // destructor
};

Le impostazioni di filtraggio durante la lettura dei risultati delle ultime operazioni e della cronologia vengono impostate utilizzando il metodo TBalanceHistory::SetFiltrParams. Ha i seguenti parametri di input:

  • _magia - "numero magico" di trade che dovrebbero essere letti dalla cronologia. Se viene specificato il valore zero, verranno letti trade con qualsiasi "numero magico".
  • _tipo - tipo di offerte che devono essere lette. Può avere i seguenti valori: DEAL_TYPE_BUY (solo per la lettura di trade lunghi), DEAL_TYPE_SELL (solo per la lettura di trade corti) e -1 (per la lettura di trade lunghi e corti).
  • _limit - limita la profondità della cronologia analizzata dei trade. Se è uguale a zero, viene analizzata tutta la cronologia disponibile.

Per impostazione predefinita, quando viene creato l'oggetto della classe TBalanceHistory vengono impostati i seguenti valori: __magic = 0, _type = -1, _limit = 0.


Il metodo principale di questa classe è TBalanceHistory::GetTradeResultsArray. È destinato al riempimento di array di membri della classe last_result_array e last_datetime_array con i risultati degli ultimi trade. Il metodo ha i seguenti parametri di input:

  • _max_trades - numero massimo di trade che devono essere letti dalla cronologia ed essere scritti negli array di output. Poiché abbiamo bisogno di almeno due punti per calcolare l'angolo di pendenza, questo valore non dovrebbe essere inferiore a due. Se questo valore è uguale a zero, viene analizzata l'intera cronologia disponibile delle negoziazioni. In pratica, il numero di punti necessari per il calcolo della pendenza della curva di bilanciamento è specificato qui.
//---------------------------------------------------------------------
//  Reads the results of last (by time) trades to arrays:
//---------------------------------------------------------------------
//  - returns the number of actually read trades but not more than specified;
//---------------------------------------------------------------------
int
TBalanceHistory::GetTradeResultsArray( int _max_trades )
{
  int       index, limit, count;
  long      deal_type, deal_magic, deal_entry;
  datetime   deal_close_time, current_time;
  ulong     deal_ticket;                        // ticket of deal
  double    trade_result;
  string    symbol, deal_symbol;

  real_trades = 0;

//  Number of trades should be no less than two:
  if( _max_trades < 2 )
  {
    return( 0 );
  }

//  If a work symbol is not specified, don't do anything:
  symbol = trade_symbol.GetTradeSymbol( );
  if( symbol == NULL )
  {
    return( 0 );
  }

//  Request the history of deals and orders from the specified time to the current moment:
  if( HistorySelect( monitoring_begin_date, TimeCurrent( )) != true )
  {
    return( 0 );
  }

//  Calculate number of trades:
  count = HistoryDealsTotal( );

//  If there are less trades in the history than it is necessary, then exit:
  if( count < _max_trades )
  {
    return( 0 );
  }

//  If there are more trades in the history than it is necessary, then limit them:
  if( current_limit_history > 0 && count > current_limit_history )
  {
    limit = count - current_limit_history;
  }
  else
  {
    limit = 0;
  }

//  If needed, adjust dimension of "raw" arrays by the specified number of trades:
  if(( ArraySize( org_datetime_array )) != ( count - limit ))
  {
    ArrayResize( org_datetime_array, count - limit );
    ArrayResize( org_result_array, count - limit );
  }

//  Fill the "raw" array with trades from history base:
  real_trades = 0;
  for( index = count - 1; index >= limit; index-- )
  {
    deal_ticket = HistoryDealGetTicket( index );

//  If those are not closed deals, don't go further:
    deal_entry = HistoryDealGetInteger( deal_ticket, DEAL_ENTRY );
    if( deal_entry != DEAL_ENTRY_OUT )
    {
      continue;
    }

//  Check "magic number" of deal if necessary:
    deal_magic = HistoryDealGetInteger( deal_ticket, DEAL_MAGIC );
    if( current_magic != 0 && deal_magic != current_magic )
    {
      continue;
    }

//  Check symbol of deal:
    deal_symbol = HistoryDealGetString( deal_ticket, DEAL_SYMBOL );
    if( symbol != deal_symbol )
    {
      continue;
    }
                
//  Check type of deal if necessary:
    deal_type = HistoryDealGetInteger( deal_ticket, DEAL_TYPE );
    if( current_type != -1 && deal_type != current_type )
    {
      continue;
    }
    else if( current_type == -1 && ( deal_type != DEAL_TYPE_BUY && deal_type != DEAL_TYPE_SELL ))
    {
      continue;
    }
                
//  Check time of closing of deal:
    deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME );
    if( deal_close_time < monitoring_begin_date )
    {
      continue;
    }

//  So, we can read another trade:
    org_datetime_array[ real_trades ] = deal_close_time / 60;
    org_result_array[ real_trades ] = HistoryDealGetDouble( deal_ticket, DEAL_PROFIT ) / HistoryDealGetDouble( deal_ticket, DEAL_VOLUME );
    real_trades++;
  }

//  if there are less trades than necessary, return:
  if( real_trades < _max_trades )
  {
    return( 0 );
  }

  count = real_trades;

//  Sort the "raw" array by date/time of closing the order:
  SortMasterSlaveArray( org_datetime_array, org_result_array );

// If necessary, adjust dimension of group arrays for the specified number of points:
  if(( ArraySize( group_datetime_array )) != count )
  {
    ArrayResize( group_datetime_array, count );
    ArrayResize( group_result_array, count );
  }
  ArrayInitialize( group_datetime_array, 0.0 );
  ArrayInitialize( group_result_array, 0.0 );

//  Fill the output array with grouped data ( group by the identity of date/time of position closing ):
  for( index = 0; index < count; index++ )
  {
//  Get another trade:
    deal_close_time = ( datetime )org_datetime_array[ index ];
    trade_result = org_result_array[ index ];

//  Now check if the same time already exists in the output array:
    current_time = ( datetime )group_datetime_array[ real_trades ];
    if( current_time > 0 && MathAbs( current_time - deal_close_time ) > 0.0 )
    {
      real_trades++;                      // move the pointer to the next element
      group_result_array[ real_trades ] = trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
    else
    {
      group_result_array[ real_trades ] += trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
  }
  real_trades++;                          // now this is the number of unique elements

//  If there are less trades than necessary, exit:
  if( real_trades < _max_trades )
  {
    return( 0 );
  }

  if( ArraySize( last_result_array ) != _max_trades )
  {
    ArrayResize( last_result_array, _max_trades );
    ArrayResize( last_datetime_array, _max_trades );
  }

//  Write the accumulated data to the output arrays with reversed indexation:
  for( index = 0; index < _max_trades; index++ )
  {
    last_result_array[ _max_trades - 1 - index ] = group_result_array[ index ];
    last_datetime_array[ _max_trades - 1 - index ] = group_datetime_array[ index ];
  }

//  In the output array replace the results of single trades with the accumulating total:
  for( index = 1; index < _max_trades; index++ )
  {
    last_result_array[ index ] += last_result_array[ index - 1 ];
  }

  return( _max_trades );
}

I controlli obbligatori vengono eseguiti all'inizio - se viene specificato un simbolo di lavoro e se i parametri di input sono corretti.

Quindi leggiamo la cronologia delle offerte e degli ordini dalla data specificata al momento corrente. Viene eseguito nella seguente parte del codice:

//  Request the history of deals and orders from the specified time to the current moment:
  if( HistorySelect( monitoring_begin_date, TimeCurrent( )) != true )
  {
    return( 0 );
  }

//  Calculate number of trades:
  count = HistoryDealsTotal( );

//  If there are less trades in the history than it is necessary, then exit:
  if( count < _max_trades )
  {
    return( 0 );
  }

Inoltre, viene controllato il numero totale di offerte nella cronologia. Se è inferiore a quanto specificato, ulteriori azioni sono prive di significato. Non appena gli array "grezzi" vengono preparati, viene eseguito il ciclo di riempimento con le informazioni della cronologia dei trade. È fatto nel modo seguente:

//  Fill the "raw" array from the base of history of trades:
  real_trades = 0;
  for( index = count - 1; index >= limit; index-- )
  {
    deal_ticket = HistoryDealGetTicket( index );

//  If the trades are not closed, don't go further:
    deal_entry = HistoryDealGetInteger( deal_ticket, DEAL_ENTRY );
    if( deal_entry != DEAL_ENTRY_OUT )
    {
      continue;
    }

//  Check "magic number" of deal if necessary:
    deal_magic = HistoryDealGetInteger( deal_ticket, DEAL_MAGIC );
    if( _magic != 0 && deal_magic != _magic )
    {
      continue;
    }

//  Check symbols of deal:
    deal_symbol = HistoryDealGetString( deal_ticket, DEAL_SYMBOL );
    if( symbol != deal_symbol )
    {
      continue;
    }
                
//  Check type of deal if necessary:
    deal_type = HistoryDealGetInteger( deal_ticket, DEAL_TYPE );
    if( _type != -1 && deal_type != _type )
    {
      continue;
    }
    else if( _type == -1 && ( deal_type != DEAL_TYPE_BUY && deal_type != DEAL_TYPE_SELL ))
    {
      continue;
    }
                
//  Check time of closing of deal:
    deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME );
    if( deal_close_time < monitoring_begin_date )
    {
      continue;
    }

//  So, we can rad another trade:
    org_datetime_array[ real_trades ] = deal_close_time / 60;
    org_result_array[ real_trades ] = HistoryDealGetDouble( deal_ticket, DEAL_PROFIT ) / HistoryDealGetDouble( deal_ticket, DEAL_VOLUME );
    real_trades++;
  }

//  If there are less trades than necessary, exit:
  if( real_trades < _max_trades )
  {
    return( 0 );
  }

All'inizio, il ticket dell'affare dalla cronologia viene letto utilizzando la funzione HistoryDealGetTicket; un'ulteriore lettura dei dettagli dell'affare viene eseguita utilizzando il biglietto ottenuto. Poiché siamo interessati solo ai trade chiusi (analizzeremo il saldo), il tipo di affare viene controllato in un primo momento. Viene eseguito chiamando la funzione HistoryDealGetInteger con il parametro DEAL_ENTRY. Se la funzione ritorna DEAL_ENTRY_OUT, allora sta chiudendo una posizione.

Dopo che il "numero magico" dell'affare, il tipo di affare (è specificato il parametro di input del metodo) e il simbolo dell'affare vengono controllati. Se tutti i parametri dell'affare soddisfano i requisiti, viene controllato l'ultimo parametro: il tempo di chiusura dell'affare. È fatto nel modo seguente:

//  Check the time of closing of deal:
    deal_close_time = ( datetime )HistoryDealGetInteger( deal_ticket, DEAL_TIME );
    if( deal_close_time < monitoring_begin_date )
    {
      continue;
    }

La data / ora dell'affare viene confrontata con la data / ora di inizio del monitoraggio della cronologia. Se la data / ora dell'affare è maggiore di quella data, allora andiamo a leggere il nostro trade nell'array - leggiamo il risultato del trade in punti e l'ora del trade in minuti (in questo caso l'ora della chiusura). Successivamente, il contatore delle offerte di lettura real_trades viene aumentato; e il ciclo continua.

Una volta che gli array "grezzi" sono pieni della quantità necessaria di informazioni, dovremmo selezionare l'array in cui è memorizzato il tempo di chiusura degli affari. Allo stesso tempo, dobbiamo mantenere la corrispondenza del tempo di chiusura nell'array org_datetime_array e i risultati degli affari nell'array org_result_array. Questo viene fatto usando il metodo appositamente scritto:

TBalanceHistory::SortMasterSlaveArray( double& _master[ ], double& _slave[ ] ). Il primo parametro è _master - l'array che è ordinato in modo ascendente. Il secondo parametro è _slave - l'array, i cui elementi devono essere spostati in modo sincrono con gli elementi del primo array. Lo smistamento viene eseguito tramite il metodo "bubble".

Dopo tutte le operazioni sopra descritte, abbiamo due array con tempo e risultati delle offerte ordinati per tempo. Poiché solo un punto sulla curva di bilanciamento (punto sull'asse Y) può corrispondere a ciascun momento del tempo (punto sull'asse X), dobbiamo raggruppare gli elementi dell'array con lo stesso tempo di chiusura (se ci sono). La parte seguente del codice esegue questa operazione:

//  Fill the output array with grouped data ( group by identity of date/time of closing of position ):
  real_trades = 0;
  for( index = 0; index < count; index++ )
  {
//  Get another trade:
    deal_close_time = ( datetime )org_datetime_array[ index ];
    trade_result = org_result_array[ index ];

//  Now check, if the same time already exists in the output array:
    current_time = ( datetime )group_datetime_array[ real_trades ];
    if( current_time > 0 && MathAbs( current_time - deal_close_time ) > 0.0 )
    {
      real_trades++;                      // move the pointer to the next element
      group_result_array[ real_trades ] = trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
    else
    {
      group_result_array[ real_trades ] += trade_result;
      group_datetime_array[ real_trades ] = deal_close_time;
    }
  }
  real_trades++;                          // now this is the number of unique elements

In pratica, tutte i trade con lo "stesso" tempo di chiusura sono riassunti qui. I risultati vengono scritti negli array TBalanceHistory::group_datetime_array (ora di chiusura) e TBalanceHistory::group_result_array (risultati dei trade). Dopodiché otteniamo due array ordinati con elementi unici. L'identità del tempo in questo caso viene considerata entro un minuto. Questa trasformazione può essere illustrata graficamente:

Insieme di affari con lo stesso tempo

Figura 3. Insieme di affari con lo stesso tempo

Tutte gli affari entro un minuto (parte sinistra della figura) sono raggruppati in uno con arrotondamento del tempo e somma dei risultati (parte destra della figura). Permette di appianare il "chiacchiericcio" del tempo di chiusura degli affari e migliorare la stabilità della regolamentazione.

Dopodiché è necessario effettuare altre due trasformazioni delle matrici ottenute. Invertire l'ordine degli elementi per far corrispondere il primo accordo all'elemento zero; e sostituire i risultati delle singole operazioni con il totale cumulativo, cioè con il saldo. Viene eseguito nel seguente frammento di codice:

//  Write the accumulated data into output arrays with reversed indexation:
  for( index = 0; index < _max_trades; index++ )
  {
    last_result_array[ _max_trades - 1 - index ] = group_result_array[ index ];
    last_datetime_array[ _max_trades - 1 - index ] = group_datetime_array[ index ];
  }

//  Replace the results of single trades with the cumulative total in the output array:
  for( index = 1; index < _max_trades; index++ )
  {
    last_result_array[ index ] += last_result_array[ index - 1 ];
  }


Classe TBalanceSlope

Questa classe è destinata a effettuare operazioni con la curva di saldo di un conto. Viene generato dalla classe TBalanceHistory ed eredita tutti i suoi dati e metodi protetti e pubblici. Diamo un'occhiata dettagliata alla sua struttura:

//---------------------------------------------------------------------
//  Operations with the balance curve:
//---------------------------------------------------------------------
class TBalanceSlope : public TBalanceHistory
{
private:
  double    current_slope;               // current angle of slope of the balance curve
  int       slope_count_points;          // number of points ( trades ) for calculation of slope angle
        
private:
  double    LR_koeff_A, LR_koeff_B;      // rates for the equation of the straight-line regression
  double    LR_points_array[ ];          // array of point of the straight-line regression

private:
  void      CalcLR( double& X[ ], double& Y[ ] );  // calculate the equation of the straight-line regression

public:
  void      SetSlopePoints( int _number );        // set the number of points for calculation of angle of slope
  double    CalcSlope( );                         // calculate the slope angle

public:
  void      TBalanceSlope( );                     // constructor
  void      ~TBalanceSlope( );                    // destructor
};


Determineremo l'angolo di pendenza della curva di bilanciamento dall'angolo di pendenza della linea di regressione lineare disegnata per la quantità specificata di punti (scambi) sulla curva di bilanciamento. Quindi, prima di tutto, dobbiamo calcolare l'equazione della regressione in linea retta della seguente forma: A*x + B. Il metodo seguente esegue questo lavoro:

//---------------------------------------------------------------------
//  Calculate the equation of the straight-line regression:
//---------------------------------------------------------------------
//  input parameters:
//    X[ ] - arras of values of number series on the X axis;
//    Y[ ] - arras of values of number series on the Y axis;
//---------------------------------------------------------------------
void
TBalanceSlope::CalcLR( double& X[ ], double& Y[ ] )
{
  double    mo_X = 0, mo_Y = 0, var_0 = 0, var_1 = 0;
  int       i;
  int       size = ArraySize( X );
  double    nmb = ( double )size;

//  If the number of points is less than two, the curve cannot be calculated:
  if( size < 2 )
  {
    return;
  }

  for( i = 0; i < size; i++ )
  {
    mo_X += X[ i ];
    mo_Y += Y[ i ];
  }
  mo_X /= nmb;
  mo_Y /= nmb;

  for( i = 0; i < size; i++ )
  {
    var_0 += ( X[ i ] - mo_X ) * ( Y[ i ] - mo_Y );
    var_1 += ( X[ i ] - mo_X ) * ( X[ i ] - mo_X );
  }

//  Value of the A coefficient:
  if( var_1 != 0.0 )
  {
    LR_koeff_A = var_0 / var_1;
  }
  else
  {
    LR_koeff_A = 0.0;
  }

//  Value of the B coefficient:
  LR_koeff_B = mo_Y - LR_koeff_A * mo_X;

//  Fill the array of points that lie on the regression line:
  ArrayResize( LR_points_array, size );
  for( i = 0; i < size; i++ )
  {
    LR_points_array[ i ] = LR_koeff_A * X[ i ] + LR_koeff_B;
  }
}

Qui usiamo il metodo dei minimi quadrati per calcolare l'errore minimo di posizione della linea di regressione relativamente ai dati iniziali. Viene riempito anche l’array che memorizza le coordinate Y, che si trovano sulla linea calcolata. Questo array non viene utilizzato per il momento ed è destinato a ulteriori sviluppi.


Il metodo principale utilizzato nella classe data è TBalanceSlope::CalcSlope. Restituisce l'angolo di pendenza della curva di bilanciamento che viene calcolato dalla quantità specificata delle ultime operazioni. Ecco la sua realizzazione:

//---------------------------------------------------------------------
//  Calculate slope angle:
//---------------------------------------------------------------------
double
TBalanceSlope::CalcSlope( )
{
//  Get result of trading from the history of trades:
  int      nmb = GetTradeResultsArray( slope_count_points );
  if( nmb < slope_count_points )
  {
    return( 0.0 );
  }

//  Calculate the regression line by the results of last trades:
  CalcLR( last_datetime_array, last_result_array );
  current_slope = LR_koeff_A;

  return( current_slope );
}

Prima di tutto, viene analizzata la quantità specificata degli ultimi punti della curva di bilanciamento. Viene eseguito chiamando il metodo della classe base TBalanceSlope::GetTradeResultsArray. Se la quantità di punti di lettura non è inferiore a quella specificata, viene calcolata la linea di regressione. Viene eseguito utilizzando il metodo TBalanceSlope::CalcLR. Riempiti nel passaggio precedente, gli array last_result_array e last_datetime_array, che appartengono alla classe base, vengono utilizzate come argomenti.

Il resto dei metodi sono semplici e non richiedono una descrizione dettagliata.


Classe TBalanceSlopeControl

È la classe base che gestisce la pendenza della curva di bilanciamento modificando il volume di lavoro. Viene generato dalla classe TBalanceSlope ed eredita tutti i suoi metodi e dati pubblici e protetti. L'unico scopo di questa classe è calcolare il volume di lavoro corrente in base all'angolo di pendenza corrente della curva di bilanciamento. Diamo un'occhiata dettagliata:

//---------------------------------------------------------------------
//  Managing slope of the balance curve:
//---------------------------------------------------------------------
enum LotsState
{
  LOTS_NORMAL = 1,            // mode of trading with normal volume
  LOTS_REJECTED = -1,         // mode of trading with lowered volume
  LOTS_INTERMEDIATE = 0,      // mode of trading with intermediate volume
};
//---------------------------------------------------------------------
class TBalanceSlopeControl : public TBalanceSlope
{
private:
  double    min_slope;          // slope angle that corresponds to the mode of volume rejection
  double    max_slope;          // slope angle that corresponds to the mode of normal volume
  double    centr_slope;        // slope angle that corresponds to the mode of volume switching without hysteresis

private:
  ControlType  control_type;    // type of the regulation function

private:
  double    rejected_lots;      // volume in the rejection mode
  double    normal_lots;        // volume in the normal mode
  double    intermed_lots;      // volume in the intermediate mode

private:
  LotsState current_lots_state; // current mode of volume

public:
  void      SetControlType( ControlType _control );  // set type of the regulation characteristic
  void      SetControlParams( double _min_slope, double _max_slope, double _centr_slope );

public:
  double    CalcTradeLots( double _min_lots, double _max_lots );  // get trade volume

protected:
  double    CalcIntermediateLots( double _min_lots, double _max_lots, double _slope );

public:
  void      TBalanceSlopeControl( );   // constructor
  void      ~TBalanceSlopeControl( );  // destructor
};


Prima di calcolare il volume corrente, dobbiamo impostare i parametri iniziali. Viene eseguito chiamando i seguenti metodi:

  void      SetControlType( ControlType _control );  // set type of the regulation characteristic

Parametro di input_control - questo è il tipo di caratteristica di regolazione. Può avere il seguente valore:

  • STEP_WITH_HYSTERESISH - passo con la caratteristica di regolazione dell'isteresi;
  • STEP_WITHOUT_HYSTERESIS - passo senza caratteristica di regolazione dell'isteresi;
  • LINEARE                                   - caratteristica di regolazione lineare;
  • NON_LINEAR - caratteristica di regolazione non lineare (non implementata in questa versione);


  void      SetControlParams( double _min_slope, double _max_slope, double _centr_slope );

I parametri di input sono i seguenti:

  • _min_slope - angolo di pendenza della curva di bilanciamento che corrisponde al trading con un volume minimo;
  • _max_slope - angolo di pendenza della curva di bilanciamento che corrisponde al trading con il volume massimo;
  • _centr_slope - angolo di pendenza della curva di bilanciamento che corrisponde alla caratteristica di regolazione a gradini senza isteresi;


Il volume viene calcolato utilizzando il seguente metodo:

//---------------------------------------------------------------------
//  Get trade volume:
//---------------------------------------------------------------------
double
TBalanceSlopeControl::CalcTradeLots( double _min_lots, double _max_lots )
{
//  Try to calculate slope of the balance curve:
  double    current_slope = CalcSlope( );

//  If the specified amount of trades is not accumulated yet, trade with minimal volume:
  if( GetRealTrades( ) < GetSlopePoints( ))
  {
    current_lots_state = LOTS_REJECTED;
    rejected_lots = trade_symbol.NormalizeLots( _min_lots );
    return( rejected_lots );
  }

//  If the regulation function is stepped without hysteresis:
  if( control_type == STEP_WITHOUT_HYSTERESIS )
  {
    if( current_slope < centr_slope )
    {
      current_lots_state = LOTS_REJECTED;
      rejected_lots = trade_symbol.NormalizeLots( _min_lots );
      return( rejected_lots );
    }
    else
    {
      current_lots_state = LOTS_NORMAL;
      normal_lots = trade_symbol.NormalizeLots( _max_lots );
      return( normal_lots );
    }
  }

//  If the slope of linear regression for the balance curve is less than the allowed one:
  if( current_slope < min_slope )
  {
    current_lots_state = LOTS_REJECTED;
    rejected_lots = trade_symbol.NormalizeLots( _min_lots );
    return( rejected_lots );
  }

//  If the slope of linear regression for the balance curve is greater than specified:
  if( current_slope > max_slope )
  {
    current_lots_state = LOTS_NORMAL;
    normal_lots = trade_symbol.NormalizeLots( _max_lots );
    return( normal_lots );
  }

//  The slope of linear regression for the balance curve is within specified borders (intermediate state):
  current_lots_state = LOTS_INTERMEDIATE;

//  Calculate the value of intermediate volume:
  intermed_lots = CalcIntermediateLots( _min_lots, _max_lots, current_slope );
  intermed_lots = trade_symbol.NormalizeLots( intermed_lots );

  return( intermed_lots );
}

I principali punti significativi di implementazione del metodo TBalanceSlopeControl::CalcTradeLotsono i seguenti:

  • Fino a quando non viene accumulata la quantità minima specificata di trade, fai trading con un volume minimo. È logico, perché non è noto, in quale periodo (redditizio o meno) si trova attualmente l'Expert Advisor, subito dopo averlo impostato per il trading.
  • Se la funzione di regolazione è quella a gradini senza isteresi, per impostare l'angolo di commutazione tra le modalità di trading tramite il metodo TBalanceSlopeControl::SetControlParams è necessario utilizzare solo il parametro _centr_slope. I parametri _min_slope e _max_slope vengono ignorati. Viene fatto per eseguire l'ottimizzazione corretta da questo parametro nello Strategy Tester MetaTrader 5.

A seconda dell'angolo di pendenza calcolato, il trading viene eseguito con un volume minimo, massimo o intermedio. Il volume intermedio viene calcolato tramite il metodo semplice - TBalanceSlopeControl::CalcIntermediateLots. Questo metodo è protetto e viene utilizzato all'interno della classe. Il suo codice è mostrato di seguito:

//---------------------------------------------------------------------
//  Calculation of intermediate volume:
//---------------------------------------------------------------------
double
TBalanceSlopeControl::CalcIntermediateLots( double _min_lots, double _max_lots, double _slope )
{
  double    lots;

//  If the regulation function is stepped with hysteresis:
  if( control_type == STEP_WITH_HYSTERESISH )
  {
    if( current_lots_state == LOTS_REJECTED && _slope > min_slope && _slope < max_slope )
    {
      lots = _min_lots;
    }
    else if( current_lots_state == LOTS_NORMAL && _slope > min_slope && _slope < max_slope )
    {
      lots = _max_lots;
    }
  }
//  If the regulation function is linear:
  else if( control_type == LINEAR )
  {
    double  a = ( _max_lots - _min_lots ) / ( max_slope - min_slope );
    double  b = normal_lots - a * .max_slope;
    lots = a * _slope + b;
  }
//  If the regulation function is non-linear ( not implemented yet ):
  else if( control_type == NON_LINEAR )
  {
    lots = _min_lots;
  }
//  If the regulation function is unknown:
  else
  {
    lots = _min_lots;
  }

  return( lots );
}

Altri metodi di questa classe non richiedono alcuna descrizione.


Esempio di Incorporamento del Sistema in un Expert Advisor

Consideriamo il processo di implementazione del sistema di controllo della pendenza della curva di bilanciamento in un Expert Advisor passo dopo passo.


Passaggio 1 - aggiunta delle istruzioni per collegare la libreria sviluppata all'Expert Advisor:

#include  <BalanceSlopeControl.mqh>


Passaggio 2 - aggiunta delle variabili esterne per l'impostazione dei parametri del sistema di controllo della pendenza della linea di bilanciamento all'Expert Advisor:

//---------------------------------------------------------------------
//  Parameters of the system of controlling the slope of the balance curve;
//---------------------------------------------------------------------
enum SetLogic 
{
  No = 0,
  Yes = 1,
};
//---------------------------------------------------------------------
input SetLogic     UseAutoBalanceControl = No;
//---------------------------------------------------------------------
input ControlType  BalanceControlType = STEP_WITHOUT_HYSTERESIS;
//---------------------------------------------------------------------
//  Amount of last trades for calculation of LR of the balance curve:
input int          TradesNumberToCalcLR = 3;
//---------------------------------------------------------------------
//  Slope of LR to decrease the volume to minimum:
input double       LRKoeffForRejectLots = -0.030;
//---------------------------------------------------------------------
//  Slope of LR to restore the normal mode of trading:
input double       LRKoeffForRestoreLots = 0.050;
//---------------------------------------------------------------------
//  Slope of LR to work in the intermediate mode:
input double       LRKoeffForIntermedLots = -0.020;
//---------------------------------------------------------------------
//  Decrease the initial volume to the specified value when the LR is inclined down
input double       RejectedLots = 0.10;
//---------------------------------------------------------------------
//  Normal work volume in the mode of MM with fixed volume:
input double       NormalLots = 1.0;


Passaggio 3 - aggiunta dell'oggetto del tipo TBalanceSlopeControl a Expert Advisor:

TBalanceSlopeControl  BalanceControl;

Questa dichiarazione può essere aggiunta all'inizio dell'Expert Advisor, prima delle definizioni delle funzioni.


Passaggio 4 - aggiunta del codice per l'inizializzazione del sistema di controllo della curva di bilanciamento alla funzione OnInit dell'Expert Advisor:

//  Adjust our system of controlling the slope of the balance curve:
  BalanceControl.SetTradeSymbol( Symbol( ));
  BalanceControl.SetControlType( BalanceControlType );
  BalanceControl.SetControlParams( LRKoeffForRejectLots, LRKoeffForRestoreLots, LRKoeffForIntermedLots );
  BalanceControl.SetSlopePoints( TradesNumberToCalcLR );
  BalanceControl.SetFiltrParams( 0, -1, 0 );
  BalanceControl.SetMonitoringBeginDate( 0 );


Passaggio 5 - aggiunta della chiamata di metodo per aggiornare le informazioni di mercato correnti alla funzione OnTick dell'Expert Advisor:

//  Refresh market information:
  BalanceControl.RefreshSymbolInfo( );

La chiamata di questo metodo può essere aggiunta all'inizio della funzione OnTick o dopo il controllo della nuova barra in arrivo (per gli Expert Advisor con tale controllo).


Passaggio 6 - aggiunta del codice per il calcolo del volume corrente prima del codice in cui vengono aperte le posizioni:

  if( UseAutoBalanceControl == Yes )
  {
    current_lots = BalanceControl.CalcTradeLots( RejectedLots, NormalLots );
  }
  else
  {
    current_lots = NormalLots;
  }

Se nell'Expert Advisor viene utilizzato un sistema di Money Management, allora invece dei NormalLots dovresti scrivere il metodo TBalanceSlopeControl::CalcTradeLots - il volume corrente calcolato dal sistema MM dell'Expert Advisor.

Test Expert Advisor BSCS-TestExpert.mq5 con il sistema integrato sopra descritto è allegato a questo articolo. Il principio del suo funzionamento si basa sull'intersezione dei livelli dell'indicatore CCI. Questo Expert Advisor è sviluppato per i test e non è adatto per lavorare su conti reali. Lo testeremo al timeframe H4 (2008.07.01 - 2010.09.01) di EURUSD.

Analizziamo il risultato del funzionamento di questo EA. Di seguito è riportato il grafico del cambio di equilibrio con il sistema di controllo della pendenza disabilitato. Per visualizzarlo, impostare il valore No per il parametro esterno UseAutoBalanceControl.

Grafico iniziale della variazione del saldo

Figura 4. Grafico iniziale della variazione del saldo


Ora impostare il parametro esterno UseAutoBalanceControl su Yes e testare Expert Advisor. Otterrai il grafico con il sistema abilitato di controllo della pendenza del saldo.

Grafico della variazione del saldo con il sistema di controllo abilitato

Figura 5. Grafico della variazione del saldo con il sistema di controllo abilitato

Puoi vedere che la maggior parte dei periodi nel grafico superiore (fig.4) appaiono come vengono tagliati e hanno una forma piatta nel grafico inferiore (fig.5). Questo è il risultato del funzionamento del nostro sistema. È possibile confrontare i principali parametri di funzionamento dell'Expert Advisor:

 Parametro
 UseAutoBalanceControl = No  UseAutoBalanceControl = Sì
 Utile netto totale:  18 378.00 17 261.73
 Fattore di profitto  1.47 1.81
 Fattore di recupero  2.66  3.74
 Profitto previsto  117.81 110.65
 Prelievo assoluto del saldo: 1 310.50 131.05
 Prelievo assoluto di capitale:  1 390.50 514.85
 Prelievo massimo di equilibrio:  5 569.50 (5.04%) 3 762.15 (3.35%)
 Prelievo massimo di capitale: 6 899.50 (6.19%)
4 609.60 (4.08%)


I migliori parametri tra quelli confrontati sono evidenziati con il colore verde. Il profitto e il payoff previsti sono leggermente diminuiti; questo è l'altro lato della regolamentazione che appare come risultato dei ritardi di commutazione tra gli stati del volume di lavoro. Tutto sommato, c'è un miglioramento dei tassi di lavoro dell'Expert Advisor. In particolare, un miglioramento del drawdown e del fattore di profitto.


Conclusione

Noto diversi modi per migliorare questo sistema:
  • Utilizzo del trading virtuale quando l'Expert Advisor entra in un periodo di lavoro sfavorevole. Quindi il normale volume di lavoro non avrà più importanza. Permetterà di diminuire il drawdown.
  • Utilizzo di algoritmi più complessi per determinare lo stato attuale di funzionamento dell'Expert Advisor (redditizio o meno). Ad esempio, possiamo provare ad applicare una rete neuronale per tale analisi. In questo caso, ovviamente, sono necessarie ulteriori indagini.

Pertanto, abbiamo considerato il principio e il risultato del funzionamento del sistema, che consente di migliorare le caratteristiche qualitative di un Expert Advisor. L'operazione congiunta con il sistema di gestione del denaro, in alcuni casi, consente di aumentare la redditività senza aumentare il rischio.

Vi ricordo ancora una volta: nessun sistema ausiliario può rendere redditizio un Expert Advisor da uno perdente.


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

File allegati |
Come Creare Rapidamente un Expert Advisor per l’Automated Trading Championship 2010 Come Creare Rapidamente un Expert Advisor per l’Automated Trading Championship 2010
Al fine di sviluppare un expert per partecipare all'Automated Trading Championship 2010, usiamo un modello di pronto dell’expert advisor. Anche il programmatore MQL5 alle prime armi sarà in grado di questo compito, perché per le tue strategie le classi di base, le funzioni, i modelli sono già sviluppati. Basta scrivere una quantità minima di codice per implementare la tua idea di trading.
L'uso delle Librerie MQL5 Standard Trade Class nella scrittura di un Expert Advisor L'uso delle Librerie MQL5 Standard Trade Class nella scrittura di un Expert Advisor
Questo articolo spiega come utilizzare le principali funzionalità di MQL5 Standard Trade Class nella scrittura di Expert Advisor che implementano la chiusura e la modifica della posizione, l'immissione e l'eliminazione di ordini in sospeso e la verifica del margine prima di piazzare un trade. Abbiamo anche dimostrato come le classi di trading possono essere utilizzate per ottenere dettagli su ordini e transazioni.
Ricerca di Errori e Registrazione Ricerca di Errori e Registrazione
MetaEditor 5 ha la funzione di debug. Tuttavia quando scrivi i tuoi programmi MQL5, spesso non vuoi visualizzare solo i singoli valori, ma tutti i messaggi che appaiono durante i test e il lavoro online. Quando il contenuto del file del log è di grandi dimensioni, è ovvio automatizzare il recupero rapido e semplice del messaggio richiesto. In questo articolo considereremo i modi per trovare errori nei programmi MQL5 e i metodi di logging. Inoltre semplificheremo l'accesso ai file e conosceremo un semplice programma LogMon per una comoda visualizzazione dei log.
Utilizzo della Funzione TesterWithdrawal() per il Modeling dei Prelievi di Profitto Utilizzo della Funzione TesterWithdrawal() per il Modeling dei Prelievi di Profitto
Questo articolo descrive l'uso della funzione TesterWithDrawal() per stimare i rischi nei sistemi commerciali che implicano il ritiro di una certa parte delle attività durante il loro funzionamento. Inoltre, descrive l'effetto di questa funzione sull'algoritmo di calcolo del drawdown del capitale nello Strategy Tester. Questa funzione è utile quando si ottimizzano i parametri dei propri Expert Advisor.