Создание системы торговой аналитики (Часть 1): Основы и архитектура системы
Введение
MetaTrader 5 хранит историю торговых операций в терминале, что может стать ограничением, когда требуется торговый журнал с воспроизводимыми данными или надежный анализ стратегий. Если вам нужны автоматически собираемые записи с возможностью запросов по символу, магическому числу и тикетам ордеров, сделок или позиций либо единообразные отчеты по разным стратегиям, хранение данных только в MetaTrader 5 вынуждает экспортировать их вручную и писать скрипты под отдельные задачи. В первой статье мы решаем эту конкретную проблему: проектируем минимальный и надежный пайплайн для передачи данных о закрытых торговых операциях из терминала во внешнее хранилище.
В первой части мы не пытаемся создать полноценную многопользовательскую платформу: определяем целевую схему передачи данных (MetaTrader 5 → советник → HTTP API → база данных) и создаем основу бэкенда для приема и хранения записей о торговых операциях. В ней описывается контракт взаимодействия с клиентами и реализуется основа на Flask и SQLite для дальнейшей интеграции. В этой статье мы намеренно не переходим к интеграции с MetaTrader 5: разработка советника и обработка данных POST-запросов оставлены для следующих частей, чтобы задачи первой части оставались конкретными и выполнимыми.
Необходимые условия
Прежде чем продолжить, отметим несколько основных требований, необходимых для работы с материалами этой серии. Этот проект объединяет две среды: Python для бэкенда и MetaTrader 5 для торговой части. Поэтому базовое понимание программирования на Python и общее знакомство с разработкой на языке MQL5 упростят освоение материала.
При этом не нужно быть экспертом ни в одной из этих областей. Если вы уверенно читаете простой код на Python и понимаете, как работают советники в MetaTrader 5, вы сможете без труда следить за изложением. Если какие-то части материала кажутся вам новыми, это совершенно нормально. Серия построена так, что материал осваивается пошагово, а в каждом разделе основное внимание уделяется практической реализации, а не теории. Рекомендуем двигаться дальше, экспериментировать с кодом и учиться на практике по мере того, как система постепенно складывается в единое целое.
Проектирование системы
Система спроектирована как простой внешний пайплайн аналитики торговых операций: данные поступают из MetaTrader 5 в аналитическую среду с базой данных. Ее цель — собирать в терминале данные о торговых операциях, передавать их во внешнюю систему в структурированном виде и подготавливать для последующего анализа.
В целом система состоит из четырех основных компонентов:
- терминал MetaTrader 5;
- советник на языке MQL5;
- API веб-сервера Flask;
- база данных.
Торговые операции совершаются в терминале MetaTrader 5. Он служит источником данных, но не отвечает за их хранение вне терминала или внешний анализ.
Советник MQL5 выполняет роль слоя сбора данных. Его задача — отслеживать соответствующие торговые события, извлекать необходимую информацию о торговых операциях и подготавливать ее к передаче. Сам советник анализ не выполняет. Его задача — связать терминал с внешней системой.
API Flask выполняет роль слоя приема данных. Он принимает данные о торговых операциях от советника, обрабатывает запрос и подготавливает данные к сохранению. Такой бэкенд четко разделяет терминальную и аналитическую части системы. Кроме того, он создает основу для последующего перехода к более сложным архитектурам.
База данных выполняет роль слоя постоянного хранения. После поступления в бэкенд записи о торговых операциях сохраняются в базе данных для последующих запросов, анализа и повторного использования. Так разрозненные сведения о торговых операциях превращаются в структурированные данные для ведения внешнего торгового журнала и последующего анализа.
В этой серии процесс начинается с фиксации нужных торговых данных в MetaTrader 5. Данные приводятся к структурированному формату, отправляются в API Flask по HTTP и сохраняются в базе данных. Это создает понятный и удобный для сопровождения процесс передачи данных от совершения торговой операции до внешнего анализа.
Основная цель проектирования — простота: каждый компонент выполняет одну четко определенную задачу. В MetaTrader 5 совершаются торговые операции; советник собирает и отправляет данные; бэкенд принимает их, а база данных хранит. Разделение этих задач с самого начала упрощает понимание и реализацию системы, а также ее расширение в последующих частях серии.
Бэкенд
В этом разделе мы приступим к разработке бэкенда нашей системы. Это важнейший этап: бэкенд отвечает за прием торговых данных из MetaTrader 5 и их подготовку к сохранению в базе данных.
Чтобы процесс разработки оставался согласованным и предсказуемым, мы будем создавать бэкенд в среде Linux на дистрибутиве Ubuntu.
Настройка среды разработки
Если вы работаете на компьютере с Windows, рекомендуется установить подсистему Windows для Linux (WSL). WSL позволяет запускать среду Linux непосредственно в Windows. По умолчанию устанавливается дистрибутив Ubuntu, которого достаточно для этого проекта.
После установки Ubuntu откройте терминал и выполните следующие действия.
Шаг 1 — создание каталога проекта
Создайте новый каталог для проекта бэкенда:
mkdir mt5TradeAnalyticsBackend
Затем перейдите в этот каталог:
cd mt5TradeAnalyticsBackend
Этот каталог станет корневым для приложения бэкенда.
Шаг 2 — обновление индекса пакетов
Перед установкой любого программного обеспечения обновите индекс пакетов:
sudo apt update
Это обеспечивает системе доступ к последним доступным версиям пакетов.
Шаг 3 — проверка установки Python
Проверьте, установлен ли Python версии 3.10 или более поздней:
python3 --version Если Python установлен правильно, вывод будет примерно таким:
Python 3.12.3
Если Python не установлен, его можно установить с помощью следующей команды:
sudo apt install python3
Шаг 4 — создание виртуальной среды
Виртуальная среда позволяет изолировать зависимости проекта от общесистемной установки Python. Это важно для предотвращения конфликтов версий, упорядоченной организации проекта и воспроизводимости его окружения.
Создайте виртуальную среду следующей командой:
python3 -m venv .venv
Примечание: в некоторых системах Ubuntu сначала может потребоваться установка модуля venv:
sudo apt install python3-venv
Шаг 5 — активация виртуальной среды
Активируйте среду следующей командой:
. .venv/bin/activate
После активации в приглашении командной строки терминала обычно появляется (.venv), указывающее, что вы работаете в виртуальной среде.
Шаг 6 — установка зависимостей проекта
Теперь установим необходимые библиотеки.
Установите Flask:
pip install Flask
Flask — легкий веб-фреймворк, с помощью которого мы создадим эндпоинты API для приема данных о торговых операциях.
При установке Flask автоматически устанавливается несколько основных зависимостей, в том числе:
- Jinja2
- Werkzeug
- Click
- Itsdangerous
- MarkupSafe
- Blinker
Эти библиотеки обеспечивают работу с шаблонами, обработку запросов, инструменты командной строки и внутренние операции фреймворка.
Установите Flask-SQLAlchemy.
Далее установите Flask-SQLAlchemy:
pip install -U Flask-SQLAlchemy
Flask-SQLAlchemy предоставляет упрощенный интерфейс для работы с базами данных через SQLAlchemy. Он упрощает определение моделей, работу с базой данных SQLite и операции добавления и выборки записей.
За более подробными сведениями об установке и использовании обратитесь к официальной документации Flask, содержащей актуальные инструкции непосредственно от разработчиков.
Моделирование данных
Для хранения и анализа данных о торговых операциях вне MetaTrader 5 необходимо структурированное представление каждой операции. Вместо того чтобы полагаться на внутреннюю историю терминала, мы задаем формат для эффективного хранения данных, выполнения запросов и повторного использования.
В этом проекте мы будем использовать базу данных SQLite. SQLite — легковесный файловый движок базы данных, доступный в Python через встроенный модуль sqlite3. Поэтому дополнительная установка или настройка сервера не требуется. Все данные хранятся в одном файле на диске. Это упрощает управление ими и делает SQLite идеальным выбором для базовой системы, подобной той, которую мы создаем в этой серии.
Использование SQLite позволяет нам:
- определять таблицы с помощью стандартного SQL;
- сохранять данные о торговых операциях вне терминала;
- впоследствии выполнять запросы и анализировать результаты;
- легко интегрировать базу данных с бэкендом на Flask.
Таблица trades
Мы определим одну таблицу trades — основное хранилище всех собранных данных о торговых операциях. Каждая строка представляет одну завершенную торговую операцию.
Помимо основных полей торговой операции, мы включаем специфичные для MetaTrader 5 идентификаторы, например тикеты ордеров, сделок и позиций. Они помогают согласовать модель данных с внутренним представлением торговых операций в MetaTrader 5 и упрощают их отслеживание при последующем анализе.
Таблица будет содержать следующие поля:
- id — уникальный идентификатор каждой записи, создаваемый автоматически;
- symbol — торговый инструмент, например EURUSD или XAUUSD;
- order_ticket — идентификатор ордера, связанного с торговой операцией;
- deal_ticket — идентификатор совершенной сделки;
- position_ticket — идентификатор позиции, к которой относится торговая операция;
- position_type — направление торговой операции, обычно BUY или SELL;
- position_reason — способ открытия позиции, например советником или вручную;
- lot_size — размер сделки в лотах;
- entry_price — цена открытия сделки;
- stop_loss — уровень стоп-лосса (необязательное поле);
- take_profit — уровень тейк-профита (также необязательное поле);
- profit — итоговый результат торговой операции после ее закрытия;
- open_time — время открытия сделки;
- close_time — время закрытия сделки;
- magic_number — идентификатор торговых операций разных стратегий или советников.
А теперь приступим к делу и начнем писать приложение. Для начала правильно настроим проект.
Настройка приложения
Перейдите в каталог проекта бэкенда (mt5TradeAnalyticsBackend) и создайте пустой файл с расширением .py для его исходного кода. В командной строке терминала выполните следующую команду:
touch app.py
Откройте файл в удобном текстовом редакторе и вставьте следующий начальный код:
from datetime import datetime from flask import Flask, jsonify, request from flask_sqlalchemy import SQLAlchemy from sqlalchemy import DateTime, Float, Integer, String from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column # Create the Flask application app = Flask(__name__) @app.route("/") def index(): return "<p>MT5 Trade Analytics Backend is running.</p>"
Разбор шаблонного кода
Кратко разберем, что делает этот код. Класс Flask используется для создания основного экземпляра приложения. Декоратор @app.route("/") задает маршрут обработки обращений к корневому URL-адресу. Функция index возвращает простое сообщение, подтверждающее, что бэкенд работает.
Импорты в начале файла позволяют работать с:
- JSON-запросами (request, jsonify);
- моделями базы данных (SQLAlchemy, mapped_column);
- типами данных, такими как Integer, Float и DateTime.
На этом этапе у нас готов минимальный веб-сервер. Далее мы расширим его функциональность, добавив поддержку операций с базой данных.
Настройка базы данных
Сразу после создания приложения Flask настройте базу данных:
# Configure the SQLite database app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///data.db" app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
Так мы указываем Flask-SQLAlchemy:
- использовать базу данных SQLite;
- хранить данные в файле data.db в каталоге проекта;
- Отключить ненужные функции отслеживания для повышения производительности
Инициализация базы данных
Далее мы определяем базовый класс и инициализируем объект базы данных.
# Define the base class for declarative models class Base(DeclarativeBase): pass
Создайте объект SQLAlchemy:
# Create the SQLAlchemy object
db = SQLAlchemy(model_class=Base)
Инициализируйте приложение с помощью расширения
# Initialize the app with the extension
db.init_app(app)
Вот что происходит:
Base служит основой для всех моделей, которые мы определим. db становится основным объектом для взаимодействия с базой данных.
Вызов db.init_app(app) подключает расширение для работы с базой данных к нашему приложению Flask.
Определение модели Trade
Теперь определим структуру таблицы trades с помощью класса на языке Python.
class Trade(db.Model): __tablename__ = "trades" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) symbol: Mapped[str] = mapped_column(String(20), nullable=False) order_ticket: Mapped[int] = mapped_column(Integer, nullable=True) deal_ticket: Mapped[int] = mapped_column(Integer, nullable=True) position_ticket: Mapped[int] = mapped_column(Integer, nullable=True) position_type: Mapped[str] = mapped_column(String(10), nullable=False) position_reason: Mapped[str] = mapped_column(String(50), nullable=True) lot_size: Mapped[float] = mapped_column(Float, nullable=False) entry_price: Mapped[float] = mapped_column(Float, nullable=False) stop_loss: Mapped[float] = mapped_column(Float, nullable=True) take_profit: Mapped[float] = mapped_column(Float, nullable=True) profit: Mapped[float] = mapped_column(Float, nullable=False) open_time: Mapped[datetime] = mapped_column(DateTime, nullable=False) close_time: Mapped[datetime] = mapped_column(DateTime, nullable=False) magic_number: Mapped[int] = mapped_column(Integer, nullable=True) def __repr__(self) -> str: return ( f"<Trade id={self.id} symbol={self.symbol} " f"position_type={self.position_type} profit={self.profit}>" )
Создание базы данных
Наконец, создаем базу данных и структуру таблиц:
with app.app_context(): db.create_all()
Это обеспечивает следующее:
- файл data.db создается, если он отсутствует;
- таблица trades создается на основе определения модели.
Эту операцию достаточно выполнить один раз при инициализации приложения.
Планирование API
Теперь, когда модель базы данных готова, нужно определить, как внешние клиенты будут взаимодействовать с нашим бэкендом. В этом проекте основной клиент — терминал MetaTrader 5. В дальнейшем советник будет отправлять из него итоговые данные о торговой операции после закрытия позиции.
На этом этапе наша цель — не полностью реализовать логику всех эндпоинтов, а создать понятную модульную структуру API для дальнейшего развития системы. Это заранее задает для приложения понятный контракт и упрощает организацию дальнейшей разработки.
Чтобы лучше понять работу системы, рассмотрим простую последовательность действий. После закрытия позиции в MetaTrader 5 советник будет собирать все необходимые данные о торговой операции и отправлять их в формате JSON на эндпоинт /api/v1/trades. Бэкенд принимает запрос, разбирает данные и подготавливает их к сохранению в базе данных. Такой однонаправленный поток данных сохраняет простоту и надежность системы и обеспечивает единообразную регистрацию всех завершенных торговых операций.
Первое небольшое изменение — импорт redirect из Flask:
from flask import Flask, jsonify, request, redirect
Это позволяет перенаправлять запросы к корневому маршруту / на корневой маршрут API с указанием версии. Мы определяем маршрут следующим образом:
@app.route("/") def index(): return redirect("/api/v1")
Это небольшой, но полезный штрих. Вместо того чтобы оставлять корневой маршрут неиспользованным, мы перенаправляем с него запросы в основное пространство имен API — /api/v1.
Корневой эндпоинт API
Начнем с определения корневого эндпоинта API версии 1:
# 1. API Root
@app.route("/api/v1")
def api_root():
return "<p>MT5 Trade Analytics API v1 is running.</p>"
Этот эндпоинт служит простой точкой входа в приложение. Его назначение в основном информационное. При обращении через браузер он подтверждает доступность API и работу версии 1.
Эндпоинт проверки работоспособности
Следующий эндпоинт предназначен для проверки работоспособности:
# 2. Health Check @app.route("/api/v1/health") def health_check(): return jsonify({"status": "running"})
Этот маршрут предоставляет удобный способ автоматически проверять, что бэкенд работает. Корневой эндпоинт API в основном удобен при работе через браузер, а эндпоинт проверки работоспособности — для быстрых проверок инструментами и скриптами, а в дальнейшем, при необходимости, и терминалом MQL5.
Пока он возвращает небольшой объект JSON с единственным полем status. Пока этого достаточно, а ответ остается простым.
Эндпоинт создания записи о торговой операции
Главный эндпоинт в текущей архитектуре отвечает за прием итоговых данных о торговой операции:
# 3. Create Trade (Core Endpoint) @app.route("/api/v1/trades", methods=["POST"]) def create_trade(): return jsonify({ "message": "Trade endpoint ready. Logic will be implemented later." })
Это ключевой эндпоинт приложения. После закрытия позиции в MetaTrader 5 советник отправит итоговую запись о торговой операции по этому маршруту. Поскольку на момент закрытия известны значения всех обязательных полей, достаточно одной отправки. Это сохраняет простоту интеграции и позволяет избежать лишней сложности, связанной с частичными обновлениями и промежуточными этапами обработки сделки.
Пока эндпоинт возвращает только ответ-заглушку. В следующих частях серии мы расширим его функциональность: эндпоинт будет принимать данные JSON, проверять их, создавать объект Trade и сохранять запись в базе данных.
Эндпоинт получения всех торговых операций
Кроме того, мы определяем эндпоинт для получения всех сохраненных торговых операций:
# 4. Get All Trades @app.route("/api/v1/trades", methods=["GET"]) def get_trades(): return jsonify({ "message": "Retrieve all trades - not implemented yet." })
В дальнейшем этот маршрут будет использоваться для получения из базы данных всех сохраненных записей о торговых операциях. Он пригодится, когда мы начнем просматривать сохраненные данные, тестировать бэкенд, а затем создавать аналитический слой.
Хотя реализация еще не завершена, определение маршрута уже сейчас делает структуру API более согласованной. Теперь для одного ресурса /api/v1/trades предусмотрены создание записей через POST и их получение через GET.
Эндпоинт получения одной торговой операции
Следующий эндпоинт позволяет получить одну запись о торговой операции по ее внутреннему идентификатору:
# 5. Get Single Trade @app.route("/api/v1/trades/<int:trade_id>", methods=["GET"]) def get_trade(trade_id): return jsonify({ "message": f"Retrieve trade {trade_id} - not implemented yet." })
В этом маршруте используется динамический параметр пути trade_id, определяющий конкретную запрашиваемую запись. В дальнейшем это позволит просматривать отдельные записи о торговых операциях непосредственно в базе данных.
Этот маршрут полезно определить уже сейчас: он дополняет API и предоставляет отдельный путь для просмотра отдельных записей, не перегружая эндпоинт получения всей коллекции.
Эндпоинт сводной аналитики
Наконец, мы определяем эндпоинт для аналитики:
# 6. Analytics Summary @app.route("/api/v1/analytics/summary", methods=["GET"]) def analytics_summary(): return jsonify({ "message": "Analytics summary - not implemented yet." })
Этот маршрут отделен от ресурса коллекции торговых операций, поскольку предназначен для обработанной информации, а не исходных записей. Его задача — возвращать сводную статистику по сохраненным данным: например, общее количество торговых операций, процент прибыльных операций или совокупную прибыль. Отделение аналитики от ресурса торговых операций сохраняет понятную структуру API и упрощает его дальнейшее расширение.
Итоговая структура API
На данный момент в приложении определены следующие маршруты:
1. GET /
Перенаправляет на корневой эндпоинт API с указанием версии
2. GET /api/v1
Возвращает простое сообщение, подтверждающее работу API
3. GET /api/v1/health
Возвращает ответ со статусом в формате JSON
4. POST /api/v1/trades
Принимает итоговые данные о торговых операциях из MetaTrader 5
5. GET /api/v1/trades
Возвращает все сохраненные записи о торговых операциях
6. GET /api/v1/trades/<trade_id>
Возвращает одну сохраненную запись о торговой операции по идентификатору
7. GET /api/v1/analytics/summary
Возвращает сводную аналитику по сохраненным данным о торговых операциях
Эта архитектура намеренно остается простой. Она дает проекту понятный модульный API без лишней сложности на этом раннем этапе. Еще важнее, что это соответствует ранее определенной архитектуре системы: итоговые данные о торговой операции будут отправляться после закрытия позиции, сохраняться в базе данных и затем становиться доступными для получения и анализа.
Весь обмен данными между MetaTrader 5 и бэкендом осуществляется в формате JSON. Советник отправляет данные о торговых операциях в формате JSON с помощью HTTP POST. Flask разбирает JSON-тело запроса и сохраняет соответствующие поля в базе данных. Ответы API возвращаются в формате JSON с помощью функции jsonify.
Тестирование локальной среды бэкенда
Теперь, когда структура API и модель базы данных готовы, нужно запустить приложение локально и убедиться, что все работает должным образом. Этот шаг важен: он позволяет проверить правильность настройки бэкенда до начала интеграции с MetaTrader 5.
Для начала перейдите в каталог проекта и активируйте виртуальную среду. Это обеспечивает доступность всех зависимостей, таких как Flask и Flask-SQLAlchemy, в изолированной среде.
После активации среды выполните в терминале следующую команду:
flask --app app run --host 0.0.0.0 --debug
Эта команда запускает сервер разработки Flask. Параметр --app app задает app.py как точку входа. Параметр --host 0.0.0.0 делает приложение доступным как в локальной сети, так и на локальном компьютере. Это пригодится в дальнейшем, если вы захотите подключиться к бэкенду с другого устройства в той же сети.
Флаг --debug включает режим отладки. В этом режиме Flask автоматически перезапускает сервер при каждом внесении изменений в исходный код. Кроме того, в этом режиме выводятся полезные сообщения об ошибках, помогающие быстро находить и устранять неполадки при разработке.
Если сервер успешно запустится, Flask выведет в терминале сообщение о том, что сервер работает и прослушивает локальный адрес. Теперь откройте веб-браузер и перейдите по следующему адресу:
http://127.0.0.1:5000/
Это адрес по умолчанию, по которому доступно приложение Flask. При обращении к этому маршруту произойдет автоматическое перенаправление на следующий адрес:
http://127.0.0.1:5000/api/v1

