MetaTrader 5 и Python: Передача данных через ZeroMQ (ZMQ) сокеты

MetaTrader 5 и Python: Передача данных через ZeroMQ (ZMQ) сокеты MQL4 / MQL5 (MetaTrader)
Подробное руководство по настройке и использованию ZeroMQ (ZMQ) сокетов для эффективной передачи данных в реальном времени из MetaTrader 5 в Python-скрипты.
Суть: Для передачи данных из MetaTrader 5 в Python-скрипт в реальном времени используйте ZeroMQ (ZMQ) сокеты. MQL5-скрипт выступает в роли Publisher (PUB) и отправляет данные (например, котировки) на определенный порт, а Python-скрипт подключается как Subscriber (SUB) к этому порту и принимает сообщения.

Исходный код

Передача данных между MetaTrader 5 (MT5) и Python является ключевой задачей для многих количественных разработчиков и трейдеров, желающих использовать мощь Python для анализа, машинного обучения или интеграции с другими системами. ZeroMQ (ZMQ) — это высокопроизводительная асинхронная библиотека обмена сообщениями, которая идеально подходит для этой цели, обеспечивая быструю и надежную передачу данных.

В этом руководстве мы рассмотрим, как настроить MQL5-скрипт в MT5 для отправки данных (например, текущих котировок) через ZMQ-сокет типа Publisher (PUB) и как создать Python-скрипт для приема этих данных с помощью ZMQ-сокета типа Subscriber (SUB).

MQL5 (MetaTrader 5) — Publisher

Для работы с ZMQ в MQL5 вам потребуется библиотека Zmq.mqh и соответствующая libzmq.dll. Эти файлы обычно можно найти в репозиториях сообщества MQL5 или на GitHub. Убедитесь, что Zmq.mqh находится в папке MQL5/Include, а libzmq.dll — в MQL5/Libraries. Также необходимо разрешить импорт DLL в настройках эксперта/скрипта MT5.

Этот MQL5-скрипт будет отправлять текущую цену Bid и Ask для символа, на котором он запущен, каждые 500 миллисекунд.


#property copyright "Finfluct"
#property version   "1.00"

#include  // Путь к библиотеке Zmq.mqh

//--- Параметры ZMQ
#define ZMQ_PORT 5555 // Порт для ZMQ сокета

CZmqContext  zmq_context;
CZmqSocket   zmq_publisher;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Инициализация ZMQ контекста
   if (!zmq_context.Create())
     {
      Print("Ошибка: Не удалось создать ZMQ контекст");
      return INIT_FAILED;
     }

   //--- Создание ZMQ сокета типа Publisher
   if (!zmq_publisher.Create(zmq_context, ZMQ_PUB))
     {
      Print("Ошибка: Не удалось создать ZMQ Publisher сокет");
      zmq_context.Destroy();
      return INIT_FAILED;
     }

   //--- Привязка сокета к порту
   string address = "tcp://*" + IntegerToString(ZMQ_PORT);
   if (!zmq_publisher.Bind(address))
     {
      Print("Ошибка: Не удалось привязать ZMQ Publisher сокет к ", address);
      zmq_publisher.Destroy();
      zmq_context.Destroy();
      return INIT_FAILED;
     }

   Print("ZMQ Publisher успешно запущен на ", address);
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //--- Закрытие сокета и уничтожение контекста
   zmq_publisher.Destroy();
   zmq_context.Destroy();
   Print("ZMQ Publisher остановлен.");
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   static datetime last_send_time = 0;
   datetime current_time = TimeCurrent();

   // Отправляем данные не чаще, чем раз в 500 мс
   if (current_time - last_send_time < 0.5) // Проверка на интервал в секундах
     {
      return;
     }

   last_send_time = current_time;

   //--- Получение текущих цен
   MqlTick tick;
   if (!SymbolInfoTick(_Symbol, tick))
     {
      Print("Ошибка: Не удалось получить тиковые данные для ", _Symbol);
      return;
     }

   //--- Формирование сообщения (например, в формате JSON)
   string message = StringFormat("{\"symbol\":\"%s\", \"bid\":%.5f, \"ask\":%.5f, \"time\":%d}",
                                 _Symbol, tick.bid, tick.ask, tick.time);

   //--- Отправка сообщения
   if (zmq_publisher.Send(message))
     {
      // Print("Отправлено: ", message); // Раскомментируйте для отладки
     }
   else
     {
      Print("Ошибка: Не удалось отправить сообщение через ZMQ");
     }
  }

Python - Subscriber

Python-скрипт будет подключаться к ZMQ-сокету, запущенному в MT5, и выводить полученные данные в консоль. Убедитесь, что у вас установлен пакет pyzmq (pip install pyzmq).


import zmq
import json
import datetime

#--- Параметры ZMQ
ZMQ_PORT = 5555
ZMQ_ADDRESS = f"tcp://localhost:{ZMQ_PORT}"

def main():
    context = zmq.Context()
    print(f"Подключение к ZMQ Publisher на {ZMQ_ADDRESS}...")
    
    # Создание ZMQ сокета типа Subscriber
    socket = context.socket(zmq.SUB)
    socket.connect(ZMQ_ADDRESS)
    
    # Подписка на все сообщения (пустая строка означает подписку на все топики)
    socket.setsockopt_string(zmq.SUBSCRIBE, "")
    
    print("Успешно подключено. Ожидание данных...")
    
    try:
        while True:
            # Получение сообщения
            message_raw = socket.recv()
            message_str = message_raw.decode('utf-8')
            
            try:
                # Попытка распарсить как JSON
                data = json.loads(message_str)
                timestamp = datetime.datetime.fromtimestamp(data['time'])
                print(f"[{timestamp}] {data['symbol']} Bid: {data['bid']:.5f}, Ask: {data['ask']:.5f}")
            except json.JSONDecodeError:
                print(f"Получено не-JSON сообщение: {message_str}")
            except KeyError as e:
                print(f"Получено JSON, но отсутствует ключ {e}: {message_str}")
                
    except KeyboardInterrupt:
        print("\nПолучено прерывание от пользователя. Закрытие сокета...")
    finally:
        socket.close()
        context.term()
        print("ZMQ сокет и контекст закрыты.")

