Сравни два нижеприведенных скрипта Nocturne1 и FXX. Почему в Nocturne1 команда MTTESTER::SetSettings2(Settings) успешно выполняется тысячи раз, а в скрипте FXX не выполняется? Хотя она и возвращает true, но в настройках робота реальных изменений не производит. //+-- Версия 22 ----------------------------------------- Nocturne --+ //| Ночная оптимизация робота | //+----------(параметр double с точкой у правой границы)-------------+ #property strict #property script_show_inputs #property link "https://www.mql5.com/ru/code/26132 MultiTester - библиотека" #property link "https://www.mql5.com/ru/code/26223 TesterCache - библиотека" #property link "https://www.mql5.com/ru/code/16280 TypeToBytes - библиотека" #include // Чтение/запись opt-файлов оптимизационных Cache MT5-тестера #include #include #define _CS(A) ((!IsStopped()) && (A)) #define PAUSE 200 #define MAX_PATH 260 struct FILETIMEE // Структура для процедуры очистки Cache { uint dwLowDateTime; uint dwHighDateTime; }; struct FIND_DATAWW { uint dwFileAttributes; FILETIMEE ftCreationTime; FILETIMEE ftLastAccessTime; FILETIMEE ftLastWriteTime; uint nFileSizeHigh; uint nFileSizeLow; uint dwReserved0; uint dwReserved1; short cFileName[MAX_PATH]; short cAlternateFileName[14]; }; #import "kernel32.dll" int DeleteFileW(const string file_name); long FindFirstFileW(const string file_name, FIND_DATAWW &find_file_data); int FindNextFileW(long find_file, FIND_DATAWW &find_file_data); int FindClose(long find_file); bool CopyFileW(string lpExistingFileName, string lpNewFileName, bool bFailIfExists); #import enum Mode {AD,BB,Bears,CCI,HEX,MFI,RSI,WPR,FVG,Elliot}; // Перечисление индикаторов string ModeNames[] = {"AD","BB","Bears","CCI","HEX","MFI","RSI","WPR","FVG","Elliot"}; input datetime ToDate = D'2026.02.21'; // Окончание оптимизации input int KolWeek = 3; // Количество недель в периоде оптимизации input bool PrGenetic = true; // true - нужна генетическая оптимизация предварительно (false - нет) input string Depo = "10000"; // Депозит для оптимизации input Mode Modus = AD; // Индикатор //Глобальные переменные string TickMode = "0"; // 0 все тики, 1 OHLC, 2 Open, 4 реальные тики datetime FromDate = ToDate - KolWeek*7*86400; // Начало оптимизации int delay; // Длительность ожидания начала оптимизации в секундах double Deposit; // Депозит string OptFile = "Optima.opt"; // Имя файла для записи opt-файлов в MQL5\Files string Settings = ""; // Инициализация строки текущих настроек string SettingsBeg = ""; // Инициализация начальной строки текущих настроек string NameSettings; // Имя строки текущих настроек string NameSettingsMax; // Имя строки настроек с максикумом прибыли double ExtraSohr; // Extra считывается для сохранения double SumLongSohr; // SumLong считывается для сохранения datetime Now; // Момент запуска Nocturne datetime Duration; // Продолжительность оптимизации MqlDateTime dt_struct; // Структура для разложения времени на день, час, минута, секунда MqlParam Paramas[][5]; double ProfitNew=1,ProfitOld=0; // Прибыль текущей и прошлой итераций оптимизации double ProfitGen = 0; // Прибыль от генетической оптимизации double ProfitNewDop = 0; // Прибыль от дополнительной последовательной оптимизации double ProcentProfit = 0; // Процент прибыли int IndexMaxProfitRuna=-1; // Индекс параметра, при котором достигнута максимальная прибыль double ParamMax; // Значение double-параметра, при котором достигнута максимальная прибыль int IntParamMax; // Значение int-параметра, при котором достигнута максимальная прибыль int IterNumber=0; // Номер итерации ночной оптимизации uchar Bytes[]; // Байтовый массив string Odinar; // Одиночный Y в настройках оптимизируемого робота int TypPar=-1; // Тип оптимизируемого параметра int PoseY,PosEq=-1; // Позиции Y и знака = в настройках оптимизируемого робота int Pos; int KolY; // Количество Y в настройках оптимизируемого робота int Nruna=0; // Количество проходов Runa при оптимизации робота bool prWrite,prRead; // Признаки записи и чтения bool prWrite1,prRead1; bool prWrite2,prRead2; int GLOC; bool CL; //+---------------------------------------------------------------------+ //| Последовательная оптимизация по параметрам, отмеченным Y (true = 1) | //+---------------------------------------------------------------------+ void OnStart() { TESTERCACHE Cache; // Стандартная оптимизация Now = TimeLocal(); // Момент запуска Nocturne TimeToStruct(Now,dt_struct); // Разложение момента запуска int dweek = dt_struct.day_of_week; // День недели, день, час, минута, секунда int dw = dt_struct.day; int hr = dt_struct.hour; int mn = dt_struct.min; int sc = dt_struct.sec; delay = ToDate > Now ? ToDate + 118800 - Now : 0; // Подождём до 3:00 субботы delay = ToDate > Now ? ToDate - Now : 0; Print("\n\nОтсрочка ",delay,"сек. FromDate ",TimeToString(FromDate,TIME_DATE)," ToDate ",TimeToString(ToDate,TIME_DATE)," Now ",TimeToString(Now,TIME_MINUTES)," dweek = ",dweek," dw= ",dw); Sleep(1000*delay); // Длительность ожидания в миллисекундах Now = TimeLocal(); // Момент начала реальной работы Nocturne после отсрочки MTTESTER::GetSettings(Settings); // Считываются исходные настройки робота ExtraSohr = MTTESTER::GetValue(Settings,"Extra"); // Extra считывается для сохранения SumLongSohr = MTTESTER::GetValue(Settings,"SumLong"); // SumLong считывается для сохранения //MTTESTER::SetValue(Settings,"Extra","0"); // Установка нулевого значения Extra //MTTESTER::SetValue(Settings,"SumLong","0"); // Установка нулевого значения SumLong MTTESTER::SetValue(Settings,"Deposit",Depo); // Установка заданного депозита для расчётов Deposit = MTTESTER::GetValue(Settings,"Deposit"); // Депозит считывается MTTESTER::SetValue(Settings,"Model",TickMode); // 0 все тики, 1 OHLC, 2 Open, 4 Реальные тики MTTESTER::SetValue(Settings,"OptimizationCriterion","7"); // OptimizationCriterion=7 комплексный, 0 максимум баланса MTTESTER::SetValue(Settings,"FromDate", TimeToString(TimeToString(FromDate,TIME_DATE), TIME_DATE)); // Новое начало периода оптимизации MTTESTER::SetValue(Settings,"ToDate", TimeToString(TimeToString(ToDate,TIME_DATE), TIME_DATE)); // Новое окончание периода оптимизации MTTESTER::SetSettings2(Settings); // Установка новых данных в настройки // MTTESTER::GetSettings(Settings); // Считываются исходные настройки // Print("В начале= \n",Settings); Print("Оптимизация от ",TimeToString(FromDate,TIME_DATE)," до ",TimeToString(ToDate,TIME_DATE)," Deposit = ",Depo,"\n"); // Заголовок начала оптимизации в журнале if(PrGenetic) // Предварительная генетическая оптимизация { SettingsBeg = Settings; // Сохранение исходных настроек DeleteFile(TerminalInfoString(TERMINAL_DATA_PATH) + "\\Tester\\cache\\"); // Очистка Cache, чтобы гарантировать реальный прогон (а не воспроизведение данных запомненного ранее из opt-файла) MTTESTER::SetValue(Settings,"Optimization","2"); // Optimization = 2 генетика, 1 полный перебор, 0 отключена ExecutionMode=100 MTTESTER::SetSettings2(Settings); // Установка новых данных в настройки MTTESTER::GetSettings(Settings); // Считываются исходные настройки робота // Print("Перед Run PrGenetic=",PrGenetic," NumPeriod=",NumPeriod,"\nКонтроль генетики: \n",Settings); Run(Settings); // Прогон генетической оптимизации GLOC = MTTESTER::GetLastOptCache(Bytes); // Получили opt-файл в байтовый массив Bytes TESTERCACHE Cache; CL = Cache.Load(Bytes); // Подаём в Cache байтовый массив Bytes // Print(" GLOC = ",GLOC," CL = ",CL); // Диагноз записи и чтения prWrite2 = Cache.Save(OptFile); // Сохранили результат Run в opt-файл в MQL5\Files prRead2 = Cache.Load(OptFile); // Прочитали для проверки из MQL5\Files // Print(OptFile+" записали = ",prWrite2," прочитали = ",prRead2); // Диагноз записи и чтения int NomProchMaxProfit = Cache[GetMaxProfitPos(Cache)].Pass; // Номер прохода с максимальной прибылью int Poss = GetMaxProfitPos(Cache); // Номер позиции с максимальной прибылью Cache.SaveSet(Poss); // Создали set-файл самого прибыльного прохода в MQL5\Files // Print("Settings:\n\n",Settings); // Print("Poss = ",Poss," NomProchMaxProfit = ",NomProchMaxProfit); // Print("Перед Cache.TesterString(Poss) ",PrGenetic,"\nКонтроль генетики: \n",Settings); prWrite2 = Cache.Save(OptFile); // Сохранили результат Run в opt-файл в MQL5\Files prRead2 = Cache.Load(OptFile); // Прочитали для проверки из MQL5\Files // Print(OptFile+" записали = ",prWrite2," прочитали = ",prRead2); // Диагноз записи и чтения Settings = Cache.TesterString(Poss); // Запись новых настроек в строку текущих настроек MTTESTER::SetSettings2(Settings); // Установка оптимальных генетических настроек в робота ProfitGen = Cache[GetMaxProfitPos(Cache)].profit; // Максимальная прибыль (https://www.mql5.com/ru/forum/318998/page2#comment_13846951) Print("Генетическая прибыль ProfitGen = ",ProfitGen,"\n"); // MTTESTER::GetSettings(Settings); // Считываются только что установленные настройки робота // Print("После генетики заново считанная Settings:\n",Settings); ProfitNew = ProfitGen; } ProfitNew = Optimiz(FromDate,ToDate); // Последовательная оптимизация как после генетики, так и без нее double ProfitNew1 = ProfitNew; NameSettingsMax = NameSettings; Print("NameSettingsMax = ",NameSettingsMax," ProfitNew = ",DoubleToString(ProfitNew,0)," ProfitGen = ",DoubleToString(ProfitGen,0)); if(PrGenetic) // Дополнительная последовательная оптимизация по исходным настройкам { Print("\n\nДополнительная последовательная оптимизация"); Settings = SettingsBeg; // Восстановление исходных настроек DeleteFile(TerminalInfoString(TERMINAL_DATA_PATH) + "\\Tester\\cache\\"); // Очистка Cache, чтобы гарантировать реальный прогон (а не воспроизведение данных запомненного ранее из opt-файла) MTTESTER::SetValue(Settings,"Optimization","1"); // Optimization = 2 генетика, 1 полный перебор, 0 отключена ExecutionMode=100 MTTESTER::SetSettings2(Settings); // Установка новых данных в настройки ProfitNew = 1; ProfitOld = 0; ProfitNewDop = Optimiz(FromDate,ToDate); // Дополнительная последовательная оптимизация if(ProfitNewDop > ProfitNew1) NameSettingsMax = NameSettings; Print("NameSettingsMax = ",NameSettingsMax," ProfitNewDop = ",DoubleToString(ProfitNewDop,0)," ProfitGen = ",DoubleToString(ProfitGen,0)," Дополнительная последовательная оптимизация"); } Duration = Now < ToDate ? TimeLocal() - ToDate : TimeLocal() - Now; // Продолжительность оптимизации SendNotification("Оптимизация окончена: "+NameSettingsMax); // Уведомление об окончании оптимизации Print("Окончание ",IterNumber," итераций по ",DoubleToString(Nruna/IterNumber,0)," параметрам, ",Nruna," прогонов. Длительность ",TimeToString(Duration,TIME_SECONDS),". "+NameSettingsMax," (",DoubleToString(ProcentProfit,1),"%), ",DoubleToString(ProfitGen,0)," генетическая."); } // Конец Nocturne //+-----------------------------------------------------------------------+ //| Odinar: set-файл с единственным Y; определяется тип и позиция знака = | //+-----------------------------------------------------------------------+ int NextOdinar(string &Odinar,int Pos,int &TypPar,int &PosEq) { //Print("Pos = ",Pos," Settings в NextOdinar:",Settings); int Posa = StringFind(Settings,"Y",Pos+1); // Абсолютная позиция Y в строке Settings, начиная с Pos if(Posa < 0) // Если Y больше нет в Settings return(-1); else { Odinar = Settings; StringReplace(Odinar,"Y","N"); // Замена всех Y на N //Print("\nОбнуление: Количество Y KolY = ",KolY," Начальная позиция = ",Pos," Позиция = ",Posa," Odinar после замены всех Y на N: \n",Odinar); StringSetCharacter(Odinar,Posa,'Y'); // Установка единственного Y в позицию Posa //Print("\nВставка Y: Количество Y KolY = ",KolY," Начальная позиция = ",Pos," Позиция = ",Posa," Odinar после установки единственного Y: \n",Odinar); PosEq = ZnakEq(Odinar,Posa); // Абсолютная позиция знака = в строке Odinar, начиная с Posa назад TypPar = TypePar(PosEq,Posa); // Определение типа параметра //Print("NextOdinar PosEq = ",PosEq," TypPar = ",TypPar); return(Posa); }; } //+------------------------------------------------------------------+ //| Замена значения int-параметра в исходных настройках | //+------------------------------------------------------------------+ int ZamenaIntPar(string &Settar,int PosY,int PosaEq,int ParMax) { int NovPosY,PosaVert; string Setta,obrez,raznost,vstavka,summa; MTTESTER::GetSettings(Setta); // Считываются исходные настройки робота в Setta // Print("На входе в ZamenaIntPar PosY =",PosY," ParMax=",ParMax," Исходные настройки в ZamenaPar:\n",Setta); obrez=Setta; _W(obrez)[PosaEq+1] = (uchar)0; PosaVert = StringFind(Setta,"|",PosaEq); // Абсолютная позиция знака | в строке Setta, начиная с PosaEq вперёд raznost = StringSubstr(Setta,PosaVert,-1); // Print("obrez:\n",obrez); // Print("raznost:\n",raznost); vstavka = IntegerToString(ParMax); NovPosY = PosY + StringLen(vstavka) - (PosaVert - PosaEq) + 1; // Для облегчения поиска следующего Y summa=obrez+vstavka+raznost; // Print("На входе в ZamenaIntPar ParMax = ",ParMax," PosY =",PosY," NovPosY новый = ",NovPosY," PosaEq=",PosaEq," PosaVert=",PosaVert," dlinavstavka=",StringLen(vstavka)," summa:\n",summa); Settar = summa; return(NovPosY); } //+-------------------------------------------------------------------+ //| Замена значения double-параметра в исходных настройках | //+-------------------------------------------------------------------+ int ZamenaDoublePar(string &Settar,int PosY,int PosaEq,double ParMax) { int NovPosY,PosaVert; string Setta,obrez,raznost,vstavka,summa; MTTESTER::GetSettings(Setta); // Считываются исходные настройки робота в Setta // Print("На входе в ZamenaDoublePar PosY =",PosY," ParMax=",ParMax," Исходные настройки в ZamenaPar:\n",Setta); obrez=Setta; _W(obrez)[PosaEq+1] = (uchar)0; PosaVert = StringFind(Setta,"|",PosaEq); // Абсолютная позиция знака | в строке Setta, начиная с PosaEq вперёд raznost = StringSubstr(Setta,PosaVert,-1); // Print("obrez:\n",obrez,"\nraznost:\n",raznost); vstavka = DoubleToString(ParMax); int StLen = StringLen(vstavka); // Убираем лишние нули справа // Print("vstavka до обрезки нулей ",vstavka," StLen = ",StLen); for(int i=StLen; i >= 1;i--) { int Posa0 = StringFind(vstavka,"0",i-1); // Абсолютная позиция знака 0 в строке vstavka, начиная с i вперёд int PosaT = StringFind(vstavka,".",i-2); // Абсолютная позиция знака . в строке vstavka, начиная с i-1 вперёд if(Posa0 > 0 && PosaT < 0) { _W(vstavka)[i-1] = (uchar)0; StLen--; // Print("PosaT=",PosaT," Posa0=",Posa0," vstavka после обрезки очередного нуля ",vstavka," StLen = ",StLen); } else break; } // Print("vstavka после обрезки нулей ",vstavka," StLen = ",StLen); NovPosY = PosY + StLen - (PosaVert - PosaEq) + 1; // Для облегчения поиска следующего Y summa=obrez+vstavka+raznost; // Print("На входе в ZamenaDoublePar ParMax = ",ParMax," PosY =",PosY," NovPosY новый = ",NovPosY," PosaEq=",PosaEq," PosaVert=",PosaVert," dlinavstavka=",StringLen(vstavka)," summa:\n",summa); Settar = summa; return(NovPosY); } //+------------------------------------------------------------------+ //| Позиция ближайшего слева знака = до Y | //+------------------------------------------------------------------+ int ZnakEq(string &Odan,int PosY) { int PosaEq; string ch; for(PosaEq=PosY; PosaEq > PosY-100; PosaEq--) { ch=ShortToString(StringGetCharacter(Odan,PosaEq)); if(ch == "=") break; } return(PosaEq); } //+------------------------------------------------------------------+ //| Определение типа параметра в настройках | //+------------------------------------------------------------------+ int TypePar(int PosaEq,int PosaY) { int Type =-1; string obrezEq,obrezY,raznost ; MTTESTER::GetSettings(obrezY); // Считываются исходные настройки робота в obrezY _W(obrezY)[PosaY] = (uchar)0; // obrezY обрезается, начиная с Y raznost = StringSubstr(obrezY,PosaEq+1,-1); // Выделяется подстрока между знаками = и Y if(StringFind(raznost,".",0)>0) // Тип 0 (double), если от = до Y есть хоть одна точка Type = 0; else if(StringFind(raznost,"t",0)>0 || StringFind(raznost,"f",0)>0) // Тип 2 (bool), если от = до Y есть true или false Type = 2; else // Тип 1 (int) Type = 1; return(Type); } //+------------------------------------------------------------------+ //| Определение номера записи с максимальной прибылью | //+------------------------------------------------------------------+ template // шаблон функции для поиска максимального значения в массиве любого числового типа int GetMaxProfitPos(const TESTERCACHE &Cache) { int Pos = 0; double MaxProfit = -DBL_MAX; for(int i = Cache.GetAmount() - 1; i >= 0; i--) if(Cache[i].TesterStatistics(STAT_PROFIT) > MaxProfit) { MaxProfit = Cache[i].TesterStatistics(STAT_PROFIT); Pos = i; } return(Pos); } //+-------------------------------------------------------------------+ //| Прогон последовательной оптимизации по отмеченным Y параметрам | //+-------------------------------------------------------------------+ int Runa(const string Set) { string Sohrann,Control; uchar Byts[]; DeleteFile(TerminalInfoString(TERMINAL_DATA_PATH) + "\\Tester\\cache\\"); // Очистка Cache, чтобы гарантировать реальный прогон (а не воспроизведение данных запомненного ранее из opt-файла) MTTESTER::GetSettings(Sohrann); // Считываются текущие исходные настройки робота для временного сохранения в Sohrann // bool prWrite = MTTESTER::SetSettings2(Set); // bool prRead = MTTESTER::GetSettings(Control); // Считываются только что установленные настройки робота // Print("prWriteSetRuna = ",prWrite," prReadSetRuna = ",prRead); // Print("SetBegRuna Control ","\n",Control); while(_CS(!MTTESTER::IsReady())) Sleep(PAUSE); MTTESTER::CloseNotChart(); if(_CS(MTTESTER::SetSettings2(Set))) // Установка Set в настройки робота MTTESTER::ClickStart(false); while(_CS(!MTTESTER::IsReady())) Sleep(PAUSE); int GLOCC = MTTESTER::GetLastOptCache(Byts); // Получили opt-файл в байтовый массив Byts TESTERCACHE Cache; bool CLL = Cache.Load(Byts); // Подаём в Cache байтовый массив Byts // bool prWrite2 = Cache.Save(OptFile); // Сохранили результат Runa в opt-файл в MQL5\Files // bool prRead2 = Cache.Load(OptFile); // Прочитали для проверки из MQL5\Files // Print(OptFile+" записали = ",prWrite2," прочитали = ",prRead2); // Диагноз записи и чтения bool prWrite1 = MTTESTER::SetSettings2(Sohrann);// Восстановление сохранённых настроек в робота // bool prRead1 = MTTESTER::GetSettings(Control); // Считываются для контроля настройки робота // Print("prWriteSetRuna = ",prWrite1," prReadSetRuna = ",prRead1); // Print("SetEndRuna Control ","\n",Control); return(GetMaxProfitPos(Cache)); // Индекс массива с максимальным значением прибыли } //+------------------------------------------------------------------+ //| Прогон по отмеченным Y параметрам при генетической оптимизации | //+------------------------------------------------------------------+ void Run(const string Settings) { while(_CS(!MTTESTER::IsReady())) Sleep(PAUSE); MTTESTER::CloseNotChart(); if(_CS(MTTESTER::SetSettings2(Settings))) MTTESTER::ClickStart(false); while(_CS(!MTTESTER::IsReady())) Sleep(PAUSE); } //+-------------------------------------------------------------------+ //| Очистка папки по заданному адресу | //+-------------------------------------------------------------------+ void DeleteFile(string Path) { FIND_DATAWW FindData; FindData.cFileName[0] = 0; const long handle = FindFirstFileW(Path + "*.*", FindData); if(handle != INVALID_HANDLE) { do { bool PrDelete = DeleteFileW(Path + ShortArrayToString(FindData.cFileName)); } while(FindNextFileW(handle,FindData)); FindClose(handle); } } //+------------------------------------------------------------------+ //| Архивирование оптимального набора параметров | //+------------------------------------------------------------------+ string SetArchive(string Settin,datetime Todata) { string NameSettin; // Формируемое имя оптимального набора параметров TimeToStruct(Todata,dt_struct); // Разложение момента формирования имени для Settin string mnth = dt_struct.mon; // Месяц, день int dyy = dt_struct.day; string dy = dt_struct.day; if(dyy <= 9) dy = "0" + dy; double LimPos = MTTESTER::GetValue(Settin,"ShortMax"); // ShortMax считывается NameSettin = mnth+dy+"-"+Depo+" "+ModeNames[Modus]+"-"+DoubleToString(ProfitNew,0)+".set"; // Формируется имя для Settin int file_handle=FileOpen("//"+NameSettin,FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI); FileWriteString(file_handle,Settin+"\r\n"); // Записывается строка Settin в песочницу Files FileClose(file_handle); // Закрываем открытый файл, чтобы его можно было дальше перезаписывать string Path = TerminalInfoString(TERMINAL_DATA_PATH); string SrcPath = Path + "\\MQL5\\Files\\" + NameSettin; string DstPath = Path + "\\MQL5\\Profiles\\Tester\\" + NameSettin; ResetLastError(); if(!kernel32::CopyFileW(SrcPath,DstPath,false)) // Переписываем Settin из песочницы Files в папку Tester PrintFormat("Error = %d",GetLastError()); // 4009 Неинициализированная строка return(NameSettin); } //+---------------------------------------------------------------------+ //| Оптимизация перебором отмеченных Y параметров | //+---------------------------------------------------------------------+ double Optimiz(datetime FromData,datetime ToData) { MTTESTER::SetValue(Settings,"Optimization","1"); // Optimization = 2 генетика, 1 полный перебор, 0 отключена ExecutionMode=100 MTTESTER::SetSettings2(Settings); // Установка новых данных в настройки while(ProfitNew > ProfitOld) // Итерации оптимизаций по отмеченным Y параметрам пока не перестанет расти прибыль { IterNumber++; Print("Итерация ",IterNumber," начата: ProfitNew = ",DoubleToString(ProfitNew,0)," ProfitOld = ",DoubleToString(ProfitOld,0)); ProfitOld = ProfitNew; PoseY = 1; // Начальная позиция для поиска Y в настройках оптимизируемого робота Settings KolY = 0; // Количество сформированных в данной итерации Odinar с единственным Y (порядковый номер Y в настройках оптимизируемого робота) while(PoseY > 0) // Цикл по числу параметров (оптимизация по количеству Y в настройках) { MTTESTER::GetSettings(Settings); // Считываются текущие исходные настройки робота для временного сохранения в Sohrann // Print("Цикл while(PoseY > 0) \nSettings:\n",Settings); PoseY = NextOdinar(Odinar,PoseY,TypPar,PosEq); // Формируется Odinar, определяются тип параметра, положения знаков Y и = if(PoseY>0) // Если найден очередной Y и сформирован новый Odinar { // Поиск значения параметра с максимальной прибылью KolY++; // Количество сформированных Odinar с единственным Y IndexMaxProfitRuna = Runa(Odinar); // Прогон оптимизации по отмеченным Y параметрам Nruna++; // Подсчёт числа прогонов // Print("Итерация ",IterNumber,", Odinar ",KolY,", Прогон ",Nruna,", TypPar ",TypPar,", PoseY ",PoseY,", PosEq ",PosEq,", ProfitNew ",DoubleToString(ProfitNew,0)," ProfitOld ",DoubleToString(ProfitOld,0)); // Print("\nOdinar\n",Odinar); GLOC = MTTESTER::GetLastOptCache(Bytes); // Получили opt-файл в байтовый массив Bytes TESTERCACHE Cache; CL = Cache.Load(Bytes); // Подаём в Cache байтовый массив Bytes prWrite2 = Cache.Save(OptFile); // Сохранили результат Runa в opt-файл в MQL5\Files prRead2 = Cache.Load(OptFile); // Прочитали для проверки из MQL5\Files // Print(OptFile+" записали = ",prWrite2," прочитали = ",prRead2); // Диагноз записи и чтения // Print("SettingEnd Odinar ",KolY," PoseY = ",PoseY," Итерация ",IterNumber,"\n",Contr); ProfitNew = Cache[GetMaxProfitPos(Cache)].profit; // Максимальная прибыль https://www.mql5.com/ru/forum/318998/page2#comment_13846951 int NomProchodaMaxProfit = Cache[GetMaxProfitPos(Cache)].Pass; // Номер прохода с максимальной прибылью Cache.GetInputs(IndexMaxProfitRuna,Paramas); if(prRead2 && TypPar == 0) // Прочитали оптимизационный Cache для double-параметра { ParamMax = Paramas[0,1].double_value; // Значение double-параметра, при котором достигнута максимальная прибыль if(Cache.Load(Bytes)) // Прочитали оптимизационный Cache { Pos = GetMaxProfitPos(Cache); Cache.SaveSet(Pos); // Создали set-файл самого прибыльного прохода в MQL5\Files Print("Итерация ",IterNumber," Odinar ",KolY," ProfitNew = ",DoubleToString(ProfitNew,0)," ProfitOld = ",DoubleToString(ProfitOld,0)," double-параметр = ",ParamMax," PoseY = ",PoseY); if(IterNumber == 1 && KolY == 1) ProfitOld = ProfitNew; // Print("До обновления double Settings: \n",Settings); PoseY = ZamenaDoublePar(Settings,PoseY,PosEq,ParamMax); // Запись в Settings нового double-параметра после знака = в строке с Y в позиции PoseY MTTESTER::SetSettings2(Settings); // Установка оптимальных настроек в робота // MTTESTER::GetSettings(Settings); // Считываются установленные настройки робота // Print("После обновления double Settings: KolY = ",KolY," НовPoseY = ",PoseY," Итерация ",IterNumber," ProfitNew = ",DoubleToString(ProfitNew,0),"\n",Settings); } prWrite = Cache.Save(OptFile); // Сохранили результат Runa в opt-файл в MQL5\Files prRead = Cache.Load(OptFile); // Прочитали для проверки из MQL5\Files // Print(OptFile+" записали = ",prWrite," прочитали = ",prRead); // Диагноз записи и чтения } if(prRead2 && TypPar >= 1) // Прочитали оптимизационный Cache для int-параметра { int IntParamMax = Paramas[0,1].integer_value; // Значение int-параметра, при котором достигнута максимальная прибыль if(Cache.Load(Bytes)) // Прочитали оптимизационный Cache { // Print(Cache.Header.ToString()); // Вывели основные данные оптимизационного Cache Pos = GetMaxProfitPos(Cache); // Print("Pos = ",Pos); // Print(Cache[Pos].ToString()); // Вывели статистику записи с максимальной прибылью Cache.SaveSet(Pos); // Создали set-файл самого прибыльного прохода в MQL5\Files (это нужно только по окончании итераций) Print("Итерация ",IterNumber," Odinar ",KolY," ProfitNew = ",DoubleToString(ProfitNew,0)," ProfitOld = ",DoubleToString(ProfitOld,0)," int-параметр = ",IntParamMax," PoseY = ",PoseY); if(IterNumber == 1 && KolY == 1) ProfitOld = ProfitNew; // Print("До обновления int Settings: \n",Settings); PoseY = ZamenaIntPar(Settings,PoseY,PosEq,IntParamMax); // Запись в Settings нового int-параметра после знака = в строке с Y в позиции PoseY MTTESTER::SetSettings2(Settings); // Установка оптимальных настроек в робота // MTTESTER::GetSettings(Settings); // Считываются установленные настройки робота // Print("После обновления integer Settings: KolY = ",KolY," НовPoseY = ",PoseY," Итерация ",IterNumber,"\n",Settings); } } } else // Конец очередной итерации, так как не удалось найти новый Y и сформировать Odinar { Print("Итерация ",IterNumber," по ",KolY," параметрам закончена: ProfitNew = ",DoubleToString(ProfitNew,0)," ProfitOld = ",DoubleToString(ProfitOld,0),"\n"); } } // Конец цикла по параметрам (оптимизация по количеству Y в настройках) } // Конец всех итераций оптимизаций по отмеченным Y параметрам пока не перестанет расти прибыль ProcentProfit = 100 * ProfitNew / Deposit; // Процент прибыли MTTESTER::SetValue(Settings,"Extra",ExtraSohr); // Восстановление Extra MTTESTER::SetValue(Settings,"SumLong",SumLongSohr); // Восстановление SumLong MTTESTER::SetSettings2(Settings); // Установка восстановленных данных в настройки // MTTESTER::GetSettings(Settings); // Считываются настройки робота для контроля // Print("Апосля =",PrGenetic,"\n",Settings); NameSettings = SetArchive(Settings,ToDate); // Запись оптимального набора параметров в папки Files и Tester return(ProfitNew); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| FXX.mq5 | //| Load/Save Tester's set-file | //+------------------------------------------------------------------+ #property script_show_inputs input bool inLoad = true; // Settings Load/Save - true/false #include // https://www.mql5.com/ru/code/26132 //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnStart() { // const string FileName = __FILE__ + ".set"; const string FileName = "214-10000 HEX-449.set"; // Добавка // Print("Полный путь к файлу (относительно Common\\Files): ", search_path, " -> Найденный файл: ", first_set_filename); // Добавка string Settings; ushort Words[]; if(inLoad) // Load settings. { Print("Load: " + (string)FileLoad(FileName, Words)); Settings = ShortArrayToString(Words, 1); // 1 - Unicode Print("\nSettings от FileLoad \n", Settings,"\nРезультат от MTTESTER::SetSettings(Settings):"); // Добавка bool efes = MTTESTER::SetSettings2(Settings); Print(efes); // Print(MTTESTER::SetSettings2(Settings)); // Print(MTTESTESetSettingsR::SetSettings(FileName)); // Передаём имя файла, а не строку параметров. Так подсказал Qwen-coder } else if(MTTESTER::GetSettings(Settings)) // Save settings. { Words[ArrayResize(Words, 1) - 1] = 0xFEFF; // 1 - Unicode StringToShortArray(Settings, Words, ArraySize(Words)); Print("Save: " + (FileSave(FileName, Words) ? (string)ArraySize(Words) : "error")); } } //+------------------------------------------------------------------+ Сравнивая два скрипта, `Nocturne1` и `FXX`, и учитывая ваш предыдущий лог `Pasted_Text_1771411375745.txt`, где `MTTESTER::SetSettings2(Settings)` в цикле загружал разные сеты, но `MTTESTER::GetSettings` возвращал одинаковые параметры, можно сделать следующий вывод: Почему `MTTESTER::SetSettings2(Settings)` "успешно" работает в `Nocturne1` тысячи раз, но не работает в `FXX`: 1. Контекст использования: * `Nocturne1`: `SetSettings2(Settings)` используется внутри циклического процесса оптимизации. Он устанавливает *небольшие изменения* в параметрах (например, изменение значения одного параметра, найденного на итерации оптимизации), запускает тестер, читает результаты, восстанавливает предыдущие параметры (`Sohrann`), и повторяет. Важно: после *каждого* вызова `SetSettings2(Settings)` обычно следует запуск тестера (`MTTESTER::ClickStart`), и между итерациями часто происходит восстановление исходного состояния. Это интенсивное, но контролируемое использование `MTTester`, часто с ожиданием завершения предыдущего запуска (`while(!_CS(MTTESTER::IsReady()))`). * `FXX`: `SetSettings2(Settings)` вызывается один раз, после того как строка `Settings` была загружена из внешнего файла `.set` с помощью `FileLoad` и `ShortArrayToString`. Цель — однократно загрузить в тестер конкретный набор параметров из файла. Никакого запуска тестера или других действий с `MTTester` после этого не происходит. 2. Проблема `FXX` (и, как мы видим, потенциальная проблема `Nocturne1` в определённых условиях): * `MTTester.mqh` работает через эмуляцию пользовательского интерфейса (GUI). Он не устанавливает параметры напрямую в ядро тестера, а нажимает кнопки, вводит текст в поля и т.д., как это сделал бы человек. * `MTTESTER::SetSettings2(Settings)` отправляет строку `Settings` в текстовое поле параметров в окне тестера. Затем, теоретически, `MTTester` должен имитировать нажатие клавиши Enter или другое действие, которое применит эти изменения к внутреннему состоянию тестера. * Однако, GUI может быть медленным, требовать времени на обработку, или сбрасывать изменения, если состояние тестера (например, запущен ли тест, в каком режиме находится) мешает применению новых параметров. Также возможно, что после `SetSettings2` GUI тестера не сразу обновляется, и `GetSettings` читает старое значение, если вызывается слишком быстро. * В `Nocturne1`, часто восстанавливаются старые параметры и запускается тестер, что "прокачивает" или "фиксирует" состояние GUI, и ожидания `IsReady()` могут скрывать ненадёжность `SetSettings2`. * В `FXX`, после `SetSettings2(Settings)` ничего не происходит, и скрипт завершается. GUI тестера не успевает или не может корректно обработать и применить переданную строку `Settings`. Вывод: `MTTESTER::SetSettings2(Settings)` ненадёжен для однократной загрузки параметров из строки, особенно если за этим не следует запуск теста или достаточная задержка/проверка. Он лучше работает в динамическом цикле, как в `Nocturne1`, где изменения малы, часто происходят восстановления и перезапуски, и используется `IsReady()`. Рекомендации для `FXX`: 1. Попробуйте `MTTESTER::SetSettings(имя_файла)` (не `SetSettings2`), как обсуждалось ранее. Это намного более надёжный способ загрузить параметры из существующего файла `.set`. Убедитесь, что `214-10000 HEX-449.set` находится в папке `tester\files` терминала. ```mql5 if(inLoad) // Load settings. { // ... (FileLoad и ShortArrayToString остаются, если нужно для других целей) Print("Load: " + (string)FileLoad(FileName, Words)); Settings = ShortArrayToString(Words, 1); // 1 - Unicode Print("\nSettings от FileLoad \n", Settings, "\nРезультат от MTTESTER::SetSettings(FileName):"); // Загружаем файл по его имени bool load_result = MTTESTER::SetSettings(FileName); // Передаём имя файла, а не строку параметров Print(load_result); } ``` 2. Если вы *действительно* хотите использовать `SetSettings2(Settings)`, добавьте значительную задержку после вызова и проверьте, действительно ли параметры изменились, например, с помощью `MTTESTER::GetSettings`. ```mql5 // ... bool efes = MTTESTER::SetSettings2(Settings); Print("MTTESTER::SetSettings2 returned: ", efes); Sleep(3000); // Подождать 3 секунды, чтобы GUI мог обновиться string check_settings = ""; bool check_success = MTTESTER::GetSettings(check_settings); Print("MTTESTER::GetSettings after SetSettings2: ", check_success); if(check_success) { // Сравните Settings и check_settings, или просто посмотрите в логе Print("Settings loaded via SetSettings2 seem to be applied."); // Print("Applied Settings: ", check_settings); // Будет длинно } else { Print("Failed to read back settings after SetSettings2."); } // ... ``` Скорее всего, вариант с `MTTESTER::SetSettings(FileName)` будет работать гораздо стабильнее для вашей задачи загрузки конкретного сет-файла.