English
preview
От клонирования и коммитов к CI: Расширенные возможности MQL5 Algo Forge

От клонирования и коммитов к CI: Расширенные возможности MQL5 Algo Forge

MetaTrader 5 — Трейдинг |
24 1
Shahzaib
Shahzaib

Введение

Существующие материалы по MQL5 Algo Forge охватывают основы: создание репозитория и коммитов из MetaEditor, операции push и pull, создание веток. В этой статье рассматриваются следующие шаги. Я последовательно изучил возможности платформы и проверил каждую на действующем экземпляре сервиса: Algo Forge построен на Forgejo, но возможности из документации Forgejo не всегда включены в этом экземпляре.

Далее — практический обзор возможностей, важных для разработки на MQL5, включая три дополнения MetaQuotes для этого сообщества и несколько ограничений, о которых стоит узнать заранее, чтобы не тратить время впустую. Для возможностей, требующих настройки, например пайплайна CI, который компилирует файлы .mq5 и помечает сборку как неуспешную при обнаружении ошибок, я привожу последовательность действий и вывод.


Что такое Algo Forge на самом деле

Algo Forge работает на базе Forgejo — поддерживаемого сообществом форка Gitea — с аутентификацией через MQL5.com. Это помогает заранее понять, как работает большинство его механизмов. Эндпоинт версии возвращает ответ без входа в систему:

GET https://forge.mql5.io/api/v1/version

{"version":"16.0.5+gitea-1.22.0"}

Листинг 1. Эндпоинт версии

Две части этой строки сообщают разную информацию. 16.0.5 — версия Forgejo; Gitea нумерует собственные версии в формате 1.x и никогда не достигала версии 16. Суффикс +gitea-1.22.0 — метаданные сборки, которые Forgejo добавляет для указания совместимой версии API Gitea. Благодаря этому инструменты, написанные для Gitea, продолжают работать. Маршрут /api/forgejo/v1/version, доступный только в Forgejo, возвращает ту же строку, а на обычном сервере Gitea такого маршрута нет.

Поскольку это действующий сервис, даты имеют значение. Я проверил перечисленные ниже возможности в августе 2026 года, когда экземпляр сервиса сообщал версию Forgejo 16.0.1, а 12 октября 2026 года повторно проверил эндпоинты настроек и приведенные в статье ограничения; тогда сервис сообщал версию 16.0.5. Все описанные здесь состояния относятся к указанным датам.

Forgejo унаследовал от Gitea ревью запросов изменений (pull request), защиту веток, реестр пакетов, веб-хуки, LFS и CI. Унаследованный код определяет возможности платформы, но не то, что включено и как настроено в конкретном экземпляре сервиса. Поэтому я проверил каждую возможность, а не ограничился ссылкой на документацию Forgejo. Спецификация API общедоступна по адресу https://forge.mql5.io/swagger.v1.json и описывает 326 путей и 506 операций.

В таблице 1 показано, какие возможности включены, причем последний столбец так же важен, как и средний. "Работает" означает, что я проверил полный цикл работы функции. "Включено" означает, что эндпоинт настроек или страница настройки подтверждает включение функции, но весь ее жизненный цикл я не проверял. Три дополнения относятся непосредственно к MQL5: ссылка Open with MetaEditor и переводчик README, которые рассматриваются далее, а также шаблон gitignore для MetaTrader 5, описанный ближе к концу статьи.

Возможность
Состояние в Algo Forge
Как я проверял
Actions (CI/CD)
Работает
Выполнил реальные сборки на раннере
Реестр пакетов
Работает
Загрузил пакет generic
Веб-хуки
Работает
Создал веб-хук и проверил доставку уведомлений
Защита веток и тегов
Включено
Конструктор правил, 20+ полей
Git LFS
Включено
lfs_disabled: false
Зеркала push и pull
Включено
mirrors_disabled: false, настройки доступны
Миграция репозитория
Включено Страница миграции, десять источников
Доступ по SSH
Отключено
В панели клонирования доступен только HTTPS
Поиск кода по всей платформе
Отключено
/explore/code выполняет перенаправление
Поиск кода в одном репозитории
Работает
Возвращаются совпадения на уровне отдельных строчек кода
Федерация ActivityPub
Отключено
Эндпоинты спецификации возвращают код 404

Таблица 1. Возможности, проверенные на действующем экземпляре сервиса, август 2026 года

Репозитории могут быть публичными или приватными; видимость выбирается в форме создания. Приватный репозиторий виден только вам и добавленным соавторам, а описанный ниже пайплайн CI выполняет сборку без дополнительных учетных данных.


Одна ссылка, открывающая репозиторий в MetaEditor

Откройте любой репозиторий и нажмите кнопку с тремя точками рядом с URL-адресом для клонирования. Рядом с привычными вариантами загрузки ZIP и TAR.GZ находятся два менее обычных пункта. Download BUNDLE позволяет получить весь репозиторий вместе с историей в одном файле, из которого можно клонировать репозиторий без подключения к сети. Open with MetaEditor — дополнение MetaQuotes.

Algo Forge clone panel with the Open with MetaEditor entry expanded

Рис. 1. Выпадающее меню клонирования с обработчиком MetaEditor

Ссылка этого пункта использует специальную схему URL, зарегистрированную установщиком MetaTrader 5:

metaeditor5://checkout?repo=https%3A%2F%2Fforge.mql5.io%2Fmql5dev%2Fmql5.git

Листинг 2. Обработчик протокола MetaEditor

При нажатии на нее запускается MetaEditor и начинается получение рабочей копии указанного репозитория. Поскольку обработчик протокола работает из любого HTML-документа, отображаемого браузером, ссылка не привязана к Algo Forge. Ее можно разместить в README, сообщении на форуме или на своем сайте, и читатель с установленным MetaTrader 5 одним щелчком перейдет от вашего текста к клонированному репозиторию. Чтобы создать такую ссылку, примените процентное кодирование к URL для клонирования и добавьте результат к metaeditor5://checkout?repo=.

