Sistemi di Trading Adattivi e loro utilizzo nel Client MetaTrader 5

MetaQuotes | 9 dicembre, 2021


Introduzione

Centinaia di migliaia di trader in tutto il mondo utilizzano le piattaforme di trading sviluppate da MetaQuotes Software Corp. Il fattore chiave che porta al successo è la superiorità tecnologica basata sull'esperienza di molti anni e sulle migliori soluzioni software.

Molte persone hanno valutato nuove opportunità che sono ora disponibili con il nuovo linguaggio MQL5. Le sue caratteristiche principali sono le alte prestazioni e la possibilità di utilizzare la programmazione orientata agli oggetti. Inoltre, con la comparsa dello strategy tester multi-valuta nel client terminal MetaTrader 5, molti trader hanno acquisito strumenti unici per lo sviluppo, l'apprendimento e l'utilizzo di sistemi di trading complessi.

Automated Trading Championship 2010 inizia questo autunno; migliaia di robot di trading scritti in MQL5 vi parteciperanno. Vincerà un Expert Advisor che guadagna il massimo profitto durante la competizione. Ma quale strategia sembrerà la più efficace?

Lo strategy tester del terminale MetaTrader 5 consente di trovare il miglior set di parametri, utilizzando il quale il sistema guadagna la massima quantità di profitto durante un determinato periodo di tempo. Ma può essere fatto in tempo reale? L'idea del trading virtuale utilizzando diverse strategie in un Expert Advisor è stata considerata nell'articolo "Contest di Expert Advisor all’interno di un Expert Advisor" che contiene la sua implementazione in MQL4. 

In questo articolo, mostreremo che la creazione e l'analisi di strategie adattive è diventata significativamente più semplice in MQL5 a causa dell'uso della programmazione orientata agli oggetti, classi per lavorare con dati e classi di trading della Libreria Standard.


1. Strategie di Trading Adattive

I mercati cambiano costantemente. Le strategie di trading devono essere aggiornate alle attuali condizioni di mercato.

I valori dei parametri che danno la massima redditività della strategia possono essere trovati senza utilizzare l'ottimizzazione attraverso il cambiamento sequenziale dei parametri e l'analisi dei risultati dei test. 

La Figura 1 illustra le curve di equità per dieci Expert Advisor (MA_3,... MA_93); ognuno di essi è stato scambiato secondo la strategia delle medie mobili ma con periodi diversi (3,13,.. 93). Il test è stato eseguito a EURUSD H1, il periodo di test è 4.01.2010-20.08.2010.

Figura 1. Diagrammi delle curve di equità di dieci Expert Advisor sull’account

Figura 1. Diagrammi delle curve di equità di dieci Expert Advisor sull’account

Come si può vedere nella Figura 1, gli Expert Advisor hanno avuto quasi gli stessi risultati durante le prime due settimane di lavoro, ma ulteriormente i loro profitti hanno iniziato a divergere in modo significativo. Al termine del periodo di test, gli Expert Advisor hanno mostrato i migliori risultati di trading con i periodi 63, 53 e 43.

Il mercato ha scelto i migliori. Perché non dovremmo seguire la sua scelta? Cosa succede se combiniamo tutte e dieci le strategie in un unico Expert Advisor, forniamo la possibilità di trading "virtuale" per ogni strategia e periodicamente (ad esempio, all'inizio di ogni nuova barra) determiniamo la migliore strategia per il trading reale e il trading in base ai suoi segnali?

I risultati della strategia adattiva ottenuta sono mostrati nella Figura 2. La curva di equità dell’account con trading adattivo è mostrata con il colore rosso. Si noti che per più di metà periodo la forma della curva di equità per la strategia adattiva è la stessa di quella della strategia MA_63 che è sembrata essere la vincitrice finale.

Figura 2. Curve di equità sull’account con la strategia adattiva che utilizza i segnali di 10 sistemi di trading

Figura 2. Curve di equità sull’account con la strategia adattiva che utilizza i segnali di 10 sistemi di trading

Le curve di saldo hanno dinamiche simili (Fig. 3):

Figura 3.Curve di saldo della strategia adattiva che utilizza i segnali di 10 sistemi di trading

Figura 3. Curve di saldo della strategia adattiva che utilizza i segnali di 10 sistemi di trading

Se nessuna delle strategie è redditizia al momento, i sistemi adattivi non dovrebbero eseguire operazioni di trading. L'esempio di questo caso è mostrato nella fig. 4 (periodo dal 4 al 22 gennaio 2010).

Figura 4. Il periodo di tempo in cui la strategia adattiva ha smesso di aprire nuove posizioni a causa dell'assenza di strategie redditizie

Figura 4. Il periodo di tempo in cui la strategia adattiva ha smesso di aprire nuove posizioni a causa dell'assenza di strategie redditizie

A partire da gennaio 2010, la migliore efficacia è dimostrata dalla strategia MA_3. Poiché la MA_3 (blu) aveva la massima quantità di denaro guadagnata in quel momento, la strategia adattiva (rosso) seguiva i suoi segnali. Nel periodo dall'8 al 20 gennaio tutte le strategie considerate hanno avuto un risultato negativo, ecco perché la strategia adattiva non ha aperto nuove posizioni di trading. 

Se tutte le strategie hanno un risultato negativo, è meglio stare lontano dal trading. Questa è la cosa significativa che consente di interrompere il trading non redditizio e di salvare il tuo denaro.


2. Implementazione della Strategia di Trading Adattivo

In questa sezione, considereremo la struttura della strategia adattiva che esegue il trading "virtuale" utilizzando più strategie di trading contemporaneamente e sceglie quella più redditizia per il trading reale in base ai suoi segnali. Si noti che l'uso dell'approccio orientato agli oggetti rende la soluzione di questo problema significativamente più semplice.

Prima di tutto, studieremo il codice dell'Expert Advisor adattivo, quindi daremo uno sguardo dettagliato alla CAdaptiveStrategy in cui è implementata la funzionalità del sistema adattivo, quindi mostreremo la struttura della classe CSampleStrategy - la classe base delle strategie di trading in cui viene implementata la funzionalità del trading virtuale.

Inoltre, considereremo il codice di due dei suoi figli: le classi CStrategyMA e CStrategyStoch che rappresentano le strategie di trading delle medie mobili e dell'oscillatore stocastico. Dopo aver analizzato la loro struttura, sarai in grado di scrivere e aggiungere facilmente le tue classi che realizzano le tue strategie.

2.1. Codice dell'Expert Advisor

Il codice dell'Expert Advisor sembra molto semplice:

//+------------------------------------------------------------------+
//|                                       Adaptive_Expert_Sample.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <CAdaptiveStrategy.mqh>

CAdaptiveStrategy Adaptive_Expert;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(Adaptive_Expert.Expert_OnInit());
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Adaptive_Expert.Expert_OnDeInit(reason);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   Adaptive_Expert.Expert_OnTick();
  }