Это происходит потому, что корневой маршрут настроен на перенаправление к корневому эндпоинту API. На экране должно появиться простое сообщение, подтверждающее, что API работает. Хотя вывод минимален, он подтверждает корректность маршрутизации и то, что приложение отвечает ожидаемым образом.
Здесь стоит отметить еще один важный момент. Просмотрев каталог проекта, вы заметите, что в нем создана новая папка instance. В этой папке вы найдете файл базы данных data.db.
Это подтверждает, что Flask-SQLAlchemy успешно инициализировал базу данных SQLite и создал нужную таблицу на основе ранее определенной модели. База данных хранится локально в виде файла, что упрощает ее просмотр, резервное копирование и сброс при разработке.
На этом этапе проверяются основные условия: приложение запускается, маршрутизация работает, база данных успешно создается. После этой проверки можно перейти к следующему этапу — реализации логики приема и сохранения бэкендом реальных данных о торговых операциях.Полный исходный код этого этапа предоставлен в файле app.py, чтобы вы могли воспроизвести настройку окружения и выполнять описываемые шаги разработки.
Заключение
В этой статье вы заложили практическую основу для внешнего пайплайна аналитики торговых операций. В частности, теперь у вас есть:
- приложение Flask (app.py) и структура проекта;
- модель данных на базе SQLite с созданной таблицей trades, содержащей идентификаторы MetaTrader 5 (тикеты ордера, сделки и позиции), поля позиции, прибыль/убыток, время открытия и закрытия сделки и магическое число;
- понятный контракт API в пространстве /api/v1 (корневой эндпоинт, проверка работоспособности, GET/POST /api/v1/trades, GET /api/v1/trades/ и /api/v1/analytics/summary);
- этап локальной проверки — выполнение команды flask --app app run --host 0.0.0.0 --debug запускает сервер и создает файл instance/data.db.
Обратите внимание, что намеренно оставлено незавершенным: эндпоинт POST /api/v1/trades пока возвращает заглушку, а советник для MetaTrader 5, отправляющий данные о закрытых торговых операциях в формате JSON, еще не реализован. Эти пробелы оставлены намеренно: цель — создать проверяемый, хорошо документированный бэкенд и схему долговременного хранения данных для простой и воспроизводимой интеграции.
В части 2 мы реализуем советник, определим точную структуру данных JSON и завершим обработку POST-запросов (проверку данных и добавление записи в базу), чтобы данные о закрытых торговых операциях автоматически поступали из MetaTrader 5 в этот бэкенд.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22107
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Как выявлять ликвидность на круглых ценовых уровнях в MQL5
Алгоритм оптимизации, вдохновлённый охотой лисы — Fox-inspired Optimization Algorithm (FOX)
Нейросети в трейдинге: Habi — быстрое принятие решений без сложного планирования
Торговые инструменты MQL5 (Часть 33): Создание системы документации с расширенным форматированием для программ на языке MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования