Дмитрий К
Дмитрий К личный блог
24 февраля 2020, 14:49

Как экспортировать данные из квика через сокеты - ответ и тут же вопрос

Последние несколько месяцев время от времени начинал времени ломать голову над одной задачкой.
Суть в следующем.
Я сделал скрипт на питоне, на основе торговых данных пишет заявки в tri файл квиковский.
Чтоб заявку создать нужно принять решение на основе каких то данных из таблиц квика (например исполнилась какая то ранняя заявка, или банально цена дошла до нужного уровня, и т.п.)
Данные из таблиц квика, как известно, встроенными методами можно экспортировать через ДДЕ сервер, или в базы данных через ODBC.
То есть — для этого не надо обладать знаниями по программированию, это простые, очевидные способы, доступные всем, у кого установлен квик.
Я выбрал способ по ODBC, и пользуюсь им.
Связка работает стабильно, ничего не рушится, правда пару раз за несколько месяцев зависал сам квик из за того, что кончалась оперативная память (сервер слабенький у меня).

Но у такой связки есть слабое место, приходится в питоне запускать таймер, по кjторому питон опрашивает базу данных.
Это не логично, робот должен совершать какие то действия в ответ на событие, а не просто молотить запросы в БД.

В итоге, путем гугления выяснилось что существуют в  теории еще два пособа экспорта данных из квика.
Первый способ — это экспорт в библиотеку dll trans2quik — библиотека позволяет  как импортировать заявки в квик, так и экспортировать заявки и сделки из квика.
С импортом заявок я разобрался (кому интересно, по простому могу объяснить), а вот по поводу экспорта так и не понял, суть в том, что сделки  приходят в бибдиотеку dll а оттуда уже в скрипт питона. Подписаться на отправку удается, а вот как получать сами сделки в переменные, я так и не понял.

второй способ, использование сокет соединений. Общий смысл в том, что в локальной сети (в данном случае в рамках одного компьютера), создается сокет сервер и сокет клиент, и клиент может отправлять что либо серверу.
В итоге идея в том, чтобы создать на питоне сокет сервер, а на языке LUA встроенном в квик, создать клиент, который будет отправлять все нужные события в робота на питоне.
На питоне очень много понятной для нубов инфы в интернете, все просто найти. Сокет библиотеку для питона даже устанавливать не надо, она уже встроена.
А вот с LUA это просто капец насчет инфы, для тех кто начинает с нуля, разобраться не возможно. Там какой то междусобойчик, люди чего то там знают уже, какие то вопросы задают, сами себе отвечают.

В итоге методом простого тыканья пальцем в небо удалось найти старую библиотеку Сокет для луа, также пример кода на LUA, и создать наконец рабочий клиент.

Как это все я запустил?
Из библиотеки с архивом сокета кинул все файлы в корневой каталог квика, не заменял только основной файл lua5.1.exe (оставил родной)
туда же положил и файл со скриптом клиента, перед тем как запускать клиент, само собой надо запустить сервер на питоне, 
и после этого все сразу заработало на 32 разрядной версии квика.

Кому надо, могу прислать  ту библиотеку сокет, которая у меня заработала, а также примеры скриптов сервера и клиента

проблема в том, что на 64 разрядной версии не запускается, пишет ошибку

error loading module 'socket.core' from file 'C:\QUIK_VTB24\socket\core.dll':
%1 не является приложением Win32.

пока времени нет нагуглить решение, может кто знает как наладить?

Update:
библиотеку 64 нашел, все заработало с сокетами


63 Комментария
  • Karim
    24 февраля 2020, 15:02
    По всей видимости у вас старая библиотека Сокет для луа 32 разрядная, нужно либо найти вариант 64 разрядной, либо исходники и потом их собрать для 64 разряда.
  • Свой Мужик
    24 февраля 2020, 15:04
    Читал мануил и единственное, что там написано про 64 версию — ищите dll под неё :(
  • luks sluk
    24 февраля 2020, 15:13
    RPC-сервис для вызова API Lua-библиотеки торгового терминала QUIK
    https://github.com/Enfernuz/quik-lua-rpc

    Пример клиента на python для quik-lua-rpc JSON? 

    import zmq
    
    context = zmq.Context()
    socket = self.context.socket(zmq.REQ)
    socket.connect('tcp://127.0.0.1:5560')
    socket.send_string('{"method":"datasource.CreateDataSource","args":{"class_code":"SPBFUT", "sec_code":"SiH0", "interval":"INTERVAL_M1", "param":""}}')
    datasource_uuid = json.loads(socket.recv_string())['result']['datasource_uuid']
    socket.send_string('{"method":"datasource.Size","args":{"datasource_uuid":"%s"}}' % (datasource_uuid))
    num_candles = json.loads(socket.recv_string())['result']['value']
    for i in range(0,num_candles):
        socket.send_string('{"method":"datasource.C","args":{"datasource_uuid":"%s","candle_index":%d}}' % (datasource_uuid, i))
        candle_close = json.loads(socket.recv_string())['result']['value']
        socket.send_string('{"method":"datasource.O","args":{"datasource_uuid":"%s","candle_index":%d}}' % (datasource_uuid, i))
        candle_open = json.loads(socket.recv_string())['result']['value']
        socket.send_string('{"method":"datasource.T","args":{"datasource_uuid":"%s","candle_index":%d}}' % (datasource_uuid, i))
        candle_time = json.loads(socket.recv_string())['result']['value']
    
  • luks sluk
    24 февраля 2020, 15:42

    иллюстрация, куда можно это дальше применить

    quotes = {}
    quotes['open']=numpy.asarray([item['open'] for item in data])
    quotes['close']=numpy.asarray([item['close'] for item in data])
    quotes['high']=numpy.asarray([item['high'] for item in data])
    quotes['low']=numpy.asarray([item['low'] for item in data])
    quotes['datetime']=numpy.asarray([item['datetime'] for item in data])
    fig, ax = plt.subplots(3, sharex=True)
    candlestick2_ohlc(ax[0],quotes['open'],quotes['high'],quotes['low'],quotes['close'],width=0.6)
    ax[0].xaxis.set_major_locator(ticker.MaxNLocator(6))
    def chart_date(x,pos):
        try:
            return quotes['datetime'][int(x)]
        except IndexError:
            return ''
    ax[0].xaxis.set_major_formatter(ticker.FuncFormatter(chart_date))
    fig.autofmt_xdate()
    fig.tight_layout()
    quotes['close'] = numpy.array([float(x) for x in quotes['close']])
    sma = talib.SMA(quotes['close'], timeperiod=50)
    ax[0].plot(sma)
    ema = talib.EMA(quotes['close'], timeperiod=20)
    ax[0].plot(ema)
    macd, macdsignal, macdhist = talib.MACD(quotes['close'], fastperiod=12, slowperiod=26, signalperiod=9)
    ax[1].plot(macd, color="y")
    ax[1].plot(macdsignal)
    hist_data = []
    for elem in macdhist:
        if not numpy.isnan(elem):
            v = 0 if numpy.isnan(elem) else elem
            hist_data.append(v*100)
    ax[2].fill_between([x for x in range(len(macdhist))], 0,macdhist)
    plt.show()
    
  • 3Qu
    24 февраля 2020, 15:54
    Возможно уже писал.
    Не стоит искать сокеты для Луа.
    Уходим из Луа в С++ DLL, а в ДЛЛ уже делаем сокет(ы) клиент(ы) на С++. Экземплы сокетов на С++ есть в инете. Куча.
      • 3Qu
        24 февраля 2020, 17:53
        Дмитрий К, мне жаль, но я не смогу вам объяснить ни в двух, ни в десяти словах.(
        Это надо хотя бы пару часов с вами посидеть — поразбираться.
        ЗЫ Посмотрел профиль, далековато вы.
          • 3Qu
            24 февраля 2020, 18:22
            Дмитрий К, на вскидку не скажу. Позднее, вечером посмотрю доки 
          • 3Qu
            24 февраля 2020, 20:48
            Дмитрий К, теперь могу написать
            как передать 11 аргументов в качестве каллбэка в эту функцию?
            Никак!
            Коллбэки — это события терминала. Вы должны определить эти функции в своей программе строго в соответствии с их шаблонами.
            При вызове их из терминала вы можете использовать переданные вам аргументы в определенной вами функции, и также вызывать из своей коллбэк функции некоторый ограниченный набор функций-запросов к терминалу. Сами эти функции-запросы определены в документации.
              • 3Qu
                24 февраля 2020, 21:47
                Дмитрий К, если мы имеем в виду trans2quik.dll, то формат этих коллбэк функций четко определен.
                1. void __stdcall TRADE_STATUS_CALLBACK (long nMode, double dNumber, double dOrderNumber, LPCTSTR ClassCode, LPCTSTR SecCode, double dPrice, long nQty, double dValue, long nIsSell, long nTradeDescriptor)
                2. void __stdcall ORDER_STATUS_CALLBACK(long nMode, DWORD dwTransID, double dNumber, LPCTSTR ClassCode, LPCTSTR SecCode, double dPrice, long nBalance, double dValue, long nIsSell, long nStatus, long nOrderDescriptor)
                и никак иначе (это в С++). Внутреннее наполнение функции вы делаете сами. Функции вызываются непосредственно из терминала, не вами. Вы туда ничего не передаете, и не можете передать.
                Или уже я что-то не понимаю. Мы же про коллбэки говорим?
                  • 3Qu
                    26 февраля 2020, 02:25
                    Дмитрий К, это определения функций ORDER_STATUS_CALLBACK и TRANS2QUIK_START_ORDERS для работы с DLL. И некой start_orders.
                    Как их конкретно использовать в Питон? — Понятия не имею. Мало информации. В этом супе чего-то не хватает.)
                    Пока не разберетесь, этот код лучше вообще руками не трогать, и скорее всего, вообще менять не придется, если в нем нет ошибок.
                    ЗЫ В общем, ясно, это PyQuik. Проект не обновлялся с 12 года, если не ошибаюсь. Не факт, что он будет работать без редактирования на современном Питон. Хотя, и не факт, что не будет.
  • luks sluk
    24 февраля 2020, 16:11
    import zmq
    
    context = zmq.Context()
    socket = context.socket(zmq.SUB)
    socket.connect("tcp://127.0.0.1:5561")
    socket.setsockopt(zmq.SUBSCRIBE, b"OnAllTrade")
    while True:
        print(json.loads(socket.recv_unicode()))
    
  • luks sluk
    24 февраля 2020, 16:14
     

    Схемы сообщений JSON
    В общих чертах, формат сообщений такой:

    Запрос:

    {
      "method":"НАЗВАНИЕ_QLUA-ФУНКЦИИ",
      "args": {
        // АРГУМЕНТЫ QLUA-ФУНКЦИИ
      }
    }
    

    Ответ:

    {
      "method": "НАЗВАНИЕ_QLUA-ФУНКЦИИ",
      "result": // РЕЗУЛЬТАТ QLUA-ФУНКЦИИ (число, объект, строка -- в зависимости от вызываемой функции)
    }<br>Схемы:<br><a href="https://github.com/Enfernuz/quik-lua-rpc/tree/master/json/schema">https://github.com/Enfernuz/quik-lua-rpc/tree/master/json/schema</a>
    
  • Kot_Begemot
    24 февраля 2020, 16:22
    аналогичные проблемы в аналогичных ситуациях (Матлаб-COM-Lua). Проблема в том, что подключаемая dll 32х битная и LUA 64x напрочь отказывается её подгружать. 

    Решение :
    1. добавить в связку коммуникатор 32бит/64бит (не занимался).
    2. перекомпилировать библиотеку под 64 бита через… VS14+ (пробовал через MinGW и встроенные скрипты компиляции — у меня не вышло)
  • luks sluk
    24 февраля 2020, 16:39
    Автор спросил: Как экспортировать данные из квика через сокеты
    Ответ: Готовое решение: с помощью https://github.com/Enfernuz/quik-lua-rpc
  • Replikant_mih
    24 февраля 2020, 17:28
    А такая тема может сработать? — В СУБД могут быть триггеры, не все СУБД это поддерживают, но некоторые да, например MySQL.

    Можно сделать триггер, что при вставке-изменении значений в конкретной таблице что-то делать, кажется там можно дергать стороннее приложение даже в рамках этой функциональности.
      • Replikant_mih
        24 февраля 2020, 18:04
        Дмитрий К, А не пробовали копать в более специлазированные алго-API?) — там точно будет подписка на события. Такие платформы конечно более брокер-специфичны, ну в смысле у каждого брокера своя — у финами, ITI Capital такие есть, например. Но там в обоих примерах Python не прокатит без прослоек дополнительных.
          • asfa
            24 февраля 2020, 18:59
            Дмитрий К, чем хорош ВТБ относительно других??
            (Возможно через другого брокера/др. софт решить вашу проблему будет гораздо проще)
          • Replikant_mih
            24 февраля 2020, 21:39
            Дмитрий К, Ну хз, я как-то посвободней в выборе брокера, т.е. это не что-то что выбрал одного и на века, можно и разных попробовать.

      • luks sluk
        25 февраля 2020, 09:48
        trans2quik python https://github.com/alexanderarcha95/py2quik

        trans2quik – это официальное документированное API для взаимодействия внешних приложений с торговым терминалом QUIK. Оно позволяет из внешних программ выполнять различные операции, в основном это отсылка транзакций, получение информации о заявках и сделках. Рыночная информация через это API не доступна

        Технически данное API устроено следующим образом: разработчиками QUIK поставляется DLL-библиотека, которую внешнее приложение загружает и вызывает из нее доступные интерфейсные функции.

    • alewmt
      24 февраля 2020, 18:49
      Replikant_mih, триггер то можно, но это не для того, чтобы внешний софт дергать, а внутренние процессы запускать. Подходы для дергания внешнего софта существуют, но их традиционно избегают. Лучше все-таки дергать в цикле, но стоит учитывать, чтобы запрос был легким для БД.
      • Replikant_mih
        24 февраля 2020, 21:41
        alewmt, Ну да, понятно, что это для внутренних целей. Ну, кстати, как вариант можно сделать так:
        сделать триггер, чтобы изменения в тяжелых таблицах делали изменения в легкой сигнальной, а опрашивать эту легкую — её вообще можно сделать чисто в оперативке.
  • Replikant_mih
    24 февраля 2020, 17:33
     Даже если там можно тупо запускать стороннее приложение, то можно сделать простейший скрипт на питоне, основная задача которого, будучи запущенным СУБД, будет установить сокет соединение с основным приложением и сказать ему, что было изменение (даже если нет возможности сразу сказать, какое именно), а дальше уже делаешь селект к базе основным приложением.
  • luks sluk
    24 февраля 2020, 17:40
    Данные из QUIK должны сначала дойти до СУБД. QUIK выступает socket сервером с помощью quik-lua-rpc. СУБД может являться socket клиентом без дополнительных прослоек?
  • Евгений Шибаев
    24 февраля 2020, 18:23
    https://github.com/finsight/QUIKSharp/tree/master/src/QuikSharp/lua
    Вот здесь гляньте 64-битная, там все как в 32-битной, сама dll называется lua51.dll (без точки 5.1). Работает «странненько» на восьмерке Квика. Может полгига данных передать, поработать минут 30 — потом внезапно отваливается. А бывает почти сразу отваливается. Время не было разобраться. У кого получится стабильная работа — напишите, если не трудно.

  • luks sluk
    24 февраля 2020, 18:24
    quik = socket server <--> python script = socket client <--> mysql 
  • alewmt
    24 февраля 2020, 18:53
    Но у такой связки есть слабое место, приходится в питоне запускать таймер, по кjторому питон опрашивает базу данных.
    Это не логично, робот должен совершать какие то действия в ответ на событие, а не просто молотить запросы в БД.
    Почему это слабое место? Только по тому что не логично? Или есть проблемы с производительностью?
      • Андрей К
        24 февраля 2020, 20:02
        Дмитрий К, в таких задачах еще надо уметь правильно создавать БД. Ключи правильные делать, индексы.. 
      • alewmt
        24 февраля 2020, 20:17
        Дмитрий К, не сочтите за оскорбление, это дилетантский аргумент, есть простая, надежная и рабочая схема без бутылочных горлышек. И не надо пытаться нагородить глючных велосипедов без достаточных на то оснований. Вы не очень представляете как работает сервер бд. единственное, что нужно обеспечить это чтобы запрос был легким и не вытаскивал каждый раз из бд 100500 мегабайт.
          • alewmt
            24 февраля 2020, 20:54
            Дмитрий К, зато я программист, и как программист я вам и говорю, не занимайтесь ерундой
          • alewmt
            24 февраля 2020, 21:38
            Дмитрий К, мой предыдущий ответ звучит резковато и не очень аргументировано, поэтому я решил немного развернуть мысль чтобы и сгладить и лучше донести мысль. за свою карьеру я имел много возможностьей убедиться насколько разумнее делать good enough и не гнаться за идеальным решением. Я прекрасно понимаю о чем вы говорите, но профессиональный опыт говорит другое, исходить надо из практической целесообразности, а не соображений совершенства. То о чем вы говорите методом логических рассуждений приведет к колокации на бирже с проводом воткнутым прямо в fpga как самому эффективному способу. Вы наверняка, возразите, что это уже перебор, т.к. дорого и не окупится, но то что вы хотите сделать тоже дорого и не окупиться с тз потраченных усилий. Я вас уверяю, если вы не будуте тратить время на переделку работы на дде сервер, то денег будет либо столько же либо больше, т.к. эти много десятков часов вы потратите на исследования рынка на не создание велосипеда.
      • Свой Мужик
        24 февраля 2020, 21:27
        Дмитрий К, да в индексы загоняешь и всё ок ) на край можно MOMORY TABLE юзать… там всё супер быстро и без нагрузки…
      • Свой Мужик
        24 февраля 2020, 21:30
        Дмитрий К, выложите плз, что у Вас есть что можно в mysql загонять плз со структурой базы, а то я буквально в воскресение открыл lua... 
        Спасибо.
          • Свой Мужик
            24 февраля 2020, 21:46
            Дмитрий К, да )
              • Свой Мужик
                24 февраля 2020, 22:25
                Дмитрий К, спасибо завтра попробую разобраться начиная с того как  odbc к mysql прикрутить )
  • luks sluk
    24 февраля 2020, 18:54
    Если нужно ohlc или ticks загонять в mysql, то socket client получает от socket server нужные данные с помощью CreateDataSource(создать источник и получить свечи или тики) и размещает их в mysql с помощью любого python mysql connector
  • luks sluk
    24 февраля 2020, 18:58
    Тики или свечи можно попутно собирать в mysql или любую другую СУБД, при этом с помощью quik-lua-rpc из «коробки» можно получать данные из quik, анализировать их средствами python и выполнять ордера без использования TRANS2QUIK и без tri файлов
  • П М
    24 февраля 2020, 21:37
    https://smart-lab.ru/blog/550216.php

    + с
    м там последний коммент от swerg'a, 
  • luks sluk
    25 февраля 2020, 00:22
    А зачем в СУБД заливать данные? Через сокет доступны все исторические данные, которые есть на открытых графиках
  • luks sluk
    29 июля 2020, 17:41
    WebQuik прекрасно принимает и передает всю необходимую информацию через Websocket. Рабочий пример https://github.com/DmitryPukhov/pytrade
    WebQuik <--> Websocket <--> Python
  • Виктор Понедельник
    15 августа 2020, 20:38
    Скажите пожалуйста, я сделал вывод по odbc в  mysql, но меняется только одна строчка в таблице. Как сделать, чтобы таблица наполнялась?
      • Виктор Понедельник
        16 августа 2020, 08:06
        Дмитрий К, экспортирую имя символа, время и объем сделки. Создал соответствующие колонки, но пишется только одна строчка, которая все время меняется. Видать, квик свою работу делает, а в базе что-то не то, не могу понять что
        ЗЫ. Понял в чем дело. С автонумерацией первого столбца. 

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

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