//+------------------------------------------------------------------+

Le prime tre righe definiscono le proprietà del programma,, poi c’è la direttiva #include che dice al preprocessore di includere il file CAdaptiveStrategy.mqh. Le parentesi angolari specificano che il file deve essere preso dalla directory standard (in genere, è terminal_folder\MQL5\Include).

La riga successiva contiene la dichiarazione dell'oggetto Adaptive_Expert (istanza della classe CAdaptiveStrategy); e il codice delle funzioni OnInit, OnDeinit e OnTick dell'Expert Advisor è costituito dalle chiamate delle funzioni corrispondenti Expert_OnInit, Expert_OnDeInit e Expert_OnTick e l'oggetto Adaptive_Expert.

2.2. Classe CAdaptiveStrategy

La classe di thr adaptive Expert Advisor (classe CAdaptiveStrategy) si trova nel file CAdaptiveStrategy.mqh. Iniziamo con i file di inclusione:

#include <Arrays\ArrayObj.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\Trade.mqh>
#include <CStrategyMA.mqh>
#include <CStrategyStoch.mqh>

Il motivo per cui includiamo il file ArrayObj.mqh è la comodità di lavorare con classi di strategie diverse utilizzando l'oggetto della classe CArrayObj, che rappresenta un array dinamico di puntatori alle istanze di classe generate dalla classe base CObject e dai suoi figli. Questo oggetto sarà l'array m_all_strategies , verrà utilizzato un "contenitore" di strategie di trading.

Ogni strategia è rappresentata come una classe. In questo caso, abbiamo incluso i file che contengono le classi CStrategyMA e CStrategyStoch che rappresentano le strategie di trading con medie mobili e trading da parte dell'oscillatore stocastico.

Per richiedere le proprietà delle posizioni correnti e per eseguire operazioni di trading, utilizzeremo le classi CPositionInfo e CTrade della libreria Standard: ecco perché includiamo i file PositionInfo.mqh e Trade.mqh.

Diamo un'occhiata alla struttura della classe CAdaptiveStrategy.

//+------------------------------------------------------------------+
//| Class CAdaptiveStrategy                                          |
//+------------------------------------------------------------------+
class CAdaptiveStrategy
  {
protected:
   CArrayObj        *m_all_strategies;   // objects of trade strategies

   void              ProceedSignalReal(int state,double trade_volume);
   int               RealPositionDirection();

public:
   // initialization of the adaptive strategy
   int               Expert_OnInit();
   // deinitialization of the adaptive strategy
   int               Expert_OnDeInit(const int reason);
   // check of trade conditions and opening of virtual positions
   void              Expert_OnTick();
  };

Per implementare un approccio unito agli oggetti di classi diverse, le strategie di trading (o meglio le istanze delle loro classi) sono memorizzate nell'array dinamico m_all_strategies (del tipo CArrayObj) che viene utilizzato come "contenitore" di classi delle strategie. Questo è il motivo per cui la classe di strategie di trading SampleStrategy viene generata dalla classe CObject.

La funzione ProceedSignalReal implementa la "sincronizzazione" della direzione e del volume di una posizione reale con la direzione e il volume dati:

//+------------------------------------------------------------------+
//| This method is intended for "synchronization" of current         |
//| real trade position with the value of the 'state' state          |
//+------------------------------------------------------------------+
void CAdaptiveStrategy::ProceedSignalReal(int state,double trade_volume)
  {
   CPositionInfo posinfo;
   CTrade trade;

   bool buy_opened=false;
   bool sell_opened=false;

   if(posinfo.Select(_Symbol)) // if there are open positions
     {
      if(posinfo.Type()==POSITION_TYPE_BUY) buy_opened=true;    // a buy position is opened
      if(posinfo.Type()==POSITION_TYPE_SELL) sell_opened=true;  // a sell position is opened

      // if state = 0, then we need to close open positions
      if((state==POSITION_NEUTRAL) && (buy_opened || sell_opened))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
        }
      //reverse: closing buy position and opening sell position
      if((state==POSITION_SHORT) && (buy_opened))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,trade_volume,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0))
            Print(trade.ResultRetcodeDescription());
        }
      //reverse: close sell position and open buy position
      if(((state==POSITION_LONG) && (sell_opened)))
        {
         if(!trade.PositionClose(_Symbol,200))
            Print(trade.ResultRetcodeDescription());
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,trade_volume,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0))
            Print(trade.ResultRetcodeDescription());
        }
     }
   else // if there are no open positions
     {
      // open a buy position
      if(state==POSITION_LONG)
        {
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,0.1,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0))
            Print(trade.ResultRetcodeDescription());
        }
      // open a sell position
      if(state==POSITION_SHORT)
        {
         if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,0.1,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0))
            Print(trade.ResultRetcodeDescription());
        }
     }
  }

Si noti che è più facile lavorare con la posizione di trading utilizzando le classi di trading. Abbiamo utilizzato gli oggetti delle classi CPositionInfo e CTrade per richiedere, rispettivamente, le proprietà della posizione di mercato e per eseguire operazioni di trading.

La funzione RealPositionDirection richiede i parametri della posizione aperta reale e ne restituisce la direzione:

//+------------------------------------------------------------------+
//| Returns direction (0,+1,-1) of the current real position         |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::RealPositionDirection()
  {
   int direction=POSITION_NEUTRAL;
   CPositionInfo posinfo;

   if(posinfo.Select(_Symbol)) // if there are open positions
     {
      if(posinfo.Type()==POSITION_TYPE_BUY) direction=POSITION_LONG;    // a buy position is opened
      if(posinfo.Type()==POSITION_TYPE_SELL) direction=POSITION_SHORT;  // a short position is opened
     }
   return(direction);
  }

A questo punto, daremo un'occhiata alle principali funzioni della classe СAdaptiveStrategy.

Iniziamo con la funzione Expert_OnInit:

//+------------------------------------------------------------------+
//| Function of initialization of the Adaptive Expert Advisor        |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::Expert_OnInit()
  {
//--- Create array of objects m_all_strategies
//--- we will put our object with strategies in it 
   m_all_strategies=new CArrayObj;
   if(m_all_strategies==NULL)
     {
      Print("Error of creation of the object m_all_strategies"); return(-1);
     }

// create 10 trading strategies CStrategyMA (trading by moving averages)
// initialize them, set parameters
// and add to the m_all_strategies container 
   for(int i=0; i<10; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Error of creation of object of the CStrategyMA type");
         return(-1);
        }
      //set period for each strategy
      int period=3+i*10;
      // initialize strategy
      t_StrategyMA.Initialization(period,true);
      // set details of the strategy
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //t_StrategyMA.Set_Stops(3500,1000);

      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      CSampleStrategy *t_SampleStrategy;
      t_SampleStrategy=m_all_strategies.At(i);
      Print(i," Strategy name:",t_SampleStrategy.StrategyName(),
              " Strategy ID:",t_SampleStrategy.StrategyID(),
              " Virtual trading:",t_SampleStrategy.IsVirtualTradeAllowed());
     }
