MQL достаточно (со слов разработчиков, самый) быстрый язык. И в плане производительности и возможностей рассчётов никаких нареканий нет.
Но надо помнить что программы,скрипты/советники/индикаторы исполняются на вполне себе многозадачной платформе, в окружении себе подобных и внутри терминала.
И могут получать совершенно неожиданный "болт" просто на казалось-бы ровном месте. Простейший пример, который все знают - обращение к истории. Технически это "обращение к общему ресурсу". По мере возможности разработчики терминала и библиотек кешируют и аккуратно синхронизируют данные. Всё равно остаётся вероятность что один из вызовов OrdersTotal или OrderSelect исполняется дольше прочих. Эти функции ведь не просто возвращают заранее известную константу, они проверяют чтобы всё было правильно и при необходимости очевидно синхронизуют данные.
Такая-же ситуация и с функциями обращения к чарту. Советник работает в своей отдельной нити, чарт в отдельной. Обращение к функциям рисования или чтения свойств объектов может дать неожиданную задержку. Конечно стоит во первых читать документацию, там указано которые методы вообще "синхронны", то остановят вашу нить. Но и прочие методы тоже могут породить неконтроллируемую задержку
И казалось-бы вовсе безобидные функции IsStopped(), Print(), PrintFormat() могут сбить темп и привести к реквотам,проскальзыванию и пропуску тиков. Они провоцируют переключение контекстов.
Рецепт "лечения" стар, надёжен и уныл : не использовать блокирующие функции в момент когда требуется максимальная скорость. Всё что может быть вычислено заранее, должно быть сделано заранее.
Всё что может быть сохранено и запомнено должно быть запомнено.
внутри OnTick() противопоказано обращение к OrdersTotal(), OrdersHistoryTotal(). Эти функции вообще крайне редко нужны, советник не склеротик, он должен помнить какие ордера открывал,зачем и что из этого вышло.
При инициализации считать требуемое и дальше использовать внутренние структуры. Дополнительно синхронизовать при явном расхождении; Даже обращения к OrderSelect можно минимизировать.
То-же самое про торговое окружение. Единственное что периодично меняться - StopLevel при новостном всплеске. Да и то сначала прыгает спред, а по его прыжкам можно понять что стоит переспросить про стоп-левел. Всё прочее требуемое торговое окружение известно заранее, его нужно запоминать.
CopyRates потенциально может вызвать подкачку и синхронизацию истории, его вызовы надо минимизировать. Меняется только последний бар и при смене бара 2 (прежний закрывается, новый открывается). Не надо на каждом тике копировать 100500 баров.
CopyBuffer потенциально может вызвать целый водопад действий помимо подкачки истории, индикаторные функции дают произвольную задержку. По возможности работа с индикаторами или в таймере или в конце OnTick, после всего прочего.
Вывод, изменение граф.объектов чарта лучше отдавать в не-частый таймер и производить при отсутствии прочей нагрузки. Вообще следовать MVC и подобным: физика отдельно, представление отдельно. Между ними модель.
Печать, журналирование, сохранение результатов всё это делается после критически важного участка. В самом конце OnTick() например, когда всё требуемое обработано и приказы отосланы. Не надо полагаться на оптимизацию и буферизацию Print(), хоть она там и довольно разумна. Всегда вместо непосредственно вывода в Print, пишите во внутренний буфер. И отдавайте в общий журнал в подходящий для вас момент
Избегайте циклов for/while. Если внутрь попадёт (а она обязательно попадёт), "плохая" функция, то штраф будет велик. Логика советников не требует циклов. Основной цикл приложения обеспечен терминалом, и необходимо как можно быстрее возвращать ему управление, а не крутить свои круги.
Попробуйте. Это непривычный для многих подход к организации советника, зато дающий существенный результат.
Простая перестановка функций/реорганизация логики, значительно ускоряет даже тестирование и оптимизацию. А в реале снижает проскальзывания.