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++;
     }
   }
 

14 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!

Blogger ha detto...

Forex trading is alot more easier when its done by an expert advisor and EA Builder allows you to create your own free expert advisor.

Posta un commento