//---
   return(0);
  }

L'insieme delle strategie di trading è preparato nella funzione Expert_OnInit. Prima di tutto, viene creato l'oggetto dell'array dinamico m_all_strategies.

In questo caso, sono stati creati dieci istanze della classe CStrategyMA. Ognuno di essi è stato inizializzato (in questo caso, abbiamo impostato periodi diversi e consentito il trading "virtuale") utilizzando la funzione di Inizializzazione.

Quindi, utilizzando la funzione SetStrategyInfo, impostiamo lo strumento finanziario, il nome della strategia e il commento.

Se necessario, utilizzando la funzione Set_Stops(TP,SL) possiamo specificare un valore (in punti) di Take Profit e Stop Loss che verrà eseguito durante il trading "virtuale". Abbiamo commentato questa linea.

Una volta creata e regolata la classe di strategia, la aggiungiamo al contenitore m_all_strategies.

Tutte le classi di strategie di trading dovrebbero avere la funzione CheckTradeConditions() che esegue i controlli delle condizioni di trading. Nella classe della strategia adattiva questa funzione è chiamata all'inizio di ogni nuova barra, quindi diamo alle strategie la possibilità di controllare i valori degli indicatori e di effettuare le operazioni di trading "virtuali".

Invece di dieci medie mobili specificate (3, 13, 23...93), possiamo aggiungere centinaia di medie mobili (istanze se la classe CStrategyMA):

  for(int i=0; i<100; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Error of creation of object of the CStrategyMA type");
         return(-1);
        }
      //set period for each strategy
      int period=3+i*10;
      // initialization of strategy
      t_StrategyMA.Initialization(period,true);
      // set details of the strategy
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

Oppure possiamo aggiungere le classi di strategia che funzionano dai segnali dell'oscillatore stocastico (istanze della classe CStrategyStoch):

  for(int i=0; i<5; i++)
     {
      CStrategyStoch *t_StrategyStoch;
      t_StrategyStoch=new CStrategyStoch;
      if(t_StrategyStoch==NULL)
        {
         delete m_all_strategies;
         printf("Error of creation of object of the CStrategyStoch type");
         return(-1);
        }
      //set period for each strategy
      int Kperiod=2+i*5;
      int Dperiod=2+i*5;
      int Slowing=3+i;
      // initialization of strategy
      t_StrategyStoch.Initialization(Kperiod,Dperiod,Slowing,true);
      // set details of the strategy
      string s=IntegerToString(Kperiod)+"/"+IntegerToString(Dperiod)+"/"+IntegerToString(Slowing);
      t_StrategyStoch.SetStrategyInfo(_Symbol,"[Stoch_"+s+"]",100+i," Stochastic "+s);
      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyStoch);
     }

In questo caso, il contenitore include 10 strategie di medie mobili e 5 strategie dell'oscillatore stocastico.

Le istanze di classi di strategie di trading dovrebbero essere figlie della classe CObject e dovrebbero contenere la funzione CheckTradeConditions(). È meglio ereditarli dalla classe CSampleStrategy. Le classi che implementano strategie di trading possono essere diverse e il loro numero non è limitato.

La funzione Expert_OnInit termina con l'elenco delle strategie presenti nel contenitore m_all_strategies. Si noti che tutte le strategie nel contenitore sono considerate come figli della classe CSampleStrategy. Le classi di strategie commerciali CStrategyMA e CStrategyStoch sono anche i suoi figli.

Lo stesso trucco viene utilizzato nella funzione Expert_OnDeInit. Nel contenitore, chiamiamo la funzione SaveVirtualDeals per ogni strategia; memorizza la cronologia delle operazioni virtuali eseguite.

Usiamo il nome della strategia per il nome del file passato come parametro. Quindi reinizializziamo le strategie chiamando la funzione Deinitialization() ed eliminando il contenitore m_all_strategies:

//+------------------------------------------------------------------+
//| Function of deinitialization the adaptive Expert Advisor         |
//+------------------------------------------------------------------+
int CAdaptiveStrategy::Expert_OnDeInit(const int reason)
  {
   // deinitialize all strategies
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      CSampleStrategy *t_Strategy;
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.SaveVirtualDeals(t_Strategy.StrategyName()+"_deals.txt");
      t_Strategy.Deinitialization();
     }
   //delete the array of object with strategies 
   delete m_all_strategies;
   return(0);
  }

Se non hai bisogno di conoscere le operazioni virtuali eseguite dalle strategie, rimuovi la riga in cui viene chiamato tStrategy.SaveVirtualDeals. Si noti che quando si utilizza lo strategy tester i file vengono salvati nella directory /tester_directory/Files/.

Consideriamo la funzione Expert_OnTick della classe CAdaptiveStrategy che viene chiamata ogni volta che arriva un nuovo tick:

//+------------------------------------------------------------------+
//| Function of processing ticks of the adaptive strategy            |
//+------------------------------------------------------------------+
void CAdaptiveStrategy::Expert_OnTick()
  {
   CSampleStrategy *t_Strategy;

   // recalculate the information about positions for all strategies
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.UpdatePositionData();
     }

   // the expert advisor should check the conditions of making trade operations only when a new bar comes
   if(IsNewBar()==false) { return; }

   // check trading conditions for all strategies 
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      t_Strategy.CheckTradeConditions();
     }

   //search for the best position
   //prepare the array performance[] 
   double performance[];
   ArrayResize(performance,m_all_strategies.Total());
   
   //request the current effectiveness for each strategy,
   //each strategy returns it in the Strategyperformance() function
   for(int i=0; i<m_all_strategies.Total(); i++)
     {
      t_Strategy=m_all_strategies.At(i);
      performance[i]=t_Strategy.StrategyPerformance();
     }
   //find the strategy (or rather its index in the m_all_strategies container)
   //with maximum value of Strategyperformance()
   int best_strategy_index=ArrayMaximum(performance,0,WHOLE_ARRAY);

   //this strategy is - t_Strategy
   t_Strategy=m_all_strategies.At(best_strategy_index);
   //request the direction of its current position
   int best_direction=t_Strategy.PositionDirection();

   string s=s+" "+t_Strategy.StrategyName()+" "+DoubleToString(t_Strategy.GetVirtualEquity())+" "+IntegerToString(best_direction);
   Print(TimeCurrent()," TOTAL=",m_all_strategies.Total(),
                       " BEST IND=",best_strategy_index,
                       " BEST STRATEGY="," ",t_Strategy.StrategyName(),
                       " BEST=",performance[best_strategy_index],"  =",
                       " BEST DIRECTION=",best_direction,
                       " Performance=",t_Strategy.StrategyPerformance());

   //if the best strategy has a negative result and doesn't have open positions, it's better to stay away from trading
   if((performance[best_strategy_index]<0) && (RealPositionDirection()==POSITION_NEUTRAL)) {return;}

   if(best_direction!=RealPositionDirection())
     {
      ProceedSignalReal(best_direction,t_Strategy.GetCurrentLotSize());
     }
  }

