441 lines
26 KiB
MQL5
441 lines
26 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| BuddyIlanOptimizer.mq5 |
|
|
//| Copyright 2018, BPASoftware Thai Co. Ltd |
|
|
//| https://www.mql5.com/en/market/product/28759 |
|
|
//+------------------------------------------------------------------+
|
|
|
|
/*
|
|
* https://docs.microsoft.com/fr-fr/windows/desktop/api/shellapi/nf-shellapi-shellexecutea :
|
|
*
|
|
* If the function succeeds, it returns a value greater than 32. If the function fails,
|
|
* it returns an error value that indicates the cause of the failure. The return value
|
|
* is cast as an HINSTANCE for backward compatibility with 16-bit Windows applications.
|
|
* It is not a true HINSTANCE, however. It can be cast only to an int and compared to
|
|
* either 32 or the following error codes below.
|
|
*/
|
|
|
|
#import "shell32.dll"
|
|
int ShellExecuteW(int hwnd,string Operation,string
|
|
File,string Parameters,string Directory,int ShowCmd);
|
|
#import
|
|
|
|
#include <EasyXML\EasyXml.mqh>
|
|
|
|
|
|
input string sOptimiser=""; // --------------- Optimiser Settings
|
|
|
|
//Здесь задаем пути к другому терминалу (к копии).
|
|
input string sTerminalTesterPath="C:\Program Files\Alpari MT5 1";
|
|
input string sTerminalTesterDataPath="C:\Users\Пользователь\AppData\Roaming\MetaQuotes\Terminal\E0D00B1F6450CA08FAC85F562DAADA76";
|
|
|
|
input int InpTesterPeriod=45; // Tester Period (days)
|
|
|
|
input string sBuddyIlan=""; // --------------- Buddy Ilan Settings
|
|
|
|
input int TakeProfit = 40.0; // Take Profit (Point)
|
|
input int Stoploss = 1000.0; // Stop Loss (Point)
|
|
|
|
string gVarStop,gVarSL,gVarTP,gVarSTOFilter,gVarSTOTimeFrameFilter;
|
|
|
|
string str;
|
|
|
|
int BetterSL = 0.0;
|
|
int BetterTP = 0.0;
|
|
|
|
double BetterProfit=0.0;
|
|
double BetterDD=0.0;
|
|
bool BetterSTOFilter;
|
|
bool BetterSTOTimeFrameFilter;
|
|
|
|
enum OptimisationType { _SL,_TP,_STO };
|
|
|
|
bool bOptimisationDone=false;
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void DisplayNodesSL(CEasyXmlNode *Node)
|
|
{
|
|
for(int i=0; i<Node.Children().Total(); i++)
|
|
{
|
|
CEasyXmlNode *ChildNode=Node.Children().At(i);
|
|
|
|
if(ChildNode.Children().Total()==0)
|
|
{
|
|
str+=ChildNode.getValue()+",";
|
|
}
|
|
else
|
|
{
|
|
DisplayNodesSL(ChildNode);
|
|
|
|
if(Node.getName()=="Table" && ChildNode.getName()=="Row")
|
|
{
|
|
string res[];
|
|
StringSplit(str,',',res);
|
|
|
|
// Bypass columns titles
|
|
if(StringCompare(res[0],"Pass",true)!=0)
|
|
{
|
|
double profit=StringToDouble(res[2]);
|
|
int sl=(int) StringToInteger(res[10]);
|
|
|
|
PrintFormat("[%s] Profit=%.2lf StopLoss=%d DD=%s",str,profit,sl,res[8]);
|
|
|
|
if(profit>BetterProfit || (profit==BetterProfit && sl<BetterSL))
|
|
{
|
|
BetterProfit=profit;
|
|
BetterSL=sl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Node.getName()=="Table")
|
|
str="";
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void DisplayNodesTP(CEasyXmlNode *Node)
|
|
{
|
|
for(int i=0; i<Node.Children().Total(); i++)
|
|
{
|
|
CEasyXmlNode *ChildNode=Node.Children().At(i);
|
|
|
|
if(ChildNode.Children().Total()==0)
|
|
{
|
|
str+=ChildNode.getValue()+",";
|
|
}
|
|
else
|
|
{
|
|
DisplayNodesTP(ChildNode);
|
|
|
|
if(Node.getName()=="Table" && ChildNode.getName()=="Row")
|
|
{
|
|
string res[];
|
|
StringSplit(str,',',res);
|
|
|
|
if(StringCompare(res[0],"Pass",true)!=0)
|
|
{
|
|
double profit=StringToDouble(res[2]);
|
|
int tp=(int) StringToInteger(res[10]);
|
|
double dd=StringToDouble(res[8]);
|
|
|
|
PrintFormat("[%s] Profit=%.2lf TakeProfit=%d DD=%lf",str,profit,tp,dd);
|
|
|
|
if((profit>BetterProfit || (profit==BetterProfit && tp<BetterTP)) && dd<50.0)
|
|
{
|
|
BetterProfit=profit;
|
|
BetterTP = tp;
|
|
BetterDD = dd;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Node.getName()=="Table")
|
|
str="";
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
void DisplayNodesSTO(CEasyXmlNode *Node)
|
|
{
|
|
for(int i=0; i<Node.Children().Total(); i++)
|
|
{
|
|
CEasyXmlNode *ChildNode=Node.Children().At(i);
|
|
|
|
if(ChildNode.Children().Total()==0)
|
|
{
|
|
str+=ChildNode.getValue()+",";
|
|
}
|
|
else
|
|
{
|
|
DisplayNodesSTO(ChildNode);
|
|
|
|
if(Node.getName()=="Table" && ChildNode.getName()=="Row")
|
|
{
|
|
string res[];
|
|
StringSplit(str,',',res);
|
|
|
|
if(StringCompare(res[0],"Pass",true)!=0)
|
|
{
|
|
double profit=StringToDouble(res[2]);
|
|
|
|
PrintFormat("[%s] Profit=%.2lf %s %s DD=%s",str,profit,res[10],res[11],res[8]);
|
|
|
|
if(profit>BetterProfit) // On ne tient pas compte du DD ici
|
|
{
|
|
BetterProfit=profit;
|
|
BetterSTOFilter=(res[10]=="true")?true:false;
|
|
BetterSTOTimeFrameFilter=(res[11]=="true")?true:false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Node.getName()=="Table")
|
|
str="";
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CopyAndMoveCommonIni()
|
|
{
|
|
string PathIniFile= sTerminalTesterDataPath+"\\config\\common.ini";
|
|
string PathTester = TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\";
|
|
|
|
int ret=ShellExecuteW(0,"Open","xcopy","\""+PathIniFile+"\" \""+PathTester+"\" /y","",0);
|
|
|
|
// wait until the file is copied
|
|
Sleep(2500);
|
|
if(ret<32)
|
|
{
|
|
Print("Failed copying ini file");
|
|
return false;
|
|
}
|
|
|
|
// We are working now in the sand box, we can use usual MT5 File commands
|
|
string IniFileName="Optimiser\\common.ini";
|
|
string CopyTo="Optimiser\\optimise.ini";
|
|
return FileMove( IniFileName, 0, CopyTo, 0 );
|
|
//return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CreateAndCopyParametersFile(bool SL,bool TP,int SLValue,int TPValue)
|
|
{
|
|
// MQL5\Profiles\Tester
|
|
int filehandle=FileOpen("Optimiser\\bollinger.set",FILE_WRITE|FILE_TXT);
|
|
|
|
if(filehandle!=INVALID_HANDLE)
|
|
{
|
|
FileWrite(filehandle,
|
|
"_EA_IDENTIFIER=Buddy Ilan\n",
|
|
"_EA_MAGIC_NUMBER=1111||0||1||10||N\n",
|
|
|
|
StringFormat("TakeProfit=%d||30||10||70||%s\n",(TPValue==0)?30:TPValue,(TP==true)?"Y":"N"),
|
|
StringFormat("Stoploss=%d||500||250||1500||%s\n",(SLValue==0)?1000:SLValue,(SL==true)?"Y":"N"));
|
|
|
|
FileClose(filehandle);
|
|
}
|
|
else
|
|
{
|
|
Print("Invalid handle of FileOpen (bollinger.set), error number: ",GetLastError());
|
|
return false;
|
|
}
|
|
|
|
Sleep(1500);
|
|
|
|
string PathTester=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\bollinger.set";
|
|
string PathProfile=sTerminalTesterDataPath+"\\MQL5\\Profiles\\Tester\\";
|
|
|
|
//PrintFormat( "[%s]", PathTester );
|
|
//PrintFormat( "-> [%s]", PathProfile );
|
|
|
|
// copy the ini file into the tester folder
|
|
int ret=ShellExecuteW(0,"Open","xcopy","\""+PathTester+"\" \""+PathProfile+"\" /y","",0);
|
|
|
|
// wait until the file is copied
|
|
Sleep(5000);
|
|
if(ret<32)
|
|
{
|
|
Print("Failed copying parameters file");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool StartOptimizer()
|
|
{
|
|
// Suppression local
|
|
FileDelete("Optimiser\\BuddyIlanReport.xml");
|
|
|
|
// Suppression dans Instance Optimiser
|
|
string PathReport=sTerminalTesterDataPath+"\\MQL5\\Files\\Reports\\BuddyIlanReport.xml";
|
|
|
|
ShellExecuteW(0,"Open","cmd.exe"," /C del "+PathReport,"",0);
|
|
|
|
Sleep(2500);
|
|
|
|
int start=ShellExecuteW(0,"Open",sTerminalTesterPath+"\\terminal64.exe","/config:"+TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\Optimiser\\optimise.ini","",0);
|
|
if(start<32)
|
|
{
|
|
Print("Failed starting Tester");
|
|
return false;
|
|
}
|
|
|
|
Sleep(15000);
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool CopyReport()
|
|
{
|
|
int nTry=0;
|
|
|
|
while(nTry++<500) // Timeout : 2 heures
|
|
{
|
|
string PathReport = sTerminalTesterDataPath + "\\MQL5\\Files\\Reports\\BuddyIlanReport.xml";
|
|
string PathTarget = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\Optimiser\\";
|
|
|
|
int ret=ShellExecuteW(0,"Open","xcopy","\""+PathReport+"\" \""+PathTarget+"\" /y","",0);
|
|
|
|
if(ret<32)
|
|
{
|
|
PrintFormat("Waiting generation report (%d) ...",nTry);
|
|
Sleep(15000);
|
|
}
|
|
else
|
|
{
|
|
if(FileIsExist("Optimiser\\BuddyIlanReport.xml")==true)
|
|
{
|
|
PrintFormat("Report found (ret=%d) ...",ret);
|
|
Sleep(2500);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
PrintFormat("Waiting report (%d) ...",nTry);
|
|
Sleep(15000);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool LoadResults(OptimisationType eType)
|
|
{
|
|
/*
|
|
* Réinitialistion variable
|
|
*/
|
|
|
|
BetterProfit=0.0;
|
|
|
|
/*
|
|
* Lecture des résultats
|
|
*/
|
|
|
|
CEasyXml EasyXmlDocument;
|
|
EasyXmlDocument.setDebugging(false);
|
|
|
|
// MQL5/Files/Reports/
|
|
if(EasyXmlDocument.loadXmlFromFullPathFile("Optimiser\\BuddyIlanReport.xml")==true)
|
|
{
|
|
str="";
|
|
|
|
CEasyXmlNode *RootNode=EasyXmlDocument.getDocumentRoot();
|
|
|
|
for(int j=0; j<RootNode.Children().Total(); j++)
|
|
{
|
|
CEasyXmlNode *ChildNode=RootNode.Children().At(j);
|
|
|
|
// PrintFormat( "[%s] Children Total=%d", ChildNode.getName(), ChildNode.Children().Total() );
|
|
|
|
for(int i=0; i<ChildNode.Children().Total(); i++)
|
|
{
|
|
CEasyXmlNode *cNode=ChildNode.Children().At(i);
|
|
|
|
// PrintFormat( "%d/%d [%s] children=%d", j, i, cNode.getName(), cNode.Children().Total() );
|
|
|
|
if(cNode.getName()=="Worksheet")
|
|
{
|
|
switch(eType)
|
|
{
|
|
case _SL :
|
|
|
|
DisplayNodesSL(cNode);
|
|
|
|
PrintFormat("-> SL=%d (Profit=%.2lf)",BetterSL,BetterProfit);
|
|
|
|
break;
|
|
|
|
case _TP :
|
|
|
|
DisplayNodesTP(cNode);
|
|
|
|
PrintFormat("-> TP=%d (Profit=%.2lf DD=%lf)",BetterTP,BetterProfit,BetterDD);
|
|
|
|
break;
|
|
|
|
case _STO :
|
|
|
|
DisplayNodesSTO(cNode);
|
|
|
|
PrintFormat("-> STOFilter=%s STOTimeFrameFilter=%s (Profit=%.2lf)",(BetterSTOFilter==true)?"true":"false",(BetterSTOTimeFrameFilter==true)?"true":"false",BetterProfit);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
PrintFormat("Error found");
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
bool AddTesterStanza()
|
|
{
|
|
int filehandle=FileOpen("Optimiser\\Optimise.ini",FILE_READ|FILE_WRITE|FILE_TXT);
|
|
Print("File stanza: ", filehandle);
|
|
if(filehandle!=INVALID_HANDLE)
|
|
{
|
|
FileSeek(filehandle,0,SEEK_END);
|
|
|
|
FileWrite(filehandle,"[Tester]\n",
|
|
"Expert=Project_N_1\\Project_N_1\n",
|
|
"ExpertParameters=bollinger.set\n",
|
|
"Symbol="+_Symbol+"\n",
|
|
"Period=M15\n",
|
|
"Login=\n",
|
|
"Model=4\n",
|
|
"ExecutionMode=0\n",
|
|
"Optimization=2\n",
|
|
"OptimizationCriterion=0\n",
|
|
"FromDate="+TimeToString(TimeGMT()-InpTesterPeriod*86400,TIME_DATE)+"\n",
|
|
"ToDate="+TimeToString(TimeGMT(),TIME_DATE)+"\n",
|
|
"ForwardMode=0\n",
|
|
"Report=MQL5\\Files\\Reports\\BuddyIlanReport\n",
|
|
"ReplaceReport=1\n",
|
|
"ShutdownTerminal=1\n",
|
|
"Deposit=10000\n",
|
|
"Currency=EURUSD\n",
|
|
"Leverage=1:100\n",
|
|
"UseLocal=1\n",
|
|
"UseRemote=0\n",
|
|
"UseCloud=0\n",
|
|
"Visual=1\n");
|
|
|
|
FileClose(filehandle);
|
|
Print("Stanza success");
|
|
}
|
|
else
|
|
{
|
|
Print("FileOpen, error ",GetLastError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|