Adwizard/Optimization/OptimizerTask.mqh

338 lines
25 KiB
MQL5

//+------------------------------------------------------------------+
//| OptimizerTask.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/users/antekov"
#property version "1.05"
// Функция запуска исполняемого файла в операционной системе
#import "shell32.dll"
int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);
#import
#include "../Database/Database.mqh"
#include "../Utils/MTTester.mqh" // https://www.mql5.com/ru/code/26132
//+------------------------------------------------------------------+
//| Класс для задачи оптимизации |
//+------------------------------------------------------------------+
class COptimizerTask {
protected:
enum {
TASK_TYPE_UNKNOWN,
TASK_TYPE_EX5,
TASK_TYPE_PY
} m_type; // Тип задачи (MQL5 или Python)
ulong m_id; // Идентификатор задачи
string m_setting; // Строка инициализации параметров советника для текущей задачи
string m_fileName; // Имя файла базы данных
string m_pythonPath; // Полный путь к интерпретатору Python
// Получение полного или относительного пути к заданному файлу в текущей папке
string GetProgramPath(string name, bool rel = true);
// Получение строки инициализации из параметров задачи
void Parse();
// Получение типа задачи из параметров задачи
void ParseType();
public:
// Структура данных для чтения одной строки результата запроса
struct params {
string expert;
int optimization;
string from_date;
string to_date;
int forward_mode;
string forward_date;
double deposit;
string symbol;
string period;
string tester_inputs;
ulong id_task;
int optimization_criterion;
} m_params;
// Конструктор
COptimizerTask(string p_fileName, string p_pythonPath = NULL) :
m_id(0), m_fileName(p_fileName), m_pythonPath(p_pythonPath) {}
// Идентификатор задачи
ulong Id() {
return m_id;
}
// Основной метод
void Process();
// Загрузка параметров задачи из базы данных
void Load(ulong p_id);
// Запуск задачи
void Start();
// Завершение задачи
void Finish();
// Задача выполнена?
bool IsDone();
};
//+------------------------------------------------------------------+
//| Получение строки инициализации из параметров задачи |
//+------------------------------------------------------------------+
void COptimizerTask::Parse() {
// Получаем тип задачи из параметров задачи
ParseType();
// Если это задача на оптимизацию советника
if(m_type == TASK_TYPE_EX5) {
// Формируем строку параметров для тестера
m_setting = StringFormat(
"[Tester]\r\n"
"Expert=%s\r\n"
"Symbol=%s\r\n"
"Period=%s\r\n"
"Optimization=%d\r\n"
"Model=1\r\n"
"FromDate=%s\r\n"
"ToDate=%s\r\n"
"ForwardMode=%d\r\n"
"%s"
"Deposit=%.2f\r\n"
"Currency=USD\r\n"
"ProfitInPips=0\r\n"
"Leverage=200\r\n"
"ExecutionMode=0\r\n"
"OptimizationCriterion=%d\r\n"
"[TesterInputs]\r\n"
"idTask_=%d\r\n"
"fileName_=%s\r\n"
"%s\r\n",
GetProgramPath(m_params.expert),
m_params.symbol,
m_params.period,
m_params.optimization,
m_params.from_date,
m_params.to_date,
m_params.forward_mode,
(m_params.forward_mode == 4 ?
StringFormat("ForwardDate=%s\r\n", m_params.forward_date) : ""),
m_params.deposit,
m_params.optimization_criterion,
m_params.id_task,
DB::FileName(),
m_params.tester_inputs
);
// Если это задача на запуск программы на Python
} else if (m_type == TASK_TYPE_PY) {
// Формируем строку запуска программы на Python с параметрами
m_setting = StringFormat("\"%s\" \"%s\" %I64u %s",
GetProgramPath(m_params.expert, false), // Файл с программой на Python
DB::FileName(true), // Путь к файлу с базой данных
m_id, // Идентификатор задачи
m_params.tester_inputs // Парамтры запуска
);
}
}
//+------------------------------------------------------------------+
//| Получение типа задачи из параметров задачи |
//+------------------------------------------------------------------+
void COptimizerTask::ParseType() {
string ext = StringSubstr(m_params.expert, StringLen(m_params.expert) - 3);
if(ext == ".py") {
m_type = TASK_TYPE_PY;
} else if (ext == "ex5") {
m_type = TASK_TYPE_EX5;
} else {
m_type = TASK_TYPE_UNKNOWN;
}
}
//+------------------------------------------------------------------+
//| Получение полного или относительного пути к заданному файлу |
//| в текущей папке |
//+------------------------------------------------------------------+
string COptimizerTask::GetProgramPath(string name, bool rel = true) {
string path = MQLInfoString(MQL_PROGRAM_PATH);
string programName = MQLInfoString(MQL_PROGRAM_NAME) + ".ex5";
string terminalPath = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Experts\\";
if(rel) {
path = StringSubstr(path,
StringLen(terminalPath),
StringLen(path) - (StringLen(terminalPath) + StringLen(programName)));
} else {
path = StringSubstr(path, 0, StringLen(path) - (0 + StringLen(programName)));
}
return path + name;
}
//+------------------------------------------------------------------+
//| Получение очередной задачи оптимизации из очереди |
//+------------------------------------------------------------------+
void COptimizerTask::Load(ulong p_id) {
// Запоминаем идентификатор задачи
m_id = p_id;
// Запрос на получение задачи оптимизации из очереди по идентификатору
string query = StringFormat(
"SELECT s.expert,"
" s.optimization,"
" s.from_date,"
" s.to_date,"
" s.forward_mode,"
" s.forward_date,"
" s.deposit,"
" j.symbol,"
" j.period,"
" j.tester_inputs,"
" t.id_task,"
" t.optimization_criterion"
" FROM tasks t"
" JOIN"
" jobs j ON t.id_job = j.id_job"
" JOIN"
" stages s ON j.id_stage = s.id_stage"
" WHERE t.id_task=%I64u;", m_id);
// Открываем базу данных
if(DB::Connect(m_fileName)) {
// Выполняем запрос
int request = DatabasePrepare(DB::Id(), query);
// Если нет ошибки
if(request != INVALID_HANDLE) {
// Читаем данные из первой строки результата
if(DatabaseReadBind(request, m_params)) {
Parse();
} else {
// Сообщаем об ошибке при необходимости
PrintFormat(__FUNCTION__" | ERROR: Reading row for request \n%s\nfailed with code %d",
query, GetLastError());
}
} else {
// Сообщаем об ошибке при необходимости
PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
}
// Закрываем базу данных
DB::Close();
}
}
//+------------------------------------------------------------------+
//| Запуск задачи |
//+------------------------------------------------------------------+
void COptimizerTask::Start() {
PrintFormat(__FUNCTION__" | Task ID = %d\n%s", m_id, m_setting);
// Если это задача на оптимизацию советника
if(m_type == TASK_TYPE_EX5) {
// Запускаем новую задачу оптимизации в тестере
MTTESTER::CloseNotChart();
MTTESTER::SetSettings2(m_setting);
MTTESTER::ClickStart();
// Обновляем статус задачи в базе данных
DB::Connect(m_fileName);
string query = StringFormat(
"UPDATE tasks SET "
" status='Process' "
" WHERE id_task=%d",
m_id);
DB::Execute(query);
DB::Close();
// Если это задача на запуск программы на Python
} else if (m_type == TASK_TYPE_PY) {
PrintFormat(__FUNCTION__" | SHELL EXEC: %s", m_pythonPath);
// Вызываем системную функцию запуска программы с параметрами
ShellExecuteW(NULL, NULL, m_pythonPath, m_setting, NULL, 1);
}
}
//+------------------------------------------------------------------+
//| Завершение задачи |
//+------------------------------------------------------------------+
void COptimizerTask::Finish() {
PrintFormat(__FUNCTION__" | Task ID = %d", m_id);
// Обновляем статус задачи в базе данных
DB::Connect(m_fileName);
string query = StringFormat(
"UPDATE tasks SET "
" status='Done' "
" WHERE id_task=%d",
m_id);
DB::Execute(query);
DB::Close();
}
//+------------------------------------------------------------------+
//| Задача выполнена? |
//+------------------------------------------------------------------+
bool COptimizerTask::IsDone() {
// Если нет текущей задачи, то всё выполнено
if(m_id == 0) {
return true;
}
// Результат
bool res = false;
// Если это задача на оптимизацию советника
if(m_type == TASK_TYPE_EX5) {
// Проверяем, завершил ли работу тестер стратегий
res = MTTESTER::IsReady();
// Если это задача на запуск программы на Python, то
} else if(m_type == TASK_TYPE_PY) {
// Запрос на получение статуса текущей задачи
string query = StringFormat("SELECT status "
" FROM tasks"
" WHERE id_task=%I64u;", m_id);
// Открываем базу данных
if(DB::Connect(m_fileName)) {
// Выполняем запрос
int request = DatabasePrepare(DB::Id(), query);
// Если нет ошибки
if(request != INVALID_HANDLE) {
// Структура данных для чтения одной строки результата запроса
struct Row {
string status;
} row;
// Читаем данные из первой строки результата
if(DatabaseReadBind(request, row)) {
// Проверяем, равен ли статус Done
res = (row.status == "Done");
} else {
// Сообщаем об ошибке при необходимости
PrintFormat(__FUNCTION__" | ERROR: Reading row for request \n%s\nfailed with code %d",
query, GetLastError());
}
} else {
// Сообщаем об ошибке при необходимости
PrintFormat(__FUNCTION__" | ERROR: request \n%s\nfailed with code %d", query, GetLastError());
}
// Закрываем базу данных
DB::Close();
}
} else {
res = true;
}
return res;
}
//+------------------------------------------------------------------+