Il codice è molto semplice. Ogni strategia, situata nel contenitore, deve essere in grado di ricalcolare il risultato finanziario corrente delle sue posizioni virtuali utilizzando i prezzi correnti. Viene eseguito chiamando la funzione UpdatePositionData(). Qui, ancora una volta, chiamiamo le strategie come eredi della classe CSampleStrategy.

Tutte le operazioni di trading vengono eseguite all'inizio di una nuova barra (la funzione IsNewBar() consente di determinare questo momento e gli altri metodi di controllo della nuova barra). In questo caso, la fine della formazione di una barra significa che tutti i dati della barra precedente (prezzi e valori degli indicatori) non cambieranno più, quindi possono essere analizzati sulla corrispondenza alle condizioni di trading. Diamo a tutte le strategie l'opportunità di eseguire questo controllo e di eseguire le loro operazioni di trading virtuale chiamando la loro funzione CheckTradeConditions.

Ora dovremmo trovare la strategia di maggior successo tra tutte le strategie nell’array m_all_strategies. Per farlo, abbiamo usato l’array Performance[], i valori restituiti dalla funzione StrategyPerformance() di ogni strategia vengono inseriti in esso. La classe base CSampleStrategy contiene questa funzione come differenza tra i valori correnti di Equity e Balance "virtuali".

La ricerca dell'indice della strategia di maggior successo viene eseguita utilizzando la funzione ArrayMaximum. Se la migliore strategia ha un profitto negativo al momento e non ha posizioni aperte reali, allora è meglio non fare trading, questo è il motivo per cui usciamo dalla funzione (vedi sezione 1).

Inoltre, chiediamo la direzione della posizione virtuale di questa strategia (best_direction). Se differisce dalla direzione corrente della posizione reale, la direzione corrente della posizione reale verrà corretta (utilizzando la funzione ProceedSignalReal) in base alla direzione best_direction.

2.3. Classe CSampleStrategy

Le strategie collocate nel contenitore m_all_strategies sono state considerate come gli eredi della classe CSampleStrategy.

Questa classe è la base per le strategie di trading; contiene l'implementazione del trading virtuale. In questo articolo,analizzeremo un caso semplificato di implementazione del trading virtuale, gli swap non vengono presi in considerazione. Le classi di strategie di trading devono essere ereditate dalla classe CSampleStrategy.

Mostriamo la struttura di questa classe.

//+------------------------------------------------------------------+
//|                                              CSampleStrategy.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <Object.mqh>

#define POSITION_NEUTRAL   0     // no position
#define POSITION_LONG      1     // long position
#define POSITION_SHORT    -1     // short position

#define SIGNAL_OPEN_LONG    10   // signal to open a long position
#define SIGNAL_OPEN_SHORT  -10   // signal to open a short position
#define SIGNAL_CLOSE_LONG   -1   // signal to close a long position
#define SIGNAL_CLOSE_SHORT   1   // signal to close a short position
//+------------------------------------------------------------------+
//| Structure for storing the parameters of virtual position         |
//+------------------------------------------------------------------+
struct virtual_position
  {
   string            symbol;            // symbol
   int               direction;         // direction of the virtual position (0-no open position,+1 long,-1 short)
   double            volume;            // volume of the position in lots
   double            profit;            // current profit of the virtual position on points
   double            stop_loss;         // Stop Loss of the virtual position
   double            take_profit;       // Take Profit of the virtual position
   datetime          time_open;         // date and time of opening the virtual position
   datetime          time_close;        // date and time of closing the virtual position
   double            price_open;        // open price of the virtual position
   double            price_close;       // close price of the virtual position
   double            price_highest;     // maximum price during the life of the position
   double            price_lowest;      // minimal price during the lift of the position
   double            entry_eff;         // effectiveness of entering
   double            exit_eff;          // effectiveness of exiting
   double            trade_eff;         // effectiveness of deal
  };
//+------------------------------------------------------------------+
//| Class CSampleStrategy                                            |
//+------------------------------------------------------------------+
class CSampleStrategy: public CObject
  {
protected:
   int               m_strategy_id;            // Strategy ID
   string            m_strategy_symbol;        // Symbol 
   string            m_strategy_name;          // Strategy name
   string            m_strategy_comment;       // Comment

   MqlTick           m_price_last;             // Last price
   MqlRates          m_rates[];                // Array for current quotes
   bool              m_virtual_trade_allowed;  // Flag of allowing virtual trading 
   int               m_current_signal_state;   // Current state of strategy
   double            m_current_trade_volume;   // Number of lots for trading
   double            m_initial_balance;        // Initial balance (set in the constructor, default value is 10000)
   int               m_sl_points;              // Stop Loss
   int               m_tp_points;              // Take Profit

   virtual_position  m_position;               // Virtual position
   virtual_position  m_deals_history[];        // Array of deals
   int               m_virtual_deals_total;    // Total number of deals

   double            m_virtual_balance;           // "Virtual" balance
   double            m_virtual_equity;            // "Virtual" equity
   double            m_virtual_cumulative_profit; // cumulative "virtual" profit
   double            m_virtual_profit;            // profit of the current open "virtual" position

   //checks and closes the virtual position by stop levels if it is necessary
   bool              CheckVirtual_Stops(virtual_position &position);
   // recalculation of position and balance
   void              RecalcPositionProperties(virtual_position &position);
   // recalculation of open virtual position in accordance with the current prices 
   void              Position_RefreshInfo(virtual_position &position);
   // open virtual short position
   void              Position_OpenShort(virtual_position &position);
   // closes virtual short position  
   void              Position_CloseShort(virtual_position &position);
   // opens virtual long position
   void              Position_OpenLong(virtual_position &position);
   // closes the virtual long position
   void              Position_CloseLong(virtual_position &position);
   // closes open virtual position  
   void              Position_CloseOpenedPosition(virtual_position &position);
   // adds closed position to the m_deals_history[] array (history of deals)
   void              AddDealToHistory(virtual_position &position);
   //calculates and returns the recommended volume that will be used in trading
   virtual double    MoneyManagement_CalculateLots(double trade_volume);
public:
   // constructor
   void              CSampleStrategy();
   // destructor
   void             ~CSampleStrategy();

   //returns the current size of virtual balance
   double            GetVirtualBalance() { return(m_virtual_balance); }
   //returns the current size of virtual equity
   double            GetVirtualEquity() { return(m_virtual_equity); }
   //returns the current size of virtual profit of open position
   double            GetVirtualProfit() { return(m_virtual_profit); }

   //sets Stop Loss and Take Profit in points
   void              Set_Stops(int tp,int sl) {m_tp_points=tp; m_sl_points=sl;};
   //sets the current volume in lots
   void              SetLots(double trade_volume) {m_current_trade_volume=trade_volume;};
   //returns the current volume in lots
   double            GetCurrentLots() { return(m_current_trade_volume); }

   // returns strategy name
   string            StrategyName() { return(m_strategy_name); }
   // returns strategy ID
   int               StrategyID() { return(m_strategy_id); }
   // returns the comment of strategy
   string            StrategyComment() { return(m_strategy_comment); }
   // sets the details of strategy (symbol, name and ID of strategy)
   void              SetStrategyInfo(string symbol,string name,int id,string comment);

   // set the flag of virtual trading (allowed or not)
   void              SetVirtualTradeFlag(bool pFlag) { m_virtual_trade_allowed=pFlag; };
   // returns flag of allowing virtual trading
   bool              IsVirtualTradeAllowed() { return(m_virtual_trade_allowed); };

   // returns the current state of strategy
   int               GetSignalState();
   // sets the current state of strategy (changes virtual position if necessary)
   void              SetSignalState(int state);
   // changes virtual position in accordance with the current state 
   void              ProceedSignalState(virtual_position &position);

   // sets the value of cumulative "virtual" profit
   void              SetVirtualCumulativeProfit(double cumulative_profit) { m_virtual_cumulative_profit=cumulative_profit; };

   //returns the effectiveness of strategy ()
   double            StrategyPerformance();

   //updates position data
   void              UpdatePositionData();
   //closes open virtual position
   void              CloseVirtualPosition();
   //returns the direction of the current virtual position
   int               PositionDirection();
   //virtual function of initialization
   virtual int       Initialization() {return(0);};
   //virtual function of checking trade conditions
   virtual bool      CheckTradeConditions() {return(false);};
   //virtual function of deinitialization
   virtual int       Deinitialization() {return(0);};

   //saves virtual deals to a file
   void              SaveVirtualDeals(string file_name);
  };

