forked from antekov/Adwizard
		
	
		
			
				
	
	
		
			338 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			338 lines
		
	
	
	
		
			14 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;
 | 
						|
}
 | 
						|
//+------------------------------------------------------------------+
 |