Вопросы от начинающих MQL5 MT5 MetaTrader 5 - страница 1586

 
Alexey Viktorov # :

В какой момент вы пытаетесь сделать снимок? Произвольно, в момент открытия нового бара, по сигналу индикатора или как-то еще?

Покажите свой кусок кода...

Все методы, которые вы упомянули, действительны. Все зависит от того, насколько сильно вы хотите это сделать. В этом нет никакой тайны.

Быстрый пример, основанный на открытии нового бара:

input int W = 1280 ;        // ширина скриншота
input int H = 720 ;         // высота скриншота
input int TimerSecs = 1 ;   // интервал таймера в секундах

bool need_shot = false ;    // флаг для запуска скриншота
string fname;               // имя файла скриншота
datetime last_bar_time = 0 ;
//--- отформатируйте время даты в безопасное имя файла (без ":" и пробелов)
string SafeStamp( datetime t)
  {
   string s = TimeToString (t, TIME_DATE | TIME_MINUTES | TIME_SECONDS ); // "YYYY.MM.DD HH:MM:SS"
   StringReplace (s, ":" , "-" );   // замените ":" на "-"
   StringReplace (s, " " , "_" );   // замените пробел на "_"
// Необязательно: замените точки, если хотите
// StringReplace(s, ".", "-");
   return s;
  }
//--- инициализация
int OnInit ()
  {
   EventSetTimer (TimerSecs); // запуск таймера
   return INIT_SUCCEEDED ;
  }
//--- деинициализация
void OnDeinit (const int reason)
  {
   EventKillTimer (); // остановить таймер
  }
//--- обнаруживаем новый бар на тике
void OnTick ()
  {
   datetime bar_time = iTime (_Symbol , _Period , 0);
   if (bar_time != last_bar_time) // обнаружен новый бар
     {
      last_bar_time = bar_time;
      need_shot = true ;
      fname = "bar_" + SafeStamp(bar_time) + ".png" ; // безопасное имя файла
     }
  }
//--- делайте снимок экрана по событию таймера
void OnTimer ()
  {
   if (!need_shot)
       return ; // ничего не делать
// принудительный рендеринг перед скриншотом
   ChartRedraw (0);
   ChartRedraw (0);
   ResetLastError ();
   bool ok = ChartScreenShot (0 , fname, W, H);
   int err = GetLastError ();
   if (ok)
       Print ("Screenshot saved: " , fname);
   else
       PrintFormat ("Screenshot failed: %s, err=%d" , fname, err);
   need_shot = false ; // сбросить флаг
  }


Файлы:
 
Miguel Angel Vico Alba #:

Все методы, которые вы упомянули, действительны. Все зависит от того, насколько сильно вы хотите это сделать. В этом нет никакой тайны.

Быстрый пример, основанный на открытии нового бара:


Я был очень невнимателен. Автор вопроса совсем другой человек.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Вопросы от начинающих MQL5 MT5 MetaTrader 5

Nauris Zukas, 2025.08.13 14:15

Надежно ли работает функция ChartScreenShot() в визуальном режиме MetaTrader 5 Strategy Tester? В документации нет никаких ограничений, но я получаю пустые изображения. Кто-нибудь сталкивался с этим или нашел решение?


 
Alexey Viktorov #:

Это может показаться очевидным, но я думаю, что об этом стоит упомянуть. Вы запускаете тест в визуальном режиме? Без визуального режима нет рендеринга графиков, что объясняет пустые изображения.

 
Miguel Angel Vico Alba #:

Все перечисленные вами методы действенны. Все зависит от того, насколько сильно вы хотите это сделать. В этом нет никакой тайны.

Быстрый пример, основанный на открытии нового бара:


Спасибо за код и изображение, но ваш скриншот (bar_2025.08.15_11-00-00.png) сделан в реальной торговле, а не в визуальном бэктесте, как я и спрашивал: "Надежно ли работает функция ChartScreenShot() в визуальном режиме MetaTrader 5 Strategy Tester?". Я протестировал ваш код с включенным визуальным режимом, и он по-прежнему выдает пустые изображения в бэктесте. В использовании OnTimer() и ChartRedraw() нет ничего нового - я уже пробовал это делать. Ваш расплывчатый совет о "задержках, повторных попытках или запуске по таймеру" не решает проблему. Если у вас есть рабочий пример кода для режима визуального бэктестинга или если вы способны его предоставить, покажите его.

 
Nauris Zukas #:

Спасибо за код и изображение, но ваш скриншот (bar_2025.08.15_11-00-00.png) сделан в реальной торговле, а не в визуальном бэктесте, как я и спрашивал: "Надежно ли работает функция ChartScreenShot() в визуальном режиме MetaTrader 5 Strategy Tester?". Я протестировал ваш код с включенным визуальным режимом, и он по-прежнему выдает пустые изображения в бэктесте. В использовании OnTimer() и ChartRedraw() нет ничего нового - я уже пробовал это делать. Ваш расплывчатый совет о "задержках, повторных попытках или запуске по таймеру" не решает проблему. Если у вас есть рабочий пример кода для режима визуального бэктестинга или если вы способны его предоставить, покажите его.

Во-первых, прошу прощения, если мои предыдущие ответы показались вам неясными или бесполезными. Я должен пояснить, что у меня нет личной потребности в ChartScreenShot() в тестере стратегий MT5, и я никогда не использовал его в своей работе. Все, что я здесь написал, было создано с нуля с единственной целью - помочь вам и другим найти возможные решения.

После глубокого изучения истории форума я считаю необходимым быть полностью прозрачным. Насколько я могу судить, ChartScreenShot() никогда не работал надежно в тестере стратегий MT5, даже в визуальном режиме. Нет ни одного подтвержденного случая, когда она работала бы стабильно. Напротив, существуют многолетние отчеты, описывающие пустые или нуль-байтовые файлы.

Причина кроется в том, как устроен MT5. В MT4 тестер работает в одном потоке и отображает график в том же контексте, что и окно терминала, поэтому, когда вы делаете скриншот, в памяти находится реальное изображение графика, которое нужно захватить. В MT5 тестер многопоточный и распределен по процессам агента, а рендеринг графика происходит асинхронно. В визуальном режиме график, который вы видите, отрисовывается интерфейсом терминала, но код советника в тестере выполняется в агентском процессе, который не имеет прямого доступа к этому отрисованному изображению. Функция возвращает "true", потому что считает, что она преуспела, но в этом контексте нет завершенного графика, который можно было бы перехватить.

Из-за такого разделения проблему нельзя решить с помощью трюков с таймингом, повторных попыток или исправлений кода. Это ограничение является прямым результатом архитектуры MT5 и того, как она работает с бэктестингом.

Надеюсь, это объясняет, почему вы и другие видят пустые изображения и почему нет настоящих рабочих примеров для визуального бэктестинга. Это не вопрос мастерства или креативности; эта функция просто не поддерживается в среде тестера MT5.

Форум о трейдинге, автоматических торговых системах и тестировании торговых стратегий

Вопросы от новичков MQL5 MT5 MetaTrader 5

Мигель Анхель Вико Альба, 2025.08.14 16:05

Да, по сравнению с MT4 это может показаться немного странным, но на это есть причина, рендеринг асинхронный.


 
Nauris Zukas #:

Спасибо за код и изображение, но ваш скриншот (bar_2025.08.15_11-00-00.png) сделан в реальной торговле, а не в визуальном бэктесте, как я и спрашивал: "Надежно ли работает функция ChartScreenShot() в визуальном режиме MetaTrader 5 Strategy Tester?". Я протестировал ваш код с включенным визуальным режимом, и он по-прежнему выдает пустые изображения в бэктесте. В использовании OnTimer() и ChartRedraw() нет ничего нового - я уже пробовал это делать. Ваш расплывчатый совет о "задержках, повторных попытках или запуске по таймеру" не решает проблему. Если у вас есть рабочий пример кода для режима визуального бэктестинга или если вы способны его предоставить, покажите его.

А в чём, собственно смысл делать скриншоты в тестере, если их можно сделать после?

 
Aleksey Vyazmikin #:

А в чём, собственно смысл делать скриншоты в тестере, если их можно сделать после?

В своё время я тоже сходил с ума… Правда это было на МТ4. После прогона тестера смотрю открытые ордера, показания индикаторов и удивляюсь… «Как здесь мог открыться ордер?» Сидеть таращиться в монитор утомительно. Вот и начал делать снимки в момент открытия ордера, чтобы увидеть показания индикаторов… Но теперь язык стал гораздо удобней. А с появлением отладчика вообще все проблемы исчезли.

 
Alexey Viktorov #:

В своё время я тоже сходил с ума… Правда это было на МТ4. После прогона тестера смотрю открытые ордера, показания индикаторов и удивляюсь… «Как здесь мог открыться ордер?» Сидеть таращиться в монитор утомительно. Вот и начал делать снимки в момент открытия ордера, чтобы увидеть показания индикаторов… Но теперь язык стал гораздо удобней. А с появлением отладчика вообще все проблемы исчезли.

Я смог придумать один вариант - человек делает нейронку с распознаванием картинок, тогда да - нужны скрины в процессе, без истории с правой части и с масштабированием на момент скриншота.

Если тормаза из-за асинхронной обработки, то может надо приостановить вычисления логики, что бы отрисовка подоспела? Допустим, сделать ресурсоёмкие вычисления в цикле, и в нём же сделать скриншот?

 

После выхода метатестера около 5120 GUI ограничивает максимальное количество созданных агентов половиной потоков процессора, как на приложенной картинке.

Почему так? Я даже не подключаюсь к облаку?

Файлы:
 

Всем доброго вечера и хорошего настроения!

Пытаюсь разобраться с одним вопросом. Допустим есть две сетки ордеров, установленных в обоих направлениях от текущей цены. При движении текущей цены вверх могут активироваться (это идеальный вариант) все отложенные ордера. А могут и не активироваться, но это пока не столь важно. Если активировались все отложенные ордера вверх, то как определить цены первой и последней позиции верхней сетки (на картинке пометил красными стрелками). Это самый важный момент в моём вопросе!!! При движении цены вниз - всё тоже самое.

Подготовил небольшой скрипт, чтобы для начала хотя бы от чего-то оттолкнуться. Да, и ещё один не мало важный момент - количество открытых позиций вверх и вниз может меняться в ходе торгов. Какие будут мысли по этому вопросу? 

С уважением, Владимир.

//+------------------------------------------------------------------+
//|                                                         Test.mq5 |
//+------------------------------------------------------------------+
#property script_show_inputs
input int Quantity = 3; // Количество последних ордеров
void OnStart()
  {
   if(HistorySelect(0, TimeCurrent()))
     {
      Print("Выбор истории = ", HistorySelect(0, TimeCurrent()));
      int total_deals = HistoryDealsTotal(); // получим количество сделок в списке
      Print("Количество сделок по текущему счёту = ", total_deals); // и выводим значение в журнал
      int total_orders = HistoryOrdersTotal(); // получим количество ордеров в списке
      Print("Количество исторических ордеров = ", total_orders); // и выводим значение в журнал
      for(int i = total_orders - Quantity; i < total_orders; i++) // пройдем в цикле по нужному количеству ордеров
        {
         ulong ticket; // объявим переменную для тикета
         if((ticket = HistoryOrderGetTicket(i)) > 0) // если получили тикет ордера по его номеру в списке
           {
            //--- тогда получим нужные свойства ордера
            double open_price     = HistoryOrderGetDouble(ticket, ORDER_PRICE_OPEN);
            datetime time_setup   = (datetime)HistoryOrderGetInteger(ticket, ORDER_TIME_SETUP);
            datetime time_done    = (datetime)HistoryOrderGetInteger(ticket, ORDER_TIME_DONE);
            string symbol         = HistoryOrderGetString(ticket, ORDER_SYMBOL);
            long order_magic      = HistoryOrderGetInteger(ticket, ORDER_MAGIC);
            long positionID       = HistoryOrderGetInteger(ticket, ORDER_POSITION_ID);
            double initial_volume = HistoryOrderGetDouble(ticket, ORDER_VOLUME_INITIAL);
            string type           = GetOrderType((int)HistoryOrderGetInteger(ticket, ORDER_TYPE));
            //--- и выведм информацию о свойствах ордера
            printf("#ticket %d %s %G %s at %G was set up at %s => done at %s, pos ID=%d",
                   ticket,                   // тикет ордера
                   type,                     // тип
                   initial_volume,           // выставленный объем
                   symbol,                   // символ, по которому выставили
                   open_price,               // указанная цена открытия
                   TimeToString(time_setup), // время установки ордера
                   TimeToString(time_done),  // время исполнения илм удаления
                   positionID                // ID позиции, в которую влилась сделка по ордеру
                  );
           }
        }
     }
  }
//+------------------------------------------------------------------+
//|Функция возвращает строковое наименование типа ордера             |
//+------------------------------------------------------------------+
string GetOrderType(int type)
  {
   string str_type = "неизвестная операция";
   switch(type)
     {
      case(ORDER_TYPE_BUY): return("buy");
      case(ORDER_TYPE_SELL): return("sell");
      case(ORDER_TYPE_BUY_LIMIT): return("buy limit");
      case(ORDER_TYPE_SELL_LIMIT): return("sell limit");
      case(ORDER_TYPE_BUY_STOP): return("buy stop");
      case(ORDER_TYPE_SELL_STOP): return("sell stop");
      case(ORDER_TYPE_BUY_STOP_LIMIT): return("buy stop limit");
      case(ORDER_TYPE_SELL_STOP_LIMIT): return("sell stop limit");
     }
   return(str_type);
  }  
//+------------------------------------------------------------------+