338 lines
25 KiB
MQL5
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;
|
|
}
|
|
//+------------------------------------------------------------------+
|