Блог им. _sk_

Опыт доработки QLua-скриптов для QUIK 8.5.2

    • 15 мая 2020, 16:29
    • |
    • _sk_
  • Еще
В новой версии терминала QUIK 8.5.2 произведён апгрейд языка Lua для написания торговых скриптов с версии 5.1 до версии 5.3. Это нужно для того, чтобы корректно обрабатывать 19-значные номера заявок и сделок на срочном рынке МосБиржи. Типа number в Lua 5.1 не подходит: там все числа хранятся как double, соответственно целые числа до 2^53 = 9 007 199 254 740 992 записываются без потери точности, а 19-значные номера заявок и сделок будут больше этой границы.

Версия Lua 5.3 обратно несовместима с Lua 5.1. Я почти не использовал внешние библиотеки и для меня было два важных изменения: отказ от module (это было сделано в версии 5.2) и введение целочисленной арифметики (версия 5.3).

Для избавления от использования module пришлось переработать много кода, хотя изменения были несложные. Приведу пример. Раньше был такой код Arrays.lua для работы с массивами:

--
-- Выполнение действий с массивами.
--

local pairs = pairs
local type = type

module(...)

--- Создать копию массива (таблицы)
-- @return копию массива (таблицы)
function copy(array)
    local copy_array = {}
    if type(array) ~= "table" then
        return array
    end
    for k, v in pairs(array) do
        if type(v) == "table" then
            copy_array[k] = copy(v)
        else
            copy_array[k] = v
        end
    end
    return copy_array
end

--- Узнать, начинается ли индексация в массиве с нуля или с единицы.
-- @return 0 или 1
function base(array)
    if array[0] ~= nil then
        return 0
    else
        return 1
    end
end

--- Вычислить число элементов в массиве.
-- @return число элементов в массиве
function size(array)
    local n = 0
    for _, _ in pairs(array) do
        n = n + 1
    end
    return n
end

--- Проверить пустой или нет массив.
-- @return true/false
function isEmpty(array)
    for _, _ in pairs(array) do
        return false
    end
    return true
end

--- Получить первый индекс массива, где ничего не записано. Поиск начинается с 1.
-- @return первый индекс массива, где ничего не записано
function firstEmptyIndex(array)
    local i = 1
    while array[i] ~= nil do
        i = i + 1
    end
    return i
end

Для переработки надо:
1) удалить module и запоминание функций до него;
2) завести таблицу с функциями, которые были в модуле;
3) вернуть эту таблицу с помощью return.
В результате код становится таким:

--
-- Выполнение действий с массивами.
--

local Arrays = {}

--- Создать копию массива (таблицы)
-- @return копия массива (таблицы)
local function copy(array)
    local copy_array = {}
    if type(array) ~= "table" then
        return array
    end
    for k, v in pairs(array) do
        if type(v) == "table" then
            copy_array[k] = copy(v)
        else
            copy_array[k] = v
        end
    end
    return copy_array
end

Arrays.copy = copy

--- Узнать, начинается ли индексация в массиве с нуля или с единицы.
-- @return 0 или 1
local function base(array)
    if array[0] ~= nil then
        return 0
    else
        return 1
    end
end

Arrays.base = base

--- Вычислить число элементов в массиве.
-- @return число элементов в массиве
local function size(array)
    local n = 0
    for _, _ in pairs(array) do
        n = n + 1
    end
    return n
end

Arrays.size = size

--- Проверить пустой или нет массив.
-- @return true/false
local function isEmpty(array)
    return next(array) == nil
end

Arrays.isEmpty = isEmpty

--- Получить первый индекс массива, где ничего не записано. Поиск начинается с 1.
-- @return первый индекс массива, где ничего не записано
local function firstEmptyIndex(array)
    local i = 1
    while array[i] ~= nil do
        i = i + 1
    end
    return i
end

Arrays.firstEmptyIndex = firstEmptyIndex

return Arrays
Второе важное изменение касается целочисленной арифметики. В Lua 5.3 есть 2 типа чисел: integer и float. Для первых реализована целочисленная арифметика, вторые нужны для представления вещественных чисел. Можно считать, что в Lua 5.1 были только float-числа. Номера заявок и сделок теперь будут представляться типом integer, и там хватит места для 19-значных чисел.

В терминале QUIK все торговые данные про цены сделок, их объёмы и т.п. приходят в виде float. Раньше, до версии 8.5.2, если количество акций в сделке или цена оказывались целым числом, то в случае преобразования в строку функцией tostring() результат не имел десятичной точки. Сейчас же она появляется всегда. Например, для цены RI 109120 в старых версиях терминала получается строка «109120», а в новых — «109120.0»; три фьючерса в сделке вместо строки «3» дают «3.0».

В принципе, это не сильно мешает, но во многих местах (логах, сообщениях, создаваемых скриптами таблицах с рыночной информацией и т.п.) вылезаются эти лишние ".0" в конце. Хуже обстоит дело в том месте, где отправляется транзакция на постановку заявки. Если в новой версии терминала указать в поле QUANTITY вместо «3» значение «3.0», то sendTransaction не отправит транзакцию и выдаст ошибку:
«Сообщение об ошибке: Число не может содержать знак разделителя дробной части».

Выйти из положения можно следующим образом, который подойдёт для Lua 5.3 и 5.1 сразу. Определим функцию tryInt, которая пытается преобразовать float-число в integer, если это возможно:

local tointeger = math.tointeger or (function(x) return x end)

--- Сделать вещественное число целым, если это возможно.
-- @param x вещественное число
-- @return целое число или исходное вещественное число, если преобразование невозможно
function tryInt(x)
    return tointeger(x) or x
end
Теперь ищем все места, где заполняется таблица с параметрами транзакции для постановки заявки, и там вместо обычного
QUANTITY = tostring(выражение),
пишем
QUANTITY = tostring(tryInt(выражение)),
В принципе, этого должно быть достаточно для правильной работы скриптов.

Если же хочется сделать, чтобы не вылезали «лишние хвосты» вида ".0" в других местах, можно поставить аналогичные «ловушки» для выделения целочисленных значений в местах, где они приходят в QLua-код. У меня это происходит в функциях обратного вызова OnOrder, OnTrade и OnTransReply.

В функции OnOrder я преобразую с помощью функции tryInt() поля balance и qty, в функции OnTrade — поля order_qty и qty, в функции OnTransReply — поле quantity. Поля с ценой price решил не трогать: пусть всегда имеют тип float.

Возможно, в вашем коде будут ещё какие-то изменения.

Если есть желание помочь и передать свой опыт, — напишите в комментариях, как вы добивались работоспособности QLua-кода в терминале QUIK 8.5.2.
Данная публикация является личным мнением автора. Мнение владельца сайта может не совпадать с мнением автора.
  • обсудить на форуме:
  • Quik Lua
7.4К | ★41
23 комментария
Не понял, зачем 
return Arrays
и куда мы его возвращаем?
Вроде, поместили функции в массив, и пользуйся на здоровье.

