Libro de Recetas MQL5: Usar Indicadores para Configurar Condiciones de Trading en Asesores Expertos
Anatoli Kazharski | 28 mayo, 2014
Introducción
En este artículo reforzaremos el Asesor Experto con indicadores cuyos valores se usarán para comprobar las condiciones de apertura de posición. Además crearemos una lista desplegable en los parámetros externos para poder seleccionar uno de los tres indicadores de trading.
Por favor, tenga en cuenta que continuaremos modificando el Asesor Experto en el que hemos trabajando durante los artículos anteriores de la serie de Libros de Recetas MQL5. La última versión del Asesor Experto se puede bajar del artículo titulado "MQL5 Cookbook: The History of Deals And Function Library for Getting Position Properties" (“Libro de Recetas MQL5: El Historial de Transacciones y la Biblioteca de Funciones para Obtener Propiedades de Posición”).
Además, en este artículo trataremos una función que crearemos para comprobar si se pueden realizar operaciones de trading o no. La función de apertura de posición se modificará para permitir al Asesor Experto determinar el modo de trading (Instant Execution, o ejecución instantánea, y Market Execution, o ejecución de mercado).
Puesto que al código del Asesor Experto, siguiendo todas las modificaciones y mejoras hechas en los artículos anteriores, ya supera las 1.500 líneas, comenzará a ser menos conveniente con cada nueva prestación que le añadamos. De modo que la solución lógica sería dividirlo en varias categorías como archivos de biblioteca separados. Ahora que ya hemos establecido los objetivos, comencemos.
Desarrollo de Asesor Experto
Colocaremos el código fuente de Asesor Experto (*.mq5) del artículo anterior en una carpeta separada, TestIndicatorConditions, donde necesitaremos crear la subcarpeta Include. Esta será la carpeta en la que crearemos los archivos include (*.mqh). Se puede generar usando el MQL5 Wizard (Ctrl+N), o se pueden crear manualmente en el directorio requerido como archivos de texto estándar (*.txt), renombrándolos después como *.mqh.
A continuación tiene los nombres y comentarios para todos los archivos creados:
- Enums.mqh contendrá todas las enumeraciones;
- InfoPanel.mqh incluirá funciones para configurar el panel de información, creando y eliminando objetos gráficos;
- Errors.mqh cubrirá todas las funciones que devuelvan códigos de error y razones de desinicialización;
- TradeSignals.mqh incluirá funciones que llenarán arrays con precios y valores de indicador, así como bloques de señal;
- TradeFunctions.mqh contendrá funciones de trading;
- ToString.mqh cubrirá funciones que convierten valores numéricos a valores de cadena de caracteres;
- Auxiliary.mqh se usará para otras funciones auxiliares.
Para incluir estas bibliotecas en el archivo principal usaremos la directiva #include. Puesto que el archivo principal del Asesor Experto y la carpeta de archivos include (Include) se encuentran en la misma carpeta, el código para incluir archivos será el siguiente:
//--- Include custom libraries #include "Include\Enums.mqh" #include "Include\InfoPanel.mqh" #include "Include\Errors.mqh" #include "Include\TradeSignals.mqh" #include "Include\TradeFunctions.mqh" #include "Include\ToString.mqh" #include "Include\Auxiliary.mqh"
A continuación, podremos abrirlos y modificarlos, y mover una parte del código fuente del archivo principal del Asesor Experto.
Para navegar correctamente por el código, en cada archivo de encabezamiento se añadirán referencias a los códigos de encabezamiento adyacentes y al código principal del Asesor Experto. Por ejemplo, para nuestra biblioteca de funciones de trading, TradeFunctions.mqh, este tendrá el siguiente aspecto:
//--- Connection with the main file of the Expert Advisor #include "..\TestIndicatorConditions.mq5" //--- Include custom libraries #include "Enums.mqh" #include "InfoPanel.mqh" #include "Errors.mqh" #include "TradeSignals.mqh" #include "ToString.mqh" #include "Auxiliary.mqh"
En el caso de archivos en el mismo nivel de anidamiento, basta con especificar el nombre. Para ir a un nivel superior, debe poner dos puntos antes de la barra negra en la ruta.
Añadamos una enumeración para indicadores en el archivo Enums.mqh. Para ilustrarlo mejor, en este Asesor Experto usaremos dos indicadores estándar (Moving Average y Commodity Channel Index) y un indicador personalizado (MultiRange_PCH). La enumeración será así:
//--- Indicators enum ENUM_INDICATORS { MA = 0, // Moving Average CCI = 1, // CCI PCH = 2 // Price Channel };
Los parámetros externos se modifican de la siguiente manera:
//--- External parameters of the Expert Advisor sinput long MagicNumber=777; // Magic number sinput int Deviation=10; // Slippage input ENUM_INDICATORS Indicator=MA; // Indicator input int IndicatorPeriod=5; // Indicator period input int IndicatorSegments=2; // Number of one direction indicator segments input double Lot=0.1; // Lot input double VolumeIncrease=0.1; // Position volume increase input double VolumeIncreaseStep=10; // Step for volume increase input double StopLoss=50; // Stop Loss input double TakeProfit=100; // Take Profit input double TrailingStop=10; // Trailing Stop input bool Reverse=true; // Position reversal sinput bool ShowInfoPanel=true; // Display of the info panel
Tal y como dijimos arriba, podrá seleccionar uno de los tres indicadores de la lista desplegable del parámetro Indicator.
Solo hay un parámetro aplicable a todos los indicadores donde se puede configurar el período de indicador: IndicatorPeriod. El parámetro NumberOfBars de la versión anterior del Asesor Experto ha sido renombrado a IndicatorSegments, y ahora indica el número de barras durante las cuales un indicador debe ir hacia arriba o hacia abajo para satisfacer la condición de apertura de posición.
Además, hemos añadido otro parámetro externo, VolumeIncreaseStep, que se puede usar para configurar el paso para el aumento de volumen en puntos.
El valor de la variable AllowedNumberOfBars (que ahora se llama AllowedNumberOfSegments) se solía ajustar en la función personalizada GetBarsData(). Ahora se colocará en una función separada que solo se llamará durante la inicialización.
Puesto que ahora se comprobarán todas las condiciones de apertura usando valores de indicador, el valor que se asignará siempre será mayor de dos. En otras palabras, si a la variable externa IndicatorSegments se asigna el valor de 1, a la variable AllowedNumberOfSegments se asignará el valor de 3, puesto que para satisfacer la condición (por ejemplo, para una compra, o BUY), el valor de indicador en la barra completada debe ser mayor que el valor de la barra anterior. Para ello, necesitaremos obtener los valores de los tres últimos indicadores.
Abajo tiene el código de la función CorrectInputParameters():
//+------------------------------------------------------------------+ //| Adjusting input parameters | //+------------------------------------------------------------------+ void CorrectInputParameters() { //--- Adjust the number of bars for the position opening condition if(AllowedNumberOfSegments<=0) { if(IndicatorSegments<=1) AllowedNumberOfSegments=3; // At least three bars are required if(IndicatorSegments>=5) AllowedNumberOfSegments=5; // but no more than 7 else AllowedNumberOfSegments=IndicatorSegments+1; // and always greater by two } }
Antes de comenzar a trabajar con los indicadores, creemos una función que comprobará si se permite realizar operaciones de trading: CheckTradingPermission(). Si no está permitido por alguna de las razones especificadas en la función, se devolverá un valor de cero. Esto significará que el siguiente intento se deberá realizar en la siguiente barra. Esto significará que el siguiente intento se deberá realizar en la siguiente barra.
//+------------------------------------------------------------------+ //| Checking if trading is allowed | //+------------------------------------------------------------------+ bool CheckTradingPermission() { //--- For real-time mode if(IsRealtime()) { //--- Checking server connection if(!TerminalInfoInteger(TERMINAL_CONNECTED)) return(1); //--- Permission to trade at the running program level if(!MQL5InfoInteger(MQL5_TRADE_ALLOWED)) return(2); //--- Permission to trade at the terminal level if(!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) return(3); //--- Permission to trade for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_ALLOWED)) return(4); //--- Permission to trade automatically for the current account if(!AccountInfoInteger(ACCOUNT_TRADE_EXPERT)) return(5); } //--- return(0); }
Vayamos ahora al tema principal del artículo. Para tener acceso a los valores del indicador deberemos obtener primero su identificador. Esto se hace usando funciones especiales cuyos nombres se componen del nombre corto del indicador y el símbolo 'i' que lo precede.
Por ejemplo, el indicador Moving Average (Media Móvil, o MA) tiene la función correspondiente iMA(). Todos los identificadores de indicadores estándar en el terminal de MetaTrader 5 se pueden obtener usando estas funciones. La lista completa está disponible en la sección del Material de Referencia de MQL5 llamada Technical Indicators. Si necesita obtener el identificador de un indicador personalizado, utilice la función iCustom().
Implementaremos la función GetIndicatorHandle() allá donde, dependiendo del indicador seleccionado en el parámetro Indicator, el valor del identificador del indicador correspondiente se asigne a la variable global indicator_handle. El código de la función se puede encontrar en nuestra biblioteca de funciones de señales de trading (el archivo \Include\TradeSignals.mqh), mientras que la variable con el identificador del indicador se encuentra en el archivo principal del Asesor Experto.
//+------------------------------------------------------------------+ //| Getting the indicator handle | //+------------------------------------------------------------------+ void GetIndicatorHandle() { //--- If the Moving Average indicator is selected if(Indicator==MA) indicator_handle=iMA(_Symbol,Period(),IndicatorPeriod,0,MODE_SMA,PRICE_CLOSE); //--- If the CCI indicator is selected if(Indicator==CCI) indicator_handle=iCCI(_Symbol,Period(),IndicatorPeriod,PRICE_CLOSE); //--- If the MultiRange_PCH indicator is selected if(Indicator==PCH) indicator_handle=iCustom(_Symbol,Period(),"MultiRange_PCH",IndicatorPeriod); //--- If the indicator handle could not be obtained if(indicator_handle==INVALID_HANDLE) Print("Failed to get the indicator handle!"); }
Además, crearemos la función GetDataIndicators(), con la que, una vez obtenidos los identificadores del indicador, podremos obtener sus valores. Esto se hace usando la función CopyBuffer() de forma similar a la obtención de valores de barra usando las funciones CopyTime(), CopyClose(), CopyOpen(), CopyHigh() y CopyLow() que ya se trataron en el artículo "MQL5 Cookbook: Analyzing Position Properties in the MetaTrader 5 Strategy Tester" (“Libro de Recetas MQL5: Analizar Propiedades de Posición en el Probador de Estrategias de MetaTrader5”).
Puesto que el indicador puede tener varios buffers o filas de valores, el índice de buffer se pasa a la función CopyBuffer() como segundo parámetro. Los índices de buffer para indicadores estándar se pueden encontrar en el Material de Referencia de MQL5. En el caso de indicadores personalizados, los índices de buffer se pueden encontrar en el código, suponiendo que el código fuente esté disponible. Si no hay código, deberá encontrar el índice mediante la experimentación, observando cómo se satisfacen las condiciones en el modo de visualización de Probador de Estrategias.
Antes de ello, deberemos crear arrays dinámicos para valores de buffer de indicador en el archivo principal del Asesor Experto:
//--- Arrays for indicator values double indicator_buffer1[]; double indicator_buffer2[];
A continuación tiene el código de la función GetIndicatorsData():
//+------------------------------------------------------------------+ //| Getting indicator values | //+------------------------------------------------------------------+ bool GetIndicatorsData() { //--- If the indicator handle has been obtained if(indicator_handle!=INVALID_HANDLE) { //--- For the Moving Average or CCI indicator if(Indicator==MA || Indicator==CCI) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- For the MultiRange_PCH indicator if(Indicator==PCH) { //--- Reverse the indexing order (... 3 2 1 0) ArraySetAsSeries(indicator_buffer1,true); ArraySetAsSeries(indicator_buffer2,true); //--- Get indicator values if(CopyBuffer(indicator_handle,0,0,AllowedNumberOfSegments,indicator_buffer1)<AllowedNumberOfSegments || CopyBuffer(indicator_handle,1,0,AllowedNumberOfSegments,indicator_buffer2)<AllowedNumberOfSegments) { Print("Failed to copy the values ("+ _Symbol+"; "+TimeframeToString(Period())+") to the indicator_buffer1 or indicator_buffer2 array! Error ("+ IntegerToString(GetLastError())+"): "+ErrorDescription(GetLastError())); return(false); } } //--- return(true); } //--- If the indicator handle has not been obtained, retry else GetIndicatorHandle(); //--- return(false); }
La función GetTradingSignal() ha cambiado sustancialmente. Las condiciones son diferentes en ausencia de la posición y allá donde existe la posición. Para los indicadores Moving Average y CCI, las condiciones son las mismas. En el caso de MultiRange_PCH, se ordenan en un bloque separado. Para hacer el código más comprensible y evitar repeticiones crearemos una función auxiliar, GetSignal(), que devuelve una señal para apertura de posición o inversión, suponiendo que la posición exista y que la acción relevante esté permitida en el parámetro externo.
A continuación tiene el código de la función GetSignal():
//+------------------------------------------------------------------+ //| Checking the condition and returning a signal | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetSignal() { //--- Check conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]<indicator_buffer1[2]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5]) return(ORDER_TYPE_SELL); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]<indicator_buffer1[2] && indicator_buffer1[2]<indicator_buffer1[3] && indicator_buffer1[3]<indicator_buffer1[4] && indicator_buffer1[4]<indicator_buffer1[5] && indicator_buffer1[5]<indicator_buffer1[6]) return(ORDER_TYPE_SELL); //--- A Buy signal if(AllowedNumberOfSegments==3 && indicator_buffer1[1]>indicator_buffer1[2]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==4 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==5 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments==6 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5]) return(ORDER_TYPE_BUY); //--- if(AllowedNumberOfSegments>=7 && indicator_buffer1[1]>indicator_buffer1[2] && indicator_buffer1[2]>indicator_buffer1[3] && indicator_buffer1[3]>indicator_buffer1[4] && indicator_buffer1[4]>indicator_buffer1[5] && indicator_buffer1[5]>indicator_buffer1[6]) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- A Buy signal if(close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); } //--- No signal return(WRONG_VALUE); }
El código de la función GetTradingSignal() tiene ahora el siguiente aspecto:
//+------------------------------------------------------------------+ //| Determining trading signals | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE GetTradingSignal() { //--- If there is no position if(!pos.exists) { //--- A Sell signal if(GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- A Buy signal if(GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); } //--- If the position exists if(pos.exists) { //--- Get the position type GetPositionProperties(P_TYPE); //--- Get the last deal price GetPositionProperties(P_PRICE_LAST_DEAL); //--- Block that checks conditions for the Moving Average and CCI indicators if(Indicator==MA || Indicator==CCI) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_SELL) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && GetSignal()==ORDER_TYPE_BUY) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && GetSignal()==ORDER_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } //--- Block that checks conditions for the MultiRange_PCH indicator if(Indicator==PCH) { //--- A Sell signal if(pos.type==POSITION_TYPE_BUY && close_price[1]<indicator_buffer2[1] && open_price[1]>indicator_buffer2[1]) return(ORDER_TYPE_SELL); //--- if(pos.type==POSITION_TYPE_SELL && close_price[1]<pos.last_deal_price-CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_SELL); //--- A Buy signal if(pos.type==POSITION_TYPE_SELL && close_price[1]>indicator_buffer1[1] && open_price[1]<indicator_buffer1[1]) return(ORDER_TYPE_BUY); //--- if(pos.type==POSITION_TYPE_BUY && close_price[1]>pos.last_deal_price+CorrectValueBySymbolDigits(VolumeIncreaseStep*_Point)) return(ORDER_TYPE_BUY); } } //--- No signal return(WRONG_VALUE); }
Ahora solo debemos fijarnos en los modos Instant Execution y Market Execution, que son parte de las propiedades de símbolo, y en si la modificación de la función de apertura de posición OpenPosition() funciona correctamente. Los modos cuyos nombres explican sus cualidades se pueden encontrar en el material de referencia MQL5:
- Instant Execution
- Market Execution
Por favor, tenga en cuenta que, al trabajar con el modo Market Execution no podrá abrir una posición con los niveles Stop Loss y Take Profit configurados: deberá abrir una posición primero y después modificarla, configurando los niveles.
Añadamos el modo de ejecución a la estructura de propiedades de símbolo:
//--- Symbol properties struct symbol_properties { int digits; // Number of decimal places in the price int spread; // Spread in points int stops_level; // Stops level double point; // Point value double ask; // Ask price double bid; // Bid price double volume_min; // Minimum volume for a deal double volume_max; // Maximum volume for a deal double volume_limit; // Maximum permissible volume for a position and orders in one direction double volume_step; // Minimum volume change step for a deal double offset; // Offset from the maximum possible price for a transaction double up_level; // Upper Stop level price double down_level; // Lower Stop level price ENUM_SYMBOL_TRADE_EXECUTION execution_mode; // Execution mode };
A continuación, debemos modificar la enumeración ENUM_SYMBOL_PROPERTIES...
//--- Enumeration of position properties enum ENUM_SYMBOL_PROPERTIES { S_DIGITS = 0, S_SPREAD = 1, S_STOPSLEVEL = 2, S_POINT = 3, S_ASK = 4, S_BID = 5, S_VOLUME_MIN = 6, S_VOLUME_MAX = 7, S_VOLUME_LIMIT = 8, S_VOLUME_STEP = 9, S_FILTER = 10, S_UP_LEVEL = 11, S_DOWN_LEVEL = 12, S_EXECUTION_MODE = 13, S_ALL = 14 };
... y la función GetSymbolProperties():
case S_EXECUTION_MODE: symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //--- case S_ALL : symb.digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); symb.spread=(int)SymbolInfoInteger(_Symbol,SYMBOL_SPREAD); symb.stops_level=(int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); symb.point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); symb.ask=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),symb.digits); symb.bid=NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),symb.digits); symb.volume_min=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN); symb.volume_max=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX); symb.volume_limit=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_LIMIT); symb.volume_step=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP); symb.offset=NormalizeDouble(CorrectValueBySymbolDigits(lot_offset*symb.point),symb.digits); symb.up_level=NormalizeDouble(symb.ask+symb.stops_level*symb.point,symb.digits); symb.down_level=NormalizeDouble(symb.bid-symb.stops_level*symb.point,symb.digits); symb.execution_mode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_EXEMODE); break; //---
Como resultado, el código de la función OpenPosition() tendrá el siguiente aspecto:
//+------------------------------------------------------------------+ //| Opening a position | //+------------------------------------------------------------------+ void OpenPosition(double lot, ENUM_ORDER_TYPE order_type, double price, double sl, double tp, string comment) { //--- Set the magic number in the trading structure trade.SetExpertMagicNumber(MagicNumber); //--- Set the slippage in points trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); //--- The Instant Execution mode // A position can be opened with the Stop Loss and Take Profit levels set if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } //--- The Market Execution mode // First open a position and only then set the Stop Loss and Take Profit levels // *** Starting with build 803, Stop Loss and Take Profit can be set upon position opening *** if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET) { //--- If there is no position, first open a position and then set Stop Loss and Take Profit if(!pos.exists) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,0,0,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); //--- Get the flag of presence/absence of the position pos.exists=PositionSelect(_Symbol); //--- If the position exists if(pos.exists) { //--- Set Stop Loss and Take Profit if(!trade.PositionModify(_Symbol,sl,tp)) Print("Error modifying the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } //--- If the position exists, increase its volume and leave the Stop Loss and Take Profit levels unchanged else { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(_Symbol,order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } } }
Todavía tenemos que añadir el toque final a las funciones de gestión de eventos:
- OnInit
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Adjust the input parameters CorrectInputParameters(); //--- Get indicator handles GetIndicatorHandle(); //--- Initialize the new bar CheckNewBar(); //--- Get the properties GetPositionProperties(P_ALL); //--- Set the info panel SetInfoPanel(); //--- return(0); }
- OnDeinit
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Print the deinitialization reason to the journal Print(GetDeinitReasonText(reason)); //--- When deleting from the chart if(reason==REASON_REMOVE) { //--- Delete all objects relating to the info panel from the chart DeleteInfoPanel(); //--- Delete the indicator handle IndicatorRelease(indicator_handle); } }
- OnTick
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- If the bar is not new, exit if(!CheckNewBar()) { if(IsVisualMode() || IsRealtime()) { //--- Get the properties and update the values on the panel GetPositionProperties(P_ALL); //--- Set/update the info panel SetInfoPanel(); } return; } //--- If there is a new bar else { //--- If trading is allowed if(CheckTradingPermission()==0) { if(!GetIndicatorsData()) return; GetBarsData(); // Get bar data TradingBlock(); // Check the conditions and trade ModifyTrailingStop(); // Modify the Trailing Stop level } } //--- Get the properties GetPositionProperties(P_ALL); //--- Update the info panel SetInfoPanel(); }
Ahora que todas las funciones están listas, podemos optimizar los parámetros. Tenga en cuenta que debe compilar el código desde el archivo del programa principal.
Optimizar Parámetros y Simular el Asesor Experto
El Probador de Estrategias debe configurarse tal y como se muestra abajo:
Fig. 1. Configuración de Probador de Estrategias.
Además, configuraremos los parámetros del Asesor Experto para su optimización (vea también el archivo adjunto *.set con la configuración):
Fig. 2. Configuración del Asesor Experto.
La optimización dura unos 40 minutos en un procesador dual-core. El gráfico de optimización le permite examinar en parte la calidad de un sistema de trading basándonos en los resultados de la zona de beneficio:
Fig. 3. Gráfico de optimización.
Los resultados de la simulación del factor de recuperación máximo son los siguientes:
Fig. 4. Resultados de la simulación del factor de recuperación máximo.
Conclusión
Puede encontrar un archivo adjunto al artículo con los códigos fuente del Asesor Experto. Una vez descomprimido, debe colocar la carpeta de archivos \TestIndicatorConditions en <Metatrader 5 terminal>\MQL5\Experts. Para asegurarse de que el Asesor Experto funciona correctamente, debe descargarse el indicador MultiRange_PCH y colocarlo en <Metatrader 5 terminal>\MQL5\Indicators.