mercoledì 10 novembre 2010

Gestire il Trailing Stop con MQL

Il Trailing Stop è una delle tecniche maggiormente utilizzate nel trading perchè permette di spostare lo stop loss in profitto quando una posizione inizia a guadagnare. In poche parole rappresenta la distanza, in pips, a cui deve essere impostato lo stop rispetto all'attuale prezzo del cross.

La piattaforma metatrader permette di attivare rapidamente il trailing stop semplicemente andando a selezionare, con il tasto destro del mouse, un ordine aperto:

Trailing Stop in Metatrader

E se volessimo inserire questa funzionalità all'interno di un expert advisor? Esiste già una funzione built-in di metatrader che si occupa di modificare lo stop loss a nostro piacere?

La risposta alla seconda domanda è no, ma possiamo rimediare facilmente con poche righe di codice:

extern   double   e_TrailingStop             = 35;
extern   double   e_TrailingStep             = 5;
...
...
void TrailingStop(){
   if (OrderType() == OP_BUY)
    if ((Bid - OrderOpenPrice()) > (e_TrailingStop * Point))
      if (OrderStopLoss() < Bid - (e_TrailingStop + e_TrailingStep - 1) * Point) {
         OrderModify(OrderTicket(), OrderOpenPrice(), Ask - e_TrailingStop * Point, OrderTakeProfit(), 0, Green);
         return;
      }
  if (OrderType()==OP_SELL)
    if ((OrderOpenPrice() - Ask) > (e_TrailingStop * Point))
      if (OrderStopLoss() > Ask + (e_TrailingStop + e_TrailingStep - 1) * Point || OrderStopLoss()==0) {
         OrderModify(OrderTicket(), OrderOpenPrice(), Ask + e_TrailingStop * Point, OrderTakeProfit(), 0, Red);
         return;
      }
}

La funzione utilizza due variabili esterne:

  • TrailingStop: la distanza a cui deve essere impostato lo stop loss
  • TrailingStep: ogni quanto deve essere modificato lo stop loss
Il secondo parametro serve più che altro per evitare di modificare l'ordine in continuazione (con i conseguenti rallentamenti lato server) e si occuperà di spostare lo stop loss ad esempio di 5 pips alla volta e non ad ogni singola variazione.

Nel caso in cui il vostro expert advisor gestisca una singola posizione alla volta potete inserire la chiamata alla funzione TrailingStop() subito dopo lo start():

int v_total=OrdersTotal();
   if(v_total<1) {
      // no opened orders identified
      if(AccountFreeMargin()<(1000*e_Lots)){
         Print("We have no money. Free Margin = ", AccountFreeMargin());
         return(0);  
      }
   }
   else{
     int v_i = 0;
     while (v_i < v_total){
      //posizioni aperte della linea del cuore? (ricerca per MagicID)
      if((OrderSelect(v_i, SELECT_BY_POS, MODE_TRADES)==true) && (OrderMagicNumber() == e_MagicID)){
         if (e_TrailingStop > 0){
            //gestione trailing stop
            TrailingStop();
            return(0);
         }
      }   
      v_i++;
     }
   }
 

17 commenti:

Ale L ha detto...

Ciao Carlo! Eccomi di nuovo qui :)
Se invece volessimo impostare un trailing stop che si aggiorna automaticamente ad ogni candela settando lo stoploss al minimo della candela precedente? E' fattibile?

carlo10 ha detto...

Ciao,

è fattibile ma ovviamente bisogna effettuare delle operazioni e dei controlli diversi.

Se provi a buttare giù il codice ti do una mano in caso ti blocchi.

Ale L ha detto...

Ok allora ci provo! Se ho capito qualcosa da quel poco che ho studiato, non possiamo sfruttare la funzione OrderSend, inserendo nella voce relativa allo stoploss "Low[1]": in questo modo il programma imposta lo stoploss al minimo della candela precedente, ma non si aggiorna automaticamente al cambiare della candela. Invece servirebbe una funzione che riconosca la candela attuale. Immagino che esista, ora faccio una ricerca...appena metto giù qualcosa ti faccio sapere! Grazie per il supporto :)

carlo10 ha detto...

Forse questo articolo ti può dare uno spunto su come individuare una nuova candela:

