Adwizard/Experts/Stage2.mqh

361 lines
28 KiB
MQL5
Raw Permalink Normal View History

2025-04-11 13:28:40 +03:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| Stage2.mqh |
//| Copyright 2024, Yuriy Bykov |
//| https://www.mql5.com/ru/users/antekov |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Yuriy Bykov"
#property link "https://www.mql5.com/ru/articles/15911"
#property version "1.05"
#ifndef __NAME__
#define __NAME__ "EmptyStrategy"
#endif
#define PARAMS_FILE "db.stage2.sqlite"
#property tester_file PARAMS_FILE
#include "../Virtual/VirtualAdvisor.mqh"
//+------------------------------------------------------------------+
//| E>4=K5 ?0@0<5B@K |
//+------------------------------------------------------------------+
sinput int idTask_ = 0; // - 45=B8D8:0B>@ 7040G8 >?B8<870F88
sinput string fileName_ = "db.sqlite"; // - $09; A >A=>2=>9 107>9 40==KE
input group "::: B1>@ 2 3@C??C"
input int idParentJob_ = 1; // - 45=B8D8:0B>@ @>48B5;LA:>9 @01>BK
input bool useClusters_ = true; // - A?>;L7>20BL :;0AB5@870F8N
input double minCustomOntester_ = 0; // - 8=. =>@<8@>20==0O ?@81K;L
input int minTrades_ = 40; // - 8=. :>;8G5AB2> A45;>:
input double minSharpeRatio_ = 0.7; // - 8=. :>MDD8F85=B (0@?0
input int count_ = 16; // - >;8G5AB2> AB@0B5389 2 3@C??5 (1 .. 16)
input group "::: =45:AK M:75<?;O@>2"
input int i1_ = 1; // - =45:A AB@0B5388 #1
input int i2_ = 2; // - =45:A AB@0B5388 #2
input int i3_ = 3; // - =45:A AB@0B5388 #3
input int i4_ = 4; // - =45:A AB@0B5388 #4
input int i5_ = 5; // - =45:A AB@0B5388 #5
input int i6_ = 6; // - =45:A AB@0B5388 #6
input int i7_ = 7; // - =45:A AB@0B5388 #7
input int i8_ = 8; // - =45:A AB@0B5388 #8
input int i9_ = 9; // - =45:A AB@0B5388 #9
input int i10_ = 10; // - =45:A AB@0B5388 #10
input int i12_ = 11; // - =45:A AB@0B5388 #11
input int i11_ = 12; // - =45:A AB@0B5388 #12
input int i13_ = 13; // - =45:A AB@0B5388 #13
input int i14_ = 14; // - =45:A AB@0B5388 #14
input int i15_ = 15; // - =45:A AB@0B5388 #15
input int i16_ = 16; // - =45:A AB@0B5388 #16
// $8:A8@>20==K5 ?0@0<5B@K
double expectedDrawdown_ = 10; // - 0:A8<0;L=K9 @8A: (%)
double fixedBalance_ = 10000; // - A?>;L7C5<K9 45?>78B (0 - 8A?>;L7>20BL 25AL) 2 20;NB5 AG5B0
double scale_ = 1.00; // - 0AHB018@CNI89 <=>68B5;L 4;O 3@C??K
ulong magic_ = 27183; // - Magic
bool useOnlyNewBars_ = true; // - 01>B0BL B>;L:> =0 >B:@KB88 10@0
CVirtualAdvisor *expert; // 1J5:B M:A?5@B0
//+------------------------------------------------------------------+
//| !>740=85 107C 40==KE 4;O >B45;L=>9 7040G8 MB0?0 |
//+------------------------------------------------------------------+
void CreateTaskDB(const string fileName, const int idParentJob) {
// !>740Q< =>2CN 107C 40==KE 4;O B5:CI59 7040G8 >?B8<870F88
DB::Connect(PARAMS_FILE, DB_TYPE_CUT);
DB::Execute("DROP TABLE IF EXISTS passes;");
DB::Execute("CREATE TABLE passes (id_pass INTEGER PRIMARY KEY AUTOINCREMENT, params TEXT);");
// DB::Close();
// >4:;NG05<AO : >A=>2=>9 1075 40==KE
DB::Connect(fileName);
// 1J548=5=85
string clusterJoin = "";
if(useClusters_) {
clusterJoin = "JOIN passes_clusters pc ON pc.id_pass = p.id_pass";
}
// 0?@>A =0 ?>;CG5=85 =5>1E>48<>9 8=D>@<0F88 87 >A=>2=>9 107K 40==KE
string query = StringFormat("SELECT DISTINCT p.params"
" FROM passes p"
" JOIN "
" tasks t ON p.id_task = t.id_task "
" JOIN "
" jobs j ON t.id_job = j.id_job "
" %s "
"WHERE (j.id_job = %d AND "
" p.custom_ontester >= %.2f AND "
" trades >= %d AND "
" p.sharpe_ratio >= %.2f) "
"ORDER BY p.custom_ontester DESC;",
clusterJoin,
idParentJob_,
minCustomOntester_,
minTrades_,
minSharpeRatio_);
// K?>;=O5< 70?@>A
int request = DatabasePrepare(DB::Id(), query);
if(request == INVALID_HANDLE) {
PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
DB::Close();
return;
}
// !B@C:BC@0 4;O @57C;LB0B>2 70?@>A0
struct Row {
string params;
} row;
// 0AA82 4;O 70?@>A>2 =0 2AB02:C 40==KE 2 =>2CN 107C 40==KE
string queries[];
// 0?>;=O5< <0AA82 70?@>A>2: 1C45< A>E@0=OBL B>;L:> AB@>:8 8=8F80;870F88
while(DatabaseReadBind(request, row)) {
APPEND(queries, StringFormat("INSERT INTO passes VALUES(NULL, '%s');", row.params));
}
// 5@5?>4:;NG05<AO : =>2>9 1075 40==KE 8 70?>;=O5< 5Q
DB::Connect(PARAMS_FILE, DB_TYPE_CUT);
DB::ExecuteTransaction(queries);
DB::Close();
// 5@5?>4:;NG05<AO : >A=>2=>9 1075 40==KE
// DB::Connect(fileName);
// DB::Close();
}
//+------------------------------------------------------------------+
//| >;8G5AB2> =01>@>2 ?0@0<5B@>2 AB@0B5389 2 1075 40==KE 7040G8 |
//+------------------------------------------------------------------+
int GetParamsTotal() {
int paramsTotal = 0;
// A;8 1070 40==KE 7040G8 >B:@KB0, B>
if(DB::Connect(PARAMS_FILE, DB_TYPE_CUT)) {
// !>740Q< 70?@>A =0 ?>;CG5=85 :>;8G5AB20 ?@>E>4>2 4;O 40==>9 7040G8
string query = "SELECT COUNT(*) FROM passes p";
int request = DatabasePrepare(DB::Id(), query);
if(request != INVALID_HANDLE) {
// !B@C:BC@0 40==KE 4;O @57C;LB0B0 70?@>A0
struct Row {
int total;
} row;
// >;CG05< @57C;LB0B 70?@>A0 87 ?5@2>9 AB@>:8
if (DatabaseReadBind(request, row)) {
paramsTotal = row.total;
}
} else {
PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
}
DB::Close();
}
return paramsTotal;
}
//+------------------------------------------------------------------+
//| 03@C7:0 =01>@>2 ?0@0<5B@>2 AB@0B5389 |
//+------------------------------------------------------------------+
string LoadParams(int &indexes[]) {
string params = NULL;
// >;CG05< :>;8G5AB2> =01>@>2
int totalParams = GetParamsTotal();
// A;8 >=8 5ABL, B>
if(totalParams > 0) {
if(DB::Connect(PARAMS_FILE, DB_TYPE_CUT)) {
// $>@<8@C5< AB@>:C 87 8=45:A>2 =01>@>2, 27OBKE 87 2E>4=KE ?0@0<5B@>2 A>25B=8:0
// G5@57 70?OBCN 4;O 40;L=59H59 ?>4AB0=>2:8 2 SQL-70?@>A
string strIndexes = "";
FOREACH(indexes) strIndexes += IntegerToString(indexes[i]) + ",";
strIndexes += "0"; // >?>;=O5< =5ACI5AB2CNI8< 8=45:A><, GB>1K =5 C40;OBL ?>A;54=NN 70?OBCN
// $>@<8@C5< 70?@>A =0 ?>;CG5=85 =01>@>2 ?0@0<5B@>2 A =C6=K<8 8=45:A0<8
string query = StringFormat("SELECT params FROM passes p WHERE id_pass IN(%s)", strIndexes);
int request = DatabasePrepare(DB::Id(), query);
if(request != INVALID_HANDLE) {
// !B@C:BC@0 40==KE 4;O @57C;LB0B>2 70?@>A0
struct Row {
string params;
} row;
// '8B05< @57C;LB0BK 70?@>A0 8 A>548=O5< 8E G5@57 70?OBCN
while(DatabaseReadBind(request, row)) {
params += row.params + ",";
}
} else {
PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d",
query, GetLastError());
}
DB::Close();
}
}
return params;
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
// #AB0=02;8205< ?0@0<5B@K 2 :;0AA5 C?@02;5=8O :0?8B0;><
CMoney::DepoPart(expectedDrawdown_ / 10.0);
CMoney::FixedBalance(fixedBalance_);
// 0AA82 2A5E 8=45:A>2 87 2E>4=KE ?0@0<5B@>2 A>25B=8:0
int indexes_[] = {i1_, i2_, i3_, i4_,
i5_, i6_, i7_, i8_,
i9_, i10_, i11_, i12_,
i13_, i14_, i15_, i16_
};
// 0AA82 4;O 8=45:A>2, :>B>@K5 1C4CB CG0AB2>20BL 2 >?B8<870F88
int indexes[];
ArrayResize(indexes, count_);
// =>65AB2> 4;O 8=45:A>2 =01>@>2 ?0@0<5B@>2
CHashSet<int> setIndexes;
// >?8@C5< 2 =53> 8=45:AK 87 2E>4=KE ?0@0<5B@>2
// >102;O5< 2A5 8=45:AK 2> <=>65AB2>
FOREACH(indexes) {
indexes[i] = indexes_[i];
setIndexes.Add(indexes[i]);
}
// !>>1I05< >1 >H81:5, 5A;8
if(count_ < 1 || count_ > 16 // :>;8G5AB2> M:75<?;O@>2 =5 2 480?07>=5 1 .. 16
|| setIndexes.Count() != count_ // =5 2A5 8=45:AK C=8:0;L=K5
) {
return INIT_PARAMETERS_INCORRECT;
}
// A;8 MB> =5 >?B8<870F8O, B> =04> ?5@5A>740BL 107C 40==KE 7040G8
if(!MQLInfoInteger(MQL_OPTIMIZATION)) {
CreateTaskDB(fileName_, idParentJob_);
}
// 03@C605< =01>@K ?0@0<5B@>2 AB@0B5389
string strategiesParams = LoadParams(indexes);
// >4:;NG05<AO : >A=>2=>9 1075 40==KE
// DB::Connect(fileName_);
// DB::Close();
CVirtualAdvisor::TesterInit(idTask_, fileName_);
// A;8 =8G53> =5 703@C78;8, B> A>>1I8< >1 >H81:5
if(strategiesParams == NULL) {
PrintFormat(__FUNCTION__" | ERROR: Can't load data from file %s.\n"
"Check that it exists in data folder or in common data folder.",
fileName_);
return(INIT_PARAMETERS_INCORRECT);
}
// >43>B02;8205< AB@>:C 8=8F80;870F88 4;O M:A?5@B0 A 3@C??>9 87 =5A:>;L:8E AB@0B5389
string expertParams = StringFormat(
"class CVirtualAdvisor(\n"
" class CVirtualStrategyGroup(\n"
" [\n"
" %s\n"
" ],%f\n"
" ),\n"
" class CVirtualRiskManager(\n"
" 0,0,0,0,0,0,0,0,0,0,0,0,0"
" )\n"
" ,%d,%s,%d\n"
")",
strategiesParams, scale_,
magic_, __NAME__, useOnlyNewBars_
);
PrintFormat(__FUNCTION__" | Expert Params:\n%s", expertParams);
// !>7405< M:A?5@B0, @01>B0NI53> A 28@BC0;L=K<8 ?>78F8O<8
expert = NEW(expertParams);
if(!expert) return INIT_FAILED;
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
expert.Tick();
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
if(!!expert) delete expert;
}
//+------------------------------------------------------------------+
//| 57C;LB0B B5AB8@>20=8O |
//+------------------------------------------------------------------+
double OnTester(void) {
return expert.Tester();
}
//+------------------------------------------------------------------+
//| =8F80;870F8O ?5@54 >?B8<870F859 |
//+------------------------------------------------------------------+
int OnTesterInit(void) {
// !>740Q< 107C 40==KE 4;O >B45;L=>9 7040G8 MB0?0
CreateTaskDB(fileName_, idParentJob_);
// >;CG05< :>;8G5AB2> =01>@>2 ?0@0<5B@>2 AB@0B5389
int totalParams = GetParamsTotal();
// >4:;NG05<AO : >A=>2=>9 1075 40==KE
DB::Connect(fileName_);
DB::Close();
// A;8 =8G53> =5 703@C78;8, B> A>>1I8< >1 >H81:5
if(totalParams == 0) {
PrintFormat(__FUNCTION__" | ERROR: Can't load data from file %s.\n"
"Check that it exists in data folder or in common data folder.",
fileName_);
return(INIT_FAILED);
}
// 0@0<5B@C scale_ CAB0=02;8205< 7=0G5=85 1
ParameterSetRange("scale_", false, 1, 1, 1, 2);
// 0@0<5B@0< ?5@51>@0 8=45:A>2 =01>@>2 7040Q< 480?07>=K 87<5=5=8O
for(int i = 1; i <= 16; i++) {
if(i <= count_) {
ParameterSetRange("i" + (string) i + "_", true, 0, 1, 1, totalParams);
} else {
// ;O ;8H=8E 8=45:A>2 >B:;NG05< ?5@51>@
ParameterSetRange("i" + (string) i + "_", false, 0, 1, 1, totalParams);
}
}
return CVirtualAdvisor::TesterInit(idTask_, fileName_);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void OnTesterPass() {
CVirtualAdvisor::TesterPass();
}
//+------------------------------------------------------------------+
//| 59AB28O ?>A;5 >?B8<870F88 |
//+------------------------------------------------------------------+
void OnTesterDeinit(void) {
CVirtualAdvisor::TesterDeinit();
}
//+------------------------------------------------------------------+