//+------------------------------------------------------------------+ //| TradingByHistoryDeals.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include "SymbolTrade.mqh" //+------------------------------------------------------------------+ //| Expert | //+------------------------------------------------------------------+ //--- input parameters input string InpTestedSymbol = ""; /* The symbol being tested in the tester */ // Тестируемый символ input long InpTestedMagic = -1; /* The magic number being tested in the tester */ // Тестируемый магик sinput bool InpShowDataInLog = false; /* Show collected data in the log */ // Показать собранные данные сделок в журнале //--- global variables CSymbolTrade SymbTradeTmp; SDeal ExtArrayDeals[]={}; CArrayObj ExtListSymbols; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Если советник запущен не в тестере if(!MQLInfoInteger(MQL_TESTER)) { //--- подготовим файл со всеми историческими сделками if(!PreparesDealsHistoryFile(ExtArrayDeals)) return(INIT_FAILED); //--- распечатаем в журнале все сделки после загрузки их из файла if(InpShowDataInLog) DealsArrayPrint(ExtArrayDeals); //--- получаем первую балансовую сделку, создаём текст сообщения в выводим его при помощи Alert SDeal deal=ExtArrayDeals[0]; long leverage=AccountInfoInteger(ACCOUNT_LEVERAGE); double start_money=deal.profit; datetime first_time=deal.time; string start_time=TimeToString(deal.time, TIME_DATE); string message=StringFormat("Now you can run testing\nInterval: %s - current date\nInitial deposit: %.2f, leverage 1:%I64u", start_time, start_money, leverage); //--- сообщим алертом рекомендуемые параметры тестера стратегий для запуска тестирования Alert(message); } //--- Советник запущен в тестере else { //--- прочитаем данные из файла в массив ulong file_size=0; ArrayResize(ExtArrayDeals, 0); if(!FileReadDealsToArray(ExtArrayDeals, file_size)) { PrintFormat("Failed to read file \"%s\". Error %d", FILE_NAME, GetLastError()); return(INIT_FAILED); } //--- сообщим в журнале о количестве прочитанных байт из файла и о записи массива сделок. PrintFormat("%I64u bytes were read from the file \"%s\" and written to the deals array. A total of %u deals were received", file_size, FILE_NAME, ExtArrayDeals.Size()); } //--- Из массива исторических сделок создаём список торговых объектов по символам if(!CreateListSymbolTrades(ExtArrayDeals, &ExtListSymbols)) { Print("Errors found while creating symbol list"); return(INIT_FAILED); } //--- Распечатаем в журнале созданный список сделок SymbolsArrayPrint(&ExtListSymbols); //--- Обратимся к каждому символу для начала закачки исторических данных //--- и открытия графиков проторгованных символов в тестере стратегий datetime array[]; int total=ExtListSymbols.Total(); for(int i=0; i0); if(!res) PrintFormat("%s: FileWriteArray() failed. Error ",__FUNCTION__, GetLastError()); else file_size=FileSize(handle); //--- закрываем файл FileClose(handle); return res; } //+------------------------------------------------------------------+ //| Подготавливает файл со сделками истории | //+------------------------------------------------------------------+ bool PreparesDealsHistoryFile(SDeal &deals_array[]) { //--- сохраним все сделки счёта в массив сделок int total=SaveDealsToArray(deals_array); if(total==0) return false; //--- запишем данные массива сделок в файл ulong file_size=0; if(!FileWriteDealsFromArray(deals_array, file_size)) return false; //--- распечатаем в журнале сколько сделок было прочитано и сохранено в файл, путь к файлу и его размер PrintFormat("%u deals were saved in an array and written to a \"%s\" file of %I64u bytes in size", deals_array.Size(), "TERMINAL_COMMONDATA_PATH\\Files\\"+ PATH, file_size); //--- теперь для проверки прочитаем данные из файла в массив ArrayResize(deals_array, 0, total); if(!FileReadDealsToArray(deals_array, file_size)) return false; //--- распечатаем в журнале сколько байт было прочитано из файла и количество полученных в массив сделок PrintFormat("%I64u bytes were read from the file \"%s\" and written to the deals array. A total of %u deals were received", file_size, FILE_NAME, deals_array.Size()); return true; } //+------------------------------------------------------------------+ //| Создаёт объект сделки из структуры | //+------------------------------------------------------------------+ CDeal *CreateDeal(SDeal &deal_str) { //--- Если объект сделки создать не удалось - сообщаем в журнале об ошибке и возвращаем NULL CDeal *deal=new CDeal(deal_str.ticket, deal_str.Symbol()); if(deal==NULL) { PrintFormat("%s: Error. Failed to create deal object"); return NULL; } //--- заполняем свойства сделки из полей структуры deal.SetOrder(deal_str.order); // Ордер, на основании которого была открыта сделка deal.SetPositionID(deal_str.pos_id); // Идентификатор позиции deal.SetTimeMsc(deal_str.time_msc); // Время в миллисекундах deal.SetTime(deal_str.time); // Время deal.SetVolume(deal_str.volume); // Объём deal.SetPrice(deal_str.price); // Цена deal.SetProfit(deal_str.profit); // Прибыль deal.SetCommission(deal_str.commission); // Комиссия по сделке deal.SetSwap(deal_str.swap); // Накопленный своп при закрытии deal.SetFee(deal_str.fee); // Оплата за проведение сделки, начисляется сразу после совершения сделки deal.SetSL(deal_str.sl); // Уровень Stop Loss deal.SetTP(deal_str.tp); // Уровень Take Profit deal.SetType(deal_str.type); // Тип deal.SetEntry(deal_str.entry); // Способ изменения позиции deal.SetReason(deal_str.reason); // Причина или источник проведения сделки deal.SetMagic(deal_str.magic); // Идентификатор эксперта deal.SetComment(deal_str.Comment()); // Комментарий к сделке deal.SetExternalID(deal_str.ExternalID()); // Идентификатор сделки во внешней торговой системе (на бирже) //--- Возвращаем указатель на созданный объект return deal; } //+------------------------------------------------------------------+ //| Создаёт массив используемых символов | //+------------------------------------------------------------------+ bool CreateListSymbolTrades(SDeal &array_deals[], CArrayObj *list_symbols) { bool res=true; // результат int total=(int)array_deals.Size(); // общее количество сделок в массиве //--- если массив сделок пустой - возвращаем false if(total==0) { PrintFormat("%s: Error! Empty deals array passed",__FUNCTION__); return false; } //--- в цикле по массиву сделок CSymbolTrade *SymbolTrade=NULL; for(int i=0; i-1 && deal.Magic()!=magic) || (symbol!="" && deal.Symbol()!=symbol)) continue; //--- фильтруем сделку по типу (только сделки покупки/продажи) ENUM_DEAL_TYPE type=deal.TypeDeal(); if(type!=DEAL_TYPE_BUY && type!=DEAL_TYPE_SELL) continue; //--- если это уже обработанная в тестере сделка - идём к следующей if(deal.TicketTester()>0) continue; //--- если время сделки ещё не настало - идём к следующему торговому объекту следующего символа if(!obj.CheckTime(deal.Time())) continue; //--- если сделка входа в рынок ENUM_DEAL_ENTRY entry=deal.Entry(); if(entry==DEAL_ENTRY_IN) { //--- открываем позицию по типу сделки double sl=0; double tp=0; ulong ticket=(type==DEAL_TYPE_BUY ? obj.Buy(deal.Volume(), deal.Magic(), sl, tp, deal.Comment()) : type==DEAL_TYPE_SELL ? obj.Sell(deal.Volume(),deal.Magic(), sl, tp, deal.Comment()) : 0); //--- если позиция открыта (получили её тикет) if(ticket>0) { //--- увеличиваем количество обработанных тестером сделок и записываем тикет сделки в тестере в свойства объекта сделки obj.SetNumProcessedDeals(obj.NumProcessedDeals()+1); deal.SetTicketTester(ticket); //--- получаем идентификатор позиции в тестере и записываем его в свойства объекта сделки long pos_id_tester=0; if(HistoryDealSelect(ticket)) { pos_id_tester=HistoryDealGetInteger(ticket, DEAL_POSITION_ID); deal.SetPosIDTester(pos_id_tester); } } } //--- если сделка выхода из рынка if(entry==DEAL_ENTRY_OUT || entry==DEAL_ENTRY_INOUT || entry==DEAL_ENTRY_OUT_BY) { //--- получаем сделку, на основании которой была открыта позиция CDeal *deal_in=obj.GetDealInByPosID(deal.PositionID()); if(deal_in==NULL) continue; //--- получаем тикет позиции в тестере из свойств открывающей сделки //--- если тикет равен нулю, значит скорее всего позиция в тестере уже закрыта ulong ticket_tester=deal_in.TicketTester(); if(ticket_tester==0) { PrintFormat("Could not get position ticket, apparently position #%I64d (#%I64d) is already closed \n", deal.PositionID(), deal_in.PosIDTester()); obj.SetNextDealIndex(); continue; } //--- если позиция закрыта по тикету if(obj.ClosePos(ticket_tester)) { //--- увеличиваем количество обработанных тестером сделок и записываем тикет сделки в тестере в свойства объекта сделки obj.SetNumProcessedDeals(obj.NumProcessedDeals()+1); deal.SetTicketTester(ticket_tester); } } //--- если теперь в объекте сделки записан тикет - значит сделка успешно обработана - //--- устанавливаем индекс сделки в списке на следующую сделку if(deal.TicketTester()>0) { obj.SetNextDealIndex(); } } } //+------------------------------------------------------------------+