Giter Club home page Giter Club logo

quik-lua-rpc's Introduction

quik-lua-rpc

Build Status Coverage Status License

RPC-сервис для вызова процедур из QLUA -- Lua-библиотеки торгового терминала QUIK (ARQA Technologies).

Содержание

Зачем?

Торговый терминал QUIK -- одно из немногих средств для торговли на российском рынке. Он предоставляет API в виде библиотеки QLua, написанной на Lua. Написать торговую программу, работающую с QUIK, на чём либо отличном от Lua до сих пор было не так просто (хотя и предпринимаются попытки вытащить API QLua в другие языки, например, в C# -- QUIKSharp).

Как это работает?

Данный сервис представляет собой RPC-прокси над API библиотеки QLua. Сервис исполняется в терминале QUIK в виде Lua-скрипта и имеет прямой доступ к библиотеке QLua. Общение сторонних программ с сервисом осуществляется посредством ZeroMQ ("сокеты на стероидах"), реализуя паттерн REQ/REP (Request / Response), по протоколу TCP. Запросы на вызов удалённых процедур и ответы с результатами выполнения этих процедур передаются либо в бинарном виде, сериализованные с помощью Protocol Buffers, либо в символьном виде, сериализованные в JSON.

Помимо вызова удалённых процедур сервис также может рассылать оповещения о событиях терминала QUIK, реализуя паттерн PUB/SUB (Publisher / Subscriber).

Соответственно, выбор языка программирования для взаимодействия с QLua ограничивается лишь наличием на этом языке реализации ZeroMQ, коих довольно большое количество.

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

Версия терминала QUIK

DLL-файлы из qlua_redist/... были взяты от QUIK 9.2.3.15. Соответственно, библиотека расчитана на работу с терминалом примерно этой версии и может не(корректно) работать в терминалах QUIK версий, отличных от указанной.

Установка программы из архива

Распакуйте архив built/quik_lua_rpc.tar.xz в каталог установленного терминала Quik (например, D:/QUIK/).
Результаты антивирусной проверки built/quik_lua_rpc.tar.xz: VirusTotal.

Сборка и установка из исходников

  1. Установите Docker и Docker-compose для вашей операционной системы.
  2. Клонируйте этот репозиторий.
  3. Хорошая идея - заменить библиотеки в каталоге quik_redist на версии из вашего Quik (результаты антивирусной проверки 03.12.2021: lua53.dll, qlua.dll).
  4. Откройте консоль в каталоге репозитория.
  5. Соберите образ Docker. docker-compose build --force-rm
  6. Создайте установочный архив. docker-compose run quik-lua-rpc
  7. Распакуйте архив built/quik_lua_rpc.tar.xz в каталог установленного терминала Quik (например, D:/QUIK/).

Запуск программы

В терминале QUIK в меню Lua-скриптов добавить и запустить скрипт %PATH_TO_SERVICE%/main.lua, где %PATH_TO_SERVICE% -- путь до папки с программой включительно (например, D:/QUIK/lua/quik-lua-rpc).

Конфигурации точек подключения находятся в файле %PATH_TO_SERVICE%/config.json.

Краткая справка по формату конфигурационного файла на примере:

{
    // Точки подключения. Может быть сколько угодно различных точек подключения со своими настройками.
    "endpoints": [
    {
        // Тип точки подключения: 
        // "RPC" -- для удалённого вызова процедур,
        // "PUB" -- для рассылки событий терминала.
        "type": "RPC", 
	// Тип протокола сериализации/десериализации сообщений:
	// "json" -- JSON
	// "protobuf" -- Protocol Buffers
	"serde_protocol": "json",
        // Признак активности/неактивности точки. Ненужные на данный момент точки можно деактивировать.
        "active": true, 
        // TCP-адрес точки подключения. 
        // На данный момент ZeroMQ не поддерживает ipc-абстракцию под Windows, 
        // поэтому  для транспорта остаётся TCP.
        "address": {
            "host": "127.0.0.1",
            "port": 5560
        },
        // Секция настройки аутентификации для точки подключения.
        "auth": {
            // Механизм аутентификации ZeroMQ: 
            // "NULL" или пустая строка (нет аутентификации), 
            // "PLAIN" (пара логин/пароль без шифрования трафика),
            // "CURVE" (ключевая пара и шифрование трафика).
            "mechanism": "CURVE",
            // Секция настройки PLAIN-аутентификации.
	    // Может отсутствовать при выборе механизма NULL или CURVE.
            "plain": {
                // Список пользователей для точки подключения.
                "users": [
                    {"username": "test_user", "password": "test_password"}
                ]
            },
            // Секция настройки CURVE-аутентификации.
	    // Может отсутствовать при выборе механизма NULL или PLAIN.
            "curve": {
                    // Серверная ключевая пара CURVE 
		    // (сгенерировать новую пару можно с помощью скрипта curve_keypair_generator.lua)
                    "server": {
                        "public": "rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7",
                        "secret": "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6"
                    }, 
                    // Список публичных CURVE-ключей пользователей
                    "clients": ["Yne@$w-vo<fVvi]a<NY6T1ed:M$fCG*[IaLV{hID"]
            }
        }
    }, 

    {
        "type": "PUB", 
	"serde_protocol": "json",
        "active": true, 
        "address": {
            "host": "127.0.0.1",
            "port": 5561
        },
        "auth": {
            "mechanism": "PLAIN", 
            "plain": {
                    "users": [
                        {"username": "admin", "password": "letmein"}
                    ]
            }
        }
    }]
}

Убедитесь, что используемые вами порты открыты.

Схемы сообщений Protocol Buffers

Схемы сообщений расположены внутри директории qlua/rpc в виде файлов .proto.

Схемы сообщений JSON

** В РАЗРАБОТКЕ **

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

Запрос:

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

Ответ:

{
  "method": "НАЗВАНИЕ_QLUA-ФУНКЦИИ",
  "result": // РЕЗУЛЬТАТ QLUA-ФУНКЦИИ (число, объект, строка -- в зависимости от вызываемой функции)
}

Ответ в случае ошибки сервиса:

{
  "method":"НАЗВАНИЕ_QLUA-ФУНКЦИИ",
  "error": {
    "code": // ЧИСЛОВОЙ КОД ОШИБКИ,
    "message": "ИНФОРМАЦИЯ ОБ ОШИБКЕ"
  }
}

!!! Все дробные числа передаются как строки (что в аргументах, что в ответе от сервиса).

API-клиенты

Примеры и гайды

Разработчикам

Инструкцию для разработчиков для версий v1.x можно найти здесь: https://github.com/Enfernuz/quik-lua-rpc/blob/master/CONTRIBUTING.md

Для версий v2.x документация находится в разработке.

FAQ

Q: Используешь Protocol Buffers, но не используешь gRPC. Как так?

A: Для Lua пока не запилили генерацию стабов gRPC. Сообщите, когда появится.

Q: А что насчёт Thrift? Там вроде есть поддержка Lua.

A: Если мне память не изменяет, там в зависимостях библиотеки, для которых исходники только под UNIX (например, luabpack).

English version

If you deliberately want to have the English version of this README or just want some answers, feel free to reach me via GitHub or email. I'm planning to do some English translation, but the laziness is unbearable... Go on, kick my ass a little :)

quik-lua-rpc's People

Contributors

abrosimov-a-a avatar alexveden avatar enfernuz avatar euvgub avatar gojmpz avatar yse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quik-lua-rpc's Issues

Ошибка при сериализации эвента OnOrder / OnOrder event serialization issue

0

Инспекция объекта order на предмет наличия данного числа выдала два поля: order_num и ordernum. В схеме qlua_events.proto класс Order имеет только поле order_num с типом int64. Возможны два варианта:

  1. недостающее поле 'ordernum' автоматически маппится с типом int32 (маловероятно)
  2. в биндинге protobuf для Lua тип int64 равносилен int32

Inspection of the order object in order to find the long number revealed two fields order_num & ordernum. The class Order in the schema qlua_events.proto has only the field order_num of type int64.
There are two possible options:

  1. the lacking field 'ordernum' maps automatically with type int32 (less likely)
  2. the type int64 maps to int32 in the protobuf Lua binding.

Поддержка GraphQL.

Как вариант API over HTTP, можно имплементировать API на GraphQL.

GraphQL удобен в обращении, имеет реализацию на многих общеиспользуемых языках и позволит генерировать клиентский код автоматически. Примерно того же самого можно добиться с помощью, к примеру, gRPC, но у GraphQL есть преимущество в виде "встроенной" гибкости запросов -- можно составить запросы таким образом, чтобы исключить передачу ненужной информации, что позволит сэкономить на времени обработки запросов.

Внимательно выслушаю ваши "за" и "против".

Сборка под QUIK-8.5+/lua-5.3

В QUIK-8.5 произошло обновление до lua-5.3, так что предлагаемая сборка, к сожалению, не работает.

Сломана обработка сделок в Quik 8.8.4

При обработке сделок возникает ошибка:

...lua\quik-lua-rpc/impl\protobuf_event_data_serializer.lua:25: bad argument #2 to 'encode' (string expected for field 'uid', got number)

Я не смогу заняться этой проблемой раньше середины следующей недели.

AddLabel label_params просьба пример

AddLabel label_params просьба пример
График с иднетификатором "test_siu" открыт.
Посылаем два варианта добавления текстовой и графической метки
{"method":"AddLabel", "args":{"chart_tag":"test_siu","label_params":{"TEXT":"Test Label","YVALUE":64000,"DATE":20190626}}}
Текстовая метка не появляется
{"IMAGE_PATH":"C:\Users\tester\Labels\buy.bmp","YVALUE":64000,"DATE":20190626}}}
Метка-изображение не появляется

Не работает сборка докер-образа с zeromq 4.3.4

Проблема сборки появляется начиная с версии 4.3.1.

Dockerfile:
...
ENV LIBZMQ_VER 4.3.4
...

In file included from src/mailbox_safe.hpp:43,
                 from src/mailbox_safe.cpp:31:
src/condition_variable.hpp:129:10: error: 'condition_variable_any' in namespace 'std' does not name a type
     std::condition_variable_any _cv;
          ^~~~~~~~~~~~~~~~~~~~~~
src/condition_variable.hpp:129:5: note: 'std::condition_variable_any' is defined in header ''; did you forget to '#include '?
src/condition_variable.hpp:98:1:
+#include 
 
src/condition_variable.hpp:129:5:
     std::condition_variable_any _cv;
     ^~~
src/condition_variable.hpp: In member function 'int zmq::condition_variable_t::wait(zmq::mutex_t*, int)':
src/condition_variable.hpp:111:13: error: '_cv' was not declared in this scope
             _cv.wait (
             ^~~
src/condition_variable.hpp:111:13: note: suggested alternative: 'recv'
             _cv.wait (
             ^~~
             recv
src/condition_variable.hpp:113:20: error: '_cv' was not declared in this scope
         } else if (_cv.wait_for (*mutex_, std::chrono::milliseconds (timeout_))
                    ^~~
src/condition_variable.hpp:113:20: note: suggested alternative: 'recv'
         } else if (_cv.wait_for (*mutex_, std::chrono::milliseconds (timeout_))
                    ^~~
                    recv
src/condition_variable.hpp:114:28: error: 'std::cv_status' has not been declared
                    == std::cv_status::timeout) {
                            ^~~~~~~~~
src/condition_variable.hpp: In member function 'void zmq::condition_variable_t::broadcast()':
src/condition_variable.hpp:125:9: error: '_cv' was not declared in this scope
         _cv.notify_all ();
         ^~~
src/condition_variable.hpp:125:9: note: suggested alternative: 'recv'
         _cv.notify_all ();
         ^~~
         recv
make[1]: *** [Makefile:5574: src/libzmq_la-mailbox_safe.lo] Error 1

Открытый список вызываемых функций

Здравствуйте!
Потребовалось настроить удаленный запуск новой кастомной LUA функции. Пришлось внести правки в 100500+ файлов проекта, но задачку решил и в итоге потерял возможность использовать обновления из репозитория.
Было бы здорово, если бы сервис использовал специальный справочник с описанием допустимых функций и параметров.
Есть ли какие-нибудь архитектурные ограничения, которые не позволят это реализовать?

Сломанный декодер protobuf в lua

При попытке отправить запрос GetItem с index = 128 скрипт со стороны quik выдаёт вот такую ошибку:
...:\Program Files\QUIK-Junior\lua/protobuf\decoder.lua:305: Truncated message.

Если же отправлять index=127 - всё нормально, есть запрос-ответ. Отправка из java, версия protoc - 3.2.0. Временное решение - сказать строго, что поле index фиксированного размера в 4 байта. В этом случае приёмная сторона lua работает. Вероятно, в lua устаревший декодер.

Такая же ошибка при отсылке структуры sendTransaction, ошибка тоже в декодере, но уже в другом месте:
...:\Program Files\QUIK-Junior\lua/protobuf\decoder.lua:220: Truncated string.

Вместо времени в структурах отдаваемых getItem отсылаются id таблицы с временем

В обработке функции getItem результат пропускается через функцию to_string_string_table(), которая значения всех полей приводит к строкам через tostring(). Однако время в таблицах Quik обычно представлено структурой {year, month, day, week_day, hour, min, sec, ms, mcs}. Функция tostring() же от такой таблицы возвращает лишь строку типа 'table: 115F2B78', т.е. id объекта вместо его содержимого. Что делает невозможным через getItem получать значение времени в колонках таких таблиц, например, как ORDERS или TRADES.

Асинхронные вызовы из Python (пример кода) и ACCESS_VIOLATION

Пытаюсь экспериментировать с библиотекой, и сделать асинхронные вызовы. Сам на Linux Debian и пробую работать как через Wine так и на виртуальной машине под Win7.

В целом асинхронный код работает сносно, только возникает проблема ACCESS_VIOLATION в main.lua когда я запускаю этот запрос на квике в котором открыто много таблиц. В квике без открытых окон получается все ок.

Вопросы к разработчикам:

  1. Поддерживает ли lua в квике многопоточность / асинхронность (судя по всему да)
  2. Как можно отлаживать подобные ACCESS_VIOLATION проблемы? Debug Log? М.б. какие-нибудь логи включить?

Хочу поделиться примером кода как можно асинхронно получать данные из квика через QuikLua:

Тут для сравнения получаем последовательно и асинхронно. Разница для долгих запросов весьма существенна
13 запросов 1-мин истории асинхронно занимают 17сек, они же последовательно около 100сек.

import zmq
import json

print("Connecting to test server")
import time
import asyncio
import zmq
import zmq.asyncio

context = zmq.asyncio.Context()

HOST = 'tcp://127.0.0.1:5560'

async def recv_history(ticker):
    print(f'recv_history({ticker})')
    socket = context.socket(zmq.REQ)
    socket.connect(HOST)
    await socket.send_string(json.dumps({"method":"datasource.CreateDataSource",
                                         "args":{"class_code":"SPBFUT", "sec_code": ticker, "interval":"INTERVAL_M1", "param":""}}))
    datasource_uuid = json.loads(await socket.recv_string())['result']['datasource_uuid']
    socket.send_string('{"method":"datasource.Size","args":{"datasource_uuid":"%s"}}' % (datasource_uuid))
    num_candles = json.loads(await socket.recv_string())['result']['value']
    candle_close = None
    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(await 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(await 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(await socket.recv_string())['result'] #['time']

        #print(f'{candle_time}: {candle_close}')

    print(f'{ticker}: #{num_candles} bars, last close: {candle_close}')

async def recv_classes():

    socket = context.socket(zmq.REQ)
    socket.connect(HOST)
    await socket.send_string("{\"method\":\"getClassesList\"}")
    print('Sent recv_classes')
    message = await socket.recv()
    print("Received reply [ %s ]" % (message))


async def recv_and_process():
    socket = context.socket(zmq.REQ)
    socket.connect(HOST)

    print("\nSending request")
    await socket.send_string("{\"method\":\"message\", \"args\":{\"message\":\"Hello!\",\"icon_type\":\"WARNING\"}}")
    message = await socket.recv()
    print("Received reply [ %s ]\n" % (json.loads(message)))

loop = asyncio.get_event_loop()
t_begin = time.time()

print('Gather Async')
loop.run_until_complete(asyncio.gather(
        recv_history('SiH1'),
        recv_history('RIH1'),
        recv_history('SiH1'),
        recv_history('RIH1'),
        recv_history('SiH1'),
        recv_classes(),
        recv_history('RIH1'),
        recv_history('SiH1'),
        recv_history('RIH1'),
        recv_history('SiH1'),
        recv_history('RIH1'),
        recv_history('SiH1'),
        recv_history('RIH1'),
        recv_history('SiH1'),
        recv_classes(),
        recv_history('RIH1'),
        recv_history('SiH1'),
        recv_history('RIH1'),
    ))
print(f'Gather Async Finished in {time.time()-t_begin}')

t_begin = time.time()
print('Run sync')
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
loop.run_until_complete(recv_classes())
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
loop.run_until_complete(recv_classes())
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
loop.run_until_complete(recv_history('SiH1'))
loop.run_until_complete(recv_history('RIH1'))
print(f'Run sync Finished in {time.time()-t_begin}')

Добавить API на основе JSON / Add JSON API implementation.

Реализация обмена информацией на основе бинарного протокола Protocol Buffers имеет преимущество в производительности, но в то же время несколько усложняет реализацию клиентов, а для тех ЯП, где реализации protobuf пока нет, дела совсем плохи.
Внедрение API на основе JSON позволит найти компромисс между производительностью и простотой написания клиентской части.


The binary Protocol Buffers protocol has an advantage of being an efficient serialization/deserialization tool but this efficiency comes at a price: not many programming languages have a protobuf binding (and for those ones which do, the bindings often lack quality and/or aren't in active development).
An API implementation based on JSON could offer a tradeoff between the efficiency of the serialization/deserialization process and the ease of the client-side development.

assert error в protobuf_request_response_serde.lua:46

Например, Subscribe_Level_II_Quotes_pb2 или getMoneyEx_pb2

Падает на такой ошибке в quik-lua-rpc Subscribe_Level_II_Quotes_pb2:

stack traceback:
	G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:374: in function <G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:373>
	[C]: in function 'assert'
	...ua\quik-lua-rpc/impl\protobuf_request_response_serde.lua:46: in function 'impl.protobuf_request_response_serde.serialize_response'
	G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:141: in function <G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:98>
	(...tail calls...)
	G:\quik\QUIK_BCS\lua\lzmq\poller.lua:80: in method 'poll'
	G:\quik\QUIK_BCS\lua\lzmq\poller.lua:88: in function <G:\quik\QUIK_BCS\lua\lzmq\poller.lua:85>
	(...tail calls...)
	[C]: in function 'xpcall'
	G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:369: in function 'service.start'
	[C]: in function 'pcall'
	G:\quik\QUIK_BCS\lua\quik-lua-rpc\main.lua:48: in function 'main'

При отсутствии всех обезличенных сделок вместо onAllTrade используем CreateDataSource?

При отсутствии всех обезличенных сделок (например, чтобы не грузить лишнюю инфу) вместо onAllTrade используем CreateDataSource - получаем datasource_uuid - посылаем datasource.SetUpdateCallback с аргументами datasource_uuid и f_cb_def?
Непонятно, что такое и откуда берется "f_cb_def"?

send_string конвертит int в string

json и send_string конвертит int в string. Если надо послать сообщение с числовым аргументом, то результат - ошибка. Например в таком сообщении аргумент index, который должен быть integer, шлется string-ом
socket.send_string("{"method":"getItem", "args":{"table_name":"client_codes","index":3}}")

Падает при вызове getQuoteLevel2_pb2

Падает на такой ошибке в quik-lua-rpc getQuoteLevel2_pb2:

stack traceback:
	G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:374: in function <G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:373>
	[C]: in function 'table.sinsert'
	...ua\quik-lua-rpc/impl\protobuf_request_response_serde.lua:461: in function <...ua\quik-lua-rpc/impl\protobuf_request_response_serde.lua:450>
	...ua\quik-lua-rpc/impl\protobuf_request_response_serde.lua:46: in function 'impl.protobuf_request_response_serde.serialize_response'
	G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:141: in function <G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:98>
	(...tail calls...)
	G:\quik\QUIK_BCS\lua\lzmq\poller.lua:80: in method 'poll'
	G:\quik\QUIK_BCS\lua\lzmq\poller.lua:88: in function <G:\quik\QUIK_BCS\lua\lzmq\poller.lua:85>
	(...tail calls...)
	[C]: in function 'xpcall'
	G:\quik\QUIK_BCS\lua\quik-lua-rpc/service.lua:369: in functi

get_depo возвращает 0 по всем позициям, get_depo_ex ничего не возвращают

Пытаюсь получить позицию по бумагам, например по SBER (в портфеле есть 1 лот T+2), get_depo возвращает:

depo_limit_locked_buy_value: "0.0"
depo_current_balance: "0.0"
depo_limit_locked_buy: "0.0"
depo_limit_locked: "0.0"
depo_limit_available: "0.0"
depo_current_limit: "0.0"
depo_open_balance: "0.0"
depo_open_limit: "0.0"

get_depo_ex ничего не возвращает.
Параметры счетов указаны верно.
У кого-нибудь работает?

datasource.SetUpdateCallback не работает

Если не задать параметр f_cb_def, то установка колбэка не срабатывает.

watch_code = "local props = {uuid = " .. ds_uuid ..", index = index} " .. watch_code .. " _G.OnDataSourceUpdate(props)" - значение ds_uuid должно быть в кавычках, это же строка.

ds внутри колбэка не видна (nil). Предполагалось что она в результате замыкания будет видна?

getParamEx param_image в cp1251

getParamEx param_image в cp1251
Если результат getParamEx получать socket.recv_string(encoding='cp1251'), то param_image читается нормально. param_image это число с разделителями. напр 68 007 Если просто socket.recv(), то
b'{"result":{"param_ex":{"param_type":"1","param_value":"66082.000000","result":"1","param_image":"66\xa0082"}}}'
print(json.loads(message)['result']['param_ex']['param_image'])
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa0 in position 99: invalid start byte

Иногда скрипт падает после нескольких минут(или секунд) работы

Ошибка в poller:start. Стек вызовов:
Ошибка в poller:start. Стек вызовов:
stack traceback:
C:\QUIK\lua\quik-lua-rpc/service.lua:374: in function <C:\QUIK\lua\quik-lua-rpc/service.lua:373>
C:\QUIK\lua\lzmq\poller.lua:78: in method 'poll'
C:\QUIK\lua\lzmq\poller.lua:88: in function <C:\QUIK\lua\lzmq\poller.lua:85>
(...tail calls...)
[C]: in function 'xpcall'
C:\QUIK\lua\quik-lua-rpc/service.lua:369: in function 'service.start'
[C]: in function 'pcall'
C:\QUIK\lua\quik-lua-rpc\main.lua:48: in function 'main'

Версия Quik 8.8.4.3

У меня периодически падает quik с ошибкой Exception code: 0xc0000374

У меня операционная система Debian Buster, quik работает в VirtualBox на windows 10. Иногда просто падает, когда запущен quik-lua-rpc. Воспроизводится, например, если послать getSecurityInfo_pb2 без пауз раз 200 подряд.
Ошибка в системном журнале следующая:
quik_error1
quik_error2

Кто-то с таким сталкивался?

Явно пригодился бы json/schema generator

Явно пригодился бы json/schema generator для args.json и для result.json, только найти бы удобный источник для распарсивания. Из proto файлов не очень, потому что в них нет "description". Не помешало бы в args.json иметь "description":"getFuturesLimit - Функция предназначена для получения информации по фьючерсным лимитам. Функция возвращает таблицу Lua с параметрами Лимиты по фьючерсам. В случае ошибки функция возвращает «nil»."
Хотя, может быть description - это лишнее, и его можно посмотреть в справочнике Qlua.
Вот это примерно оно? protobuf to json https://github.com/dpp-name/protobuf-json

python json getCandlesByIndex - данные приходят неупорядоченные по индексу, даты вразнобой

python json getCandlesByIndex - данные приходят неупорядоченные по индексу, даты вразнобой
Quik версия 7.27.2.1 Открыт график SiU9 Price Timeframe = Month
Данные свечей с графика приходят в случайном порядке, а не по индексу от 0-ой свечи до 23-й свечи.
'year': 2017, 'month': 9
'year': 2017, 'month': 11
'year': 2018, 'month': 1
'year': 2019, 'month': 4
'year': 2018, 'month': 12
'year': 2017, 'month': 10

socket.send_string('{"method":"getNumCandles","args":{"tag":"siu9_price_chart"}}')
number_of_candles = json.loads(socket.recv_string())['result']['num_candles']
socket.send_string('{"method":"getCandlesByIndex","args":{"tag":"siu9_price_chart","line":0,"first_candle":0,"count":%d}}' % (number_of_candles-1))
result = json.loads(socket.recv_string())
print(result)
{'result': {'t': [{'low': '64901', 'doesExist': 1, 'close': '65911', 'datetime': {'ms': 0, 'year': 2017, 'day': 1, 'week_day': 5, 'month': 9, 'sec': 0, 'hour': 0, 'min': 0}, 'high': '67482', 'open': '67482', 'volume': '146'}, {'low': '64311', 'doesExist': 1, 'close': '64500', 'datetime': {'ms': 0, 'year': 2017, 'day': 1, 'week_day': 3, 'month': 11, 'sec': 0, 'hour': 0, 'min': 0}, 'high': '66350', 'open': '64600', 'volume': '38'}, {'low': '60233', 'doesExist': 1, 'close': '61342', 'datetime': {'ms': 0, 'year': 2018, 'day': 1, 'week_day': 1, 'month': 1, 'sec': 0, 'hour': 0, 'min': 0}, 'high': '63000', 'open': '63000', 'volume': '40'}, ....... , {'low': '64984', 'doesExist': 1, 'close': '65861', 'datetime': {'ms': 0, 'year': 2019, 'day': 1, 'week_day': 1, 'month': 4, 'sec': 0, 'hour': 0, 'min': 0}, 'high': '67401', 'open': '67135', 'volume': '72814'}, {'low': '68500', 'doesExist': 1, 'close': '72117', 'datetime': {'ms': 0, 'year': 2018, 'day': 1, 'week_day': 6, 'month': 12, 'sec': 0, 'hour': 0, 'min': 0}, 'high': '72260', 'open': '68560', 'volume': '57455'}, {'low': '63694', 'doesExist': 1, 'close': '64500', 'datetime': {'ms': 0, 'year': 2017, 'day': 1, 'week_day': 0, 'month': 10, 'sec': 0, 'hour': 0, 'min': 0}, 'high': '65364', 'open': '65087', 'volume': '55'}], 'l': 'Si-9.19 [Price]', 'n': 23}}

Где-то по пути теряется индекс свечи.

Вроде нашлось решение: в procedure_wrappers.lua строка 471

for i, candle in pairs(t) do 

заменить на

for i, candle in ipairs(t) do 

Для сохранения порядка следования индекса заменить pairs на ipairs - правильно ли?

Имена GET_PARAM_EX_2 и GET_QUOTE_LEVEL2

Проблема:

Разный стиль именования: GET_PARAM_EX_2 и GET_QUOTE_LEVEL2, а именно _2 и 2.

Мотивация:

Необходимо учитывать разный стиль именования именно этих двух переменных, во время конверсии имен функций в соответствующие константы RPC, с использованием регулярного выражения. На клиентской стороне.

Варианты решения:

  • Переименовать GET_PARAM_EX_2 в GET_PARAM_EX2. Нарушит обратную совместимость.
  • Добавить еще одну переменную GET_PARAM_EX2 = 41 в RPC.proto. Сохранит обратную совместимость и решит проблему.
  • Ничего не делать. Все обработать регулярками, но для GET_PARAM_EX_2 писать отдельный if.

Добавить аутентификацию / Add authentication

Т.к. RPC-сервис предполагает доступ к персональным и финансовым данным, критически важным является предоставление пользователю механизмов аутентификации клиентов сервиса.
ZeroMQ поддерживает, помимо никакой, два вида аутентификации:

  • PLAIN: базовая аутентификация по user/password. Лучше чем ничего, но всё равно очень уязвимо;
  • CURVE: аутентификация и шифрование с использованием криптографии.

Есть ещё механизм GSSAPI -- он был добавлен в более поздних версиях ZeroMQ и имплементирован не во всех языковых обвязках ZeroMQ.

Для работы с CURVE библиотеке ZeroMQ нужна библиотека libsodium. В поставке бинарных файлов для Windows находятся библиотеки, не имеющие связи с libsodium. К тому же, крайняя версия ZeroMQ там ограничивается 4.0.4, в которой всё ещё присутствует баг, при котором при использовании PLAIN-аутентификации клиент автоматически переоткрывает соединение при неудачной попытке аутентификации (см. zeromq/libzmq#882 -- исправлено в 4.1.0).

Таким образом, необходимо собрать крайнюю стабильную версию библиотеки ZeroMQ под Windows, слинкованную с libsodium, и внедрить её в проект.


As the RPC-service is meant to give access to personal and financial data, it is of critical importance to give the user some authentication mechanisms.
ZeroMQ supports two types of authentication (aside from none):

  • PLAIN: basic authentication on a user/password pair. Better than nothing but still very unreliable;
  • CURVE: cryptographic authentication & encryption

There's another one called GSSAPI -- it was added in the later versions of ZeroMQ and haven't yet been implemented in many ZeroMQ language bindings.

To support CURVE ZeroMQ needs a library named libsodium. The binary files package for Windows consists of binaries that are not linked with libsodium. Besides, the ZeroMQ library in those packages has the version up to 4.0.4 in which there's still a bug when using the PLAIN authentication: the client automatically reopens the connection after a failed authentication attempt (see zeromq/libzmq#882 -- it was fixed in 4.1.0).

Thus, we need to build the last stable version of ZeroMQ on Windows linked against libsodium and integrate it into the project.

getOrderByNumber: Номер заявки не помещается в uint32 / Order number doesn't fit into uint32

Имею в терминале заявку с номером 30745970930. Такое число не помещается в uint32: в частности, в Java этот тип маппится в int, а не long.


I have an order with the order number 30745970930. This number does not fit into uint32. In particular, Java protobuf maps this type into not Java's long but into int.

AsyncIO Python клиент для работы с Quik Lua RPC

Сделал прототип клиента на Python AsyncIO работающий на JSON если кому-нибудь будет полезно

Основные возможности

  • Клиент создан на основе асинхронных вызовов AsyncIO
  • Клиент использует JSON протокол QuikLua (который в данный момент в статусе альфа)
  • Клиент поддерживает параллельные запросы через несколько сокетов и организует SocketPool
  • Клиент пока работает только с сокетами без авторизации и только localhost (из соображений безопасности)
  • Клиент пока не поддерживает подписку на события
  • Клиент поддерживает получение исторических данных и их кеширование в памяти
  • Клиент хранит данные истории котировок в формате Pandas.DataFrame
  • Клиент поддерживает проверку наличия соединения через heartbeat и выдает ошибку если socket QuikLua недоступен
  • Клиент поддерживает load-balancing т.е. вы в теории можете запустить 100 асинхронных запросов истории котировок
    и клиент распределит нагрузку, чтобы LUA скрипт на той стороне не упал и не сожрал всю память
  • У меня вроде даже работает на Linux Debian с квиком под Wine! Если что, то можно подключиться к квику на
    виртуальной машине.

Пример кода

import asyncio
from aioquiklua import QuikLuaClientBase, QuikLuaException, QuikLuaConnectionException, QuikLuaNoHistoryException
import traceback
import time

class QuikLuaClientSample(QuikLuaClientBase):

    async def main(self):
        # Вызываем main() основного класса для инициализации внутренних переменных
        await super().main()

        try:
            # Тут вызываем логику модели, подписываемся на события и т.п.
            class_list = await self.rpc_call('getClassesList')
            print('RPC: getClassesList')
            print(class_list)

            # RPC с параметрами
            rpc_result = await self.rpc_call('message', message='Hello world', icon_type='WARNING')
            print('RPC: message')
            print(rpc_result)

            # Заказываем историю котировок (первый запуск может занимать до 10 секунд), потом котировки заполняют кеш и
            # обновляются только последние данные
            time_begin = time.time()
            print(f'RPC: price history')
            quotes_df = await self.get_price_history('SPBFUT', 'RIH1', 'INTERVAL_M1', use_caching=True)
            print(quotes_df.tail(5))
            print(f'Price backfill took {time.time()-time_begin}sec')

            time_begin = time.time()
            print(f'RPC: price history cached')
            quotes_df = await self.get_price_history('SPBFUT', 'RIH1', 'INTERVAL_M1', use_caching=True)
            print(quotes_df.tail(5))
            print(f'Price backfill took {time.time() - time_begin}sec')

            while True:
                # Нажмите ctrl+c чтобы завершить
                # Получаем heartbeat(), он возвращает результат RPC getInfoParam('LASTRECORDTIME')
                await self.heartbeat()
                await asyncio.sleep(1)
        except asyncio.CancelledError:
            # AsyncIO valid stop
            raise
        except KeyboardInterrupt:
            print('KeyboardInterrupt')
        except:
            print(traceback.format_exc())
        finally:
            # Завершаем выполнение (ОБЯЗАТЕЛЬНО вызывайте shutdown() особенно если вы заказывали историю котировок!)
            await self.shutdown()


if __name__ == '__main__':
    qclient = QuikLuaClientSample("tcp://localhost:5560",               # RPC сокет
                                  None,                                 # PUB сокет
                                  socket_timeout=100,                   # Таймаут сокета после которого он выдает ошибку (в миллисекундах)
                                  n_simultaneous_sockets=5,             # Количество одновременно открытых сокетов
                                  history_backfill_interval_sec=10,     # Таймаут на ожидание истории (в секундах) (обычно занимает менее 1 сек)
                                  cache_min_update_sec=0.2,             # Время актуальности истории котировок к кеше, после последнего обновления
                                  )
    asyncio.run(qclient.main())

https://github.com/alexveden/quik-lua-async-client-python

getCandlesByIndex json invalid table: mixed or invalid key types

Версия serde_protocol = json.
График открыт, графику присвоен tag "test_siu"
При отправке c любым числом значения "count", например
socket.send_string("{"method":"getCandlesByIndex", "args":{"tag":"test_siu","line":0,"first_candle":0,"count":10}}")
вылетает с ошибкой error("invalid table: mixed or invalid key types") из utils/json.lua т.е. не распознает "count" в качестве числа if type(k) ~= "number" then
Пропускает только если "count":0
В этом случае ожидаемо приходит пустая таблица
{"result":{"t":[],"l":"Si-9.19 [Price]","n":0}}
В getCandlesByIndex.args.scheme.json все правильно прописано
"count": {
"description": "Параметр count",
"type": "integer",
"minimum": 0
}

Использовать последнюю версию lzmq с поддержкой ZMQ_LOOPBACK_FASTPATH.

Необходимо включить в дистрибутив обновлённую версию lzmq с поддержкой socket-опции ZMQ_LOOPBACK_FASTPATH, чтобы оптимизировать работу ZeroMQ TCP-сокета на localhost под Windows.

После этого необходимо также в service.lua проставить эту опцию сокетам:

socket:set_loopback_fastpath(true)

Библиотеки libzmq для линковки lzmq.dll можно скачать отсюда: https://zeromq.org/download/

Исправить параметры события ON_CONNECTED / Fix parameters of the ON_CONNECTED event

В документации написано, что qlua в коллбэк OnConnected передаёт аргумент flag. В текущей версии сервис не передаёт этот аргумент клиентам.


The reference manual says that qlua passes a flag argument into the OnConnected callback function. The current version of the service does not pass this argument back to the clients.

Скрипт отъедает 30% процессорного времени на холостом ходу.

Здравствуйте!
После запуска скрипта нагрузка на процессор подскакивает на 30%. Никаких запросов в это время не обрабатывается. Можно ли с этим бороться? Какие средства диагностики/отладки/протоколирования можете посоветовать?
Quik 8.8.4.3

sendTransaction

Вопрос немного не по теме - он относится к обёртке на Python, но что-то не получаю никакого ответа от мейнтейнера.
Может тут кто-нибудь поможет.

Всё никак не получается выставить заявку, например:

        operation: str = 'B'
        price: str = str(70.00)
        quantity: str = str(1)
        transaction_id: str = str(1)
        transaction = {'FIRM_ID': self.firmid,
              'CLIENT_CODE': self.client_code,
              'TYPE': 'L',
              'TRANS_ID': transaction_id,
              'CLASSCODE': 'TQBR',
              'SECCODE': 'ALRS',
              'ACTION': 'NEW_ORDER',
              'OPERATION': operation,
              'PRICE': price,
              'QUANTITY': quantity
             }
        args = sendTransaction_pb2.Args(transaction=transaction)

После отправки запроса, получаю следующую ошибку:

Response: b'\n) \xcd\xe5 \xf3\xea\xe0\xe7\xe0\xed\xee \xe7\xed\xe0\xf7\xe5\xed\xe8\xe5 \xef\xee\xeb\xff "\xd2\xee\xf0\xe3\xee\xe2\xfb\xe9 \xf1\xf7\xe5\xf2"'

[libprotobuf ERROR google/protobuf/wire_format_lite.cc:577] String field 'qlua.rpc.sendTransaction.Result.result' contains invalid UTF-8 data when parsing a protocol buffer. Use the 'bytes' type if you intend to send raw bytes.

msg.ParseFromString(resp.result)

google.protobuf.message.DecodeError: Error parsing message

Куда копать? Кто-нибудь может дать пример кода на java, который корректно отрабатывает?

Обновить README.md / Update README.md

После реализации механизма аутентификации и сопутствующего рефакторинга некоторые секции мануала устарели (например, Как пользоваться). Требуется обновить информацию как можно скорее.


Some of the chapters of the manual (namely Как пользоваться / How to use) became deprecated after the implementation of the auth feature. Need to update them as soon as possible.

Улучшить информационную безопасность пользователей репозитория.

Так как проект касается финансовой сферы, то крайне важно обеспечить информационную безопасность для пользователей и разработчиков.
Цель данной задачи состоит в том, чтобы:

  1. подписать доверенной цифровой подписью все бинарные файлы в репозитории и/или приложить хэш-суммы файлов, скачанных с доверенного источника;
  2. обновить README.md внесением туда секции с информацией о том, как можно проверить целостность и происхождение выложенных бинарных файлов.

Скрипт луа не запускается, если в config.json только один endpoint типа PUB

Если в конфиге прописать только один endpoint - PUB, то скрипт в quik не запускается с Access Violation Error.
Пример конфигурации.

{
  "endpoints": [
    {
      "type": "PUB", 
      "serde_protocol": "protobuf",
      "active": true, 
      "address": {
        "host": "127.0.0.1",
        "port": 5561
      },
      "auth": {
        "mechanism": "PLAIN", 
        "plain": {
          "users": [
            {"username": "name", "password": "password"}
          ]
        }
      }
    } ]
}

Разобраться со сборкой libzmq под Windows XP / Deal with the build of libzmq for Windows XP

Собирал библиотеку libzmq на Windows 7 в Visual Studio 2017 поочерёдно с тулсетами v141_xp, v140_xp, v110_xp, слинкованную статически с libsodium (сборка там же, тулсеты те же) и параметрами /MT и /MD (поочерёдно). Собранная DLL успешно работала в QUIK на Windows 7 и 10, но отказывалась работать в Windows XP (при запуске скрипта сервиса вылетает ошибка с описанием "не найдена процедура ...").
В программе Process Monitor в Windows XP загрузка скрипта заканчивалась на чтении двух файлов libzmq.dll.2.Manifest и libzmq.dll.2.Config, в то время как в Windows 7 и 10 далее шла загрузка рантайм-библиотеки msvcp*.dll.


I built libzmq on Windows 7 in Visual Studio 2017 with the platform toolsets v141_xp, v140_xp, v110_xp (in turn), linked statically with libsodium (built in VS2017 with the same toolsets) and the parameters /MT and /MD (also in turn). The built DLL successfully worked in QUIK on Windows 7 & 10 but refused to work on Windows XP (running the service's script gives an error "Cannot find the procedure ...").
On Windows XP in the Process Monitor program the script loading was ending on reading of two files libzmq.dll.2.Manifest and libzmq.dll.2.Config, while on Windows 7 & 10 the process was proceeding with loading of the runtime library msvcp*.dll.

Написать CONTRIBUTING.md / Write CONTRIBUTING.md

Файл должен содержать следующую информацию:

  1. Как собрать Lua-биндинг для Protocol Buffers
  2. Как собрать Lua-биндинг для ZeroMQ
  3. Как вносить изменения в .proto-файлы
  4. (Опционально) Примеры юнит-тестов

The file should contain the following information:

  1. How to build a Lua-binding for Protocol Buffers
  2. How to build a Lua-binding for ZeroMQ
  3. How to make changes to .proto-files
  4. (Optional) Examples of unit tests

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.