avatar
3Qu, потом используем в другом файле через
local Arrays = require(«util.Arrays»)
avatar
_sk_, я бы не стал. С пространствами имен проще работать, за именами следить не надо. Но это дело вкуса.
avatar
у меня дак все простенько, где надо убрать ноль использую math.floor и все:)
как у Вас терминал 8.5.2 не падал? у меня дак рекорд 7 дней и сегодня упал без дампа( но стал заметно стабильнее чем 8.5.1 тот совсем только два дня держался
Андрей Иванов, терминал 8.5.1 после запуска скриптов крашился через одну-две секунды без дампов. Совсем не работоспособная версия была. Версия 8.5.2 сильно лучше, ещё не падала, но есть кое-какие недоделки. На форуме forum.quik.ru про это можно долго читать.
avatar
_sk_, очень хорошо что у Вас не падал) а я дак уже думаю над автоматизацией перезапуска квика и запуском скриптов, если упадет. До 8 июня осталось совсем чуть чуть.
Андрей Иванов, где-то читал слухи, что возможен ещё один перенос срока релиза на бирже на более поздний срок. Чтобы терминал не падал, я раз в неделю (в понедельник с утра) его перезагружаю с перезаказом всех данных.
avatar
_sk_, Хорошо бы если перенесут.
Андрей Иванов, math.floor для нецелых чисел нельзя применять. Я хотел какой-то более мягкий способ склонить Lua к использованию целых чисел. Получилось — хорошо, не получилось — будем с дробными работать.
avatar
_sk_, кто тебе сказал, что «math.floor для нецелых чисел нельзя применять»?
А вот для целых чисел применение math.floor бессмысленно!
Rostislav Kudryashov, исходная задача был такая: если число представимо как целое, перевести его в целое, иначе оставить как было. Функцию math.floor(), конечно, можно применять для получения целого числа из дробного. В комментарии выше я не совсем точно выразился.
avatar
Если иметь дело с положительными числами, отбросить дробную часть x проще всего, написав x — x % 1.
Отрицательное число это округлит до ближайшего меньшего целого.
Rostislav Kudryashov, можно по всякому. Универсальные способы, конечно, предпочтительнее.
avatar
Может, подойдёт math.modf ???
Rostislav Kudryashov, и эту функцию можно адаптировать под задачу сделать число типом integer.
avatar
_sk_, не подскажешь, где  скачать бесплатно «Programming in Lua. Fourth Ed» со встроенным древовидным оглавлением как в Third Ed?
Rostislav Kudryashov, не подскажу. В самом начале я читал книгу автора языка Lua, а сейчас референсом пользуюсь
www.lua.org/manual/5.3/
avatar
_sk_, не надо адаптировать! modf выдаёт целочисленный результат без адаптации. Заведи се6е автономный lua53 и  запустив start lua53.exe сможешь получить
> = math.modf (-1.2345)
-1         -0.2345
Чтобы вернуть строковое представление чисел в том же виде, что и в луа 5.1, достаточно просто переписать функцию tostring и ничего больше не править в текстах. Что-то типа следующего

local tostring__ = tostring
Function tostring(x)
If math.tointeger(x) then
Return string.format("%0i",x)
End

Return tostring__(x)
End
s_mike@rambler.ru, хороший вариант!
avatar
s_mike@rambler.ru, спасибо!
avatar
Заодно тогда… функцию isempty можно выкинуть, ee полностью заменяет станлартная next().

Ну и размер массива можно вычислить встроенной функцией, а не пользовательской. Функция появилась в 5.3 в пространстве table

Также там есть теперь и table.copy… Одним словом, выкинуть можно весь этот модуль полностью)))))

что-то у меня скомпиленный индикатор не хочет запускать
даже после перекомпиляции под lua 5.3.5

а без компиляции норм

upd: разобрался, надо использовать x64 luac, а я случайно собрал x86 luac из исходников

avatar

Читайте на SMART-LAB:
Фото
BRENT: Мелкими шагами направляемся вниз
Нефть марки Brent продолжает свое снижение и практически коснулась психологического уровня 70. Под закрытие торговой недели мы можем увидеть...
Каждый десятый заемщик МФО допускает обращение к «черным» кредиторам
Мы провели исследование и выяснили, насколько хорошо заемщики отличают легальные МФО от нелегальных кредиторов, а также готовы ли обращаться к...
Фото
Ценные бумаги. Взгляд в прошлое. Восточное общество товарных складов, страхования и транспортирования товаров с выдачей ссуд.
Если вас интересуют другие аналитические и информационные материалы от банка АО АКБ «ЦентроКредит», смотрите их на нашем сайте...
Фото
Мой инвест портфель. Структура портфеля, последние действия по портфелю. Состав портфеля валютных облигаций
Сегодня делал действия по портфелю. Кроме того, решил пособирать инфу по счетам и посмотреть как там дела.  

теги блога _sk_

....все тэги



UPDONW
Новый дизайн