Funzioni Mql4 scritte bene: il trailing stop

È innegabile che il trailing stop sia un elemento importante nella nostra attività, e il trailing offerto “in bundle” sulla MetaTrader è un pochino scrauso.

Se non lo sapessi ancora, il trailing stop serve per seguire “in scia” il prezzo, ad una distanza prefissata ed eventualmente a blocchi di tot punti. Questo permette, specie nelle strategie momentum, di portarsi a casa movimenti importanti senza avere il limite dello stoploss.

In altre situazioni, il trailing stop può aiutare a migliorare la curva dell’equity, limitando il range di perdita e assicurandosi una porzione di profitti sempre maggiore.

Immagina di avere un’operazione a mercato con Stop a 50 ticks e Take a 250 ticks. Puoi decidere che i 250 ticks siano il target primario, ma che preferisci assicurartene 25 piuttosto che incassare la perdita.

In questo caso, la funzione trailing stop che ti vado a presentare sarà un’alleata fedele e scoprirai anche come evitare difetti di esecuzione.

Dichiariamo la funzione Trailing Stop

Apri il tuo editor Mql, e dichiara una funzione di tipo bool. Chiamala come vuoi, ma ti consiglio “Trailing Stop”, ahahahha. Naturalmente vogliamo una funzione “Agnostica”, per cui inseriamo come parametri simbolo e magic number. Dovremo sapere anche a che distanza vogliamo tenere il trailing, ed eventualmente ogni quanti ticks vogliamo attivare lo spostamento. Mettiamo come default zero in modo da poter chiamare la funzione anche senza questo parametro opzionale.

bool TrailingStop(string _s,int _mn, int _trail, int _step=0)
   {
   }

Inseriamo quindi una serie di variabili che ci serviranno per fare le nostre porcherie con i calcoli: ci salviamo ask, bid, point, ticksize e digits (ovvero quanti decimali ha il simbolo che stiamo tradando).

bool TrailingStop(string _s,int _mn, int _trail, int _step=0)
   {
   double _ask=MarketInfo(_s,MODE_ASK);
   double _bid=MarketInfo(_s,MODE_BID);

   int _dgts=(int)MarketInfo(_s,MODE_DIGITS);
   double _pnt=MarketInfo(_s,MODE_POINT);
   double _ts=MarketInfo

   }

Iniziamo dai casi limite: se non siamo in condizione di tradare, evitiamo di perdere tempo con il resto della logica. Inoltre inseriamo subito un return true, in tal modo se arriviamo al termine della funzione significa che abbiamo risolto il trail e siamo a posto.

