Una plantilla para una orden trailing stop y de salida del mercado
Introducción
Los desarrolladores de algoritmos de modificación/cierre sufren un infortunio perenne: ¿cómo comparar los resultados obtenidos por distintos métodos? El mecanismo de comprobación es bien conocido: el probador de estrategias. Pero ¿cómo realizar un asesor experto que funcione igualmente para las órdenes de apertura y cierre? El artículo describe una herramienta que proporciona una sólida repetición de aperturas de órdenes que nos permite mantener una plataforma matemáticamente correcta para comparar los resultados de algoritmos distintos para órdenes trailing stop y de salida del mercado.
Si está depurando un asesor experto complejo que se supone que calcula de forma independiente el momento de entrada al mercado, trailing stops y salida del mercado, entonces es prácticamente imposible obtener un patrón repetitivo para la comparación con las demás. Imagine una situación en la que haya una señal bastante larga para la apertura de una orden. Idealmente, la orden se abrirá. Luego, si la dirección elegida es correcta y el precio se mueve en la dirección prevista, la orden trailing stop comienza a trabajar. De acuerdo con las fluctuaciones del precio, una orden stop colocada demasiado cerca puede cerrar una orden demasiado pronto que de lo contrario podría "llenar por completo" los beneficios. Si la señal de apertura es aún válida en ese momento, el asesor experto cerrará una nueva orden. Como consecuencia de ello, tendremos que comparar los resultados de una orden "correcta" con los resultados de unas pocas órdenes que hayan sido abiertas después de cierres anticipados. Para evitar esta situación, se recomienda lo siguiente.
Planteamiento del problema
- Las plantillas de punto de apertura/cierre se marcan en el gráfico.
- Los momentos de apertura/cierre y la dirección de la transacción (buy/sell) se guardan en un archivo.
- Se crea el asesor experto para leer el archivo creado y ejecutar estrictamente sus comandos.
Los puntos de apertura deben fijarse en los cambios de dirección del mercado: es bueno que sean bastante obvios en el historial. No obstante, los puntos de cierre deben seleccionarse no cuando el precio alcance el punto de cambio de dirección opuesto, sino después de este. No debemos olvidar que nuestra tarea es optimizar trailing y la salida del mercado, por lo que debemos permitir cualquier algoritmo, incluso uno inadecuado, para trabajar "hasta su final". Si todavía es incapaz de fijar el beneficio, veremos sus pérdidas que será una señal para que nosotros rehagamos el algoritmo.
Observemos la imagen anterior. La línea violeta muestra la entrada y salida idealmente correctas. Puede usarse para el cálculo de los beneficios máximos que queremos o podemos ganar. No obstante, para la finalidad de las pruebas de trailing, usaremos algo similar a la línea azul. Muestra la naturaleza del trading real: entrar con algún retraso (como si estuviéramos esperando una confirmación de inversión) y cierre en el extremo de break-even (como si estuviéramos asustados por una fuerte inversión iniciada y por esta pudiéramos perderlo todo).
En el trading realizado "a lo largo de la línea azul" hay tres puntos potenciales de inicio de stops después de los trailings:
- Un trailing agresivo en la mínima distancia desde el precio actual.
- Trailing normal y "paciente".
- Trailing ideal "sudando" beneficios hasta la última caída.
Además, puede tener lugar un inicio falso del trailing demasiado "impaciente" en la zona alrededor del punto 4.
Ahora que sabemos cómo "marcar" las áreas ideales, lo único que queda es hacerlo lo más cómodo posible.
Herramientas de marcado
Para facilitar el marcado del gráfico con líneas ideales, vamos a crear un grupo de scripts. Dos scripts, TL_Buy y TL_Sell, crearán líneas de marcado para las operaciones de compra y venta, respectivamente. El script TL_Write buscará en todas las líneas creadas y guardará sus características en un archivo para que el asesor experto TL_Trade trabaje con ellas. Un script más, TL_Read, podrá leer el archivo creado y reformar todas las líneas en base a él. Esto puede ser útil para corregir las líneas disponibles o para añadir algunas nuevas, o para eliminar las existentes.
Para que los script de lectura/escritura puedan trabajar con sus líneas, llamaremos a todas las líneas según ciertas reglas:
- los nombres de todas las líneas ideales comienzan con el mismo prefijo (TL_). Podemos usar más tarde el prefijo para seleccionar y borrar las líneas;
- el prefijo es seguido por un carácter que es el código de la operación: B-buy, S-sell;
- el código de la operación en el nombre de la línea es seguido por el número de línea para distinguir las líneas entre sí.
Como resultado, debemos obtener en el gráfico las líneas de, por ejemplo, los nombres siguientes: TL_B1 TL_B2, TL_S3, etc.
Los scripts que crean líneas simplemente se colocan en el gráfico y aparece la línea correspondiente en el punto de colocación. Puede mover sus extremos de tal forma que marquen la "línea azul" ideal necesaria para el trading. Cuando se adjuntan al gráfico, los scripts de lectura/escritura solicitan el nombre del archivo a guardar y leer. Esto nos permitirá usar fácilmente diferentes grupos de líneas, por ejemplo, para diferentes pares de divisas.
El código de los scripts es bastante transparente y dispone de todos los comentarios necesarios, por lo que me tomaré la libertad de pasar por alto la descripción de sus algoritmos: pueden verse en su código.
/**************************************************************** PATTERN TRADING: TL_Buy - creación de una nueva plantilla de línea de compra Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" int start() { int MaxNo=0,i,No; if(WindowOnDropped()!=0) { MessageBox("Script should be dropped in the main window","ERROR", IDOK + MB_ICONERROR); return(1); } // find the maximum suffix number for all lines for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // select the line number if(MaxNo<No) MaxNo=No; // store it, if it is larger } } datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // find the coordinates of the script dropping point int width = 5*Period()*60; // width of the created line in bars converted into time units double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // height of the created line in ticks converted into price units string LineName = _prefix_+"B"+(MaxNo+1); // create a name for a new line ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0-height, t0+width,p0+height); // create a line ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray ObjectSet(LineName,OBJPROP_WIDTH,2); // set its width ObjectSet(LineName,OBJPROP_COLOR,Blue); // set its color }
/**************************************************************** PATTERN TRADING: TL_Sell - creación de una nueva plantilla de línea de venta Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" int start() { int MaxNo=0,i,No; if(WindowOnDropped()!=0) { MessageBox("Script should be dropped in the main window","ERROR", IDOK + MB_ICONERROR); return(1); } // find the maximum suffix number for all lines for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { No=StrToInteger(StringSubstr(ObjectName(i),StringLen(_prefix_)+1)); // select the line number if(MaxNo<No) MaxNo=No; // store it, if it is larger } } datetime t0=WindowTimeOnDropped(); double p0=WindowPriceOnDropped(); // find the coordinates of the script dropping point int width = 5*Period()*60; // width of the created line in bars converted into time units double height = 20*MarketInfo(Symbol(),MODE_TICKSIZE); // height of the created line in ticks converted into price units string LineName = _prefix_+"S"+(MaxNo+1); // create a name for a new line ObjectCreate(LineName,OBJ_TREND,0,t0-width,p0+height, t0+width,p0-height); // create a line ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray ObjectSet(LineName,OBJPROP_WIDTH,2); // set its width ObjectSet(LineName,OBJPROP_COLOR,Red); // set its color }
/**************************************************************** PATTERN TRADING: TL_Write - guardando las coordenadas de las líneas de plantilla en un archivo Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" #property show_inputs extern string FileNameForWrite = "TL_DATA.TXT"; int start() { int LinesCNT=0,i; string Operation; double p; datetime t; int fh=FileOpen(FileNameForWrite,FILE_CSV|FILE_WRITE,';'); // look through all lines created and save the opening commands for the EA from them for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) // our line { string LineName = ObjectName(i); datetime t1=ObjectGet(LineName,OBJPROP_TIME1); datetime t2=ObjectGet(LineName,OBJPROP_TIME2); double p1=ObjectGet(LineName,OBJPROP_PRICE1); double p2=ObjectGet(LineName,OBJPROP_PRICE2); LinesCNT++; // increase the counter for producing the final message Operation = StringSubstr(ObjectName(i),StringLen(_prefix_),1); // prices are necessary only for restoring the line in the chart FileWrite(fh,Operation,TimeToStr(t1),DoubleToStr(p1,Digits),TimeToStr(t2),DoubleToStr(p2,Digits)); } } FileClose(fh); MessageBox("Stored sections "+(LinesCNT)+" pcs.","Done", IDOK + MB_ICONINFORMATION); }
/**************************************************************** PATTERN TRADING: TL_Read - dibujando líneas de plantilla desde el archivo Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" #property show_inputs extern string FileNameForRead = "TL_DATA.TXT"; int start() { int LinesCNT=0,i; int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';'); if(fh<0) { MessageBox("Error opening file \"" + FileNameForRead + "\"","ERROR", IDOK + MB_ICONERROR); return(1); } // first of all, delete everything for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; } } // look through all lines created and save the opening commands for the EA from them while(true) { string Operation=FileReadString(fh); if(FileIsEnding(fh)) break; // file ended? - exit // read the section's coordinates datetime t1=StrToTime(FileReadString(fh)); double p1=StrToDouble(FileReadString(fh)); datetime t2=StrToTime(FileReadString(fh)); double p2=StrToDouble(FileReadString(fh)); // draw a section LinesCNT++; string LineName = _prefix_+Operation+(LinesCNT); // create a name for a new line ObjectCreate(LineName,OBJ_TREND,0,t1,p1, t2,p2); // create a line ObjectSet(LineName,OBJPROP_RAY,False); // make it a section, not a ray ObjectSet(LineName,OBJPROP_WIDTH,2); // set its width if(Operation=="B") ObjectSet(LineName,OBJPROP_COLOR,Blue); else ObjectSet(LineName,OBJPROP_COLOR,Red);// set its color } FileClose(fh); MessageBox("Read sections "+(LinesCNT)+" pcs.","Done", IDOK + MB_ICONINFORMATION); }
/**************************************************************** PATTERN TRADING: TL_Clear - borrado de todas las líneas de plantilla Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" int start() { int LinesCNT=0,i; for(i=0;i<ObjectsTotal();i++) { if(StringFind(ObjectName(i),_prefix_)==0) { ObjectDelete(ObjectName(i)); i--; LinesCNT++; } } }
Posicionamiento del archivo
El posicionamiento de archivos es un punto muy importante. Usando medios estándar, los scripts de trabajo solo pueden crear archivos en el directorio c:\Program Files\MetaTrader 4\experts\files. No obstante, cuando se pruebas los asesores expertos, el probador tiene acceso a la carpeta del mismo nombre situada "dentro de su propio directorio", c:\Program Files\MetaTrader 4\tester\files.
Por ello, después de la creación de los archivos y antes de usarlos en la prueba del asesor experto, debemos copiarlos de forma independiente desde c:\Program Files\MetaTrader 4\experts\files a c:\Program Files\MetaTrader 4\tester\files.
Tendremos que repetir esta operación después de volver a crear el archivo habiendo cambiado algo en las líneas.
Asesor experto de prueba
El asesor experto de prueba no presenta dificultades en su código. Los siguientes bloques se destacan en él:
- bloque de cierre de la orden cuando alcanza el final de la sección de la plantilla;
- bloque de apertura de la orden cuando alcanza el inicio de la sección de la plantilla;
- bloque de prueba de trailing stop y salida del mercado.
Su funcionamiento es bastante obvio en el código fuente. Solo deben darse algunos comentarios más aquí:
- Como las líneas pueden crearse sin un orden particular, todo el conjunto de líneas será probado en cada "tick" del probador, para encontrar aquellas en las que sea necesario abrir/cerrar.
- Los tiempos de apertura/cierre se escriben en el comentario de la orden en el formato interno de representación de hora y fecha. Esto es necesario, en primer lugar, para que no abramos la misma orden varias veces si se cierra por trailing anticipadamente antes de que finalice su línea de la plantilla. En segundo lugar, cuando comprobamos las órdenes abiertas y tomamos sus tiempos de cierre de sus comentarios, cerraremos la orden exactamente en el momento en el que finaliza su línea de control, ya que el tiempo de cierre se escribe en la propia orden abierta.
- El parámetro ProcedTrailing habilita/deshabilita el procesamiento de trailing. Esto nos permitirá pasar el asesor experto sin trailing para obtener el resultado más pesimista para compararlo con los resultados obtenidos de la optimización.
/**************************************************************** PATTERN TRADING: TL_Trader - trading en líneas de plantilla Copyright © 2006-2008, Sergey Kravchuk. http://forextools.com.ua *****************************************************************/ #include <WinUser32.mqh> #define _prefix_ "TL_" extern string FileNameForRead = "TL_DATA.TXT"; extern double Lots = 0.1; extern double StopLoss = 0; extern double TrailingStop = 30; extern bool ProcedTrailing=true; // process the trailing block double SL; // to calculate the SL values for opening an order int start() { int LinesCNT=0,i,ticket,pos; double p; datetime t; string s; int fh=FileOpen(FileNameForRead,FILE_CSV|FILE_READ,';'); // to test the file, it is necessary to pout it into tester\files\TL_DATA.txt if(fh<0) { MessageBox("Error opening file \"" + FileNameForRead + "\"","ERROR", IDOK + MB_ICONERROR); return(1); } // check all entries: if the opening time has already passed and no order with such a comment is found in history or in open orders // then it has not been opened yet - open it as it's said there while(true) { string Operation=FileReadString(fh); if(FileIsEnding(fh)) break; // file ended? - exit // count the section coordinates string st1=FileReadString(fh); string sp1=FileReadString(fh); string st2=FileReadString(fh); string sp2=FileReadString(fh); datetime t1=StrToTime(st1); double p1=StrToDouble(sp1); datetime t2=StrToTime(st2); double p2=StrToDouble(sp2); // what if sections' ends are mixed? if(t1>t2) { p=p1; p1=p2; p2=p; t=t1; t1=t2; t2=t; s=st1; st1=st2; st2=s; s=sp1; sp1=sp2; sp2=s; } string MarkComent = t1+"="+t2; //********************************************************************************** // 1) block closing the orders as soon as the end of the pattern section is reached. //********************************************************************************** for(i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue; if(OrderComment()==MarkComent && TimeCurrent()>=t2) // order must be closed { if(OrderType()==OP_BUY) OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position else OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position } } //********************************************************************************** // 2) block opening orders as soon as the beginning of the pattern section is passed. //********************************************************************************** bool OrderNotPresent=true; // a sign showing that we haven't opened such an order yet if(t1<=TimeCurrent() && TimeCurrent()<t2) // time to open - make sure that this order is not opened or closed { for(i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue; if(OrderComment()==MarkComent) { OrderNotPresent=false; break; } // order already exists } for(i=0;i<OrdersHistoryTotal() && OrderNotPresent;i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false) continue; // order in history is added to the end, something like "[sl]" - it must be cut off!! pos = StringFind(OrderComment(),"["); string CurOrderComment = StringSubstr(OrderComment(),0,pos); if(CurOrderComment==MarkComent) { OrderNotPresent=false; break; } // order already exists } if(OrderNotPresent) // no such order - open it { // open an order if(Operation=="B") // Buy { if(StopLoss<=0) SL=0; else SL=Ask-StopLoss*Point; ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,SL,0,MarkComent,1235,0,Blue); OrderSelect(ticket,SELECT_BY_TICKET); } else // Sell { if(StopLoss<=0) SL=0; else SL=Bid+StopLoss*Point; ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,SL,0,MarkComent,1235,0,Red); OrderSelect(ticket,SELECT_BY_TICKET); } } } } FileClose(fh); //****************************************************** // 3) block testing trailing stop and exit the market //****************************************************** if(ProcedTrailing) { for(i=0;i<OrdersTotal();i++) { if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) continue; if(OrderType()==OP_BUY) { if(Bid-OrderOpenPrice()>Point*TrailingStop) { if(OrderStopLoss()<Bid-Point*TrailingStop) { OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green); return(0); } } } if(OrderType()==OP_SELL) { if((OrderOpenPrice()-Ask)>(Point*TrailingStop)) { if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0)) { OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red); return(0); } } } } } return(0); }
Cómo funciona el sistema cuando está "completamente integrado"
Para probar el sistema usamos un microconjunto de 14 líneas. A continuación se muestra el contenido de la línea de la plantilla llamado TL_DATA.txt:
B;2007.12.28 05:00;1.4605;2008.01.09 22:00;1.4658
B;2008.01.29 05:00;1.4767;2008.02.05 05:00;1.4811
B;2008.02.15 16:00;1.4687;2008.02.21 09:00;1.4735
B;2008.02.21 14:00;1.4738;2008.02.26 07:00;1.4812
B;2008.02.28 14:00;1.5129;2008.03.05 12:00;1.5186
B;2008.03.05 22:00;1.5261;2008.03.11 20:00;1.5316
B;2008.03.13 01:00;1.5539;2008.03.18 22:00;1.5620
B;2008.03.26 14:00;1.5724;2008.03.28 10:00;1.5758
S;2007.11.30 13:00;1.4761;2007.12.10 22:00;1.4711
S;2007.12.14 04:00;1.4626;2007.12.28 00:00;1.4610
S;2008.01.17 17:00;1.4688;2008.01.24 13:00;1.4671
S;2008.02.07 12:00;1.4633;2008.02.14 11:00;1.4617
S;2008.03.19 23:00;1.5641;2008.03.25 23:00;1.5629
S;2008.03.31 19:00;1.5811;2008.04.08 04:00;1.5796
Así es como aparece en un gráfico:
No es el trading más eficiente, pero es una base ideal para probar el trailing. ;)
Si iniciamos el probador con las órdenes trailing stop deshabilitadas (ProcedTrailing=false), obtendremos un resultado pobre:
Una simple optimización mediante el tamaño de trailing-stop proporciona el valor óptimo de 95 puntos.
El valor óptimo que produce el mayor beneficio está bastante en concordancia con las estadísticas del periodo a prueba: 95 puntos hacen el 98 % de todas las barras de este periodo, que se ve claramente en el gráfico de ft.BarStat indicator.
Como puede ver, los resultados optimizados tienen un aspecto mucho más atractivo:
Se ven en el gráfico incluso con más claridad. Recuerde que todas las órdenes se abrieron precisamente al comienzo de las líneas de plantilla.
Consulte la primera sección (5 de marzo). Los beneficios fueron eliminados por un pico descendente, pero la tendencia de compra general siguió siendo la misma. Si probamos el trailing con un asesor experto normal, probablemente abra una posición de compra que retuviera hasta el final de la segunda línea (después del 17 de marzo). En este caso, obtendríamos resultados incomparables. El beneficio obtenido en el segundo caso, resultaría de la apertura repetida con éxito de una nueva orden, no en el mecanismo de trailing stop. No obstante, el problema no es obtener el mayor beneficio, sino conseguir un mecanismo de trailing stop que sea lo más eficiente posible. Por esta razón es muy importante para nosotros que todas las órdenes estén abiertas al mismo tiempo y no estén retenidas demasiado tiempo. No obstante, en este caso el incremento en los beneficios derivado de la optimización reflejará la eficiencia del algoritmo de trailing.
Conclusión
Espero que la discusión sobre este artículo no quede manchada por comentarios que yo NO haya emitido: un lector interesado encontrará un uso para el algoritmo de investigación propuesto incluso tal y como se ha presentado aquí. El artículo describe una herramienta a la que yo he llegado. La idea que durante mucho tiempo tenía en la cabeza se hizo finalmente clara y se materializó como código MQL4. No tenía tiempo de realizar la investigación para la que esta herramienta había sido creada. Por esta razón, el artículo no proporciona un análisis comparativo sobre varios algoritmos de trailing. Voy a tratar sobre ellos en el futuro próximo, por lo que la segunda parte del artículo estará lista para la publicación pronto. No obstante, este artículo parece ser útil para la Comunidad MQL4 también.
Una razón más por la que decidí publicar la herramienta al "desnudo" es que soy un programador profesional y un trader novato. Esto significa que puedo desarrollar de forma independiente mi código MQL4 hasta la mayor simplicidad o complejidad posible, pero solo puedo desarrollar tácticas de trading y algoritmos de trailing tan eficientemente como programo el código solo junto con traders que sean tan profesionales en el trading como yo lo soy en la programación. Por esta razón estoy muy interesado en establecer comunicación con mis compañeros de Forex "compañeros de gremio" y estaré encantado de trabajar en cooperación para llevar mi herramienta a un estado que permita usarla en el trading real. Si está interesado en colaborar conmigo, por favor, contacte conmigo a través de mi perfil.
Como desarrollo adicional, podemos probar varios algoritmos de trading en un gráfico simultáneamente. Al comienzo de la sección de la plantilla, abriremos unas cuantas órdenes (para la cantidad de algoritmos a prueba) cada una de ellas rastreada de acuerdo con su algoritmo. Conceptualmente, debemos obtener la imagen que represente varias imágenes superpuestas unas sobre otras. Luego podremos comparar los resultados por cantidad (beneficios netos) y por calidad. Podremos ver qué algoritmos cumplen sus funciones con mayor precisión. No obstante, esto requerirá de nosotros que enseñemos a nuestros asesores expertos a que distingan las órdenes por su alternativa de trailing, pero este problema puede solventarse fácilmente.
A propósito, estos métodos pueden usarse para comparar las tácticas de entrada y las de salida. Para ello, debemos preparar un conjunto de líneas algo diferente y asegurarnos de que nuestros puntos de apertura de órdenes de acuerdo con nuestros métodos, están situados tan cerca de las aperturas de la plantilla en el gráfico como sea posible. No obstante, esta es una cuestión para una investigación separada y un nuevo artículo.
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/1527
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso