![Filtrar Señales Basadas en Datos Estadísticos de Correlación de Precios](https://c.mql5.com/i/articles/overlay.png)
Filtrar Señales Basadas en Datos Estadísticos de Correlación de Precios
Cómo Empezó Todo
La idea que me llevó a escribir este artículo se me ocurrió tras leer el libro de Larry Williams "Long-Term Secrets to Short-Term Trading" ("Secretos a Largo Plazo para el Trading a Corto Plazo"), en el que la persona con el récord mundial de inversiones (en 1987 aumentó su capital en un 11.000%) echa por tierra los mitos creados por "... profesores universitarios y otros académicos con mucho conocimiento de la teoría, pero poco conocimiento del mercado..." sobre la ausencia de correlación alguna entre el comportamiento pasado de precios y las tendencias futuras.
Si lanza una moneda al aire 100 veces, 50 veces obtendrá cara, y las otras 50 veces, cruz. Con cada lanzamiento de la moneda, la probabilidad de que salga cara es del 50%, al igual que cruz. La probabilidad no cambia de lanzamiento en lanzamiento, porque este juego es aleatorio y no tiene memoria. Supongamos que los mercados se comportan como una moneda, de forma caótica.
Consecuentemente, cuando aparece una nueva barra, un precio tiene las mismas posibilidades de ir hacia arriba que hacia abajo, y las barras anteriores no le afectarán en absoluto. ¡Idílico! Cree un sistema de trading, configure el Take Profit mayor que el Stop Loss (es decir, configure las expectativas matemáticas en la zona positiva), y el truco está listo. Simplemente maravilloso. No obstante, el problema está en que nuestras suposiciones sobre el comportamiento del mercado no son del todo ciertas. Hablando francamente, ¡es absurdo! Y se lo demostraré.
Creemos una plantilla de Asesor Experto (EA, por sus siglas en inglés) usando el MQL5 Wizard y, usando intervenciones alfanuméricas simples, presentémosla en buenas condiciones para el cumplimiento de esta tarea. Codificaremos un Asesor Experto para simular una compra que sigue a una, dos y tres barras cerradas. La simulación significa que el programa simplemente recordará los parámetros de las barras analizadas. Enviar órdenes (una forma más común) no funcionará en este caso, porque los diferenciales e intercambios pueden poner en tela de juicio la fiabilidad de la información recibida.
Aquí está el código:
//+------------------------------------------------------------------+ //| explorer.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //---Variables--- double profit_percent,open_cur,close_cur; double profit_trades=0,loss_trades=0,day_cur,hour_cur,min_cur,count; double open[],close[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { /* Calculate percent of closures with increase from the total number */ profit_percent=NormalizeDouble(profit_trades*100/(profit_trades+loss_trades),2); Print("Percent of closures with increase ",profit_percent,"%"); // Enter data to the Journal } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //---find out the time--- MqlDateTime time; // Create a structure to store time TimeToStruct(TimeCurrent(),time); // Structuring the data day_cur=time.day_of_week; // Receive the value of the current day hour_cur=time.hour; // Receive the current hour min_cur=time.min; // Receive the current minute //---Find out the prices--- CopyOpen(NULL,0,0,4,open);ArraySetAsSeries(open,true); CopyClose(NULL,0,0,4,close);ArraySetAsSeries(close,true); if(close[1]<open[1]/*&&close[2]<open[2]&&close[3]<open[3]*/ && count==0) // If it closed with a loss { open_cur=open[0]; // Remember open price of the current bar count=1; } if(open_cur!=open[0] && count==1) // The current bar has closed { close_cur=close[1]; // Remember the close price of the formed bar count=0; if(close_cur>=open_cur)profit_trades+=1; // If the close price is higher than open, else loss_trades+=1; // +1 to closures with profit, otherwise +1 to closures with loss } } //+------------------------------------------------------------------+
La simulación se llevará a cabo en EUR/USD, o en el intervalo desde el 1 de enero del 2000 al 31 de diciembre de 2010:
Figura 1. El porcentaje de cierres con el aumento
(La primera columna muestra datos para el período entero; la segunda, tercera y cuarta, tras una solo cierre, uno doble y uno triple.)
¡A esto me refería! Las barras anteriores tienen un impacto bastante significativo en la actual, porque el precio siempre tiende a recuperar sus pérdidas.
Otro paso adelante
¡Genial! Una vez que estamos seguros de que el comportamiento de precios no es accidental, deberíamos aprovechar este sorprendente hecho lo antes posible. Por supuesto, no es suficiente para un sistema de trading independiente, pero será una buena herramienta que le liberará de las pesadas y a menudo incorrectas señales. ¡Implementémoslo, pues!
Esto es lo que necesitamos:
- Un sistema de auto-trading que muestre resultados positivos al menos durante el pasado año.
- Un divertido ejemplo que confirme la presencia de correlaciones en el comportamiento de los precios.
Yo encontré muchas ideas útiles en el libro de L. Williams. Compartiré una de ellas con usted.
La estrategia TDW (Trade Day Of Week, o Día de Trading de la Semana). Nos permitirá ver qué ocurre si algunos de los días de la semana solo compramos, y en los otros solo abrimos posiciones cortas. Después de todo, podemos asumir que el precio dentro de un día crece en un porcentaje mayor de casos que dentro de otro. ¿Cuál es la razón? ¿La situación geopolítica, estadísticas macroeconómicas o el hecho de que, como se dice en el libro de A. Elder, los lunes y martes son días de gente sencilla, mientras que los jueves y los viernes son días de profesionales? Tratemos de entenderlo.
Primero, solo compraremos cada día de la semana, y después solo venderemos. Al final del estudio compararemos los resultados, y esto será un filtro para nuestro sistema de trading. Por cierto, tengo algunos comentarios sobre ello. ¡Es un clásico!
El sistema está basado en dos MAs y MACDake. Señales:
- Si la media móvil rápida cruza la lenta de abajo hacia arriba y el histograma MACD se encuentra por debajo de la línea de cero, entonces COMPRE.
- Si la media móvil rápida cruza la lenta de arriba hacia abajo y el histograma MACD se encuentra por encima de la línea de cero, entonces VENDA.
Salga de una posición usando un trailing stop (rastreo de detención) desde un punto. El lote está fijado - 0,1.
Por motivos de conveniencia, he colocado la clase del Asesor Experto en un archivo de encabezamiento separado:
//+------------------------------------------------------------------+ //| moving.mqh | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| Класс my_expert | //+------------------------------------------------------------------+ class my_expert { // Creating a class // Closed class members private: int ma_red_per,ma_yel_per; // Periods of MAs int ma_red_han,ma_yel_han,macd_han; // Handles double sl,ts; // Stop orders double lots; // Lot double MA_RED[],MA_YEL[],MACD[]; // Arrays for the indicator values MqlTradeRequest request; // Structure of a trade request MqlTradeResult result; // Structure of a server response // Open class members public: void ma_expert(); // Constructor void get_lot(double lot){lots=lot;} // Receiving a lot void get_periods(int red,int yel){ma_red_per=red;ma_yel_per=yel;} // Receiving the periods of MAs void get_stops(double SL,double TS){sl=SL;ts=TS;} // Receiving the values of stops void init(); // Receiving the indicator values bool check_for_buy(); // Checking for buy bool check_for_sell(); // Checking for sell void open_buy(); // Open buy void open_sell(); // Open sell void position_modify(); // Position modification }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ /* Function definition */ //---Constructor--- void my_expert::ma_expert(void) { //--- Reset the values of variables ZeroMemory(ma_red_han); ZeroMemory(ma_yel_han); ZeroMemory(macd_han); } //---The function for receiving the indicator values--- void my_expert::init(void) { ma_red_han=iMA(_Symbol,_Period,ma_red_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the slow MA ma_yel_han=iMA(_Symbol,_Period,ma_yel_per,0,MODE_EMA,PRICE_CLOSE); // Handle of the fast MA macd_han=iMACD(_Symbol,_Period,12,26,9,PRICE_CLOSE); // Handle of MACDaka //---Copy data into arrays and set indexing like in a time-series--- CopyBuffer(ma_red_han,0,0,4,MA_RED); CopyBuffer(ma_yel_han,0,0,4,MA_YEL); CopyBuffer(macd_han,0,0,2,MACD); ArraySetAsSeries(MA_RED,true); ArraySetAsSeries(MA_YEL,true); ArraySetAsSeries(MACD,true); } //---Function to check conditions to open buy--- bool my_expert::check_for_buy(void) { init(); //Receive values of indicator buffers /* If the fast MA has crossed the slow MA from bottom up between 2nd and 3rd bars, and there was no crossing back. MACD-hist is below zero */ if(MA_RED[3]>MA_YEL[3] && MA_RED[1]<MA_YEL[1] && MA_RED[0]<MA_YEL[0] && MACD[1]<0) { return(true); } return(false); } //----Function to check conditions to open sell--- bool my_expert::check_for_sell(void) { init(); //Receive values of indicator buffers /* If the fast MA has crossed the slow MA from up downwards between 2nd and 3rd bars, y no hubo un cruce de vuelta. MACD-hist is above zero */ if(MA_RED[3]<MA_YEL[3] && MA_RED[1]>MA_YEL[1] && MA_RED[0]>MA_YEL[0] && MACD[1]>0) { return(true); } return(false); } //---Open buy--- /* Form a standard trade request to buy */ void my_expert::open_buy(void) { request.action=TRADE_ACTION_DEAL; request.symbol=_Symbol; request.volume=lots; request.price=SymbolInfoDouble(Symbol(),SYMBOL_ASK); request.sl=request.price-sl*_Point; request.tp=0; request.deviation=10; request.type=ORDER_TYPE_BUY; request.type_filling=ORDER_FILLING_FOK; OrderSend(request,result); return; } //---Open sell--- /* Form a standard trade request to sell */ void my_expert::open_sell(void) { request.action=TRADE_ACTION_DEAL; request.symbol=_Symbol; request.volume=lots; request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID); request.sl=request.price+sl*_Point; request.tp=0; request.deviation=10; request.type=ORDER_TYPE_SELL; request.type_filling=ORDER_FILLING_FOK; OrderSend(request,result); return; } //---Position modification--- void my_expert::position_modify(void) { if(PositionGetSymbol(0)==_Symbol) { //If a position is for our symbol request.action=TRADE_ACTION_SLTP; request.symbol=_Symbol; request.deviation=10; //---If a buy position--- if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { /* if distance from price to stop loss is more than trailing stop and the new stop loss is not less than the previous one */ if(SymbolInfoDouble(Symbol(),SYMBOL_BID)-PositionGetDouble(POSITION_SL)>_Point*ts) { if(PositionGetDouble(POSITION_SL)<SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts) { request.sl=SymbolInfoDouble(Symbol(),SYMBOL_BID)-_Point*ts; request.tp=PositionGetDouble(POSITION_TP); OrderSend(request,result); } } } //---If it is a sell position--- else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { /* if distance from price to stop loss is more than the trailing stop value and the new stop loss is not above the previous one. Or the stop loss from the moment of opening is equal to zero */ if((PositionGetDouble(POSITION_SL)-SymbolInfoDouble(Symbol(),SYMBOL_ASK))>(_Point*ts)) { if((PositionGetDouble(POSITION_SL)>(SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts)) || (PositionGetDouble(POSITION_SL)==0)) { request.sl=SymbolInfoDouble(Symbol(),SYMBOL_ASK)+_Point*ts; request.tp=PositionGetDouble(POSITION_TP); OrderSend(request,result); } } } } } //+------------------------------------------------------------------
Mis humildes reconocimientos para el autor del artículo "Escribir un Asesor Experto usando el Enfoque Orientado al Objeto de MQL5". ¡Qué haría sin él! Recomiendo leer este artículo a cualquiera que no esté muy familiarizado con esta perversa pero extremadamente funcional programación orientada al objeto.
Añada el archivo con la clase al código principal del Asesor Experto, cree un objeto e inicialice las funciones:
//+------------------------------------------------------------------+ //| Moving.mq5 | //| Copyright 2011, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2011, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //---Include a file with the class--- #include <moving.mqh> //---External Variables--- input int MA_RED_PERIOD=7; // The period of a slow MA input int MA_YEL_PERIOD=2; // The period of a fast MA input int STOP_LOSS=800; // Stop loss input int TRAL_STOP=800; // Trailing stop input double LOTS=0.1; // Lot //---Create an object--- my_expert expert; //---Initialize the MqlDataTime structure--- MqlDateTime time; int day_of_week; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //---Initialize the EA expert.get_periods(MA_RED_PERIOD,MA_YEL_PERIOD); // Set the MA periods expert.get_lot(LOTS); // Set the lot expert.get_stops(STOP_LOSS,TRAL_STOP); // Set stop orders return(0); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { TimeToStruct(TimeCurrent(),time); day_of_week=time.day_of_week; if(PositionsTotal()<1) { if(day_of_week==5 && expert.check_for_buy()==true){expert.open_buy();} else if(day_of_week==1 && expert.check_for_sell()==true){expert.open_sell();} } else expert.position_modify(); } //+------------------------------------------------------------------+
¡Conseguido! Me gustaría anotar algunos elementos especiales. Utilicé la estructura MqlDateTime para identificar los días de la semana en el nivel de software. Primero, transformamos la fecha actual del servidor a un formato estructurado. Obtuvimos así un índice del día actual (1-lunes, ..., 5-viernes) y lo comparamos con el valor que hemos configurado.
¡Probémoslo! Para no sobrecargarle con investigaciones pesadas y más dígitos, aquí le pongo todos los resultados sobre la mesa.
Aquí está:
Tabla 1. Resumen de compras en cada día de la semana
Tabla 2. Resumen de ventas en cada día de la semana
Los mejores resultados se señalan en verde, y los peores en naranja.
Después de las acciones descritas arriba, el sistema debe asegurar un beneficio en combinación con un nivel relativamente bajo de reducción, un buen porcentaje de operaciones rentables (aquí, cuantas menos operaciones, mejor) y un beneficio relativamente alto por operación.
Obviamente, el sistema más efectivo es comprar los viernes y vender los lunes. Combine ambas condiciones:
if(PositionsTotal()<1){ if(day_of_week==5&&expert.check_for_buy()==true){expert.open_buy();} else if(day_of_week==1&&expert.check_for_sell()==true){expert.open_sell();}} else expert.position_modify();
Ahora, el Asesor Experto abre posiciones en ambas direcciones, pero en días definidos estrictamente. Por motivos de claridad, dibujaré los diagramas obtenidos sin y con el filtro:
Figura 2. Los resultados de la simulación del EA sin usar un filtro (EURUSD, H1, 01.01.2010-31.12.2010,)
Figura 3. Los resultados de la simulación del EA usando el filtro (EURUSD, H1, 01.01.2010-31.12.2010)
¿Qué le parecen los resultados? Usando el filtro, el sistema de trading ahora es más estable. Antes de las modificaciones, el Asesor Experto aumentaba principalmente el beneficio en la primera mitad del período de simulación, mientras que tras la "mejora", aumenta durante todo el período.
Comparemos los informes:
Tabla 3. Resultados de la simulación antes y después de usar el filtro
El único factor preocupante que no se puede ignorar es la caída del beneficio neto en casi 1.000 USD (26%). Pero estamos reduciendo el número de operaciones casi 3,5 veces, es decir, estamos reduciendo significativamente, primero, el potencial para llevar a cabo operaciones negativas, y segundo, los gastos del diferencial (218*2-62*2=312 USD y es solo para EUR/USD). El porcentaje de ganancias aumentó a un 57%, lo que ya resulta significativo. A su vez, el beneficio por operación de trading ha aumentado en un 14% a 113 USD. Como diría L. Williams: "¡Esta es la cantidad que merece la pena invertir!"
Conclusión
Los precios no se comportan de manera aleatoria, y es un hecho comprobado. Este hecho se puede usar para nuestro beneficio. Aquí solo he dado un ejemplo que es una mínima fracción de las innumerables variaciones y técnicas que pueden mejorar el rendimiento de su sistema de trading. Sin embargo, esta diversidad oculta un vicio. No todos los filtros se pueden integrar, de modo que debe elegir con cuidado, siempre teniendo en cuenta todas las posibles situaciones.
No se olvide de que, independientemente de lo perfecto que sea el filtro, también dejará fuera operaciones rentables, es decir, su posible beneficio. ¡Buena suerte!
Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/269
![3 Métodos de Aceleración de Indicadores según el Ejemplo de Regresión Lineal](https://c.mql5.com/2/0/Indirocket.png)
![El Papel de las Distribuciones Estadísticas en el Trabajo del Trader](https://c.mql5.com/2/0/statistic_measument.png)
![Cómo ser suministrador de señales para MetaTrader 4 y MetaTrader 5](https://c.mql5.com/2/0/Avatar_How_to_become_a_signal_provider.png)
![Usar Pseudo-Plantillas como Alternativa a Plantillas C++](https://c.mql5.com/2/0/MQL5_template.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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