trailing stop
evviva il trailing stop
bool TrailingStop(string _s,int _mn, int _trail, int _step=0)
   {
   double _ask=MarketInfo(_s,MODE_ASK);
   double _bid=MarketInfo(_s,MODE_BID);

   int _dgts=(int)MarketInfo(_s,MODE_DIGITS);
   double _pnt=MarketInfo(_s,MODE_POINT);
   if(!IsTradeAllowed())
     {
      Print("Non ho fatto il trail perché c'è un problema con il broker, codice errore: 
            "+IntegerToString(GetLastError()));
      return false;
     }

   // -- Spazio per il resto del codice 

   return true;
   }

Creiamo la logica di analisi del prezzo

Prendiamo il caso di un long: se il prezzo attuale supera il prezzo di apertura di almeno XXX ticks, allora sappiamo che dobbiamo spostare il prezzo.
Questo è il caso più semplice, in realtà noi vogliamo anche la possibilità di decidere ogni quanti ticks spostare lo stop loss.

Allora dobbiamo chiederci: il prezzo Bid (quando usciamo da un trade long paghiamo il bid, mi raccomando…) è distante ALMENO XXX ticks dal prezzo di attivazione del trailing?

Cos’è questo prezzo di attivazione del trailing?

Se voglio che il trail funzioni a qualsiasi condizione, allora non mi importa di trovare il prezzo di attivazione: questo prezzo è semplicemente il livello di stoploss attualmente in macchina per quell’ordine.

Ma se volessi spostare lo stoploss di 25 ticks ogni 50 ticks di movimento? Ecco che allora ci serve il prezzo di attivazione del trailing stop.

Altro non è che l’attuale stoploss MAGGIORATO del nostro trailing STEP (ovvero i nostri “scalini” di trailing) .

Scriviamolo, iniziando a ciclare gli ordini in macchina per trovare il nostro:

bool TrailingStop(string _s,int _mn, int _trail, int _step=0)
   {
   double _ask=MarketInfo(_s,MODE_ASK);
   double _bid=MarketInfo(_s,MODE_BID);

   int _dgts=(int)MarketInfo(_s,MODE_DIGITS);
   double _pnt=MarketInfo(_s,MODE_POINT);
   if(!IsTradeAllowed())
     {
      Print("Non ho fatto il trail perché c'è un problema con il broker, codice errore: 
            "+IntegerToString(GetLastError()));
      return false;
     }

   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        {
         Print("Failed to select Order for Trail, Error: "+IntegerToString(GetLastError()));
        }
     }
   else
     {
      if(OrderSymbol()==_s && OrderMagicNumber()==_mn)
        {
         if(OrderType()==OP_BUY)
           {
            // logica per il buy 
           }
         if(OrderType()==OP_SELL)
           {
            //logica per il sell
           }
        }
     }
   return true;
   }

Perfetto, abbiamo predisposto il nostro amato codice di ciclo. Ora dobbiamo scriver la logica buy.

  1. Sottraiamo al bid la distanza in ticks che vogliamo mantenere;
  2. Aggiungiamo all”attuale stoploss la distanza minima che vogliamo prima di attivare il trailing stop (il nostro prezzo di attivazione)
  3. fine, poi ti spiego come facciamo a modificare l’ordine con un do-while da vero PRO!

Nella variabile newSL vado a salvarmi il valore teorico del trailing ottenuto da BID – _trail, mentre in _prezzoAttivazione vado a salvarmi, guarda un po’, il prezzo di attivazione 😀

bool TrailingStop(string _s,int _mn, int _trail, int _step=0)
   {
   double _ask=MarketInfo(_s,MODE_ASK);
   double _bid=MarketInfo(_s,MODE_BID);

   int _dgts=(int)MarketInfo(_s,MODE_DIGITS);
   double _pnt=MarketInfo(_s,MODE_POINT);
   if(!IsTradeAllowed())
     {
      Print("Non ho fatto il trail perché c'è un problema con il broker, codice errore: 
            "+IntegerToString(GetLastError()));
      return false;
     }

   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        {
         Print("Failed to select Order for Trail, Error: "+IntegerToString(GetLastError()));
        }
     }
   else
     {
      if(OrderSymbol()==_s && OrderMagicNumber()==_mn)
        {
         if(OrderType()==OP_BUY)
           {
            double _newSL=_bid-_trail*_pnt;
            double _prezzoAttivazione = OrderStopLoss()+_step*_pnt;
            
           }
         if(OrderType()==OP_SELL)
           {
            //logica per il sell
           }
        }
     }
   return true;
   }

Normalizziamo i prezzi

Ok, fermi tutti. Ora dobbiamo fare in modo che il broker non ci rompa l’anima se consegniamo un ordine con prezzo che non può ricevere. Se ad esempio siamo sul DAX e il broker prende ordini solo con prezzi con virgola mobile indicizzati al ticksize da 0.05, la nostra richiesta di spostare lo stoploss al prezzo 12429.43 verrà rifiutata. Noi non lo vogliamo. Perché abbiamo un pessimo rapporto con il rifiuto. Parlane con il tuo psicologo, in caso, ma per ora restiamo sul codice.

Ah, volendo puoi incapsulare questa utility in un define o in una funzione, in modo da poterla richiamare ogni volta che vuoi. Nel mio caso, FUORI dalla funzione che stiamo scrivendo, ho salvato la funzione di normalizzazione così:

double NormalizzaPrezzo(double v, double to)
  {
   return to * MathRound(v / to);
  }

Ed ecco quindi come si presenta il codice ora:

bool TrailingStop(string _s,int _mn, int _trail, int _step=0)
   {
   double _ask=MarketInfo(_s,MODE_ASK);
   double _bid=MarketInfo(_s,MODE_BID);

   int _dgts=(int)MarketInfo(_s,MODE_DIGITS);
   double _pnt=MarketInfo(_s,MODE_POINT);
   double _ts = MarketInfo(_s,MODE_TICKSIZE);

   if(!IsTradeAllowed())
     {
      Print("Non ho fatto il trail perché c'è un problema con il broker, codice errore: 
            "+IntegerToString(GetLastError()));
      return false;
     }

   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        {
         Print("Failed to select Order for Trail, Error: "+IntegerToString(GetLastError()));
        }
        else
        {
         if(OrderSymbol()==_s && OrderMagicNumber()==_mn)
           {
            if(OrderType()==OP_BUY)
              {
               double _newSL=NormalizzaPrezzo(_bid-_trail*_pnt,_ts);
               double _prezzoAttivazione = NormalizzaPrezzo(OrderStopLoss()+_step*_pnt, _ts);

              }
            if(OrderType()==OP_SELL)
              {
               //logica per il sell
              }
           }
        }
     }
   return true;
   }

Condizioni per spostare lo stoploss

Ora che abbiamo il livello di stoploss teorico e il prezzo di attivazione, dobbiamo chiederci: il bid ha superato questo livello? E contemporaneamente il nuovo stoploss è maggiore del prezzo di attivazione?

Se sì, controlliamo l’ultima volta che il nuovo stoploss sia maggiore dello stoploss dell’ordine e inviamo l’ordine al broker tramite OrderModify.

// -- Dentro l'IF (OrderType()==OP_BUY) 

   double _newSL=NormalizzaPrezzo(_bid-_trail*_pnt,_ts);
   double _prezzoAttivazione = NormalizzaPrezzo(OrderStopLoss()+_step*_pnt,_ts);
      if(_newSL > _prezzoAttivazione)
        {
         if(_newSL>OrderStopLoss())
            {
            bool mod=OrderModify(OrderTicket(),OrderOpenPrice(),newSL,OrderTakeProfit(),0);
            }
         }

Eccoci! Abbiamo salvato l’esito di OrderModify in una bool (mod) perché in teoria, se dovesse fallire l’ordermodify, riceveremmo un false come risposta e dovremmo controllare quindi l’esito.

L’utilizzo di un do-while per essere sicuri dell’esecuzione

Incapsuliamo allora la funzione OrderModify() in un ciclo do-while, che si può tradurre con:
FAI questa cosa FINO A CHE SUSSISTE questa condizione.

Nel nostro caso, dobbiamo dire:
FAI l’OrderModify fino a che non ottieni una risposta positiva oppure sono scaduti X tentativi. (non vogliamo impallare la MetaTrader in caso di un loop infinito…)

In testa alla funzione inseriamo:

bool TrailingStop(string _s,int _mn,int _trail, int _step=0)
  {
   int _try=1;
   bool mod=false;


   // resto del codice
}

E wrappiamo l’ordermodify in un ciclo do-while:

 do
   {
     mod=OrderModify(OrderTicket(),OrderOpenPrice(),newSL,OrderTakeProfit(),0);
     Print("Trailing SL, tentativo n°: " + IntegerToString(_try));
     _try++;
    }
   while(mod == 0 && _try<=10);

Partiamo a contare da 1 il _try (tentativi) e saliamo ogni volta che mandiamo l’OrderModify().
Se risponde True, allora mod sarà diverso da zero, invalidando la condizione di while.

Finito!

Andiamo a riscrivere la stessa logica anche per la short side e siamo a posto.

Natualmente, se come step lasciamo lo zero di default, allora si comporterà come un trailing stop normale. Se invece inseriremo, ad esempio, 50, allora il trailing avverrà ogni 50 ticks, spostandosi dinamicamente.

Codice completo del trailing stop

Voilà. Spero per te che tu ti sia messo a scrivere e non a far copia e incolla, perché in questo modo puoi davvero imparare e fare tua la logica di impostazione di questa funzione.

bool TrailingStop(string _s,int _mn,int _trail, int _step=0)
  {
   int _try=1;
   bool mod=0;

   double _ask=MarketInfo(_s,MODE_ASK);
   double _bid=MarketInfo(_s,MODE_BID);

   int _dgts=(int)MarketInfo(_s,MODE_DIGITS);
   double _pnt=MarketInfo(_s,MODE_POINT);
   double _ts = MarketInfo(_s,MODE_TICKSIZE);
  
   double _newSL=0,_prezzoAttivazione=0;

   if(!IsTradeAllowed())
     {
      Print("Non ho fatto il trail perché c'è un problema con il broker, codice errore: "+IntegerToString(GetLastError()));
      return false;
     }

   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES))
        {
         Print("Failed to select Order for Trail, Error: "+IntegerToString(GetLastError()));
        }
      else
        {
         if(OrderSymbol()==_s && OrderMagicNumber()==_mn)
           {
            if(OrderType()==OP_BUY)
              {
               _newSL=NormalizzaPrezzo(_bid-_trail*_pnt,_ts);
               _prezzoAttivazione = NormalizzaPrezzo(OrderStopLoss()+_step*_pnt,_ts);

               if(_newSL > _prezzoAttivazione)
                 {
                  if(_newSL>OrderStopLoss())
                    {
                     do
                       {
                        mod=OrderModify(OrderTicket(),OrderOpenPrice(),newSL,OrderTakeProfit(),0);
                        Print("Trailing SL, tentativo n°: " + IntegerToString(_try));
                        _try++;
                       }
                     while(mod == 0 && _try<=10);
                    }
                 }

              }
            if(OrderType()==OP_SELL)
              {
               _newSL=NormalizzaPrezzo(_ask+_trail*_pnt, _ts);
               _prezzoAttivazione = NormalizzaPrezzo(OrderStopLoss()-_step*_pnt,_ts);

               if(_newSL < _prezzoAttivazione)
                 {
                  if(_newSL<OrderStopLoss())
                    {
                     do
                       {
                        mod=OrderModify(OrderTicket(),OrderOpenPrice(),newSL,OrderTakeProfit(),0);
                        Print("Trailing SL, tentativo n°: " + IntegerToString(_try));
                        _try++;
                       }
                     while(mod == 0 && _try<=10);
                    }
                 }
              }
           }
        }
     }
   return true;
  }

Ulteriori sviluppi

Questo codice ti può servire per tutti i tuoi EA, ricordati di richiamarlo una volta ogni barra, oppure, se vuoi scalpare in modo brutale, puoi richiamarlo ad ogni tick.

Da questo codice ho derivato alcuni trailing interessanti, per esempio puoi cambiare le condizioni per fare un trailing in percentuali. Oppure un trailing sui fractals, sul SuperTrend o ancora dinamizzando il livello di stoploss con l’ATR (in tal caso si chiama Chandelier Trailing Stop).

Potresti provare ad implementarla usando il Donchian channel.

L’unico limite è la tua fantasia e la tua capacità ma sono sicuro che con questa funzione (“SCRITTA BENE“, ahahah) tu possa portare ad un livello più alto il tuo trading automatico.

Per scrivere questo articolo mi sono andate via parecchie ore (oltre al fatto che ti sto praticamente regalando le funzioni che stanno nella mia libreria di produzione, la mitica Woollib!) se ti va, puoi sganciare un like alla pagina facebook Automazione Trading, seguire il canale YouTube e raggiungere il gruppo gratuito Telegram.

Fossi in te ci farei un pensierino. 😀

Alla prossima e al prossimo #greattradingtool!

Leave a Reply