elektroyar
elektroyar личный блог
20 декабря 2019, 03:07

"Мост" между MetaTrader и программой через socket

В жизни бывают такие моменты, когда очень хочется торговать из программы на С++, но по каким-то причинам у брокера нет API, зато есть MetaTrader. Конечно, можно просто писать код на MQL4/MQL5, на этом урезанном варианте-мутанте Си и С++, но мне как-то не в кайф это делать. Поэтому я решил сделать «мост» между MetaTrader и программой через socket. Встречайте — MT-Bridge
"Мост" между MetaTrader и программой через socket

На данный момент MT-Bridge позволяет только передавать поток котировок в программу с заданной частотой + добавлена инициализация исторических данных. Пока мне этого достаточно, но возможно в будущем функционал MT-Bridge будет расширен. Поэтому извиняйте, если здесь вы не нашли полноценного функционала, что есть то есть пока. Библиотека для подключения к советнику написана на С++11 и зависит от boost.asio, но нужны только файлы-заголовки. Вот github репозиторий с советником и библиотекой. Передача данных реализована через сокеты, советник является клинетом, а программа на С++ — сервером. Данные передаются через сокет в бинарном виде. 

Как пользоваться


В терминале MetTrader нужно добавить советник MT-Bridge на график. Советнику нужно разрешить использовать dll.

Настройки советника:
  • Server hostname or IP address  — Имя хоста, по умолчанию localhost
  • Server port  — Порт сервера, по умолчанию 5555
  • Array of used currency pairs  — Массив имен валютны пар, данные которых должен передавать советник. По умолчанию представлен следующий список: EURUSD,USDJPY,GBPUSD,USDCHF,USDCAD,EURJPY,AUDUSD,NZDUSD, EURGBP,EURCHF,AUDJPY,GBPJPY,CHFJPY,EURCAD,AUDCAD,CADJPY, NZDJPY,AUDNZD,GBPAUD,EURAUD,GBPCHF,EURNZD,AUDCHF,GBPNZD, GBPCAD,XAUUSD
  • Data update period (milliseconds)  — Период обновления данных (в миллисекундах). Чем меньше это время, тем чаще будут поступать данные на сервер.
  • Depth of history to initialize  — Глубина исторических данных во время инициализации. Это количество баров, которое будет передано на сервер во время подключения.
Затем нужно написать программу, в которой подключить header-only библиотеку mt-bridge.hpp
Программа будет пытаться установить связь с советником. Можно использовать метод wait(), чтобы дождаться соединения. После установки соединения можно получать поток котировок, получить список используемых валютных пар или обратиться к небольшому промежутку исторических данных, которые передал советник при подключении.

Пример кода

Данный код после подключения к эксперту выведет на экран список символов, затем выведет исторические данные баров нулевого символа и после этого будет показывать текущую цену ask, цену закрытия бара, объем бара, время открытия бара и время сервера.
#include <iostream>
#include <mt-bridge.hpp>

int main() {
    const uint32_t port = 5555;
    mt_bridge::MtBridge iMT(port);

    if(!iMT.wait()) {
        std::cout << "no connection" << std::endl;
        return 0;
    }
    std::cout << "connection established" << std::endl;

    const uint32_t DELAY_WAIT = 5000;
    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_WAIT));

    /* list all symbols */
    std::vector<std::string> symbol_list = iMT.get_symbol_list();
    std::for_each(symbol_list.begin(), symbol_list.end(), [&](std::string &symbol) {
        static int n = 0;
        std::cout
            << "symbol["
            << std::to_string(n++)
            << "]: "
            << symbol
            << std::endl;
    });
    std::cout << "mt-bridge version: " << iMT.get_mt_bridge_version() << std::endl;

    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_WAIT));

    const uint32_t symbol_index = 0; // first symbol in the list
    /* get historical data to initialize your indicators */
    std::cout << iMT.get_symbol_list()[symbol_index] << std::endl;
    std::vector<mt_bridge::MtCandle> candles = iMT.get_candles(symbol_index);
    for(size_t i = 0; i < candles.size(); ++i) {
        std::cout << "candle, o: " << candles[i].open
            << " h: " << candles[i].high
            << " l: " << candles[i].low
            << " c: " << candles[i].close
            << " v: " << candles[i].volume
            << " t: " << candles[i].timestamp
            << std::endl;
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(DELAY_WAIT));

    /* quotations stream */
    while(true) {
        if(!iMT.update_server_timestamp()) continue;
        mt_bridge::MtCandle candle = iMT.get_candle(symbol_index);
        std::cout
            << iMT.get_symbol_list()[symbol_index]
            << " candle,"
            << " ask: " << iMT.get_ask(symbol_index)
            << " c: " << candle.close
            << " v: " << candle.volume
            << " t: " << candle.timestamp
            << " s: " << iMT.get_server_timestamp()
            << std::endl;
    }
    return 0;
}

Зависимости

  • boost.asio (нужны только заголовочные файлы)
  • для компилятора mingw добавить библиотеки ws2_32 и wsock32
