Reimaginando las estrategias clásicas (Parte 15): Estrategia de ruptura diaria
En esta serie de artículos, a menudo buscamos emplear modelos de aprendizaje automático para ayudar a mejorar el rendimiento de nuestras estrategias de negociación. Sin embargo, a menudo pasamos por alto el hecho de que estos modelos parten de muchas suposiciones infundadas sobre los datos disponibles. Además, el aprendizaje estadístico clásico no nos ofrece ninguna guía para demostrar si las relaciones que intentamos modelar existen realmente.
Por otro lado, los traders humanos han aprendido a lo largo de muchos años de interacción con los mercados una cierta esencia fundamental de lo que podríamos llamar lógica de mercado. Este conocimiento, al que nos referiremos como lógica de mercado, solo se obtiene con la experiencia. Idealmente, podríamos integrar los conocimientos que han adquirido estos traders humanos en nuestras aplicaciones de trading basadas en datos numéricos. Los traders discrecionales han estado activos en los mercados financieros mucho antes de la existencia de las computadoras. Por lo tanto, puede que haya algo de verdad en las reglas prácticas que han aprendido a valorar a lo largo de los años, reglas que nosotros también podríamos emplear.
Como ya hemos mencionado, solemos utilizar algoritmos de aprendizaje automático para ayudarnos a aprender relaciones que proyectan el pasado sobre el futuro. Sin embargo, casi siempre asumimos que dicha relación existe y que se puede deducir de los datos. Rara vez nos tomamos el tiempo para demostrar que estas relaciones existen en primer lugar. Los traders discrecionales, por necesidad, se han visto obligados a buscar relaciones fiables que puedan sustentar sus carreras profesionales. Desde esta perspectiva, podríamos argumentar que los traders humanos se vieron obligados a realizar el trabajo manual que puede sentar las bases sobre las que se construyen nuestros modelos de aprendizaje automático.
Por lo tanto, buscamos cerrar la brecha entre el aprendizaje automático y las operaciones financieras utilizando las heurísticas y las reglas prácticas que los seres humanos han desarrollado a lo largo de muchos años de interacción con los mercados. Estas reglas que los traders han aprendido a seguir, pueden servir como un marco que permita a nuestros modelos aprender sobre los mercados financieros de una manera más estructurada. En lugar de que nuestros modelos demuestren relaciones desde cero, podemos extender principios que ya han demostrado su validez con el tiempo. Esto les da a nuestros modelos una ventaja inicial en lugar de pedirles que comiencen desde cero.
La parte más importante de nuestra prueba es demostrar la validez de la estrategia con la que comenzamos. Por este motivo, nuestra estrategia de trading se basó en un enfoque de ruptura bien conocido que se apoya en la relación entre días de negociación consecutivos. La estrategia, en esencia, es bastante simple.
Descripción general de nuestra estrategia de trading
Al comienzo de cada día de trading, empezamos registrando el precio máximo y el mínimo del día anterior.

Figura 1: Configuración de nuestra estrategia de ruptura diaria marcando los niveles de precios máximo y mínimo del día anterior.
Cuando el precio rompe el máximo del día anterior, abrimos una posición corta y obtenemos ganancias en el mínimo del día anterior. Por el contrario, cuando el precio rompe el mínimo del día anterior, abrimos una posición larga y obtenemos ganancias en el máximo del día anterior. Se trata esencialmente de una estrategia de trading contraria, que busca una operación de ruptura por día.

Figura 2: Nuestras señales de entrada se activan la primera vez que los niveles de precios rompen el nivel extremo del día anterior.
Curiosamente, incluso sin modelos predictivos ni redes neuronales avanzadas, observamos que un conocimiento sólido de cómo funcionan los mercados financieros es un activo invaluable. En su forma original, por primitiva que parezca, esta estrategia de ruptura diaria alcanzó un porcentaje de operaciones ganadoras que rivalizaban con el rendimiento de una red neuronal profunda, simplemente basándose en la lógica del mercado aprendida por los traders discrecionales.

Figura 3: La estrategia cierra sus posiciones cuando se rompe el nivel de precio extremo opuesto, pero como podemos ver en la ilustración anterior, la estrategia contiene cierta cantidad de error.
También observamos que la estrategia era excesivamente agresiva e inestable. Las reglas de salida definidas por la estrategia no siempre se cumplen por completo, como se ilustra en la Figura 3. Por lo tanto, fue necesario realizar modificaciones para ejercer cierto grado de control sobre su volatilidad. La volatilidad a la que me refiero aquí es el comportamiento fluctuante de las ganancias en la cuenta durante la prueba retrospectiva.
Como veremos en las secciones siguientes, primero implementamos la estrategia de trading original en su forma más simple —fiel a la descripción aceptada por la mayoría de los traders— para establecer un nivel de rendimiento de referencia. Posteriormente, modificamos la estrategia y comparamos las mejoras con la versión original para asegurarnos de que nuestros cambios nos encaminaban en la dirección deseada. Los cambios que realizamos afectaron la estrategia en tres dimensiones importantes:
RentabilidadNuestra estrategia original generó una ganancia de tan solo 40 dólares en una prueba retrospectiva de cinco años. Esto no es impresionante en ningún sentido. Sin embargo, lo más impresionante fue que el 68% de todas las operaciones realizadas fueron rentables, lo cual es notable para una prueba fuera de muestra a lo largo de cinco años, especialmente teniendo en cuenta que la estrategia no tenía parámetros de ajuste y se basaba únicamente en la lógica y la intuición. Desafortunadamente, la pérdida promedio en las operaciones tendía a ser mayor que la ganancia promedio, lo que resultaba en esa pésima ganancia de 40 dólares.
Tras varias iteraciones, nuestra versión mejorada de la estrategia generó una ganancia de 162 dólares durante el mismo período, lo que supone una mejora del 300 %. Además, logramos mejorar el índice de Sharpe de la estrategia de 0,13 a 0,40, lo que supone una mejora del 207% con respecto al original. El lector debe tener en cuenta que todas las operaciones se realizaron con la posición mínima de lote permitida, para poner a prueba nuestra capacidad de demostrar verdadera habilidad.
Eficiencia operativaLa estrategia inicial requirió un total de 886 operaciones para obtener una ganancia de tan solo 40 dólares, mientras que nuestra versión mejorada solo necesitó 246 operaciones para obtener 162 dólares. Esto significa que la estrategia mejorada logró una reducción del 72% en la actividad comercial y en la exposición innecesaria al mercado, y además obtuvo mayores rendimientos. Esta es una característica muy valorada en cualquier aplicación de trading: mayores ganancias con menor riesgo.
En la estrategia original, la ganancia promedio fue de 3,64 dólares y la pérdida promedio fue de 7,87 dólares. Logramos corregir este comportamiento. En nuestra versión mejorada, logramos una ganancia promedio de 14,82 dólares, manteniendo al mismo tiempo la pérdida promedio prácticamente al mismo nivel, en 7,95 dólares.
Precisión en las operacionesLamentablemente, aunque estos cambios mejoraron la rentabilidad y la eficiencia, afectaron negativamente a nuestra tasa de acierto. La proporción de operaciones ganadoras cayó del 68% en la estrategia original al 37% en la versión mejorada, lo que supone una reducción del 45% en la tasa de acierto. La alta tasa de acierto de la estrategia original había sido una de sus características más atractivas, y los intentos de ajustarla manualmente sin sacrificar la precisión resultaron difíciles.
Por lo tanto, podríamos considerar asignar esta tarea a un controlador de retroalimentación, como se demostró en nuestra discusión anterior (enlace proporcionado aquí). También creemos que sería útil seguir probando más reglas de negociación discrecionales en las que los traders humanos han confiado durante años, seleccionando de forma selectiva aquellas que de manera natural produzcan altos niveles de acierto, como los resultados con los que empezamos. De este modo, estas estrategias pueden ofrecer un marco estructurado para que nuestros modelos de aprendizaje automático aprendan tanto las fortalezas como las limitaciones de la intuición humana. Comencemos.
Primeros pasos en MQL5
Como en la mayoría de nuestras aplicaciones, comenzaremos definiendo las variables globales necesarias para seguir la estrategia. Necesitaremos identificadores para los dos niveles de precios que monitorizamos: el máximo y el mínimo del día anterior. También marcaremos estos niveles de precios con líneas horizontales que atraviesan nuestro gráfico, por lo que necesitaremos cadenas de texto para almacenar los nombres de nuestros niveles de precios.
//+------------------------------------------------------------------+ //| UB 2.mq5 | //| Gamuchirai Ndawana | //| https://www.mql5.com/en/users/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ double last_high,last_low; string h,l; bool rest; int atr_handler; double atr[];
Asimismo, importaremos la biblioteca de operaciones para que nos ayude a gestionar nuestras posiciones durante las pruebas retrospectivas.
//+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade;
Cuando nuestra aplicación se carga por primera vez, almacenaremos los nombres de los dos niveles de precios que estamos monitoreando, configuraremos el indicador Average True Range (ATR) para la gestión de riesgos y, por último, restableceremos una bandera del sistema llamada "rest". Como su nombre lo indica, nuestro sistema buscará operaciones hasta que "rest" se establezca en verdadero. En ese momento, el sistema dejará de buscar oportunidades de entrada y, en su lugar, gestionará las posiciones abiertas.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- h="high"; l="low"; rest = false; atr_handler = iATR(Symbol(),PERIOD_H8,4*14); //--- return(INIT_SUCCEEDED); }
Cuando nuestra aplicación deje de utilizarse, liberaremos el indicador ATR para que realice la limpieza necesaria y comparta la memoria con el resto del sistema.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- IndicatorRelease(atr_handler); }
Una vez al comienzo de cada día, almacenaremos los precios más altos y más bajos que se ofrecieron el día anterior.
//--- datetime cd = iTime(Symbol(),PERIOD_D1,0); static datetime ds; if(cd != ds) { ds = cd; last_high = iHigh(Symbol(),PERIOD_D1,1); last_low = iLow(Symbol(),PERIOD_D1,1); if((rest==true) && (PositionsTotal() == 0)) rest = false; Comment("Last High: ",last_high,"\nLast Low: ",last_low); ObjectDelete(0,l); ObjectDelete(0,h); ObjectCreate(0,h,OBJ_HLINE,0,0,last_high); ObjectCreate(0,l,OBJ_HLINE,0,0,last_low); CopyBuffer(atr_handler,0,0,1,atr); }
A medida que avance el día, entraremos en posiciones contrarias cuando se rompa el primer nivel de precio extremo; se cree que esta primera ruptura marca la verdadera tendencia del día.
datetime ch = iTime(Symbol(),PERIOD_H1,0); static datetime hs; if(ch != hs) { hs = ch; double bid,ask,close,padding; close = iClose(Symbol(),PERIOD_H1,0); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); padding = atr[0] * 2; if(rest == false) { if(PositionsTotal() == 0) { if(close<last_low) Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high); else if(close>last_high) Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low); rest = true; } } } } //+------------------------------------------------------------------+
La estrategia se basa principalmente en la intuición y, por lo tanto, tiene pocos componentes variables. Hemos completado la configuración necesaria para nuestra aplicación y ahora podemos comenzar a realizar pruebas retrospectivas de la aplicación de negociación.
Comenzamos seleccionando cinco años de datos históricos del par EUR/USD para comprobar el rendimiento de nuestra aplicación.

Figura 4: Establecimiento de un nivel de rendimiento de referencia utilizando la versión original de nuestra estrategia de trading.
Posteriormente, configuramos los ajustes de retardo para utilizar retardos aleatorios y simular así la imprevisibilidad de la operativa real.

Figura 5: El uso de ajustes de retardo aleatorios garantiza que obtendrá ajustes de simulación fiables.
Al representar gráficamente el capital de la cuenta a lo largo del tiempo, podemos observar que la estrategia, aunque bastante ingenua y simple, sí produce una tendencia positiva en el capital. Sin embargo, las fluctuaciones que genera son demasiado amplias, y necesitamos medidas para que la estrategia sea menos agresiva y más controlada.

Figura 6: La curva de capital generada por la versión original de nuestra estrategia de trading carece de consistencia y parece demasiado volátil.
Sin embargo, el beneficio total obtenido por la estrategia en su forma original fue de tan solo cuarenta dólares tras cinco años de pruebas retrospectivas. Lo que resulta impresionante, sin embargo, es la extraordinaria tasa de acierto: el 68% de todas las operaciones realizadas mediante esta estrategia fueron rentables. El problema, sin embargo, era que nuestra operación ganadora promedio era menor que nuestra operación perdedora promedio; las pérdidas eran casi el doble.

Figura 7: Las estadísticas detalladas de nuestra estrategia original destacan por su elevado porcentaje natural de operaciones rentables.
Realizar mejoras
Por lo tanto, para mejorar la estrategia original, tuvimos que pensar de forma creativa sobre cómo analiza el mercado y si existen maneras de anticipar los movimientos sin tener que esperar todo el día a que se rompa un nivel de precios. Tras mucha reflexión, me di cuenta de que durante la primera hora de cada día podríamos comparar la diferencia entre el precio de apertura del día y los puntos extremos del día anterior.
Esto nos da la magnitud de la diferencia entre el máximo y el mínimo de ayer, y la nueva apertura. Nuestra intuición nos dice que el precio tenderá a moverse en la dirección de la mayor diferencia. Por lo tanto: si el día comienza cerca de su mínimo anterior, adoptamos una posición larga. Por el contrario, si el día comienza cerca de su máximo anterior, adoptamos una posición corta.