http://metatrader-forex-trading.blogspot.com/2010/11/individuare-una-nuova-candela-in.html

Una volta che ti trovi in una nuova candela puoi spostare lo stop con la funzione OrderModify.

E' importante però che effettui dei controlli sul prezzo a cui intendi spostarlo rispetto al prezzo attuale del cross.

Ale L ha detto...

Ciao Carlo. Ho letto l'articolo su come individuare una nuova candela e ho provato a implementare la strategia di trailing stop con OrderModify. Ti scrivo come ho strutturato il programma:

_____________________________________________
extern double lots=0.1;
...
...
static datetime g_lastCandleOpenTime;

int init()
{
g_lastCandleOpenTime = Time[0];
}


int start()
{

...



// condizione di apertura ordine:

if (........)
{
ticket=OrderSend("EURUSD",OP_BUY,lots,Ask,3,Low[1],High[1]+takeprofit*Point,"Compro",e_MagicID,0,Green);
}

//gestione trailing stop

if (isNewCandle())
{
if (OrderType() == OP_BUY)
{
OrderModify(OrderTicket(), OrderOpenPrice(), Low[1], OrderTakeProfit(), 0, Blue);
}
}

return(0);
}

// Funzione che verifica se siamo su una nuova candela

bool isNewCandle()
{
//TRUE nuova candela
//FALSE vecchia candela

bool v_isNewCandle = false;
//se la candela restituita è la 0 è quella già memorizzata
int v_shift = iBarShift(NULL, 0, g_lastCandleOpenTime, true);

if (v_shift == 0)
v_isNewCandle = false;
else{
v_isNewCandle = true;
//memorizzo l'orario della nuova candela
g_lastCandleOpenTime = Time[0];
}

return (v_isNewCandle);
}
________________________________________________

Ho provato a fare il backtest in metatrader ma non mi sembra che faccia quello che mi aspetto (aggiornare lo stoploss al minimo della candela precedente). Forse manca qualcosa?

carlo10 ha detto...

Solitamente la gestione degli ordini aperti e quindi del trailing stop viene effettuata nel blocco che precede l'apertura di nuovi ordini. Questo potrebbe essere un motivo per cui non ti entra in quella parte del codice.

Ovviamente hai postato giustamente solo una porzione del codice quindi non è detto che il problema sia quello.

Quello che ti consiglio di fare per prima cosa è:

1) Verificare che nei log del backtest (sotto la voce diario) vengono riporati errori o messaggi

2) Inserire dei Print prima e dopo la modifica dell'ordine per vedere se effettivamente il programma entra in quella parte del codice

Fammi sapere

Ale L ha detto...

Ciao Carlo, ho seguito il tuo consiglio: ho inserito dei print e ho verificato che vengono stampati nel diario quindi suppongo che la parte del codice viene letta. Però nel diario compaiono anche diversi messaggi del tipo:

"Unknown ticket 62 for OrderModify function"
"OrderModify error 4108"
"Unknown ticket 63 for OrderModify function"
"OrderModify error 4108"
.......
.......
.......

Vuol dire che non riconosce l'ordine che deve modificare?

carlo10 ha detto...

Si, probabilmente perchè non li hai selezionati nella maniera corretta.

Una possibile gestione degli ordini aperti potrebbe essere la seguente:



//Gestioni ordini aperti
int v_total=OrdersTotal();
if(v_total<1) {
// no opened orders identified
if(AccountFreeMargin()<(1000*e_Lots)){
Print("We have no money. Free Margin = ", AccountFreeMargin());
return(0);
}
}
else{
int v_i = 0;
while (v_i < v_total){
//posizioni aperte?
if((OrderSelect(v_i, SELECT_BY_POS, MODE_TRADES)==true)){
if (isNewCandle()){
//modifica ordine
OrderModify(OrderTicket(), OrderOpenPrice(), Low[1], OrderTakeProfit(), 0, Blue);
return(0);
}
}
v_i++;
}
}



In pratica devi effettuare un ciclo su tutti gli ordini aperti e verificare quindi se lanciare l'orderModify.

Adattala al tuo expert.

Ciao

Ale L ha detto...

Grazie, ti faccio sapere come va!

Ale L ha detto...

Ciao Carlo, eccomi di nuovo qui a chiederti aiuto. Volevo implementare una specie di trailing stop che si attivasse su dei livelli predefiniti. In pratica definisco 4 livelli tramite variabili esterne e nel momento in cui il prezzo li raggiunge il prgramma dovrebbe modificare lo stoploss. Ti allego il codice che ho provato a implementare (spero si capisca dopo il copia incolla):

________________________________________________
for (cnt=0; cnt<=total; cnt++)
{
OrderSelect(0,SELECT_BY_POS,MODE_TRADES);
if (OrderMagicNumber() == e_MagicID)
{
if (OrderType() == OP_BUY){
if ((Bid - OrderOpenPrice()) >= (e_BreakEvenStop * Point)){
OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, Green);
return(0);
}
if ((Bid - OrderOpenPrice()) >= (Level_1 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()+Point*Profit_Level_1, OrderTakeProfit(), 0, Green);
return(0);
}
if ((Bid - OrderOpenPrice()) >= (Level_2 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()+Point*Profit_Level_2, OrderTakeProfit(), 0, Green);
return(0);
}
if ((Bid - OrderOpenPrice()) >= (Level_3 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()+Point*Profit_Level_3, OrderTakeProfit(), 0, Green);
return(0);
}
if ((Bid - OrderOpenPrice()) >= (Level_4 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()+Point*Profit_Level_4, OrderTakeProfit(), 0, Green);
return(0);
}

}

if (OrderType() == OP_SELL){
if ((OrderOpenPrice() - Ask) >= (e_BreakEvenStop * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, Red);
return(0);
}
if ((OrderOpenPrice() - Ask) >= (Level_1 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()-Point*Profit_Level_1, OrderTakeProfit(), 0, Red);
return(0);
}
if ((OrderOpenPrice() - Ask) >= (Level_2 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()-Point*Profit_Level_2, OrderTakeProfit(), 0, Red);
return(0);
}
if ((OrderOpenPrice() - Ask) >= (Level_3 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()-Point*Profit_Level_3, OrderTakeProfit(), 0, Red);
return(0);
}
if ((OrderOpenPrice() - Ask) >= (Level_4 * Point)){

OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()-Point*Profit_Level_4, OrderTakeProfit(), 0, Red);
return(0);
}

}
}
}
______________________________________________

Il ciclo è all'interno della funzione Start. Provando il backtest, il primo livello viene eseguito, ma gli altri no (in pratica mi esegue solo lo spostamento al breakeven point).
Non capisco dov'è l'errore!?

carlo10 ha detto...

Quando la prima condizione di verifica (ovvero lo spostamento al BE) il programma esce con un return 0 e riparte dall'inizio spostando ogni volta il prezzo a BE.

Oltre a spostare il prezzo a BE dovresti quindi aggiungere una variabile all'interno dell'if in modo che alla seconda esecuzione non entri più nell'if del BE ma in quello del livello 1.

La stessa cosa vale per i livelli successivi.

Ale L ha detto...

Carlo ecco cosa ho messo giù dopo il tuo consiglio:
__________________________________________
for (cnt=0; cnt<=total; cnt++)
{
OrderSelect(0,SELECT_BY_POS,MODE_TRADES);
if (OrderMagicNumber() == e_MagicID)
{
if (OrderProfit() <= 0) return(0);
if (OrderType() == OP_BUY){

if (Var1 == 1 && (Bid - OrderOpenPrice()) > (e_BreakEvenStop * Point)){
OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), 0, Green);
Var2 = 1;
Var1 = 0;
return(0);
}

if (Var2 == 1 && (Bid - OrderOpenPrice()) > (Level_1 * Point)){
Stoploss = OrderOpenPrice()+(Profit_Level_1*Point);
OrderModify(OrderTicket(), OrderOpenPrice(), Stoploss, OrderTakeProfit(), 0, Green);
Var3 = 1;
Var2 = 0;
return(0);
}

if (Var3 == 1 && (Bid - OrderOpenPrice()) > (Level_2 * Point)){
Stoploss = OrderOpenPrice()+(Profit_Level_2*Point);
OrderModify(OrderTicket(), OrderOpenPrice(), OrderOpenPrice()+(Point*Profit_Level_2), OrderTakeProfit(), 0, Green);
Var4 = 1;
Var3 = 0;
return(0);
}

if (Var4 == 1 && (Bid - OrderOpenPrice()) > (Level_3 * Point)){
Stoploss = OrderOpenPrice()+(Profit_Level_3*Point);
OrderModify(OrderTicket(), OrderOpenPrice(), Stoploss, OrderTakeProfit(), 0, Green);
Var5 = 1;
Var4 = 0;
return(0);
}

if (Var5 == 1 && (Bid - OrderOpenPrice()) > (Level_4 * Point)){
Stoploss = OrderOpenPrice()+(Profit_Level_4*Point);
OrderModify(OrderTicket(), OrderOpenPrice(), Stoploss, OrderTakeProfit(), 0, Green);
Var5 = 0;
return(0);
}}