if __name__ == "__main__":
    main()

Разбор параметров

  • ZMQ_PORT: Определяет номер порта, который будет использоваться для ZMQ-соединения. Этот порт должен быть одинаковым как в MQL5-скрипте (Publisher), так и в Python-скрипте (Subscriber). Убедитесь, что порт не занят другими приложениями.
  • #include : Директива препроцессора в MQL5, указывающая на путь к заголовочному файлу библиотеки ZeroMQ для MQL5. Путь может отличаться в зависимости от того, куда вы поместили файл Zmq.mqh.
  • zmq_context.Create() / zmq.Context(): Инициализирует контекст ZeroMQ. Контекст управляет всеми сокетами и потоками в ZMQ.
  • zmq_publisher.Create(zmq_context, ZMQ_PUB) / context.socket(zmq.SUB): Создает ZMQ-сокет. В MQL5 это сокет типа ZMQ_PUB (Publisher), который отправляет сообщения. В Python это сокет типа zmq.SUB (Subscriber), который принимает сообщения.
  • zmq_publisher.Bind("tcp://*:5555"): В MQL5 (Publisher) привязывает сокет к указанному адресу и порту. * означает, что сокет будет слушать входящие соединения на всех доступных сетевых интерфейсах.
  • socket.connect("tcp://localhost:5555"): В Python (Subscriber) подключает сокет к указанному адресу и порту, где ожидает Publisher. localhost указывает на текущую машину.
  • socket.setsockopt_string(zmq.SUBSCRIBE, ""): В Python (Subscriber) подписывает сокет на получение сообщений. Пустая строка "" означает подписку на все топики. Если Publisher отправляет сообщения с топиками (например, zmq_publisher.Send("EURUSD", message)), Subscriber должен подписаться на конкретный топик.
  • zmq_publisher.Send(message) / socket.recv(): Методы для отправки и получения сообщений. В MQL5 отправляется строка. В Python recv() возвращает байты, которые затем декодируются в строку (decode('utf-8')).
  • StringFormat() / json.loads(): В MQL5 используется для форматирования данных в строку (в данном случае, JSON-строку). В Python json.loads() используется для парсинга полученной JSON-строки в Python-словарь.

Как запустить

Выполните следующие шаги для запуска и тестирования передачи данных:

1. Подготовка MQL5 окружения

  1. Установка ZMQ библиотеки для MQL5: Скачайте Zmq.mqh и libzmq.dll. Поместите Zmq.mqh в папку MQL5/Include/Zmq (или просто MQL5/Include, если вы измените путь в #include), а libzmq.dll — в MQL5/Libraries.
  2. Создание MQL5-скрипта: Откройте MetaEditor (F4 в MT5), создайте новый Expert Advisor (Файл -> Новый -> Expert Advisor (шаблон)). Скопируйте и вставьте MQL5 код, приведенный выше. Сохраните его (например, как ZmqPublisher.mq5).
  3. Компиляция: Нажмите F7 для компиляции скрипта. Убедитесь, что нет ошибок.
  4. Разрешение импорта DLL: В MetaTrader 5 откройте меню Сервис -> Настройки -> Советники и установите флажок Разрешить импорт DLL.
  5. Запуск MQL5-скрипта: Перетащите скомпилированный Expert Advisor ZmqPublisher из окна Навигатор на график любого торгового инструмента (например, EURUSD). Убедитесь, что в правом верхнем углу графика появился смайлик, а в журнале экспертов (вкладка Эксперты в окне Терминал) отображается сообщение ZMQ Publisher успешно запущен на tcp://*:5555.

2. Подготовка Python окружения

  1. Установка pyzmq: Откройте терминал или командную строку и выполните команду: pip install pyzmq.
  2. Создание Python-скрипта: Создайте новый файл (например, zmq_subscriber.py) и скопируйте в него Python код, приведенный выше.

3. Запуск и проверка

  1. Запустите Python-скрипт: В терминале перейдите в директорию, где вы сохранили zmq_subscriber.py, и выполните: python zmq_subscriber.py.
  2. Проверка данных: Вы должны увидеть, как в консоли Python начинают появляться сообщения с текущими котировками, отправленными из MT5.

Дополнительные возможности и ссылки

Используя ZMQ, вы можете передавать не только котировки, но и информацию о сделках, позициях, а также отправлять команды из Python в MT5 (используя другие типы сокетов, например, REQ/REP). Это открывает широкие возможности для автоматизации и создания сложных торговых систем.

Например, вы можете использовать полученные данные для анализа проскальзываний, как в статье MQL5: Скрипт для сбора статистики проскальзываний Market ордеров, или для принятия решений о массовом изменении параметров позиций, что может быть реализовано с помощью скриптов, подобных описанным в MQL5: Массовое изменение Stop Loss и Take Profit по Magic Number или MQL5: Скрипт для массового закрытия убыточных позиций по Magic Number.

ZeroMQ — это мощный инструмент для построения распределенных систем, и его интеграция с MetaTrader 5 значительно расширяет возможности для разработки торговых стратегий и инструментов.

Оцените статью
FinFluct