Примечание: обработчик срабатывает только на компьютерах, где установлен MetaTrader 5, а браузеры запрашивают подтверждение перед запуском внешнего приложения. Для остальных пользователей оставьте рядом обычный URL для клонирования по HTTPS. Такая ссылка переносима в двух отношениях: ее можно разместить на любой странице и направить на любой репозиторий. Нажатие лишь запускает получение рабочей копии, которое вы подтверждаете. Но относитесь к полученному коду как к любому коду из интернета: изучите его перед компиляцией или запуском.


README с автоматическим переводом на пятнадцать языков

Второе дополнение MetaQuotes находится на панели заголовка любого отображаемого Markdown-файла: элемент управления Translate с кодом текущего языка.

README header translate control with the language list open

Рис. 2. Элемент управления Translate для README

Он предлагает пятнадцать языков — тот же набор, который используется для статей MQL5.com, — и заново отображает документ на той же странице. В стандартной версии Forgejo ничего подобного нет, и это меняет подход к созданию README: вместо поддержки отдельной копии для каждого языка можно написать один текст на языке, которым вы владеете лучше всего, и предложить читателям машинный перевод. Машинный перевод не равнозначен проверенному, поэтому он снижает языковой барьер для читателя, а не заменяет переводчика.

Таким образом, приоритет переносится со стиля на структуру. Машинный перевод хорошо справляется с короткими утвердительными предложениями, понятными заголовками и короткими абзацами, но плохо — с текстом, насыщенным идиомами, и длинными предложениями со вложенными придаточными. Файл README, разбитый на короткие разделы с содержательными заголовками, читается на японском гораздо лучше, чем сплошной текст в разговорном стиле. При переводе технического текста больше всего страдают идентификаторы, имена файлов, код и таблицы. Поэтому переключитесь на язык, который понимаете, и проверьте, как в переводе README отображаются блоки кода и таблицы, прежде чем на него полагаться.


Units — вкладка управления остальными возможностями репозитория

Вкладка Units в настройках репозитория выглядит как список переключателей "вкл./выкл.", поэтому пользователи ее пропускают. На самом деле она определяет, как будут работать задачи, запросы изменений и вики на протяжении всего срока существования проекта. Для повседневного сопровождения проекта наиболее важны четыре настройки.

Repository Units settings tab, showing the Overview switches and the issue tracker options

Рис. 3. Вкладка Units — Overview и Issues

Политика слияния. Для каждого из шести способов слияния предусмотрен свой флажок; отдельно задается способ по умолчанию. Для библиотеки с линейной историей оставьте включенным только fast-forward, и платформа не допустит непреднамеренного коммита слияния. Соседний переключатель включает удаление исходной ветки после слияния, чтобы ненужные ветки разработки не накапливались.

Учет времени и зависимости между задачами. Обе эти функции уже были включены в репозиториях, которые я проверил. Учет времени добавляет секундомер к каждой задаче. Зависимости позволяют пометить одну задачу как заблокированную другой, поэтому информация вроде "индикатор ждет рефакторинга буфера" фиксируется в системе, а не теряется в комментарии.

Внешний трекер и вики. Для вкладки Issues и вики можно задать внешние URL, настраиваемый формат и регулярное выражение для распознавания ссылок на задачи в сообщениях коммитов. Это важно, когда в Algo Forge хранится зеркало проекта, основной репозиторий которого расположен в другом месте.

Закрытие задач из любой ветки. В исходной настройке коммит закрывает задачу только в ветке по умолчанию. Один переключатель распространяет это действие на все ветки.


Защита веток и тегов

В Settings → Branches находится конструктор правил с более чем двадцатью полями для каждого правила.

Branch protection rule builder showing the branch and file pattern fields and the push restrictions

Рис. 4. Защита веток — шаблоны и ограничения отправки изменений

Branch protection rule builder continued, showing approvals, status check contexts and merge blocking

Рис. 5. Защита веток — одобрения, проверки статуса и блокировка слияния

Все обычные ограничения для ревью и слияния здесь есть. Два поля дают дополнительные возможности, хорошо подходящие для структуры проектов MQL5.

