//+------------------------------------------------------------------+
//| OnTesterInit_Sample.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 "EA exemple avec la fonction OnTesterInit(),"
#property description "dans laquelle les valeurs et limites des "
#property description "entrées pendant l'optimisation sont définies"
input double lots=0. 1; // volume en lots
input double kATR=3; // longueur de la bougie de signal dans l'ATR
input int ATRperiod=20; // période de l'ATR
input int holdbars=8; // nombre de barre pour tenir la position
input int slippage=10; // slippage autorisé
input bool revers=false; // renversement de signal ?
input ulong EXPERT_MAGIC=0; // nombre magique de l'EA
//--- pour stocker le handle de l'ATR
int atr_handle;
//--- nous stockerons ici les dernières valeurs de l'ATR et le corps de la bougie
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- heure de lancement de l'optimisation
datetime optimization_start;
//--- pour l'affichage de la durée sur un graphique à la fin de l'optimisation
string report;
//+------------------------------------------------------------------+
//| Fonction TesterInit |
//+------------------------------------------------------------------+
void OnTesterInit()
{
//--- set the values of inputs for optimization
ParameterSetRange("lots",false,0.1,0,0,0);
ParameterSetRange("kATR",true,3.0,1.0,0.3,7.0);
ParameterSetRange("ATRperiod",true,10,15,1,30);
ParameterSetRange("holdbars",true,5,3,1,15);
ParameterSetRange("slippage",false,10,0,0,0);
ParameterSetRange("revers",true,false,false,1,true);
ParameterSetRange("EXPERT_MAGIC",false,123456,0,0,0);
Print("Les valeurs initiales et les limites des paramètres d'optimisation sont définies");
//--- heure de démarrage de l'optimisation
optimization_start=TimeLocal();
report=StringFormat("%s: optimisation lancée à %s",
__FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- affiche les messages sur le graphique et dans le journal du terminal
Print(report);
Comment(report);
//---
}
//+------------------------------------------------------------------+
//| Fonction TesterDeinit |
//+------------------------------------------------------------------+
void OnTesterDeinit()
{
//--- durée de l'optimisation
string log_message=StringFormat("%s: l'optimisation a pris %d secondes",
__FUNCTION__,TimeLocal()-optimization_start);
PrintFormat(log_message);
report=report+"\r\n"+log_message;
Comment(report);
}
//+------------------------------------------------------------------+
//| Fonction d'initialisation de l'expert |
//+------------------------------------------------------------------+
int OnInit()
{
//--- initialisation des variables globales
last_atr=0;
last_body=0;
//--- définit le volume correct
double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
trade_lot=lots>min_lot? lots:min_lot;
//--- crée le handle de l'indicateur ATR
atr_handle=iATR(_Symbol,_Period,ATRperiod);
if(atr_handle==INVALID_HANDLE)
{
PrintFormat("%s: échec de la création de iATR, code d'erreur %d",__FUNCTION__,GetLastError());
return(INIT_FAILED);
}
//--- initialisation réussie de l'EA
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Fonction de tick de l'Expert |
//+------------------------------------------------------------------+
void OnTick()
{
//--- signal de trading
static int signal=0; // +1 signifie un signal d'achat, -1 signifie un signal de vente
//--- vérifie et ferme les anciennes positions ouvertes plus de 'holdbars' barres auparavant
ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- vérifie si c'est une nouvelle barre
if(isNewBar())
{
//--- vérifie la présence d'un signal
signal=CheckSignal();
}
//--- si une position de type netting est ouverte, ignore le signal - attends jusqu'à sa fermeture
if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
{
signal=0;
return; // sors de la fonction NewTick et n'entre pas sur le marché avant qu'une nouvelle barre n'apparaisse
}
//--- pour un compte de type hedging, chaque position est conservée et fermée séparément
if(signal!=0)
{
//--- signal d'achat
if(signal>0)
{
PrintFormat("%s: Signal d'achat ! Revers=%s",__FUNCTION__,string(revers));
if(Buy(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
//--- signal de vente
if(signal<0)
{
PrintFormat("%s: Signal de vente ! Revers=%s",__FUNCTION__,string(revers));
if(Sell(trade_lot,slippage,EXPERT_MAGIC))
signal=0;
}
}
//--- fin de la fonction OnTick
}
//+------------------------------------------------------------------+
//| Vérifie la présence d'un nouveau signal de trading |
//+------------------------------------------------------------------+
int CheckSignal()
{
//--- 0 signifie aucun signal
int res=0;
//--- récupère la valeur de l'ATR sur l'avant dernière barre (l'indice de la barre est 2)
double atr_value[1];
if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
{
last_atr=atr_value[0];
//--- récupère les données de la dernière barre fermée dans le tableau d'éléments de type MqlRates
MqlRates bar[1];
if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
{
//--- calcule la taille du corps de la dernière barre complétée
last_body=bar[0].close-bar[0].open;
//--- si le corps de la dernière barre (avec l'indice 1) est supérieur à la valeur précédente de l'ATR (sur la barre d'indice 2), un signal de trading est reçu
if(MathAbs(last_body)>kATR*last_atr)
res=last_body>0?1:-1; // valeur positive pour une bougie haussière
}
else
PrintFormat("%s: Impossible de recevoir la dernière barre ! Erreur",__FUNCTION__,GetLastError());
}
else
PrintFormat("%s: Impossible de recevoir la valeur de l'ATR ! Erreur",__FUNCTION__,GetLastError());
//--- si le mode de trading renversé est activé
res=revers?-res:res; // retourne le signal si nécessaire (retourne -1 au lieu de 1 et vice versa)
//--- retourne la valeur du signal de trading
return (res);
}
//+------------------------------------------------------------------+
//| Retourne 'true' lorsqu'une nouvelle barre apparaît |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
{
static datetime bartime=0; // stocke l'heure d'ouverture de la barre courante
//--- récupère l'heure d'ouverture de la barre zéro
datetime currbar_time=iTime(_Symbol,_Period,0);
//--- si l'heure d'ouverture change, une nouvelle barre est arrivée
if(bartime!=currbar_time)
{
bartime=currbar_time;
lastbar_timeopen=bartime;
//--- affiche les données de l'heure d'ouverture de la nouvelle barre dans le journal
if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
{
//--- affiche un message avec l'heure d'ouverture de la nouvelle barre
PrintFormat("%s: nouvelle barre sur %s %s ouverte à %s",__FUNCTION__,_Symbol,
StringSubstr(EnumToString(_Period),7),
TimeToString(TimeCurrent(),TIME_SECONDS));
//--- récupère les données du dernier tick
MqlTick last_tick;
if(!SymbolInfoTick(Symbol(),last_tick))
Print("Echec de SymbolInfoTick(), erreur = ",GetLastError());
//--- affiche l'heure du dernier tick en millisecondes
PrintFormat("Le dernier tick était à %s.%03d",
TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
}
//--- nous avons une nouvelle barre
return (true);
}
//--- aucune nouvelle barre
return (false);
}
//+------------------------------------------------------------------+
//| Achète au prix du marché avec le volume spécifié |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- achète au prix du marché
return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| Vends au prix du marché avec le volume spécifié |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong magicnumber=0)
{
//--- vends au prix du marché
return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
}
//+------------------------------------------------------------------+
//| Ferme les positions détenues trop longtemps (en barres) |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong magicnumber=0)
{
int total=PositionsTotal(); // nombre de positions ouvertes
//--- itère sur les positions ouvertes
for(int i=total-1; i>=0; i--)
{
//--- paramètres de la position
ulong position_ticket=PositionGetTicket(i); // ticket de la position
string position_symbol=PositionGetString(POSITION_SYMBOL); // symbole
ulong magic=PositionGetInteger(POSITION_MAGIC); // MagicNumber de la position
datetime position_open=(datetime)PositionGetInteger(POSITION_TIME); // heure d'ouverture de la position
int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1; // depuis combien de barres une position est-elle ouverte
//--- si la position est trop vieille et que le MagicNumber et le symbole correspondent
if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
{
int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // nombre de décimales
double volume=PositionGetDouble(POSITION_VOLUME); // volume de la position
ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // type de la position
string str_type=StringSubstr(EnumToString(type),14);
StringToLower(str_type); // mets le texte en minuscules
PrintFormat("Ferme la position #%d %s %s %.2f",
position_ticket,position_symbol,str_type,volume);
//--- définit le type de l'ordre et envoie une demande de trade
if(type==POSITION_TYPE_BUY)
MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
else
MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
}
}
}
//+------------------------------------------------------------------+
//| Prépare et envoie une demande de trade |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
{
//--- déclare et initialise les structures
MqlTradeRequest request={};
MqlTradeResult result={};
double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
if(type==ORDER_TYPE_BUY)
price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- paramètres de la demande
request.action =TRADE_ACTION_DEAL; // trading operation type
request.position =pos_ticket; // position ticket if closing
request.symbol =Symbol(); // symbole
request.volume =volume; // volume
request.type =type; // type de l'ordre
request.price =price; // prix du trade
request.deviation=slip; // déviation du prix autorisée
request.magic =magicnumber; // MagicNumber de l'ordre
//--- envoie une demande
if(!OrderSend(request,result))
{
//--- affiche les informations de l'échec
PrintFormat("OrderSend %s %s %.2f à %.5f, erreur %d",
request.symbol,EnumToString(type),volume,request.price,GetLastError());
return (false);
}
//--- information sur l'opération réussie
PrintFormat("retcode=%u deal=%I64u order=%I64u",result.retcode,result.deal,result.order);
return (true);
}
|