Может кому пригодится)
21 Комментарий
  • Бабло ахаха, что ты ...
    20 декабря 2019, 07:36
    Полезные труды спс. Интересно что за столько лет существования Смартлаба, ничего полезного с точки зрения софта не было произведено сообществом из 73976 Человек. 
  • Бабло ахаха, что ты ...
    20 декабря 2019, 07:43
    Так сложно понять о чем речь, добавьте фото пайпов или через что вы бинарные данные передаете.
      • 💚Ola-la☮️
        20 декабря 2019, 09:28
        elektroyar, Прочитала, интересно, но не поняла что это… Столько советников в Метаке, этот шлюз выполняет какие функции, обеспечит быстроту подключения? Извиняюсь за детские вопросы
    • tranquility
      21 декабря 2019, 04:58
      СОЛДАТ Low Risk High Reward, пайпы в винде, знающие люди говорят, очень тормозные, так что сокеты — то, что надо. А так, мост между мт5 и внешним миром я для себя произвел еще в начале 16 года. Но какой смысл делиться трудами свомими? Тем более, что они под собственную задачу были заточены.
      • Авентадор
        21 декабря 2019, 12:13
        tranquility, на хабре было сравнение под win7, там пайпы многократно уделали сокеты при тестах на локальной машине. Впрочем, если не стоит задача добиться более высокой скорости на локалке, тогда сокеты действительно более удобны, благодаря своей универсальности
  • alt
    20 декабря 2019, 09:26
    Спасибо за ваш труд…
  • Niktesla (бывш. Бабёр-Енот)
    20 декабря 2019, 10:04
    соккеты это сурово...
    можно было сделать на базе  Com(DCOM) сервера.
    за основу взять например из экселя интерфейсы IRtdServer & IRTDUpdateEvent
    … по крайней мере не надо было бы с бинарными данными возиться)
    • Андрей К
      20 декабря 2019, 17:56
      Niktesla (бывш. Бабёр-Енот), а DCOM не сурово? =)
      • Niktesla (бывш. Бабёр-Енот)
        20 декабря 2019, 19:38
        Андрей К, хе-хе, не, ну, по мне так самое то ^^)'
        • Андрей К
          20 декабря 2019, 19:39
          Niktesla (бывш. Бабёр-Енот), про детство вы верно подметили =)
        • tranquility
          21 декабря 2019, 05:05
          Niktesla (бывш. Бабёр-Енот), там тебе могут всякие хендшейки со обменом ключами и шифрованием забабахать, как в случае пайпов происходит. Так что сокет — самый легкий и быстрый вариант. Вопрос только в разработке протокола. В принципе, немного пожертвовав скоростью, можно json какой-нибудь через них гонять. Вполне толерантная к небольшим изменениям и модернизации структура должна получиться.
  • robomakerr
    20 декабря 2019, 11:17
    C++ еще ужаснее чем MQL5 )
      • robomakerr
        21 декабря 2019, 14:50
        elektroyar, ну, у меня сейчас боевой робот — это всего две тыщи строк кода на mql. С логированием, обработкой ошибок, и т.п. Потребности в memcpy и указателях никогда не возникало. Для отладки вполне хватает Print(), на мой взгляд. Я не критикую, делюсь опытом просто.
        Но mql тоже ужасен, да) точнее, ужасен сам МТ, многие его архитектурные решения.
    • tranquility
      21 декабря 2019, 05:09
      robomakerr, ужасно написать большой проект (десятки-сотни тысяч строк) под какую-то конкретную платформу, а потом переписывать все под новую ее версию. С++ от такого очень страхует. Ну и пользоваться, отлаживать С++ гораздо приятнее, чем даже тот же питон.
      • robomakerr
        22 декабря 2019, 14:12
        tranquility, я как-то попробовал переписать свой старый проект с Builder на Visual, так все функции другие оказались)
        • tranquility
          22 декабря 2019, 21:02
          robomakerr, это да, если используются фичи оси. Я недавно тоже переписывал старый проект 6й вижуал студии под последнюю. Ничего страшного, но там и проект совсем маленький. А с обычным кросплатформенным кодом, использующим новые фичи от новых стандартов по минимуму — вообще проблем никаких не должно возникать.
  • Авентадор
    20 декабря 2019, 17:16
    Предложенное автором решение ориентировано на metatrader 4.

    Но если кому надо под метатрейдер 5, тогда за основу можно взять пайпы:
    www.mql5.com/ru/articles/503
    Связь через именованные каналы без применения DLL

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

    А для передачи сложных структур может пригодиться такая библиотека:
    www.mql5.com/ru/code/13663
    Де/сериализация на основе JSON в mt5



    P.S. Ну и сюда же, до кучи:

    --для тех кто предпочитает работу с сокетами

    www.mql5.com/en/blogs/post/706665
    Socket library for MT4 and MT5 [универсальная библиотека с актуальными обновлениями]

    www.mql5.com/ru/code/169
    Работа с сокетами в MQL5 [библиотека давно не обновлялась]

    www.mql5.com/ru/articles/2599
    статья про сокеты в MQL5 [на примере функций портированных из WinAPI]


    --для буферизации данных, если возникнет подобная необходимость

    www.mql5.com/ru/articles/3047
    здесь пример организации кольцевого буфера на MQL5



    P.P.S. кстати, тут выяснилось, что в прошлом году в metatrader5 добавили нативную поддержку сокетов (правда частично урезанную, но зато официальную, и без использования динамических библиотек). Неплохая статья с разборами и отладкой примеров:
      www.mql5.com/ru/articles/7117
      Работа с сетевыми функциями, или MySQL без DLL

    Ну и, помимо прочего, тем у кого в приоритете находится скорость программы, есть интересная статейка (хоть и старая, но вполне актуальная) о том как лучше портировать библиотеки из С на MQL:
      www.mql5.com/ru/articles/364
      Избавляемся от балласта самодельных DLL
      [опять же, с примерами для сокетов, и с примерами корректной обработки указателей от API функций!]

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн