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
    Так сложно понять о чем речь, добавьте фото пайпов или через что вы бинарные данные передаете.
  • alt
    20 декабря 2019, 09:26
    Спасибо за ваш труд…
  • Niktesla (бывш. Бабёр-Енот)
    20 декабря 2019, 10:04
    соккеты это сурово...
    можно было сделать на базе  Com(DCOM) сервера.
    за основу взять например из экселя интерфейсы IRtdServer & IRTDUpdateEvent
    … по крайней мере не надо было бы с бинарными данными возиться)

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

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