Adwizard/Virtual/TesterHandler.mqh

342 lines
27 KiB
MQL5
Raw Permalink Normal View History

2025-04-11 13:28:40 +03:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| TesterHandler.mqh |
//| Copyright 2024-2025, Yuriy Bykov |
//| https://www.mql5.com/ru/users/antekov |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024-2025, Yuriy Bykov"
#property link "https://www.mql5.com/ru/users/antekov"
#property version "1.07"
#include "../Database/Database.mqh"
#include "VirtualStrategy.mqh"
//#include "VirtualFactory.mqh"
#include "../Optimization/OptimizerTask.mqh"
//+------------------------------------------------------------------+
//| ;0AA 4;O >1@01>B:8 A>1KB89 >?B8<870F88 |
//+------------------------------------------------------------------+
class CTesterHandler {
static string s_fileName; // <O 107K 40==KE >?B8<870F88
static string s_frameFileName; // <O D09;0 4;O 70?8A8 40==KE D@59<0
static void ProcessFrame(string values); // 1@01>B:0 40==KE >48=>G=>3> ?@>E>40
static void ProcessFrames(); // 1@01>B:0 ?@8H54H8E D@59<>2
static string GetFrameInputs(ulong pass); // >;CG5=85 input-?0@0<5B@>2 ?@>E>40
// $>@<8@>20=85 SQL-70?@>A0 =0 2AB02:C @57C;LB0B>2 ?@>E>40
static string GetInsertQuery(string values, string inputs, ulong pass = 0);
public:
static int TesterInit(ulong p_idTask = 0, string p_fileName = NULL); // 1@01>B:0 =0G0;0 >?B8<870F88 2 3;02=>< B5@<8=0;5
static void TesterDeinit(); // 1@01>B:0 7025@H5=8O >?B8<870F88 2 3;02=>< B5@<8=0;5
static void TesterPass(); // 1@01>B:0 7025@H5=8O ?@>E>40 =0 035=B5 2 3;02=>< B5@<8=0;5
static void Tester(const double OnTesterValue,
const string params); // 1@01>B:0 7025@H5=8O ?@>E>40 B5AB5@0 4;O 035=B0
// -:A?>@B <0AA820 AB@0B5389 2 7040==CN 107C 40==KE M:A?5@B0 :0: =>2>9 3@C??K AB@0B5389
static void Export(CStrategy* &p_strategies[], string p_groupName, string p_advFileName);
static ulong s_idTask; // 45=B8D8:0B>@ 7040G8 >?B8<870F88
static ulong s_idPass; // 45=B8D8:0B>@ B5:CI53> ?@>E>40 >?B8<870F88
};
string CTesterHandler::s_fileName = ""; // <O 107K 40==KE >?B8<870F88
string CTesterHandler::s_frameFileName = "data.bin"; // <O D09;0 4;O 70?8A8 40==KE D@59<0
ulong CTesterHandler::s_idTask = 0;
ulong CTesterHandler::s_idPass = 0;
//+------------------------------------------------------------------+
//| 1@01>B:0 =0G0;0 >?B8<870F88 2 3;02=>< B5@<8=0;5 |
//+------------------------------------------------------------------+
int CTesterHandler::TesterInit(ulong p_idTask, string p_fileName) {
// #AB0=02;8205< 845=B8D8:0B>@ 7040G8
s_idTask = p_idTask;
s_fileName = p_fileName;
// B:@K205< ACI5AB2CNICN 107C 40==KE
DB::Connect(s_fileName);
// A;8 >B:@KBL =5 C40;>AL, B> =5 70?CA:05< >?B8<870F8N
if(!DB::IsOpen()) {
return INIT_FAILED;
}
// 0:@K205< CA?5H=> >B:@KBCN 107C 40==KE
DB::Close();
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| 1@01>B:0 7025@H5=8O >?B8<870F88 2 3;02=>< B5@<8=0;5 |
//+------------------------------------------------------------------+
void CTesterHandler::TesterDeinit(void) {
// 1@010BK205< ?>A;54=85 ?@8H54H85 >B 035=B>2 D@59<K 40==KE
ProcessFrames();
// 0:@K205< 3@0D8: A A>25B=8:><, 70?CI5==K< 2 @568<5 A1>@0 D@59<>2
ChartClose();
}
//+------------------------------------------------------------------+
//| 1@01>B:0 7025@H5=8O ?@>E>40 =0 035=B5 2 3;02=>< B5@<8=0;5 |
//+------------------------------------------------------------------+
void CTesterHandler::TesterPass(void) {
// 1@010BK205< ?>ABC?82H85 >B 035=B0 D@59<K 40==KE
ProcessFrames();
}
//+------------------------------------------------------------------+
//| 1@01>B:0 7025@H5=8O ?@>E>40 B5AB5@0 4;O 035=B0 |
//+------------------------------------------------------------------+
void CTesterHandler::Tester(double custom, // >;L7>20B5;LA:89 :@8B5@89
string params // ?8A0=85 ?0@0<5B@>2 A>25B=8:0 2 B5:CI5< ?@>E>45
) {
// 0AA82 8<Q= A>E@0=O5<KE AB0B8AB8G5A:8E E0@0:B5@8AB8: ?@>E>40
ENUM_STATISTICS statNames[] = {
STAT_INITIAL_DEPOSIT,
STAT_WITHDRAWAL,
STAT_PROFIT,
STAT_GROSS_PROFIT,
STAT_GROSS_LOSS,
STAT_MAX_PROFITTRADE,
STAT_MAX_LOSSTRADE,
STAT_CONPROFITMAX,
STAT_CONPROFITMAX_TRADES,
STAT_MAX_CONWINS,
STAT_MAX_CONPROFIT_TRADES,
STAT_CONLOSSMAX,
STAT_CONLOSSMAX_TRADES,
STAT_MAX_CONLOSSES,
STAT_MAX_CONLOSS_TRADES,
STAT_BALANCEMIN,
STAT_BALANCE_DD,
STAT_BALANCEDD_PERCENT,
STAT_BALANCE_DDREL_PERCENT,
STAT_BALANCE_DD_RELATIVE,
STAT_EQUITYMIN,
STAT_EQUITY_DD,
STAT_EQUITYDD_PERCENT,
STAT_EQUITY_DDREL_PERCENT,
STAT_EQUITY_DD_RELATIVE,
STAT_EXPECTED_PAYOFF,
STAT_PROFIT_FACTOR,
STAT_RECOVERY_FACTOR,
STAT_SHARPE_RATIO,
STAT_MIN_MARGINLEVEL,
STAT_DEALS,
STAT_TRADES,
STAT_PROFIT_TRADES,
STAT_LOSS_TRADES,
STAT_SHORT_TRADES,
STAT_LONG_TRADES,
STAT_PROFIT_SHORTTRADES,
STAT_PROFIT_LONGTRADES,
STAT_PROFITTRADES_AVGCON,
STAT_LOSSTRADES_AVGCON,
STAT_COMPLEX_CRITERION
};
// 0AA82 4;O 7=0G5=89 AB0B8AB8G5A:8E E0@0:B5@8AB8: ?@>E>40 2 2845 AB@>:
string stats[];
ArrayResize(stats, ArraySize(statNames));
// 0?>;=O5< <0AA82 7=0G5=89 AB0B8AB8G5A:8E E0@0:B5@8AB8: ?@>E>40
FOREACH(statNames) stats[i] = DoubleToString(TesterStatistics(statNames[i]), 2);
// >102;O5< 2 =53> 7=0G5=85 ?>;L7>20B5;LA:>3> :@8B5@8O
APPEND(stats, DoubleToString(custom, 2));
// 1J548=O5< AB0B8AB8G5A:85 E0@0:B5@8AB8:8 2 AB@>:C
string data = "";
JOIN(stats, data, ",");
//  >?8A0=88 ?0@0<5B@>2 M:@0=8@C5< :02KG:8 (=0 2AO:89 A;CG09 =0 1C4CI55)
StringReplace(params, "'", "\\'");
// $>@<8@C5< AB@>:C A 40==K<8 > ?@>E>45
data = StringFormat("%d, %d, %s,'%s'",
MQLInfoInteger(MQL_OPTIMIZATION),
MQLInfoInteger(MQL_FORWARD),
data, params);
// A;8 MB> ?@>E>4 2 @0<:0E ?@>F5AA0 >?B8<870F88, B>
if(MQLInfoInteger(MQL_OPTIMIZATION)) {
// B:@K205< D09; 4;O 70?8A8 40==KE 4;O D@59<0
int f = FileOpen(s_frameFileName, FILE_WRITE | FILE_TXT | FILE_ANSI);
// 0?8AK205< >?8A0=85 ?0@0<5B@>2 A>25B=8:0
FileWriteString(f, data);
// 0:@K205< D09;
FileClose(f);
// !>740Q< D@59< A 40==K<8 87 70?8A0==>3> D09;0 8 >B?@02;O5< 53> 2 3;02=K9 B5@<8=0;
if(!FrameAdd("", 0, 0, s_frameFileName)) {
PrintFormat(__FUNCTION__" | ERROR: Frame add error: %d", GetLastError());
}
} else {
// =0G5 MB> >48=>G=K9 ?@>E>4, 2K7K205< <5B>4 4>102;5=8O 53> @57C;LB0B>2
// 2 107C 40==KE >?B8<870F88 (5A;8 >=0 1K;0 7040=0)
if (s_fileName != "") {
CTesterHandler::ProcessFrame(data);
}
}
}
//+------------------------------------------------------------------+
//| -:A?>@B <0AA820 AB@0B5389 2 7040==CN 107C 40==KE M:A?5@B0 |
//| :0: =>2>9 3@C??K AB@0B5389 |
//+------------------------------------------------------------------+
void CTesterHandler::Export(CStrategy* &p_strategies[], string p_groupName, string p_advFileName) {
// !>740Q< >1J5:B 7040G8 >?B8<870F88
COptimizerTask task(s_fileName);
// 03@C605< 2 =53> 40==K5 B5:CI59 7040G8 >?B8<870F88
task.Load(CTesterHandler::s_idTask);
// >4:;NG05<AO : =C6=>9 1075 40==KE M:A?5@B0
if(DB::Connect(p_advFileName, DB_TYPE_ADV)) {
string fromDate = task.m_params.from_date; // 0B0 =0G0;0 8=B5@20;0 >?B8<870F88
string toDate = task.m_params.to_date; // 0B0 :>=F0 8=B5@20;0 >?B8<870F88
// !>740Q< 70?8AL 4;O =>2>9 3@C??K AB@0B5389
string query = StringFormat("INSERT INTO strategy_groups VALUES(NULL, '%s', '%s', '%s', NULL)"
" RETURNING rowid;",
p_groupName, fromDate, toDate);
ulong groupId = DB::Insert(query);
PrintFormat(__FUNCTION__" | Export %d strategies into new group [%s] with ID=%I64u",
ArraySize(p_strategies), p_groupName, groupId);
// ;O :064>9 AB@0B5388
FOREACH(p_strategies) {
CVirtualStrategy *strategy = p_strategies[i];
// $>@<8@C5< AB@>:C 8=8F80;870F88 2 2845 3@C??K 87 >4=>9 AB@0B5388 A =>@<8@CNI8< <=>68B5;5<
string params = StringFormat("class CVirtualStrategyGroup([%s],%0.5f)",
~strategy,
strategy.Scale());
// !>E@0=O5< 5Q 2 1075 40==KE M:A?5@B0 A C:070=85< =>2>3> 845=B8D8:0B>@0 3@C??K
string query = StringFormat("INSERT INTO strategies "
"VALUES (NULL, %I64u, '%s', '%s')",
groupId, strategy.Hash(~strategy), params);
DB::Execute(query);
}
// 0:@K205< 107C 40==KE
DB::Close();
}
// TODO: >1028BL A>E@0=5=85 3@C??K 2 107C 40==KE >?B8<870F88
}
//+------------------------------------------------------------------+
//| $>@<8@>20=85 SQL-70?@>A0 =0 2AB02:C @57C;LB0B>2 ?@>E>40 |
//+------------------------------------------------------------------+
string CTesterHandler::GetInsertQuery(string values, string inputs, ulong pass) {
return StringFormat("INSERT INTO passes "
"VALUES (NULL, %d, %I64u, %s,\n'%s',\nNULL) RETURNING rowid;",
s_idTask, pass, values, inputs);
}
//+------------------------------------------------------------------+
//| 1@01>B:0 40==KE >48=>G=>3> ?@>E>40 |
//+------------------------------------------------------------------+
void CTesterHandler::ProcessFrame(string values) {
// B:@K205< 107C 40==KE
DB::Connect(s_fileName);
// $>@<8@C5< SQL-70?@>A 87 ?>;CG5==KE 40==KE
string query = GetInsertQuery(values, "", 0);
// K?>;=O5< 70?@>A
s_idPass = DB::Insert(query);
// 0:@K205< 107C 40==KE
DB::Close();
}
//+------------------------------------------------------------------+
//| 1@01>B:0 ?@8H54H8E D@59<>2 |
//+------------------------------------------------------------------+
void CTesterHandler::ProcessFrames(void) {
// B:@K205< 107C 40==KE
DB::Connect(s_fileName);
// 5@5<5==K5 4;O GB5=8O 40==KE 87 D@59<>2
string name; // 0720=85 D@59<0 (=5 8A?>;L7C5BAO)
ulong pass; // =45:A ?@>E>40 D@59<0
long id; // 45=B8D8:0B>@ B8?0 D@59<0 (=5 8A?>;L7C5BAO)
double value; // 48=>G=>5 7=0G5=85 D@59<0 (=5 8A?>;L7C5BAO)
uchar data[]; // 0AA82 40==KE D@59<0 2 2845 <0AA820 A8<2>;0
string values; // 0==K5 D@59<0 2 2845 AB@>:8
string inputs; // !B@>:0 A 8<5=0<8 8 7=0G5=8O<8 ?0@0<5B@>2 ?@>E>40
string query; // !B@>:0 >4=>3> SQL-70?@>A0
string queries[]; // SQL-70?@>AK =0 4>102;5=85 70?8A59 2 
// @>E>48< ?> D@59<0< 8 G8B05< 40==K5 87 =8E
while(FrameNext(pass, name, id, value, data)) {
// 5@52>48< 2 AB@>:C <0AA82 A8<2>;>2, ?@>G8B0==K9 87 D@59<0
values = CharArrayToString(data);
// $>@<8@C5< AB@>:C A 8<5=0<8 8 7=0G5=8O<8 ?0@0<5B@>2 ?@>E>40
inputs = GetFrameInputs(pass);
// $>@<8@C5< SQL-70?@>A 87 ?>;CG5==KE 40==KE
query = GetInsertQuery(values, inputs, pass);
// >102;O5< 53> 2 <0AA82 SQL-70?@>A>2
APPEND(queries, query);
}
// K?>;=O5< 2A5 70?@>AK
DB::ExecuteTransaction(queries);
// 0:@K205< 107C 40==KE
DB::Close();
}
//+------------------------------------------------------------------+
//| $>@<8@C5B AB@>:C A 8<5=0<8 8 7=0G5=8O<8 input-?5@5<5==KE ?@>E>40 |
//+------------------------------------------------------------------+
string CTesterHandler::GetFrameInputs(ulong pass) {
string params[]; // 0AA82 >?8A0=89 input-?5@5<5==KE
uint count; // >;8G5AB2> input-?5@5<5==KE
string inputs = ""; // !B@>:0 4;O @57C;LB0B0
if(FrameInputs(pass, params, count)) {
// A>18@05< >?B8<878@C5<K5 ?0@0<5B@K 8 8E 7=0G5=8O
for(uint i = 0; i < count; i++) {
string name2value[];
string delimeter = (i == count - 1 ? "" : ",");
// 5;8< >?8A0=85 >G5@54=>9 input-?5@5<5==>9 ?> A8<2>;C '='
int n = StringSplit(params[i], '=', name2value);
if(n == 2) {
// >;CG05< 7=0G5=85 ?> 8<5=8 2 pvalue
double pvalue, pstart, pstep, pstop;
bool enabled = false;
if(ParameterGetRange(name2value[0],
enabled, pvalue, pstart, pstep, pstop)) {
// >102;O5< 2 2KE>4=CN AB@>:C 8<O 8 7=0G5=85 input-?5@5<5==>9
if(MathAbs(pvalue - (long) pvalue) < 1e-6) {
// :0: F5;>5 G8A;>
inputs += StringFormat("%s=%d%s", name2value[0], (long) pvalue, delimeter);
} else {
// :0: 25I5AB25==>5 G8A;>
inputs += StringFormat("%s=%.2f%s", name2value[0], pvalue, delimeter);
}
}
}
}
}
//PrintFormat(__FUNCTION__" | pass %d: %s", pass, inputs);
return inputs;
}
//+------------------------------------------------------------------+