Non analizzeremo la sua descrizione dettagliata, ulteriori informazioni possono essere trovate nel file CSampleStrategy.mqh. Lì puoi anche trovare la funzione di controllo della nuova barra - IsNewBar.


3. Classi di Strategie di Trading

Questa sezione è dedicata alla struttura delle classi di strategie di trading utilizzate nell'Expert Advisor adattivo.

3.1. Classe CStrategyMA - Strategia di Trading da Medie Mobili

//+------------------------------------------------------------------+
//|                                                  CStrategyMA.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <CSampleStrategy.mqh>
//+------------------------------------------------------------------+
//| Class CStrategyMA for implementation of virtual trading          |
//| by the strategy based on moving average                          |
//+------------------------------------------------------------------+
class CStrategyMA : public CSampleStrategy
  {
protected:
   int               m_handle;     // handle of the Moving Average (iMA) indicator
   int               m_period;     // period of the Moving Average indicator
   double            m_values[];   // array for storing values of the indicator
public:
   // initialization of the strategy
   int               Initialization(int period,bool virtual_trade_flag);
   // deinitialization of the strategy
   int               Deinitialization();
   // checking trading conditions and opening virtual positions
   bool              CheckTradeConditions();
  };

La classe CStrategyMA è un figlio della classe CSampleStrategy in cui è implementata l'intera funzionalità del trading virtuale.

La sezione protetta contiene variabili interne che verranno utilizzate nella classe della strategia. Questi sono: m_handle - handle dell'indicatore iMA, m_period - periodo della media mobile, m_values [] - array che verrà utilizzato nella funzione CheckTradeConditionsper ottenere i valori correnti dell'indicatore.

La sezione pubblica contiene tre funzioni che forniscono l'attuazione della strategia di trading.

  1. Il segnale per l'apertura di una posizione lunga (SIGNAL_OPEN_LONG)
  2. Il segnale per l'apertura di una posizione corta (SIGNAL_OPEN_SHORT)
  3. Il segnale per la chiusura di una posizione lunga (SIGNAL_CLOSE_LONG)
  4. Il segnale per la chiusura di una posizione corta (SIGNAL_CLOSE_SHORT)
//+------------------------------------------------------------------+
//| Strategy Initialization Method                                   |
//+------------------------------------------------------------------+
int CStrategyMA::Initialization(int period,bool virtual_trade_flag)
  {
   // set period of the moving average
   m_period=period;
   // set specified flag of virtual trading
   SetVirtualTradeFlag(virtual_trade_flag);

   //set indexation of arrays like the one of timeseries
   ArraySetAsSeries(m_rates,true);
   ArraySetAsSeries(m_values,true);
   
   //create handle of the indicator
   m_handle=iMA(_Symbol,_Period,m_period,0,MODE_EMA,PRICE_CLOSE);
   if(m_handle<0)
     {
      Alert("Error of creation of the MA indicator - error number: ",GetLastError(),"!!");
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Strategy Deinitialization Method                                 |
//+------------------------------------------------------------------+
int CStrategyMA::Deinitialization()
  {
   Position_CloseOpenedPosition(m_position);
   IndicatorRelease(m_handle);
   return(0);
  };
//+------------------------------------------------------------------+
//| Checking trading conditions and opening virtual positions        |
//+------------------------------------------------------------------+
bool CStrategyMA::CheckTradeConditions()
  {
   RecalcPositionProperties(m_position);
   double p_close;

   // get history data of the last three bars
   if(CopyRates(_Symbol,_Period,0,3,m_rates)<0)
     {
      Alert("Error of copying history data - error:",GetLastError(),"!!");
      return(false);
     }
   // Copy the current price of closing of the previous bar (it is bar 1)
   p_close=m_rates[1].close;  // close price of the previous bar          

   if(CopyBuffer(m_handle,0,0,3,m_values)<0)
     {
      Alert("Error of copying buffers of the Moving Average indicator - error number:",GetLastError());
      return(false);
     }

   // buy condition 1: MA rises
   bool buy_condition_1=(m_values[0]>m_values[1]) && (m_values[1]>m_values[2]);
   // buy condition 2: previous price is greater than the MA
   bool buy_condition_2=(p_close>m_values[1]);

   // sell condition 1: // MA falls
   bool sell_condition_1=(m_values[0]<m_values[1]) && (m_values[1]<m_values[2]);
   // sell condition 2: // previous price is lower than the MA   
   bool sell_condition_2=(p_close<m_values[1]);

   int new_state=0;

   if(buy_condition_1  &&  buy_condition_2) new_state=SIGNAL_OPEN_LONG;
   if(sell_condition_1 && sell_condition_2) new_state=SIGNAL_OPEN_SHORT;

   if((GetSignalState()==SIGNAL_OPEN_SHORT) && (buy_condition_1 || buy_condition_2)) new_state=SIGNAL_CLOSE_SHORT;
   if((GetSignalState()==SIGNAL_OPEN_LONG) && (sell_condition_1 || sell_condition_2)) new_state=SIGNAL_CLOSE_LONG;

   if(GetSignalState()!=new_state)
     {
      SetSignalState(new_state);
     }

   return(true);
  };

Il concetto è semplice: sulla base degli stati degli indicatori e dei prezzi, viene determinato il tipo di segnale (new_state), quindi viene richiesto lo stato corrente del trading virtuale (utilizzando la funzione GetSignalState); se non sono gli stessi, viene chiamata la funzione SetSignalState per "correggere" la posizione virtuale.

3.2. Classe CStrategyStoch - la Strategia di Trading di Stochastic

Il codice della classe che esegue il trading sulla base dell'intersezione delle linee principali e del segnale dell'oscillatore iStocastico viene riportato di seguito:

//+------------------------------------------------------------------+
//|                                               CStrategyStoch.mqh |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#include <CSampleStrategy.mqh>
//+------------------------------------------------------------------+
//| Class CStrategyStoch for implementation of virtual trading by    |
//| the strategy of intersection of lines of stochastic oscillator   |
//+------------------------------------------------------------------+
class CStrategyStoch : public CSampleStrategy
  {
protected:
   int               m_handle;          // handle of the Stochastic Oscillator (iStochastic)
   int               m_period_k;        // K-period (number of bars for calculations)
   int               m_period_d;        // D-period (period of primary smoothing)
   int               m_period_slowing;  // final smoothing
   double            m_main_line[];     // array for storing indicator values
   double            m_signal_line[];   // array for storing indicator values
public:
   // initialization of strategy
   int               Initialization(int period_k,int period_d,int period_slowing,bool virtual_trade_flag);
   // deinitialization of strategy
   int               Deinitialization();
   // checking trading conditions and opening virtual positions
   bool              CheckTradeConditions();
  };
//+------------------------------------------------------------------+
//| Strategy Initialization Method                                   |
//+------------------------------------------------------------------+
int CStrategyStoch::Initialization(int period_k,int period_d,int period_slowing,bool virtual_trade_flag)
  {
   // Set period of the oscillator
   m_period_k=period_k;
   m_period_d=period_d;
   m_period_slowing=period_slowing;

   // set specified flag of the virtual trading
   SetVirtualTradeFlag(virtual_trade_flag);

   // set indexation of arrays like the one of timeseries
   ArraySetAsSeries(m_rates,true);
   ArraySetAsSeries(m_main_line,true);
   ArraySetAsSeries(m_signal_line,true);

   // create handle of the indicator
   m_handle=iStochastic(_Symbol,_Period,m_period_k,m_period_d,m_period_slowing,MODE_SMA,STO_LOWHIGH);
   if(m_handle<0)
     {
      Alert("Error of creating the Stochastic indicator - error number: ",GetLastError(),"!!");
      return(-1);
     }

   return(0);
  }
//+------------------------------------------------------------------+
//| Strategy Deinitialization Method                                 |
//+------------------------------------------------------------------+
int CStrategyStoch::Deinitialization()
  {
   // close all open positions
   Position_CloseOpenedPosition(m_position);
   // release handle of the indicator
   IndicatorRelease(m_handle);
   return(0);
  };
//+------------------------------------------------------------------+
//| Checking Trading Conditions and Opening Virtual Positions        |
//+------------------------------------------------------------------+
bool CStrategyStoch::CheckTradeConditions()
  {
   // call the functions of recalculation of position parameters
   RecalcPositionProperties(m_position);
   double p_close;

   // get history  data of the last 3 bars 
   if(CopyRates(_Symbol,_Period,0,3,m_rates)<0)
     {
      Alert("Error of copying history data - error:",GetLastError(),"!!");
      return(false);
     }
   // copy the current close price of the previous bar (it is bar 1)
   p_close=m_rates[1].close;  // close price of the previous bar          

   if((CopyBuffer(m_handle,0,0,3,m_main_line)<3) || (CopyBuffer(m_handle,1,0,3,m_signal_line)<3))
     {
      Alert("Error of copying buffers of the Stochastic indicator - error number:",GetLastError());
      return(false);
     }

   // buy condition: crossing the signal line by the main one from bottom up
   bool buy_condition=((m_signal_line[2]<m_main_line[2]) && (m_signal_line[1]>m_main_line[1]));
   // sell condition: crossing the signal line by the main one from top downwards
   bool sell_condition=((m_signal_line[2]>m_main_line[2]) && (m_signal_line[1]<m_main_line[1]));

   int new_state=0;

   if(buy_condition) new_state=SIGNAL_OPEN_LONG;
   if(sell_condition) new_state=SIGNAL_OPEN_SHORT;

   if((GetSignalState()==SIGNAL_OPEN_SHORT) && (buy_condition)) new_state=SIGNAL_CLOSE_SHORT;
   if((GetSignalState()==SIGNAL_OPEN_LONG) && (sell_condition)) new_state=SIGNAL_CLOSE_LONG;

   if(GetSignalState()!=new_state)
     {
      SetSignalState(new_state);
     }

   return(true);
  };

Come vedi, le uniche differenze tra la struttura della classe CStrategyStoch e quella di CStrategyMA sono la funzione di inizializzazione (parametri diversi), il tipo di indicatore utilizzato e i segnali di trading.

Pertanto, per utilizzare le strategie nell'Expert Advisor adattivo, è necessario riscriverle sotto forma di classi di tale tipo e caricarle nel contenitore m_all_strategies.


4. Risultati dell'Analisi delle Strategie di Trading Adattive

In questa sezione, discuteremo diversi aspetti dell'uso pratico delle strategie adattive e dei metodi per migliorarle.

4.1. Migliorare il Sistema con Strategie che Utilizzano Segnali Inversi

Le Medie Mobili non sono buone quando non ci sono tendenze. Abbiamo già incontrato questo tipo di situazione - nella figura 3, puoi vedere che non c'è stata alcuna tendenza nel periodo dall'8 al 20 gennaio; quindi tutte e 10 le strategie che utilizzano medie mobili nel trading hanno avuto una perdita virtuale. Il sistema adattivo ha smesso di fare trading a causa dell'assenza di una strategia con una quantità positiva di denaro guadagnato. Esiste un modo per evitare questo effetto negativo?

Aggiungiamo alle nostre 10 strategie (MA_3, MA_13, ... MA_93) altre 10 classi CStrategyMAinv i cui segnali di trading sono invertiti (le condizioni sono le stesse ma SIGNAL_OPEN_LONG / SIGNAL_OPEN_SHORT e SIGNAL_CLOSE_LONG / SIGNAL_CLOSE_SHORT si sono scambiati i loro posti). Pertanto, oltre a dieci strategie di tendenza (istanze della classe CStrategyMA), abbiamo altre dieci strategie di controtendenza (istanze della classe CStrategyMAinv).

Il risultato dell'utilizzo del sistema adattivo, che è composto di venti strategie, è mostrato nella figura 5.

Figura 5. Diagrammi di equità nell’account della strategia adattiva che utilizza 20 segnali di trading: 10 medie mobili CAdaptiveMA e 10 CAdaptiveMAinv "speculari"

Figura 5. Diagrammi di equità nell’account della strategia adattiva che utilizza 20 segnali di trading: 10 medie mobili CAdaptiveMA e 10CAdaptiveMAinv "speculari".

Come si può vedere nella figura 5, durante il periodo in cui tutte le strategie CAdaptiveMA hanno avuto un risultato negativo, seguire le strategie CAdaptiveMAinv ha permesso all'Expert Advisor di evitare drawdown indesiderati proprio all'inizio del trading.

Figura 6. Periodo di tempo in cui la strategia adattiva ha utilizzato i segnali delle strategie CAdaptiveMAinv di "controtendenza"

Figura 6. Periodo di tempo in cui la strategia adattiva ha utilizzato i segnali delle strategie di "controtendenza"CAdaptiveMAinv

Questo tipo di approccio può sembrare inaccettabile, dal momento in cui perdere il deposito è solo una questione di tempo quando si utilizza una strategia di controtendenza. Tuttavia, nel nostro caso, non siamo limitati da un'unica strategia. Il mercato sa meglio quali strategie sono efficaci al momento.

Il punto forte dei sistemi adattivi è che il mercato suggerisce da solo quale strategia utilizzare e quando utilizzarla.

Dà la possibilità di astrarre dalla logica delle strategie: se una strategia è efficace, il modo in cui funziona non ha alcun significato. L'approccio adattivo utilizza l'unico criterio di successo di una strategia: la sua efficacia.

4.2. Vale la Pena Invertire i Segnali della Peggiore Strategia?

Il trucco con inversione mostrato sopra porta a pensare alla potenziale possibilità di utilizzare i segnali della peggiore strategia. Se una strategia non è redditizia (e la peggiore), allora possiamo ottenere un profitto agendo al contrario?

Possiamo trasformare una strategia in perdita in una redditizia con un semplice cambiamento dei suoi segnali? Per rispondere a questa domanda, è necessario modificare ArrayMaximum con ArrayMinimum nella funzione Expert_OnTick() della classe CAdaptiveStrategy, nonché implementare il cambiamento di direzione moltiplicando il valore della variabile BestDirection per -1.

Inoltre, dobbiamo commentare la limitazione del trading virtuale in caso di efficacia negativa (poiché analizzeremo il risultato della peggiore strategia):

//if((Performance[BestStrategyIndex]<0) && (RealPositionDirection()==0)) {return;}

Il diagramma di equità dell'Expert Advisor adattivo che utilizza i segnali invertiti della peggiore strategia è mostrato nella figura 7:

Figura 7. Diagrammi di equità negli account con dieci strategie e il sistema adattivo che utilizza i segnali invertiti del peggior sistema

Figure 7. Diagrammi di equità negli account con dieci strategie e il sistema adattivo che utilizza i segnali invertiti del sistema peggiore

In questo caso, la strategia meno efficace per la maggior parte del tempo è stata quella basata sull'intersezione delle medie mobili con il periodo 3 (MA_3). Come si può vedere nella figura 7, la correlazione inversa tra MA_3 (di colore blu) e la strategia adattiva (di colore rosso) esiste, ma il risultato finanziario del sistema adattivo non colpisce.

Copiare (e invertire) i segnali della peggiore strategia non porta a migliorare l'efficacia del trading.

4.2. Perché il Gruppo di Medie Mobili non è così Efficace come Sembra?

Invece di 10 medie mobili, puoi usarne molte aggiungendo un altro centinaio di strategie CStrategyMA con periodi diversi al contenitore m_all_strategies.

Per farlo, modifica leggermente il codice nella classe CAdaptiveStrategy:

   for(int i=0; i<100; i++)
     {
      CStrategyMA *t_StrategyMA;
      t_StrategyMA=new CStrategyMA;
      if(t_StrategyMA==NULL)
        {
         delete m_all_strategies;
         Print("Error of creation of object of the CStrategyMA type");
         return(-1);
        }
      //set period for each strategy
      int period=3+i*10;
      // initialization of strategy
      t_StrategyMA.Initialization(period,true);
      // set details of the strategy
      t_StrategyMA.SetStrategyInfo(_Symbol,"[MA_"+IntegerToString(period)+"]",period,"Moving Averages "+IntegerToString(period));
      //add the object of the strategy to the array of objects m_all_strategies
      m_all_strategies.Add(t_StrategyMA);
     }

Tuttavia, dovresti capire che le medie mobili vicine si intersecheranno inevitabilmente; il leader cambierà costantemente; e il sistema adattivo cambierà i suoi stati e aprirà/chiuderà le posizioni più frequentemente di quanto non sia necessario. Di conseguenza, le caratteristiche del sistema adattivo peggioreranno. Puoi assicurartene da solo confrontando le caratteristiche statistiche del sistema (il tab "Results" dello strategy tester).

È meglio non creare sistemi adattivi basati su molte strategie con parametri vicini.


5. Cosa Dovrebbe Essere Considerato

Il contenitore m_all_strategies può avere migliaia di istanze di strategie suggerite incluse, puoi persino aggiungere tutte le strategie con parametri diversi; tuttavia, per vincere l’Automated Trading Championship 2010è necessario sviluppare il sistema avanzato di gestione del denaro. Si noti che abbiamo utilizzato il volume di trading pari a 0,1 lotti per i test sui dati storici (e nel codice delle classi) .

5.1 Come Aumentare la Redditività dell'Expert Advisor Adattivo?

La classe CSampleStrategy ha la funzione virtuale MoneyManagement_CalculateLots:

//+------------------------------------------------------------------+
//| The function returns the recommended volume for a strategy       |
//| Current volume is passed to it as a parameter                    |
//| Volume can be set depending on:                                  |
//| current m_virtual_balance and m_virtual_equity                   |
//| current statistics of deals (located in m_deals_history)         |
//| or any other thing you want                                      |
//| If a strategy doesn't require change of volume                   |
//| you can return the passed value of volume:  return(trade_volume);|
//+------------------------------------------------------------------+ 
double CSampleStrategy::MoneyManagement_CalculateLots(double trade_volume)
  {
   //return what we've got 
   return(trade_volume);
  }

Per gestire il volume per il trading, puoi utilizzare le informazioni statistiche sui risultati e le caratteristiche delle operazioni virtuali registrate nell’array m_deals_history[].

Se hai bisogno di aumentare il volume (ad esempio, raddoppiarlo se le ultime operazioni virtuali in m_deals_history[] sono redditizie; o diminuirlo), dovresti modificare il valore restituito nel modo corrispondente.

5.2 Utilizzo delle Statistiche sulle Operazioni per il Calcolo della Performance della Strategia

La funzione StrategyPerformance(), implementata nella classe CSampleStrategy, è destinata al calcolo delle prestazioni della strategia.

//+-----------------------------------------------------------------------+
//| Function StrategyPerformance - the function of strategy effectiveness |
//+-----------------------------------------------------------------------+ 
double CSampleStrategy::StrategyPerformance()
  {
   //returns effectiveness of a strategy
   //in this case it's the difference between the amount
   //of equity at the moment and the initial balance, 
   //i.e. the amount of assets earned by the strategy
   double performance=(m_virtual_equity-m_initial_balance);
   return(performance);
  }

La formula di efficacia di una strategia può essere più complessa e, ad esempio, includere l'efficacia in entrata, uscita, l'efficacia nelle operazioni, profitti, drawdown, ecc.

Il calcolo dell'efficacia in entrata, in uscita e dell'efficacia delle operazioni (i campi entry_eff, exit_eff e trade_eff delle strutture dell'array m_deals_history[]) viene eseguito automaticamente durante il trading virtuale (vedi la classe CSampeStrategy). Queste informazioni statistiche possono essere utilizzate per rendere i propri tassi più complessi dell'efficacia della strategia.

Ad esempio, come caratteristiche di efficacia è possibile utilizzare il profitto delle ultime tre operazioni (utilizzare il campo pos_Profit dall'archivio delle operazioni m_deals_history []):

double CSampleStrategy::StrategyPerformance()
  {
  //if there are deals, multiply this value by the result of three last deals
   if(m_virtual_deals_total>0)
     {
      int avdeals=MathRound(MathMin(3,m_virtual_deals_total));
      double sumprofit=0;
      for(int j=0; j<avdeals; j++)
        {
         sumprofit+=m_deals_history[m_virtual_deals_total-1-j].profit;
        }
      double performance=sumprofit/avdeals;
     }
     return(performance);

  }

Se vuoi cambiare questa funzione, cambiala solo nella classe CSampleStrategy, deve essere la stessa per tutte le strategie di trading del sistema adattivo. Tuttavia, dovresti ricordare che la differenza tra Equity e Balance è anche un buon fattore di efficacia.

5.3 Utilizzo di Take Profit e Stop Loss

È possibile modificare l'efficacia dei sistemi di trading impostando livelli di stop fissi (puoi fare ciò chiamando la funzione Set_Stops; consente di impostare i livelli di stop in punti per il trading virtuale). Se i livelli sono specificati, la chiusura delle posizioni virtuali verrà eseguita automaticamente; questa funzionalità è implementata nella classe CSampleStrategy.

Nel nostro esempio (vedi 2.2, la funzione delle classi di medie mobili), viene commentata la funzione di impostazione dei livelli di stop.

5.4. Azzeramento Periodico del Profitto Virtuale Cumulativo

L'approccio adattivo ha lo stesso svantaggio delle strategie comuni. Se la strategia principale inizia a perdere, anche il sistema adattivo inizia a perdere. Questo è il motivo per cui a volte è necessario "azzerare" i risultati del lavoro di tutte le strategie e chiudere tutte le loro posizioni virtuali. 

A tale riguardo, nella classe CSampleStrategy sono implementate le seguenti funzioni:

// sets a value for cumulative "virtual profit"
 void              SetVirtualCumulativeProfit(double cumulative_profit) { m_virtual_cumulative_profit=cumulative_perofit; };
//closes an open virtual position
 void              CloseVirtualPosition();

CheckPoint di questo tipo può essere utilizzato di volta in volta, ad esempio dopo ogni N barre.

5.5. Nessun miracolo

Dovresti ricordare che il sistema adattivo non è un graal (USDJPY H1, 4.01.2010-20.08.2010):

Figura 8. Curve di saldo e di equità del sistema adattivo che utilizza i segnali della migliore delle 10 strategie (USDJPY H1)

Figura 8. Curve di saldo e di equità del sistema adattivo che utilizza i segnali della migliore delle 10 strategie (USDJPY H1)

Le curve di equità di tutte le strategie sono mostrate nella figura 9.

Figura 9. Curve di equità sull’account con il sistema adattivo basato su 10 strategie (USDJPY H1)

Figura 9. Curve di equità sull’account con il sistema adattivo basato su 10 strategie (USDJPY H1)

Se non ci sono strategie redditizie nel sistema adattivo, usarle non è efficace. Usa strategie redditizie.

Dovremmo considerare un'altra cosa importante ed interessante. Presta attenzione al comportamento della strategia adattiva fin dall'inizio del trading:

Figura 10. Curve di equità sull’account con 10 strategie della strategia adattiva

Figura 10. Curve di equità sull’account con 10 strategie della strategia adattiva

All'inizio, tutte le strategie hanno avuto risultati negativi e la strategia adattiva ha smesso di fare trading; poi ha iniziato a passare da una strategia all'altra che ha avuto un risultato positivo; e poi tutte le strategie sono diventate di nuovo non redditizie.

Tutte le strategie hanno lo stesso saldo all'inizio. E solo dopo un po’ di tempo, l'una o l'altra strategia diventa un leader; quindi si consiglia di impostare una limitazione nella strategia adattiva per evitare il trading alle prime barre. Per fare ciò, integrare la funzione Expert_OnTick della classe CAdaptiveStrategy con una variabile, il cui valore viene aumentato ogni volta che arriva una nuova barra.

All'inizio, fino a quando il mercato non sceglie la strategia migliore, dovresti stare lontano dal trading reale.


Conclusioni

In questo articolo, abbiamo considerato un esempio del sistema adattivo che consiste in molte strategie, ognuna delle quali effettua le proprie operazioni di trading "virtuali". Il trading reale viene eseguito in conformità con i segnali di una strategia più redditizia al momento.

Grazie all'utilizzo dell'approccio orientato agli oggetti, alle classi per lavorare con i dati e alle classi di trading della libreria Standard, l'architettura del sistema sembrava essere semplice e scalabile; ora è puoi creare e analizzare facilmente i sistemi adattivi che includono centinaia di strategie di trading.

P.S. Per l'analisi del comportamento dei sistemi adattivi, viene allegata la versione di debug della classe CSampleStrategy (l'archivio adaptive-systems-mql5-sources-debug-en.zip). La differenza di questa versione è la creazione di file di testo durante il suo funzionamento; contengono i report di sintesi sulle dinamiche di cambiamento del saldo/equità virtuale delle strategie incluse nel sistema.