//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+