1

Тема: Основы программирования LUA в QUIK (QLUA)

Обновлено: 24.05.2016
(Обсуждение этого материала здесь)

Введение

Начиная с версии 6.5 в терминале QUIK была добавлена возможность запуска пользовательских скриптов, написанных на языке Lua. В сравнении в ранее имевшимся внутренним языком QUPILE, добавление скриптов на Lua дало пользователям не только более мощный и удобный язык программирования, но и предоставило принципиально новые возможности для создания торговых роботов, в частности возможность мгновенной реакции на изменение рыночных данных за счет реализованной возможности событийного программирования. Событиями могут выступать: изменение стакана котировок, изменение параметров инструментов, совершение сделок на бирже, совершение сделок по поручениям (заявкам), поданным из терминала пользователя (вручную или из скриптов) и т.д.

Документация по Lua в QUIK и базовые примеры от ARQA: quik_lua.zip

Список изменений и нововведений в QUIK по части функциональности Lua-скриптов можно посмотреть здесь.

Виды скриптов на Lua для QUIK

Для выполнения разного рода задач терминал QUIK поддерживает следующие виды скриптов:
Lua-скрипты: в них доступна вся функциональность для написания роботов, включая возможность получения рыночных данных, параметров инструментов, задание реакции на изменение рыночных данные, выставление заявок, создание таблиц внутри интерфейса терминала QUIK с выводом в них нужной пользователю информации. Эти скрипты могут располагаться в любом месте, доступном терминалу (локальный диск, сетевой ресурс и т.д.).
Пользовательские индикаторы: это отдельный вид программ на Lua, предназначенный в первую очередь для вывода на графиках терминала индикаторов технического анализа, алгоритм которых реализуется в скриптах данного вида. Помимо этого, индикаторам доступны функции вывода меток на графиках. В большинстве случаев индикаторы для своего расчёта в качестве исходных берут данные с определённого графика, хотя имеется и возможность получения других данных (правда, на данный момент очень сложно обеспечить синхронизацию ганных на графике и внешних данных, с графиком не связаннх). В скриптах индикаторов доступен ограниченный набор внешних функций, по сравнению с Lua-скриптами.

Далее будут рассматриваться только Lua-скрипты и особенности их написания в QUIK. Создание индикаторов будет рассмотрено в отдельной теме.

Что включает в себя Lua в QUIK

В качестве интерпретатора используется Lua версии 5.1.5.
Включены библиотеки: io, string, math, package, os, table. ( пруф )

Помимо этого скриптам на Lua, запущенным в терминале QUIK, доступны дополнительные функции, предназначенные для получения рыночных данных, взаимодействия с терминалом. Кроме того, пользователь может размещать в своём скрипте функции с заранее предопределёнными наименованиями (так называется callback-функции), которые позволяют реализовывать в торговых роботах реакцию на рыночные события. Ввиду такого расширения Lua в рамках терминала QUIK обычно называют QLua, подчеркивая наличие указанных дополнений. Хотелось бы отметить, что эти дополнения никак не изменяют собственно синтаксис самого языка Lua, а лишь добавляют в него некоторые функции по аналогии с функциями разных библиотек. Поэтому для изучения программирования на самом языке Lua в QUIK годится абсолютно любая книга по Lua.

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

Как программировать на LUA в QUIK

Есть 3 подхода:

а) Можно сделать файл с расширением .lua и написать всю необходимую логику прямо в основной части файла. Такой код после запуска выполнится один раз. Запускать (вручную, нажатием кнопки «Запустить») можно сколько угодно. Такой вариант вполне подойдет для эпизодического подсчета каких-то параметров своих сделок, например.