Figura 8: Visualización de las mejoras que podemos realizar a la versión original de nuestra estrategia de trading.
La implementación de esta mejora aumentó nuestra ganancia neta total con respecto a los cuarenta dólares iniciales. Esto nos permitió anticiparnos a los movimientos de cada día sin tener que esperar a que se aplicaran las reglas tradicionales de la estrategia. Solo fueron necesarias ligeras modificaciones en nuestras reglas de entrada: ahora consideramos el valor absoluto de la diferencia entre el precio de cierre actual y el máximo y mínimo anteriores. Si la diferencia con el máximo era mayor, abríamos posiciones largas. De lo contrario, abrimos posiciones cortas. Tras abrir cada posición, se configuró el parámetro de reinicio y, a partir de ese momento, solo buscaremos nuevas operaciones si la anterior se ha completado.
if(rest == false) { if(PositionsTotal() == 0) { //High Gap Is Bigger if(MathAbs(close - last_high) > MathAbs(close - last_low)) Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high); rest = true; //Low Gap Is Bigger else if(MathAbs(close - last_high) < MathAbs(close - last_low)) Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low); rest = true; } }
Una vez combinadas, nuestra nueva aplicación tenía este aspecto:
//+------------------------------------------------------------------+ //| UB 2.mq5 | //| Gamuchirai Ndawana | //| https://www.mql5.com/en/users/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ double last_high,last_low; string h,l; bool rest; int atr_handler; double atr[]; //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- h="high"; l="low"; rest = false; atr_handler = iATR(Symbol(),PERIOD_H8,4*14); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- IndicatorRelease(atr_handler); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- datetime cd = iTime(Symbol(),PERIOD_D1,0); static datetime ds; if(cd != ds) { ds = cd; last_high = iHigh(Symbol(),PERIOD_D1,1); last_low = iLow(Symbol(),PERIOD_D1,1); if((rest==true) && (PositionsTotal() == 0)) rest = false; Comment("Last High: ",last_high,"\nLast Low: ",last_low); ObjectDelete(0,l); ObjectDelete(0,h); ObjectCreate(0,h,OBJ_HLINE,0,0,last_high); ObjectCreate(0,l,OBJ_HLINE,0,0,last_low); CopyBuffer(atr_handler,0,0,1,atr); } datetime ch = iTime(Symbol(),PERIOD_H1,0); static datetime hs; if(ch != hs) { hs = ch; last_high = iHigh(Symbol(),PERIOD_D1,1); last_low = iLow(Symbol(),PERIOD_D1,1); double bid,ask,close,padding; close = iClose(Symbol(),PERIOD_H1,0); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); padding = atr[0] * 2; if(rest == false) { if(PositionsTotal() == 0) { if(MathAbs(close - last_high) > MathAbs(close - last_low)) Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high); else if(MathAbs(close - last_high) < MathAbs(close - last_low)) Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low); rest = true; } } } } //+------------------------------------------------------------------+
La curva de capital generada por nuestra estrategia mejorada alcanzó nuevos máximos que no se lograron con la versión original. Nuestro beneficio neto total aumentó de 40 a 148 dólares. Sin embargo, ya podíamos observar que la proporción de operaciones ganadoras estaba empezando a disminuir.

Figura 9: Visualización de la curva de capital generada por nuestra nueva y mejorada versión de la estrategia de trading original.
Por un lado, nuestra eficiencia mejoró porque obtuvimos 108 dólares más en ganancias. Pero, mientras que la estrategia original requería 886 operaciones para obtener 40 dólares, nuestra nueva versión solo requirió 469 operaciones para obtener 148 dólares. La estrategia avanza claramente en la dirección correcta, pero aún es posible realizar ajustes adicionales.

Figura 10: Análisis estadístico de las mejoras que hemos realizado con respecto a la versión original de la estrategia.
Realizar mejoras adicionales
Para explorar esto, consideremos el siguiente diagrama. Representamos la distancia al máximo anterior con el rectángulo verde y la distancia al mínimo anterior con el rectángulo azul. Nuestra teoría de trabajo hasta el momento es que el precio se mueve en la dirección de la mayor diferencia. Actualmente, obtenemos beneficios una vez que el precio supera el punto extremo del día anterior.

Figura 11: Los rectángulos verdes y azules representan las brechas máxima y mínima de ayer, respectivamente.
Sin embargo, como podemos ver, el precio no siempre supera por completo estos extremos; a veces se acerca, pero no llega a cruzarlos.

Figura 12: Las condiciones de salida de la estrategia original no se satisfacen de forma consistente en todas las posibles condiciones del mercado.
Por lo tanto, podríamos razonar que, en lugar de colocar nuestros objetivos de beneficio por encima del extremo anterior, deberíamos colocarlos justo por debajo, tal vez una fracción del ATR por debajo del máximo anterior. De esa forma, siempre que el precio se mantenga lo suficientemente cerca, seguiremos obteniendo beneficios.
Además, queremos ajustar nuestros límites de pérdidas a medida que las operaciones se mueven a nuestro favor, para obtener la mayor ganancia posible. Para implementar esto, modificamos nuestras reglas: en lugar de agregar un margen igual a las órdenes de stop loss y take profit, ahora agregamos una fracción menor a take profit que a stop loss. Esto hace que el stop loss sea más permisivo, al tiempo que mantiene el take profit más ajustado para capturar el impulso.
Mientras las posiciones están abiertas, también comprobamos, de forma condicional, si una nueva sugerencia de stop loss es más favorable que la actual. Lo ideal es que, siempre que sea posible, nuestra orden de stop loss esté vinculada al último precio extremo.
if(ch != hs) { hs = ch; last_high = iHigh(Symbol(),PERIOD_D1,1); last_low = iLow(Symbol(),PERIOD_D1,1); double bid,ask,close,padding; close = iClose(Symbol(),PERIOD_H1,0); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); padding = atr[0] * 2; if(rest == false) { if(PositionsTotal() == 0) { if((MathAbs(close - last_high) > MathAbs(close - last_low))) Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high+(padding*0.9)); else if((MathAbs(close - last_high) < MathAbs(close - last_low))) Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low-(padding*0.9)); rest = true; } } if(rest == true) { if(PositionsTotal() > 0) { if(PositionSelectByTicket(PositionGetTicket(0))) { double sl,tp; sl = PositionGetDouble(POSITION_SL); tp = PositionGetDouble(POSITION_TP); //--- Buy if(sl < tp) { if(last_low > sl) Trade.PositionModify(Symbol(),last_low,tp); } if(tp < sl) { if(last_high < sl) Trade.PositionModify(Symbol(),last_high,tp); } } } } }
En conjunto, esto nos dio la versión final de la aplicación hasta el momento:
//+------------------------------------------------------------------+ //| UB 2.mq5 | //| Gamuchirai Ndawana | //| https://www.mql5.com/en/users/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Ndawana" #property link "https://www.mql5.com/en/users/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ double last_high,last_low; string h,l; bool rest; int atr_handler; double atr[]; //+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- h="high"; l="low"; rest = false; atr_handler = iATR(Symbol(),PERIOD_H8,4*14); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- IndicatorRelease(atr_handler); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- datetime cd = iTime(Symbol(),PERIOD_D1,0); static datetime ds; if(cd != ds) { ds = cd; last_high = iHigh(Symbol(),PERIOD_D1,1); last_low = iLow(Symbol(),PERIOD_D1,1); if((rest==true) && (PositionsTotal() == 0)) rest = false; Comment("Last High: ",last_high,"\nLast Low: ",last_low); ObjectDelete(0,l); ObjectDelete(0,h); ObjectCreate(0,h,OBJ_HLINE,0,0,last_high); ObjectCreate(0,l,OBJ_HLINE,0,0,last_low); CopyBuffer(atr_handler,0,0,1,atr); } datetime ch = iTime(Symbol(),PERIOD_H1,0); static datetime hs; if(ch != hs) { hs = ch; last_high = iHigh(Symbol(),PERIOD_D1,1); last_low = iLow(Symbol(),PERIOD_D1,1); double bid,ask,close,padding; close = iClose(Symbol(),PERIOD_H1,0); ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK); bid = SymbolInfoDouble(Symbol(),SYMBOL_BID); padding = atr[0] * 2; if(rest == false) { if(PositionsTotal() == 0) { if((MathAbs(close - last_high) > MathAbs(close - last_low))) Trade.Buy(0.01,Symbol(),ask,(bid-(padding)),last_high+(padding*0.9)); else if((MathAbs(close - last_high) < MathAbs(close - last_low))) Trade.Sell(0.01,Symbol(),bid,(ask+(padding)),last_low-(padding*0.9)); rest = true; } } if(rest == true) { if(PositionsTotal() > 0) { if(PositionSelectByTicket(PositionGetTicket(0))) { double sl,tp; sl = PositionGetDouble(POSITION_SL); tp = PositionGetDouble(POSITION_TP); //--- Buy if(sl < tp) { if(last_low > sl) Trade.PositionModify(Symbol(),last_low,tp); } if(tp < sl) { if(last_high < sl) Trade.PositionModify(Symbol(),last_high,tp); } } } } } }
La curva de capital ahora asciende a niveles más altos que en cualquiera de las estrategias anteriores. La volatilidad —la diferencia entre ganancias y pérdidas— también está más controlada.

Figura 13: La versión final de nuestra estrategia de trading produjo una curva de capital con la estructura deseada que no teníamos originalmente.
Al examinar las estadísticas detalladas, observamos que el beneficio neto total ha vuelto a aumentar, pasando de 148 dólares en la versión anterior a 162 dólares en esta versión. Sin embargo, la proporción de operaciones ganadoras es nuevamente menor que antes, e incluso menor que en la estrategia original. Esto demuestra que nuestras mejoras no solo están afectando a las operaciones rentables, sino también a las no rentables de maneras que no deseamos.
Esto pone de relieve un desafío más profundo: mejorar las estrategias comerciales basadas únicamente en la intuición humana no se resuelve fácilmente con más intuición. Para avanzar de verdad, debemos considerar otras formas de mejorar que vayan más allá del juicio humano.

Figura 14: Tras una inspección más detallada, nos dimos cuenta de que nuestras modificaciones también afectaron negativamente el rendimiento del sistema en términos de tasa de acierto.
Conclusión
Los traders humanos no han sobrevivido en los mercados financieros por casualidad ni abriéndose camino a base de operaciones sueltas. Se vieron obligados, por necesidad y por el miedo a la pérdida, a buscar relaciones reales y fiables. Esta disciplina suele pasarse por alto hoy en día, ya que los profesionales del aprendizaje automático se apresuran a trabajar con datos más grandes y modelos más complejos sin cuestionarse si sus métricas reflejan realmente un progreso significativo.
Nuestro trabajo sugiere otro camino. Al fundamentar las estrategias en la lógica discrecional del mercado, partimos de una validez demostrada en lugar de una suposición ciega. Los residuos de estas estrategias —el error en nuestras estrategias— se convierten en un terreno fértil para los modelos de aprendizaje automático. De esta forma, ya no les pedimos a nuestros algoritmos que demuestren relaciones desde cero; en cambio, les proporcionamos un marco donde las entradas y las salidas ya están conectadas.
Este cambio también nos acerca a satisfacer los supuestos estadísticos en los que se basan implícitamente nuestros modelos. Una estrategia fija frente a un mercado cambiante genera resultados que se asemejan a las condiciones independientes e idénticamente distribuidas necesarias para un aprendizaje sólido. Puede que el mundo real nunca sea perfectamente independiente e idénticamente distribuido (IID), pero este enfoque nos acerca a ese ideal, haciendo que nuestros modelos sean menos frágiles y más fiables. Creemos que, partiendo de la lógica de mercado aprendida por los humanos y entrenando luego los modelos con base en el error de esa lógica, podremos permitir que nuestros modelos de aprendizaje automático comiencen a aprender desde una perspectiva más sólida.
| Nombre del archivo | Detalles del archivo |
|---|---|
| Daily Breakout Strategy V1.mq5 | La versión inicial y agresiva de nuestra estrategia de trading que tuvo una alta tasa de acierto inicial en las operaciones. |
| Daily Breakout Strategy V2.mq5 | La segunda versión de nuestra aplicación de trading, con una precisión ligeramente inferior pero una rentabilidad significativamente mayor. |
| Daily Breakout Strategy V3.mq5 | La versión definitiva de nuestra estrategia de trading, que generó los mayores beneficios, pero con los niveles de exactitud más bajos. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/19130
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 36): Cómo habilitar el acceso directo desde Python a los flujos de mercado de MetaTrader 5
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 37): Sentiment Tilt Meter
Aprendiendo MQL5 de principiante a profesional (Parte VII): Principios de depuración de aplicaciones MQL
Operaciones CRUD en Firebase con MQL
- 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