mt5-manager/docs/architecture.md

Устройство приложения и API

Документация · Конфигурация · Эксплуатация

Компоненты

Браузер → FastAPI → TerminalManager → процессы MT5 (/portable)
                 → ConfigStore   → личный JSON
                 → MT5_Control   → psutil + чтение SQLite
Каждый MT5 → ServiceStateWriter → общая SQLite в Common/Files
Файл Ответственность
run.py Параметры запуска Uvicorn; --reload включается только явно
main.py Страницы, проверка входных данных, HTTP API и время жизни менеджера
config.py Проверка JSON, разрешение путей и атомарное сохранение
terminal_manager.py Создание, подключение счетов, запуск, остановка и очередь задач
mt5_control.py Поиск процессов по пути EXE и чтение последних данных сервиса
project_updates.py Проверка upstream, fast-forward, сборка и копирование сервиса
ServiceStateWriter.mq5 Регистрация терминала и запись текущего состояния
deploy_service.py Первоначальный сценарий развёртывания; общая функция компиляции
templates/, static/ Jinja2, локальные CSS/SVG и JavaScript без Bootstrap/jQuery

SQLite содержит terminals с уникальным именем папки и сведениями об установке и states с последним снимком счёта/соединения. Сервис работает независимо от графиков, использует WAL, тайм-аут блокировки и UPSERT. История ещё не записывается. Временные метки — Unix-секунды UTC (TimeGMT() на стороне MQL5).

Python определяет наличие процесса через psutil. Если процесс есть и данные не старее 30 секунд, внутренний статус — running; иначе при живом процессе starting, при отсутствии — stopped. Интерфейс дополнительно различает подключённый счёт и отсутствие торгового соединения.

Фоновые операции

Длительная команда возвращает HTTP 202 и объект задачи. Это подтверждает постановку в очередь, а не успех. Клиент запрашивает GET /api/jobs/{id} до done или error. Задачи выполняются одним рабочим потоком; повторная команда для того же экземпляра пока его задача не завершена отклоняется. Глобальные операции проекта не принимаются при других незавершённых задачах и на время своей работы блокируют новые команды терминалам.

{
  "id": "идентификатор",
  "folder": "MetaTrader5.5",
  "action": "create",
  "status": "queued",
  "stage": "Ожидание",
  "warnings": []
}

Состояния: queued, running, done, error. При ошибке добавляется error, у операций проекта — также result при наличии результата. В глобальных задачах folder — пустая строка. Задачи находятся в памяти; старые завершённые записи постепенно удаляются (сохраняется примерно 50 последних). Они не переживают перезапуск менеджера и не являются постоянным журналом.

Маршруты

Метод и путь Назначение
GET / Главная страница
GET /settings Настройки
POST /instances/{name} Текущее состояние указанного экземпляра
POST /start/{name} Запуск и проверка, 202
POST /stop/{name} Остановка, 202
POST /create/{name} Создание с именем в пути, 202; тело AccountInput необязательно
POST /api/terminals Создание, 202; имя можно предложить автоматически
DELETE /api/terminals/{name} Удаление остановленного экземпляра и его папки, 202
PUT /api/terminals/{name}/account Сохранение настроек без запуска и авторизации, 202
POST /api/terminals/{name}/test-connection Явная проверка реквизитов из тела запроса, без их сохранения, 202; без тела — проверка сохранённых
GET /api/jobs/{id} Состояние задачи; неизвестный id — 404
GET /api/settings Общие параметры, записи счетов без паролей, файлы и предлагаемое имя
POST /api/settings/plan Предварительная проверка переноса, список папок/процессов и токен подтверждения
PUT /api/settings Сохранение общих настроек; перенос папок выполняется фоновой задачей с ответом 202
PUT /api/files/{kind} Загрузка исходного файла как необработанного тела запроса
GET /api/project Локальные сведения Git, сборки, различий EX5 и автозапуска без сетевого fetch
POST /api/project/check Проверка удалённой ветки, 202
POST /api/project/update Загрузка через fast-forward, 202
POST /api/project/deploy Принудительная сборка/копирование сервиса, 202

AccountInput содержит name, login, server, password, clear_account. Для создания через /api/terminals к нему добавляются folder и servers_dat; пустое имя выбирается автоматически. Пример структуры запроса без счёта (Base64 в servers_dat нужно заменить содержимым своего файла):

{"folder": "MetaTrader5.5", "name": "Новый экземпляр", "servers_dat": "..."}

PUT /api/settings принимает полный набор редактируемых общих полей: mt5_folder, terminal_template; startup_timeout_sec и disk_warning_mb могут передаваться дополнительно. Пропущенные дополнительные параметры не сбрасываются. Остальная конфигурация сохраняется. Это не универсальный PATCH произвольного JSON.

Перед изменением базовой папки интерфейс запрашивает /api/settings/plan. Если найдены папки экземпляров, сохранение требует move_token из плана; остановка запущенных терминалов дополнительно разрешается флагом stop_running. Перенос — глобальная задача: другие операции временно не принимаются, чтение состояния задачи остаётся доступным. JSON заменяется после перемещения всех папок. При переносе между дисками исходные данные сохраняются в резервных папках до записи JSON. При ошибке выполняется откат, при неполном откате сообщаются пути. Ответ настроек содержит base_folder_lock с причиной блокировки поля или пустой строкой.

Пути экземпляров вычисляются как mt5_folder / ключ в terminals общей функцией instance_directory(). В JSON у экземпляров нет собственного path; старое поле игнорируется при чтении. В ответах API поле path остаётся вычисляемым значением для отображения, а не настройкой расположения.

Загрузки EXE: kind равен terminal или editor. Используется бинарное тело с пределом 256 МиБ. Загрузка разрешена только для отсутствующего файла. При создании POST /api/terminals получает servers_dat в Base64 внутри JSON вместе с полями экземпляра. После декодирования предел — 16 МиБ; файл обязателен. Содержимое передаётся только фоновой задаче, не сохраняется в конфигурацию и не возвращается в ответах. Пустые файлы отклоняются, у EXE проверяется сигнатура MZ. Это не полная проверка содержимого исполняемого файла.

Ошибки входных типов возвращают 422 без исходного объекта и пароля; известные ошибки запуска задач/настроек — обычно 400. Ошибка уже принятой операции видна в объекте задачи при HTTP 200 на запрос её состояния. Интерактивная схема FastAPI доступна по /docs, OpenAPI — /openapi.json.

Создание и авторизация

Новый экземпляр получает два EXE, config/servers.dat, сервис и config/services.ini с именем общей базы. В JSON он регистрируется до запуска. Ожидание сервиса проверяет свежесть, полный путь установки и при заданном счёте — логин, сервер и подключение. При неудаче удаляется только новая папка, принадлежащая этой операции, и её запись.

Для первого входа используются временный INI и KeepPrivate=1 в основной конфигурации терминала. После успешного подключения главное окно закрывается через WM_CLOSE, временный файл удаляется, затем проверяется обычный запуск. Обновление существующего счёта не удаляет папку терминала при ошибке.

Проверки разработки

python -m unittest discover -s tests -v

test_provisioning.py использует временные папки и имитирует границу с MT5, проверяя выбор файлов, учётные записи, сохранение и откат. test_http.py поднимает изолированный сервер на свободном порту и проверяет маршруты и отсутствие паролей в ответах. test_project_updates.py работает с временным локальным Git-remote: проверяет fetch, fast-forward, защиту правок и распространение EX5 с имитацией ME. Сетевой репозиторий и рабочие терминалы пользователя тесты не меняют.

Для ручной проверки реального MT5 есть tools/smoke_create_terminal.py. Он создаёт отдельную тестовую копию и базу, затем удаляет собственные файлы. Инструмент привязан к локальной установке C:\MT5\MetaTrader5.1 и пути общей базы из config.json; перед использованием проверьте эти значения. Вариант --with-account запрашивает реквизиты при запуске; логин и сервер можно передать через --login и --server, пароль вводится скрыто через getpass и не передаётся аргументом командной строки. Файл tools/test_first_login.py не используется. Это инструмент разработчика, а не шаг установки.