б) Можно написать в Lua-скрипте одну единственную функцию с предопределенным именем main() и всю логику работы робота (или вычислительного скрипта) поместить в эту функцию. Функция main выполняется в отдельном потоке, т.е. она не мешает работе основного функционала терминала QUIK, наличие функции sleep() позволяет выполнять периодически приостанавливать скрипт и возобновлять его работу через какой-то промежуток времени.
Фактически, если зациклить main() и вставить sleep(), то получаем полную эмуляцию подхода, использующегося при программировании на встроенном QPILE: периодический расчет чего-либо через заданный интервал времени.

в) В QLUA доступна событийная модель программирования. Т.е. теперь нет необходимости в одной "главной" функции "выявлять" происходящие изменения и на основании анализа этих изменений выполнять какие-то действия.

С первым и вторым подходом все достаточно понятно, да и используемый для этого интерфейс весьма напоминает тот, который использовался в QPILE, так что на нем подробно останавливаться не буду.

Программирование на QLUA с использованием событий

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

Для того, чтобы обработать то или иное событие, необходимо просто прописать в своем скрипте функцию с предопределенным названием. Все доступные функции обработки событий есть в документации по QLUA. Поддерживаются самые различные события совершение на бирже очередной сделки, выставление новой заявки пользователем (или скриптом), изменение стакана котировок и т.д.

Схематически принцип выполнения скриптов LUA внутри терминала QUIK можно изобразить следующим образом:
QLUA общий обзор

Скрипт LUA в QUIK может содержать несколько функций с предопределенными названиями, являющимися обработчиками событий (таких как новая сделка, изменение лимитов, изменение котировок и т.д.).
Выполнение скрипта происходит после пожатия на кнопку "Запустить" в диалоге "Таблицы -> Lua -> Доступные скрипты", и всегда начинается с обработки тела скрипта вне каких-либо функций (на схеме обозначено [BODY]) и вызова обработчика с именем Init() (если он есть). После того, как функция Init() завершится, происходит создание отдельного потока приложения QUIK, и в этом потоке начинает выполняться функция main(), которая обязательно должна быть. Скрипт считается работающим до тех пор, пока выполняется функция main. Как только она завершится - прекращается и выполнение скрипта, т.е. вызов из него обработчиков событий.

Обратите внимание, что все функции обработки событий, в отличие от функции main(), выполняются в рамках основного потока терминала QUIK, а значит время их работы должно быть сравнительно небольшим, иначе будут заметны "подвисания" в работе терминала.

2

Re: Основы программирования LUA в QUIK (QLUA)

Типичный скрипт QLUA, в случае использования подхода обработки событий, должен содержать обязательно примерно следующий код:

is_run = true

function main()
    while is_run do
        sleep(50)
    end;
end

function OnStop()
    is_run = false
end

Здесь мы объявляем глобальную переменную is_run, которая будет содержать значение true до тех пор, пока не будет нажата кнопка «Остановить скрипт». В функции main() крутится пустой бесконечный цикл, который через каждые 50 мс. проверяет состояние этой глобальной переменной.  При нажатии кнопки “Остановить” эта переменная сбрасывается в false внутри обработчика OnStop(), благодаря чему функция main() завершается и скрипт переходит в состояние “остановлен”. После этого обработчики событий в нем не вызываются.

(Замечание: QUIK версии 6.4.0.169 есть ошибка, из-за которой обработчики вызываются и после остановки скрипта, не забывайте об этом.)

Для примера напишем небольшой скрипт, подсчитывающий общий оборот по нашим сделкам. Для этого приведенный выше текст дополним обработчиком OnTrade().

is_run = true

function main()
    while is_run do
        sleep(50)
    end;
end

function OnStop()
    is_run = false
end

function OnTrade(trade_data)
    qty = trade_data.qty
    sum = trade_data.value
    n = getNumberOf("trades")
    for i=0, n-1 do
        t = getItem("trades", i)
        sum = sum + t.value
        qty = qty + t.qty
    end
    message("Total: qty=" .. tostring(qty) .. " sum=" .. tostring(sum), 1)
end

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

https://quik2dde.ru/static-img/summ-trade-qlua.png

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