//+------------------------------------------------------------------+
//| TradeByATR.mq5 |
//| Copyright 2018, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property link "https://www.mql5.com"
#property version "1.00"
#property description "Ejemplo de asesor que comercia en la dirección \"explosiva\" de la vela"
#property description La vela "\"explosiva\" tiene el cuerpo de un tamaño superior a k*ATR"
#property description "El parámetro \"revers\" invierte la dirección de la señal"
input double lots=0.1; // volumen en lotes
input double kATR=3; // longitud de la vela de señal en ATR
input int ATRperiod=20; // periodo del indicador ATR
input int holdbars=8; // cuántas barras mantenemos la posición
input int slippage=10; // Deslizamiento permitido
input bool revers=false; // ¿viramos la señal?
input ulong EXPERT_MAGIC=0; // MagicNumber del experto
//--- para guardar el manejador del indicador ATR
int atr_handle;
//--- aquí vamos a guardar los últimos valores de ATR y del cuerpo de la vela
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- inicializamos las variables globales
last_atr=0;
last_body=0;
//--- establecemos el volumen correcto
double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
trade_lot=lots>min_lot? lots:min_lot;
//--- creamos el manejador del indicador ATR
atr_handle=iATR(_Symbol,_Period,ATRperiod);
if(atr_handle==INVALID_HANDLE)
{
PrintFormat("%s: no se ha logrado crear iATR, código de error %d",__FUNCTION__,GetLastError());
return(INIT_FAILED);
}
//--- el experto se ha inicializado correctamente
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- comunicamos el código de finalización del funcionamiento del experto
Print(__FILE__,": Código del motivo de la desinicialización = ",reason);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//--- señal comercial
static int signal=0; // +1 indica la señal de compra, -1 indica la señal de venta
//--- comprobamos y cerramos las posiciones antiguas, abiertas hace más de "holdbars" barras
ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- comprobamos la aparición de una nueva barra
if(isNewBar())
{
//--- comprobamos la presencia de una señal
signal=CheckSignal();
}
//--- si tenemos abierta una posición de compensación, omitimos la señal hasta que la posición se cierre
if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
{
signal=0;
return; // salimos del manejador de eventos NewTick y no entramos en el mercado hasta que aparezca una nueva barra
}
//--- para la cuenta de cobertura, cada posición vive y se cierra por separado
if(signal!=0)
{
//--- señal de compra
if(signal>0)
{
PrintFormat("%s: ¡Hay señal de compra! Revers=%s",__FUNCTION__,string(revers));
if(Buy(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
//--- señal de venta
if(signal<0)
{
PrintFormat("%s: ¡Hay señal de venta! Revers=%s",__FUNCTION__,string(revers));
if(Sell(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
}
//--- final de la función OnTick
}
//+------------------------------------------------------------------+
//| Comprueba la presencia de una señal comercial |
//+------------------------------------------------------------------+
int CheckSignal()
{
//--- 0 indica la ausencia de señal
int res=0;
//--- obtenemos el valor de ATR en la penúltima barra finalizada (el índice de la barra es igual a 2)
double atr_value[1];
if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
{
last_atr=atr_value[0];
//--- obtenemos los datos de la última barra cerrada en una matriz del tipo MqlRates
MqlRates bar[1];
if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
{
//--- calculamos el tamaño del cuerpo de la barra en la última barra cerrada
last_body=bar[0].close-bar[0].open;
//--- si el cuerpo de la última barra (con índice 1) supera el anterior valor de ATR (en la barra con índice 2), entonces la señal comercial ha sido recibida
if(MathAbs(last_body)>kATR*last_atr)
res=last_body>0?1:-1; // para la vela ascendente el valor es positivo
}
else
PrintFormat("%s: ¡No se ha podido obtener la última barra! Error",__FUNCTION__,GetLastError());
}
else
PrintFormat("%s: ¡No se ha podido obtener el valor del indicador ATR! Error",__FUNCTION__,GetLastError());
//--- si está activado el modo de comercio reverso
res=revers?-res:res; // si es necesario, viramos la señal (en lugar de 1, retornamos -1, y en lugar de -1, retornamos +1)
//--- retornamos el valor de la señal comercial
return (res);
}
//+------------------------------------------------------------------+
//| Retorna true al aparecer una nueva barra |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
{
static datetime bartime=0; // guardamos la hora de apertura de la barra actual
//--- obtenemos la hora de apertura de la barra cero
datetime currbar_time=iTime(_Symbol,_Period,0);
//--- si la hora de apertura ha cambiado, significa que ha aparecido una nueva barra
if(bartime!=currbar_time)
{
bartime=currbar_time;
lastbar_timeopen=bartime;
//--- es necesario o no mostrar en el log la información sobre la hora de apertura de una nueva barra
if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
{
//--- mostramos el mensaje sobre la hora de apertura de una nueva barra
PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol,
StringSubstr(EnumToString(_Period),7),
TimeToString(TimeCurrent(),TIME_SECONDS));
//--- obtenemos los datos sobre el último tick
MqlTick last_tick;
if(!SymbolInfoTick(Symbol(),last_tick))
Print("SymbolInfoTick() failed, error = ",GetLastError());
//--- mostramos la hora del útlimo tick con una precisión de hasta un milisegundo
PrintFormat("Last tick was at %s.%03d",
TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
}
//--- tenemos una nueva barra
return (true);
}
//--- no hay barras nuevas
return (false);
}
//+------------------------------------------------------------------+
//| Comprar según el mercado con un volumen establecido |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- compramos al precio de mercado
return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| Vender según el mercado con un volumen establecido |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- vendemos al precio de mercado
return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| Cierre de posiciones según el tiempo de mantenimiento en barras |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong magicnumber=0)
{
int total=PositionsTotal(); // número de posiciones abiertas
//--- iterar todas las posiciones abiertas
for(int i=total-1; i>=0; i--)
{
//--- parámetros de la posición
ulong position_ticket=PositionGetTicket(i); // ticket de la posición
string position_symbol=PositionGetString(POSITION_SYMBOL); // símbolo
ulong magic=PositionGetInteger(POSITION_MAGIC); // MagicNumber de la posición
datetime position_open=(datetime)PositionGetInteger(POSITION_TIME); // hora de apertura de la posición
int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1; // cuántas barras atrás se ha abierto la posición
//--- si la posición vive durante bastante tiempo, y además el MagicNumber y el símbolo coinciden
if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
{
int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // número de dígitos decimales
double volume=PositionGetDouble(POSITION_VOLUME); // volumen de la posición
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // tipo de la posición
string str_type=StringSubstr(EnumToString(type),14);
StringToLower(str_type); // ponemos el texto en minúsculas para formatear correctamente el mensaje
PrintFormat("Cerramos la posición #%d %s %s %.2f",
position_ticket,position_symbol,str_type,volume);
//--- estableciendo el tipo de orden y enviando la solicitud comercial
if(type==POSITION_TYPE_BUY)
MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
else
MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
}
}
}
//+------------------------------------------------------------------+
//| Preparando y enviando la solicitud comercial |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
{
//--- declarando e inicializando las estructuras
MqlTradeRequest request={};
MqlTradeResult result={};
double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
if(type==ORDER_TYPE_BUY)
price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- parámetros de la solicitud
request.action =TRADE_ACTION_DEAL; // tipo de operación comercial
request.position =pos_ticket; // ticket de la posición, si cerramos
request.symbol =Symbol(); // símbolo
request.volume =volume; // volumen
request.type =type; // tipo de orden
request.price =price; // precio de finalización de la transacción
request.deviation=slip; // desviación permitida respecto al precio
request.magic =magicnumber; // MagicNumber de la orden
//--- envío de la solicitud
if(!OrderSend(request,result))
{
//--- mostramos la información sobre el fallo
PrintFormat("OrderSend %s %s %.2f at %.5f error %d",
request.symbol,EnumToString(type),volume,request.price,GetLastError());
return (false);
}
//--- comunicamos que la operación ha tenido éxito
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
return (true);
}
|