Поле Protected file patterns принимает список glob-шаблонов; прямая отправка изменений в защищенную ветку отклоняется, если затронут файл, соответствующий одному из шаблонов. В репозитории с каталогом Include/ в корне шаблон Include/** означает, что заголовочные файлы в этой ветке меняются только через запрос изменений, а изменения скриптов и советников можно отправлять напрямую. Поле Unprotected file patterns задает исключения из этого ограничения.

Шаблон должен соответствовать структуре вашего проекта. Шаблон сравнивается с путем относительно корня репозитория, поэтому для структуры MQL5/Include/ нужен MQL5/Include/**. Пути Git чувствительны к регистру, поэтому include/** не охватывает Include/. Правило распространяется только на ветки, соответствующие его шаблону имен веток, а список разрешенных пользователей в той же форме определяет, кто вообще может отправлять изменения. Тестовая отправка изменений, затрагивающая заголовочный файл, подтверждает, что правило работает так, как задумано; эта проверка включена в контрольный список в конце статьи.

Поле Status check contexts задает имена проверок CI, которые должны завершиться успешно, прежде чем станет доступна кнопка слияния. Это имена, которые сообщает ваша система CI; их настройка описана ниже, в разделе о CI.

Для тегов есть отдельная страница Settings → Tags с шаблоном имени и списком разрешенных пользователей. Если вы выпускаете версии с помощью тегов, стоит потратить минуту на защиту v*, чтобы только вы могли создавать теги релизов.


Запросы изменений (pull request) и механизм их ревью

Когда проект становится слишком большим, чтобы держать его в голове, механизм ревью начинает выявлять ваши собственные ошибки, даже если вы работаете в одиночку.

Ревью структурированы. Ревью может быть одобрением, запросом на внесение изменений или обычным комментарием; комментарии можно привязать к отдельным строчкам сравнения версий. Одобрение впоследствии может быть снято, если после его выдачи ветка изменилась.

Есть шесть способов выполнить слияние. Коммит слияния, rebase, rebase с коммитом слияния, squash, только fast-forward и пометка о ручном слиянии запроса изменений. Squash подходит для большинства задач разработки библиотек: четырнадцать коммитов с сообщениями "fix" и "fix again" попадают в main одним коммитом с осмысленным сообщением.

Отставшие ветки можно обновить через интерфейс с помощью слияния или rebase. В сочетании с настройкой защиты, запрещающей слияние отставшей ветки, это гарантирует проверку запроса изменений относительно актуального состояния main.

Еще две небольшие настройки оправдывают себя. Настройка Ignore whitespace for conflicts не позволяет считать изменение только отступов конфликтом слияния, что важно, поскольку MetaEditor активно переформатирует код. Настройка Allow edits from maintainers позволяет владельцу проекта отправить небольшое исправление в ветку участника вместо очередного обмена комментариями. Для форков sync fork получает изменения из исходного репозитория в вашу копию по отдельным веткам, без ручного добавления второго удаленного репозитория.


Инструменты в браузере и чтение кода

Над списком файлов расположены кнопки трех инструментов, у которых нет аналогов во встроенных средствах работы с Git в MetaEditor. Find a file выполняет нечеткий поиск по всем путям в ветке, что гораздо быстрее переходов по глубоко вложенному дереву Include. Поиск выполняется только по путям; поиск по содержимому файлов рассматривается в последнем разделе. Add file создает или загружает файл без локального клонирования репозитория. Apply patch, доступный по пути /_diffpatch/ в любой ветке, принимает вставленный diff в унифицированном формате и создает коммит.

The apply patch page, where a unified diff becomes a commit

Рис. 6. Применение патча из браузера

Форма применения патча предлагает те же настройки, что и веб-редактор: создать коммит напрямую в текущей или в новой ветке, добавить sign-off и выбрать адрес электронной почты автора. Исправление из трех строчек, опубликованное в виде diff в теме форума, можно превратить в коммит, не создавая учетную запись и не открывая запрос изменений.

Откройте любой файл .mq5: в заголовке над кодом будет указан язык MQL5, а код получит подсветку синтаксиса.

 [An MQL5 source file rendered with syntax highlighting and file actions]

Рис. 7. Файл .mq5, распознанный как код MQL5

Настройка не требуется: даже в репозитории без файла .gitattributes язык MQL5 определялся корректно. Файл .gitattributes все же стоит добавить для настройки окончаний строк и обозначения бинарных файлов. Обозначение файлов *.ex5 как бинарных не позволяет Git строить для них diff и преобразовывать окончания строк, а * text=auto помогает поддерживать согласованное состояние репозитория при клонировании в Windows и Linux.

Если вы храните файлы .set тестера в системе контроля версий, с ними нужен противоположный подход. MetaTrader 5 сохраняет их в UTF-16, и Git считает такие файлы бинарными, поэтому изменение входного параметра отображается как изменение blob без diff. Строчка *.set text working-tree-encoding=UTF-16LE-BOM eol=crlf задает хранение этих файлов в Git в UTF-8 и их запись в рабочую копию в UTF-16. Для файла .set, сохраненного терминалом, коммит показывал количество строчек, git grep находил параметры, а заново полученная рабочая копия совпадала с оригиналом байт в байт.

На той же панели заголовка есть Permalink: он привязывает URL к конкретному коммиту, поэтому ссылка на код в сообщении на форуме продолжает указывать на тот же код после отправки изменений. Blame показывает для каждой строчки последний изменивший ее коммит; в торговой библиотеке это помогает понять, когда изменилось пороговое значение и что изменилось вместе с ним.

Стоит знать еще о трех возможностях. Вкладка Activity показывает сводку по репозиторию за период от одного дня до года — это быстрый способ оценить, поддерживается ли библиотека, которую вы собираетесь использовать как зависимость. Вики — отдельный Git-репозиторий со своим URL для клонирования, поэтому документацию можно писать локально и отправлять в репозиторий; содержимое страниц _Sidebar и _Footer отображается на каждой странице. Кроме того, новый репозиторий можно создать на основе шаблона, отдельно выбрав копирование содержимого, веб-хуков, тем, меток, защиты веток и Git-хуков.


Релизы и тип файла, который нельзя прикрепить

Форма создания релиза поддерживает черновики, предварительные релизы, скрытие автоматически создаваемых архивов исходного кода и использование описания релиза в качестве сообщения тега.

The new release form with draft, pre-release and external asset options

Рис. 8. Создание релиза

Сложность возникает именно с вложениями. Эндпоинт /api/v1/settings/attachment, доступный без входа в систему, перечисляет разрешенные расширения файлов: в основном это документы, изображения, архивы и форматы данных; .ex5, .mq5, .mqh и .set среди них нет. В том же ответе max_files равно 5, а max_size — 2048 (МБ), поэтому к релизу можно прикрепить не более пяти файлов. Поэтому для прикрепления скомпилированного советника непосредственно к тегу версии нужен дополнительный шаг.

Есть два способа обойти эту проблему. Упакуйте скомпилированные файлы в разрешенный архив .zip или используйте Add external asset в той же форме, чтобы добавить ссылку для скачивания по выбранному вами URL. Такая ссылка может вести в реестр пакетов.


Реестр пакетов

Пакеты принадлежат учетной записи, а не репозиторию. У каждой учетной записи есть раздел пакетов по адресу /{user}/-/packages, а в API пакеты адресуются по владельцу, как показано в листинге 3. На вкладке Packages репозитория перечислены связанные с ним пакеты; связь задается на странице настроек самого пакета и относится ко всему пакету, а не к отдельной версии. Я загрузил пакет generic, чтобы убедиться, что реестр действительно работает, а не только упоминается в спецификации. Общего каталога пакетов по всей платформе нет: /explore/packages не существует, поэтому нужно знать, в чьей учетной записи искать.

Среди поддерживаемых типов пакетов для MQL5 важен именно реестр generic. Он принимает любые файлы без ограничения по списку разрешенных расширений; обращение к ним выполняется по имени пакета и версии:

PUT https://forge.mql5.io/api/packages/{owner}/generic/{package}/{version}/{filename}

curl -H "Authorization: token YOUR_TOKEN" \
     --upload-file MyExpert.ex5 \
     https://forge.mql5.io/api/packages/yourname/generic/myexpert/1.4.0/MyExpert.ex5

Листинг 3. Загрузка скомпилированного бинарного файла в реестр generic

Это персональный токен доступа с областью действия write:package; его создание описано в разделе о REST API. Скомпилированные бинарные файлы размещаются в реестре generic с указанием версии, а в релиз добавляются ссылки на них как на внешние ресурсы.

Будет ли ссылка доступна другим пользователям, зависит от владельца пакета, а не от репозитория. В Forgejo пакеты пользователя доступны для чтения всем при публичной учетной записи и только самому пользователю в противном случае. Доступность пакетов организации так же определяется видимостью организации. Перед публикацией ссылки откройте ее в браузере без входа в систему.

Правила очистки пакетов в настройках учетной записи удаляют старые версии по количеству, возрасту или шаблону версии. Каждое правило применяется к одному типу пакетов, поэтому публикация сборки для каждого тега не приводит к многолетнему накоплению старых артефактов в реестре. Кроме того, они удаляют файлы, на которые ссылаются старые релизы. Если релизы ссылаются на файлы реестра, задайте в правиле шаблон сохранения, соответствующий версиям релизов, например ^\d+\.\d+\.\d+$, чтобы удалялись только остальные версии.


Непрерывная интеграция для компиляции кода MQL5

В репозиториях Algo Forge есть вкладка Actions, и она работает. Ниже приведен созданный и проверенный мной пайплайн, который компилирует указанный файл .mq5 и помечает сборку как неуспешную при ошибках. Я также описываю необходимые настройки и две особенности командной строки MetaEditor, которые нужно учесть.

Выбор раннера

Для официального раннера Forgejo публикуются только сборки Linux: каждый файл релиза предназначен для linux-amd64 или linux-arm64. MetaEditor — программа для Windows, поэтому такой раннер не может выполнять сборку кода MQL5. У act_runner от Gitea есть исполняемый файл для Windows — один файл без установщика. Он регистрируется в Forgejo, поскольку раннер Forgejo является его форком; более новые релизы публикуются под именем gitea-runner. Эта совместимость обусловлена общим происхождением, а не обязательствами какого-либо из проектов, поэтому считайте ее проверенной, но не гарантированной на будущее. Все описанное ниже выполнялось на Forgejo 16.0.1 с act_runner 0.3.0; если более новый раннер не проходит начальное согласование с сервером, прежде всего проверьте сочетание версий.

Регистрация

По умолчанию метки раннера указывают на Docker, а в таком контейнере MetaEditor отсутствует. Регистрация с суффиксом :host позволяет запускать задания непосредственно на компьютере, где уже установлен MetaTrader 5. Получите регистрационный токен в Settings → Actions → Runners, нажав Show registration token. Это не персональный токен доступа для реестра пакетов: у него короткий срок действия, и он служит только для подключения раннера. Выполните эту команду в Command Prompt, где символ ^ обозначает продолжение команды на следующей строчке:

act_runner register --no-interactive ^
  --instance https://forge.mql5.io ^
  --token YOUR_REGISTRATION_TOKEN ^
  --name mt5-windows-host ^
  --labels "self-hosted:host,windows-mt5:host"

act_runner daemon

Листинг 4. Регистрация раннера на собственном хосте

Repository runners page listing a registered self-hosted runner with its labels

Рис. 9. Зарегистрированный раннер

Та же страница доступна на уровне учетной записи по адресу /user/settings/actions/runners; в этом случае один раннер обслуживает все ваши репозитории.

Рабочий процесс

Algo Forge сканирует каталог .forgejo/workflows/ в поисках YAML-файлов. В демонстрационном репозитории каталоги Scripts/ и .forgejo/workflows/ находятся в корне, поэтому все приведенные ниже пути отсчитываются от корня репозитория, а не от каталога данных терминала. Рабочий процесс получает рабочую копию, компилирует код через командную строку MetaEditor, декодирует лог и определяет, успешно ли прошла сборка:

name: MQL5 compile

on:
  push:
  workflow_dispatch:

jobs:
  compile:
    runs-on: windows-mt5
    steps:
      - name: Checkout
        uses: https://code.forgejo.org/actions/checkout@v4

      - name: Compile with MetaEditor
        shell: powershell
        run: |
          $me = "C:\Program Files\MetaTrader 5\metaeditor64.exe"
          if (-not (Test-Path $me)) { Write-Host "::error::MetaEditor not found at $me"; exit 1 }
          $src = Join-Path $PWD "Scripts\HelloForge.mq5"
          $log = Join-Path $PWD "compile.log"
          Remove-Item $log -ErrorAction SilentlyContinue
          & $me /compile:"$src" /log:"$log" | Out-Null
          Write-Host "metaeditor exit code = $LASTEXITCODE   <- not a pass/fail signal, see below"
          if (-not (Test-Path $log)) { Write-Host "::error::no log file produced"; exit 1 }
          $lines = Get-Content $log -Encoding Unicode
          Write-Host "----- compile log (UTF-16, decoded) -----"
          $lines | ForEach-Object { Write-Host $_ }
          Write-Host "----------------------------------------"
          $result = $lines | Where-Object { $_ -match 'Result:' } | Select-Object -Last 1
          if (-not $result) { Write-Host "::error::no Result line in log"; exit 1 }
          if ($result -match '(\d+)\s+error') { $errs = [int]$Matches[1] } else { $errs = 0 }
          if ($errs -gt 0) { Write-Host "::error::compile failed with $errs error(s)"; exit 1 }
          Write-Host "compile clean -> $result"
          Get-ChildItem -Recurse -Filter *.ex5 | ForEach-Object { Write-Host "artifact: $($_.Name)  $($_.Length) bytes" }
          exit 0

Листинг 5. .forgejo/workflows/compile.yml

В строчке uses: указан полный URL, а не сокращенная запись actions/checkout@v4, поскольку в этом экземпляре сервиса не задан псевдоним реестра действий по умолчанию. Действие checkout записывает временные учетные данные в локальную конфигурацию Git и благодаря этому клонирует приватный репозиторий без указания вашего токена в файле сценария. Перед компиляцией лог удаляется, чтобы лог предыдущего запуска не мог быть принят за результат новой сборки.

Две строчки зависят от моего компьютера; у себя их стоит изменить. Путь к MetaEditor соответствует установке по умолчанию; если терминалов несколько или используется портативная установка, укажите в $me путь к metaeditor64.exe того терминала, с заголовочными файлами которого выполняется сборка. А @v4 — тег, который владелец контроля над репозиторием может переназначить на другой код. На раннере в режиме host действие выполняется с вашими правами, поэтому после настройки работающего пайплайна замените @v4 полным SHA коммита, на который тег сейчас указывает.

Компилируемый пример намеренно прост, чтобы сосредоточиться на пайплайне, а не на коде:

//+------------------------------------------------------------------+
//|                                                   HelloForge.mq5 |
//|                                         Copyright 2026, ShahZaib |
//+------------------------------------------------------------------+
#property script_show_inputs

input int InpBars = 10;   // how many bars to report

//+------------------------------------------------------------------+
//| Script entry point: prints a one-line proof of compilation.      |
//+------------------------------------------------------------------+
void OnStart()
  {
   Print("HelloForge on ", _Symbol, ", reporting ", InpBars, " bars");
  }
//+------------------------------------------------------------------+

Листинг 6. Scripts/HelloForge.mq5

Чтение результата компиляции

Компилятор MetaEditor, запускаемый из командной строки, возвращает количество успешно скомпилированных файлов, а не код ошибки. В сборке 6230 MetaEditor компиляция файла без ошибок возвращала 1, того же файла с ошибкой — 0, а каталога из пяти файлов с одной ошибкой — 4. CI интерпретирует коды завершения противоположным образом. Раннер добавляет проверку кода завершения в конец каждого шага PowerShell, поэтому 1 после успешной компиляции, сохраняющаяся в $LASTEXITCODE к завершению шага, приводит к неуспешному завершению задания. Зеленый лог успешной компиляции внутри красного неуспешного задания направляет поиск причины не туда. Поэтому рабочий процесс сначала выводит код завершения, но не использует его как итоговый критерий: он находит в логе строку "Result:", самостоятельно определяет успех сборки и завершает работу явной командой "exit 0".

Job log showing MetaEditor returning exit code 1 after a clean compile

Рис. 10. Код завершения 1 после компиляции без ошибок

Сам лог записан в UTF-16. Если прочитать его как UTF-8, получится непригодный текст, поэтому скрипт использует -Encoding Unicode. После учета обеих особенностей компиляция заняла 795 мс и создала файл .ex5 размером 6 038 байт; все задание, включая получение рабочей копии, завершилось за 24 секунды.

Compile result and generated ex5 artifact in the job log

Рис. 11. Строка результата и скомпилированный артефакт

Система сборки, которая не может завершиться с ошибкой, — лишь украшение, поэтому я также отправил версию с необъявленным идентификатором:

Scripts\HelloForge.mq5(14,28) : error 256: undeclared identifier '_Symbol_TYPO_UNDECLARED'
Result: 1 errors, 0 warnings
::error::compile failed with 1 error(s)

Листинг 7. Сценарий ошибки

От одного файла к проекту

Код из листинга 5 компилирует один указанный файл: это подтверждает работоспособность пайплайна, но не обеспечивает сборку проекта. Параметр /compile: в MetaEditor также принимает каталог: в этом случае компилируются все файлы .mq5 в нем и его подкаталогах, а для каждого файла выводится отдельная строка "Result:". Это меняет логику разбора. Select-Object -Last 1 считывает только строку результата последнего файла, поэтому ошибка в любом другом файле останется незамеченной. Вместо этого суммируйте количество ошибок по всем строкам "Result:" либо сравнивайте код завершения с количеством файлов .mq5, которые должны были быть собраны.

Разбор также зависит от текста лога — метки "Result:" и слова error в том виде, в каком их выводит сборка 6230. При отсутствии строки "Result:" задание останавливается — это безопасный вариант поведения. Изменение формата записи количества ошибок уже небезопасно: регулярное выражение ничего не найдет, и на этом шаге будет получено 0 ошибок. Описанное выше сравнение кода завершения дает дополнительный признак, не зависящий от текста лога.

Наконец, рабочий процесс лишь выводит список созданных файлов .ex5. Они остаются в рабочей папке раннера, где следующий запуск может их перезаписать. Для сохранения сборки добавьте шаг загрузки с вызовом из листинга 3 и чтением токена из секрета репозитория. Тогда каждая сборка по тегу будет попадать в реестр пакетов.

Подключаемые файлы

В файле HelloForge.mq5 нет директив #include. Если исходный код находится в папке рабочей копии, а не в каталоге данных терминала, компилятору неоткуда определить базовый путь для поиска Trade\Trade.mqh или собственных заголовочных файлов проекта. Аргумент /inc: в MetaEditor указывает компилятору корневой каталог MQL5 для поиска: задайте в $inc путь к каталогу MQL5 терминала с нужными заголовочными файлами и измените строчку компиляции на & $me /compile:"$src" /inc:"$inc" /log:"$log". Отсутствие заголовочного файла вызывает ошибку компиляции, поэтому пайплайн сообщит, если путь указан неверно.

Для секретов и переменных есть отдельные вкладки рядом с Runners, поэтому учетные данные для развертывания не попадают в YAML. Укажите имя этого задания в Status check contexts правила защиты ветки, и запрос изменений, приводящий к ошибке компиляции, нельзя будет слить.

Важно: раннер на собственном хосте в режиме host выполняет все команды рабочего процесса непосредственно на вашем компьютере с правами вашей учетной записи. Для ваших собственных репозиториев это вполне нормально. Тщательно подумайте, прежде чем включать такой режим для репозитория, принимающего запросы изменений от посторонних пользователей: рабочий процесс — это код, который тоже можно изменить через запрос изменений. Рабочий процесс из листинга 5 запускается только по событиям push и workflow_dispatch, поэтому выполняет код, отправленный пользователем с правом записи. Добавление триггера pull_request позволяет коду участника, включая измененный рабочий процесс, попасть на ваш компьютер; секреты репозитория — именно то, что такой рабочий процесс попытается вывести. Не включайте этот триггер для раннера в режиме host.


Веб-хуки

В разделе Settings → Webhooks доступно тринадцать типов веб-хуков. Большинство из них форматируют сообщения для чатов, например Slack, Discord, Telegram и Microsoft Teams, подготавливая их для соответствующего сервиса. Если получатель — ваш собственный код, выберите один из трех универсальных типов — Forgejo, Gitea или Gogs, которые отправляют обычный JSON на любой эндпоинт. Выбирайте Forgejo, если получатель еще не настроен на один из других вариантов.

Webhook creation form continued, showing the pull request and Action Run events, the branch filter and the authorization header

Рис. 12. События Action Run, фильтр веток и заголовок авторизации

Из 24 событий три связывают веб-хуки с CI: action_failure, action_success и action_recover срабатывают по результату запуска, поэтому веб-хук на action_failure сразу сообщит об ошибке компиляции. Фильтр веток (branch filter) ограничивает веб-хук ветками, соответствующими фильтру, а Authorization header задает передачу учетных данных с каждым запросом, поэтому эндпоинту с аутентификацией не нужен токен в URL.

Каждая доставка уведомления регистрируется. Откройте сохраненный веб-хук и разверните запись в разделе Recent deliveries.

Webhook delivery record showing request headers and a 200 response

Рис. 13. Зарегистрированная доставка уведомления

Каждое уведомление содержит заголовки с префиксами X-Forgejo-, X-GitHub-, X-Gitea- и X-Gogs-, а также X-Hub-Signature и X-Hub-Signature-256. Однако совпадение имен заголовков само по себе еще не делает его веб-хуком GitHub. Тело запроса соответствует схеме Gitea, похожей на схему GitHub, но отличающейся именами событий и полями. Поэтому, прежде чем полагаться на программу-получатель, написанную для GitHub, проверьте разбор реального уведомления. Данные уведомления сохраняются целиком, а кнопка Test delivery и элемент повторной доставки позволяют разрабатывать получатель на реальных данных без отправки лишних коммитов.


Токены доступа и REST API

В этой статье два типа учетных данных называются токенами. Регистрационный токен из раздела CI служит только для подключения раннера. Персональный токен доступа из Settings → Applications используется для загрузки пакетов и в скриптах, работающих с API. Веб-хукам не нужен ни один из этих токенов; они используют заданный вами секрет.

Access token and OAuth2 application management page

Рис. 14. Токены доступа и приложения OAuth2

Помимо областей действия разрешений, токен можно ограничить конкретными репозиториями, чтобы токен скрипта публикации не мог затронуть остальные ваши ресурсы. На той же странице регистрируются приложения OAuth2 для инструментов, позволяющих пользователям входить через учетную запись Algo Forge. Данные для аутентификации передаются в заголовке:

Authorization: token YOUR_TOKEN

Листинг 8. Аутентификация в API

Эндпоинт /api/v1/settings/api, доступный без входа в систему, сообщает ограничения, которые нужно учитывать при разработке: max_response_items — 250, default_paging_num — 30, default_git_trees_per_page — 1000 и default_max_blob_size — 10485760, то есть 10 МБ. Это общесерверные значения по умолчанию для эндпоинтов списков с постраничной выдачей, а не гарантия для каждого эндпоинта. Поэтому читайте заголовки X-Total-Count и Link в ответе со списком, а не полагайтесь на предполагаемый размер страницы. Скрипт, игнорирующий постраничную выдачу, без предупреждения обрабатывает только первые 30 элементов.

Одна особенность API содержимого: для создания файла используется POST, а для обновления — PUT с SHA текущего blob-объекта. PUT-запрос для нового файла возвращает код 422 с сообщением [SHA]: Required, что больше похоже на сообщение о некорректном теле запроса, чем о неверном HTTP-методе.

Со стороны MetaTrader эндпоинт релизов публичного репозитория доступен без аутентификации, поэтому советник может проверить наличие своей более новой версии:

GET https://forge.mql5.io/api/v1/repos/{owner}/{repo}/releases/latest

Листинг 9. Последний релиз, аутентификация не требуется

Этот вызов выполняется через WebRequest. Как поясняется в документации, сначала нужно добавить сервер в список разрешенных URL на вкладке "Советники" окна "Настройки". Вызов синхронный и доступен в советниках и скриптах, но не в индикаторах и Тестере стратегий. Однократная проверка версии после запуска соответствует этим ограничениям, а проверка на каждом тике — нет. Сравните поле tag_name в ответе с версией, встроенной в программу при компиляции, а неудавшийся запрос расценивайте как отсутствие новых сведений, а не как ошибку.


Большие файлы и зеркала

Git LFS включен; на странице настроек перечислены отслеживаемые файлы и блокировки, а также доступно средство поиска файлов-указателей, некорректно включенных в коммит. При работе с машинным обучением в MQL5 модели ONNX и обучающие наборы данных следует хранить в LFS, а не в обычной истории Git. LFS тоже хранит все версии, но на сервере: при клонировании загружаются только версии для рабочей копии, тогда как при обычном хранении каждая версия файла размером 200 МБ попадает в каждый клон. Я не нашел опубликованной квоты хранилища LFS для этого экземпляра сервиса, поэтому перед добавлением большого набора данных в коммит проверяйте объем на странице настроек LFS.

Зеркала push настраиваются в основных настройках репозитория и отправляют изменения в удаленный репозиторий, например на GitHub, с заданным интервалом или при каждом коммите; при необходимости применяется фильтр веток. Зеркала pull поддерживают синхронизацию репозитория Algo Forge с внешним источником. Кроме того, /repo/migrate импортирует существующий репозиторий с GitHub, GitLab и других хостингов либо по любому URL для клонирования через HTTPS, поэтому проект MQL5 с уже имеющейся историей не нужно начинать с нуля.


Шаблон .gitignore для MetaTrader 5 и то, как Git его читает

На странице создания репозитория доступен встроенный шаблон .gitignore для MetaTrader 5 — третье дополнение, предназначенное специально для MQL5. Он охватывает нужные категории: скомпилированные бинарные файлы, логи терминала, артефакты Тестера стратегий, шаблоны графиков и временные файлы MetaEditor — ничему из этого не место в Git.

Прежде чем полагаться на шаблон, нужно знать одно общее правило синтаксиса gitignore в Git, не связанное с особенностями этого конкретного файла. В нескольких строчках файла после шаблона пути приведено пояснение:

# MetaTrader 5 and MQL5 gitignore template

# Compiled MQL5 executables (binaries)
*.ex5
*.ex4 # For MQL4 compatibility if you also manage MT4 projects in a similar structure

# Log files
*.log
*.slog # Strategy Tester logs

# Strategy Tester specific files
*.fxt  # FXT files (history data for testing)
*.csv  # CSV export files (e.g., from tester reports)

# Market Watch sets and profiles
*.set  # Market Watch symbol sets

# MetaEditor project files
.mqproj

Листинг 10. Фрагмент поставляемого шаблона

Выделенный текст выглядит как комментарии, и в большинстве форматов конфигурации так бы и было. В файле gitignore символ # начинает комментарий только в начале строчки, как указано в документации Git: "A line starting with # serves as a comment". В любом другом месте он является частью шаблона. Таким образом, *.set # Market Watch symbol sets — единый шаблон, который ищет имена с буквальным текстом пояснения. На диске нет файлов с таким именем, поэтому строчка не действует, а Git не выдает предупреждения, поскольку шаблон допустим.

Я проверил полный шаблон в пустом репозитории, создав по одному файлу с реалистичным именем для каждого указанного в нем типа. Из 26 шаблонов только пять дали совпадения: *.ex5, *.log, *.dll, *.mqh.bak и *.mq5.bak. Все строчки с пояснением в конце, в том числе *.csv, *.set, *.fxt и .env, не дали совпадений. Сначала проверьте файл .env, поскольку именно там хранят пароль брокерского счета или токен Telegram-бота.

Оставшаяся строчка .mqproj не срабатывает по другой причине. Без подстановочного символа шаблон соответствует только файлу с точным именем .mqproj, тогда как MetaEditor называет файлы по имени проекта, например MyExpert.mqproj. Нужно отдельно решить, исключать ли файлы проектов вообще: именно файл проекта позволяет корректно открыть репозиторий как проект на другом компьютере.

В исправленном варианте каждый комментарий вынесен в отдельную строчку, для исключения файлов проектов при необходимости используется *.mqproj, а общие правила *.json, *.ini и *.cfg отсутствуют: в проекте MQL5 такие файлы с равной вероятностью могут содержать как нужную отслеживаемую конфигурацию, так и лишние данные:

# .gitignore for an MQL5 project

# Compiled binaries
*.ex4
*.ex5

# Terminal, tester and custom logs
*.log
*.slog
*.jrn

# Strategy Tester history and cache
*.fxt
*.hst
*.dat

# Chart and terminal state
*.tpl
*.chr

# MetaEditor scratch and backups
*.bak
*.~*

# Credentials, never commit these

.env

# MetaEditor project files. Remove this line if you want
# collaborators to open the repository as a project.
*.mqproj

# Tester parameter sets. Remove this line if your .set files
# are inputs you want to version rather than outputs you discard.
*.set

Листинг 11. Рабочая замена

Проверяйте шаблон, а не просто доверяйте ему — в том числе и моему варианту. Команда git check-ignore -v yourfile.set выводит совпавший шаблон и строчку, в которой он задан; если совпадений нет, вывода не будет.

Важно: файл .gitignore действует только на неотслеживаемые файлы. Если репозиторий уже отправлялся на сервер, все ранее добавленное Git находится в истории; обновленный .gitignore предотвращает его включение в будущие коммиты, но не удаляет из истории. Выполните git rm --cached для файлов, которых не должно быть в репозитории, зафиксируйте удаление коммитом и заодно проверьте, не попала ли на сервер конфиденциальная информация.


Два ограничения, которые нужно учитывать при планировании

Две строки таблицы 1 требуют более подробного пояснения: в обоих случаях платформа создает впечатление, не соответствующее действительности.

SSH отключен. В панели клонирования доступен только HTTPS, на странице ключей SSH и GPG нет формы добавления ключа, а на странице ключей развертывания прямо указано, что SSH отключен.

Repository deploy keys page reporting that SSH is disabled

Рис. 15. Страница ключей развертывания с указанием, что SSH отключен

REST API сообщает обратное: каждый ответ с данными репозитория содержит ssh_url с портом 65022. Попытка воспользоваться этим адресом ведет к бесполезной настройке ключей и поиску проблем в межсетевом экране. Выполните клонирование через HTTPS и пройдите аутентификацию с помощью токена доступа.

Поиск кода работает в пределах одного репозитория. В Explore доступен только поиск репозиториев, пользователей и организаций, а /explore/code перенаправляет на поиск репозиториев, поэтому поиск кода по всем репозиториям пользователей недоступен. В публичном репозитории поиск по адресу /{owner}/{repo}/search работает без входа в систему; для поиска в приватном репозитории сначала нужно получить к нему доступ. Поиск поддерживает режимы точного совпадения, объединения и регулярных выражений и возвращает совпадающие строчки с путями к файлам, поэтому запрос OnTick приводит прямо к самой функции.

Особенность в том, что поиск использует git grep, который пропускает файлы, считаемые бинарными, а файл UTF-16 воспринимается им как бинарный. Git считает файл бинарным, если находит байт NUL вблизи его начала, а в UTF-16 каждый символ ASCII хранится рядом с байтом NUL. Эту классификацию можно проверить локально: git diff --numstat для коммита, добавляющего файл UTF-16, выводит - - вместо количества строчек, а git grep не находит идентификатор внутри этого файла. MetaEditor сохраняет файлы .mq5 и .mqh в UTF-8, поэтому современный код не вызывает этой проблемы, но старые исходные файлы и сторонние библиотеки по-прежнему встречаются в UTF-16. В Algo Forge поиск идентификаторов из файлов UTF-16 не дал результатов ни в одном из трех режимов, тогда как такой же поиск в репозиториях с файлами UTF-8 сразу находил совпадения — именно этого и следовало ожидать при такой классификации. В интерфейсе ничто не указывает, с какой из этих ситуаций вы столкнулись. Если в репозитории с унаследованными файлами важен поиск, преобразуйте эти файлы в UTF-8. Изменение отразится в одном diff, зато затем содержимое файлов будет видно в любом сравнении Git.

Картину дополняют два менее значительных ограничения: репозитории используют только SHA-1, без возможности выбрать SHA-256, а эндпоинты федерации ActivityPub, хотя она присутствует в спецификации API, в этом экземпляре сервиса возвращают 404.


Заключение

Algo Forge предоставляет сообществу MQL5 зрелую платформу совместной разработки с привычной аутентификацией, три дополнения для этой аудитории и гораздо больше возможностей, чем описано во вводных материалах. На этой неделе стоит выполнить четыре действия; для каждого есть проверяемый критерий завершения:

  1. Проверьте, что именно исключает ваш файл .gitignore, независимо от того, с какого варианта вы начали. Готово, когда команда git check-ignore -v something.set выводит шаблон, которому соответствует этот файл.
  2. Размещайте ссылку metaeditor5://checkout везде, где делитесь кодом, рядом с обычным URL для клонирования по HTTPS. Готово, когда нажатие на вашу ссылку на компьютере с установленным MetaTrader 5 открывает MetaEditor с запросом на получение рабочей копии.
  3. Добавьте правило защиты ветки для любой важной библиотеки, указав каталог ее заголовочных файлов в Protected file patterns; в используемой здесь структуре это Include/**. Готово, когда прямая отправка изменений в защищенную ветку отклоняется, если затронут заголовочный файл, а то же изменение проходит через запрос изменений.
  4. Если ошибки компиляции отнимают у вас время, зарегистрируйте раннер и поручите платформе компиляцию после каждого push. Готово, когда коммит с намеренной ошибкой делает результат запуска красным, а исправленный коммит — зеленым. Помните, что код завершения MetaEditor равен количеству успешно скомпилированных файлов, поэтому успешная сборка одного файла возвращает 1, а лог записывается в UTF-16.

Все описанное проверено на действующем экземпляре сервиса в указанные в начале даты и в объеме, отраженном в таблице 1. Ниже приведены файлы пайплайна CI: поместите их в репозиторий с подключенным раннером на собственном хосте, настройте путь к MetaEditor, добавьте /inc:, если исходный код подключает другие файлы, и выполните push.

# Файл
Описание
1 HelloForge.mq5
Минимальный скрипт, который компилируется в пайплайне CI. В демонстрационном репозитории он находится по пути Scripts/HelloForge.mq5 относительно корня репозитория.
2 compile.yml
Рабочий процесс Forgejo Actions: получение рабочей копии, компиляция с помощью MetaEditor, декодирование лога UTF-16 и неуспешное завершение при обнаружении ошибок. Разместите его по пути .forgejo/workflows/compile.yml в своем репозитории.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/23991

Прикрепленные файлы |
MQL5.zip (1.69 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Niquel Mendoza
Niquel Mendoza | 15 окт. 2026 в 18:55
Здравствуйте, спасибо за статью, она очень полезна.

У меня вопрос по поводу веб-хуков. Я пользовался ими (мой бот в Telegram отправлял уведомления), но несколько недель назад уведомления перестали приходить. Когда я пытаюсь открыть настройки/хуки в своём репозитории и /user/settings/hooks на уровне учётной записи, я вижу только страницу с надписью «Доступ запрещён»:

изображение


Хотя я являюсь владельцем и авторизовался.
У вас веб-хуки по-прежнему работают? Интересно, не были ли они ограничены после того, как вы тестировали их в августе, или нужно включить какую-то настройку в аккаунте. Спасибо!
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Альтернатива тиковому объему: Измерение рыночной активности по интервалам ожидания между котировками Альтернатива тиковому объему: Измерение рыночной активности по интервалам ожидания между котировками
Тиковый объем в основном отражает время суток, а его окончательное значение известно только после закрытия бара. Мы строим индикатор активности, который обновляется в реальном времени при каждой котировке, используя интервалы ожидания между изменениями котировок, устраняем внутридневной ритм и моделируем оставшуюся составляющую с помощью модели авторегрессионной условной длительности (ACD). Затем мы проверяем, улучшает ли этот показатель активности краткосрочные прогнозы волатильности, если текущая волатильность цены уже учтена в модели.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Построение моделей волатильности в MQL5 (Часть III): Реализация алгоритма SLSQP для оценки моделей Построение моделей волатильности в MQL5 (Часть III): Реализация алгоритма SLSQP для оценки моделей
На языке MQL5 реализован оптимизатор SLSQP для устранения расхождений в параметрах между библиотекой волатильности и модулем ARCH для Python. В статье подробно описаны обработка ограничений, варианты вычисления градиента, настройка и средства управления сходимостью, а также показано, как интегрировать решатель в существующий код. Практические примеры и сравнения демонстрируют совпадение логарифмов правдоподобия и параметров на общих наборах данных.