if (OrderType() == OP_SELL){

....
_____________________________
In pratica mi sono accorto che era necessario riazzerare le variabili dopo l'Ordermodify altrimenti eseguiva solo la condizione corrispondente alla variabile precedentemente memorizzata.
Comunque adesso scritto così funziona, ho provato nel backtest. Certo è un pò pesante come numero di righe di codice ma forse si può alleggerire...

Come sempre sei di grande aiuto! Grazie Carlo

carlo10 ha detto...

Concordo, la soluzione non è elegante ma l'importante è che funzioni!

Poi con il tempo se trovi un modo migliore per gestirla la modificherai.

Piacere di averti aiutato!

Unknown ha detto...

Ciao Carlo ho modificato la funzione in questo modo
void TrailingStop(double e_trailingstop,double e_trailingstep){
Alert("Ts");
if (OrderType() == OP_BUY)
if ((Bid - OrderOpenPrice()) > (e_trailingstop * Point))
if (OrderStopLoss() < Bid - (e_trailingstop + e_trailingstep - 1) * Point) {
bool res=OrderModify(OrderTicket(), OrderOpenPrice(), Bid - e_trailingstop * Point, OrderTakeProfit(), 0, clrGreen);
if(!res)
Print("Error in OrderModify. Error code=",GetLastError());
else
Print("Order modified successfully.");
return;
}
if (OrderType()==OP_SELL)
if ((OrderOpenPrice() - Ask) > (e_trailingstop * Point))
if (OrderStopLoss() > Ask + (e_trailingstop + e_trailingstep - 1) * Point || OrderStopLoss()==0) {
bool res=OrderModify(OrderTicket(), OrderOpenPrice(), Ask - e_trailingstop * Point, OrderTakeProfit(), 0, clrRed);
if(!res)
Print("Error in OrderModify. Error code=",GetLastError());
else
Print("Order modified successfully.");
return;
}
}
Pero mi da il primo alert "Ts" quindi entra nella funzione però non modifica l'ordine e non mi da nessun errore.. non capisco il motivo

Andrea Bertini ha detto...
Questo commento è stato eliminato dall'autore.
Andrea Bertini ha detto...

Grazie per il post: mi chiedo una cosa, il primo stop di partenza del trailing di un ordine deve essere superiore allo stoplevel del prodotto, altrimenti abbiamo errore. Mi confermi?

Unknown ha detto...

Buongiorno, ma se volessi creare un trailing stop che mantenesse in stop profit per esempio di 10 pips ogni stop in profit tra di loro?. Mi spiego. Ho circa 50 ordini tutti in profitto e voglio che una volta raggiunti i 50 pips di profitto ad ordine, l'ea metta un trailing stop a distanza di 10 pips dal prezzo, ma tenendo anche conto che tra un trailing stop (in profitto) ed un'altro, ci siano sempre 10 o 15 pips di distanza tra di essi. Cioè non voglio che una volta che il prezzo inverta, l'ea chiuda tutti gli ordini in profitto che hanno lo stesso stop impostato dal trailing stop,. Voglio che il livello di stop impostato dal trailing stop, sia ad una certa distanza dal livello di stop impostato per un'altro ordine. Per intenderci meglio, voglio che i livelli di stop (in profitto) siano tra di loro equidistanti come se ci fosse una sorta di grigli tra i livelli stessi di stop in profitto. Grazie della risposta.

Posta un commento