QLua

QLua

Тема сегодняшней публикации - скриптовый язык QLUA. QLUA - мощнейший инструмент для написания торговых роботов. И сегодня мы обсудим его плюсы и минусы. Посмотрим как написать простой индикатор и первого робота на нём. В начале своего пути долго выбирал среду разработки, искал эффективное и в тоже время простое решение для написания торговых роботов и советников. Этот путь занял несколько лет, и что я только не перепробовал в то время. Excel, Metastock, TsLab. Но в итоге остановился на QLua, справедливо решив, что это то, что мне нужно.

Оглавление

1) Почему стоит выбрать QLua.

2) Немного истории и о преимуществах QLua в сравнении с предшественником QPile.

3) Написание индикаторов на QLua.

4) Написание робота на QLua под Quik

5) Заключение


1. Почему стоит выбрать QLua.

Постараюсь коротенько описать плюсы и минусы. Начать стоит с того, что в рамках РФ самый распространенный терминал для торговли акциями и фьючерсами является Quik. Если отталкиваться от того, что наш терминал Quik, то выбор будет между: программами для тестирования, языками высокого уровня и встроенными языками в терминал Qlua , QPile.

Если сравнивать с системами, где алгоритм выстраивается путем построения блок-схем, то преимуществом Qlua является отсутствие платы за использование торговой системы как в TsLab и нет необходимости в использовании доп. «прокладок» как Wealth-lab. В системах построения из блоков таких как: TsLab, Wealth-lab и т.д. проблематично выстраивать сложные системы, а для уменьшения количества блоков и получения доп. функций требуется знание высокоуровневых языков типа: C#, Pascal для написания кубиков вручную.

Если рассматривать написание роботов на таких языках как: C#, Delphi , то здесь однозначным минусом является сложность написания кода и его размер для создания простейшего робота. Скриптовые же язык QLua или Qpile позволяют реализовать идею в более короткие сроки.

Сценарный язык (язык сценариев, жарг. скриптовый язык, от англ. scripting language) - высокоуровневый язык сценариев (англ. script) - кратких описаний действий, выполняемых системой. Сценарий - это программа, имеющая дело с готовыми программными компонентами, что очень сильно упрощает написание кода торгового робота. То есть для получения данных по значению закрытия свечи достаточно написать строчку кода типа: ds:C(20) все, и мы получаем значение закрытия двадцатой свечи инструмента. Нет необходимости писать «километры» кода. При этом доступен весь функционал как в полноценном языке программирования.

Из минусов можно отметить, что QLua используется только в рамках терминала Quik и данный язык не доступен для работы с другими терминалами и торговыми системами. Так же учитывая, что Quik является только терминалом и не позволяет производить тестирование торговых алгоритмов. В QLua нет простого решения для тестирования роботов, как это можно сделать в рамках таких платформ как: TsLab и Wealth-Lab.


2. Немного истории и о преимуществах QLua в сравнении с предшественником QPile.

До QLua был QPile, в общем–то он и сейчас есть. Интерпретатор скриптового языка QPile был разработан ARQA Technologies в 2002г. и развивался до 2012г., позже в терминал Quik был добавлен Lua интерпретатор. Так же есть вероятность, что компания ARQA Technologies возможно в ближайшем будущем откажется от дальнейшей поддержки QPile. Qpile как и Qlua является скриптовым языком и обладает рядом возможностей. По праву он является первым языком для написания роботов и советников для терминала Quik. Является простым для изучения и функциональным инструментом, но обладает рядом минусов.

И так, какими плюсами обладает QLua в сравнении с QPile:

- у QLua нет задержки на обработку торговой логики робота в 1сек. Это конечно не критично для среднесрочных стратегий, но для написания скальперских роботов может стать критичным.

- у QLua есть многопоточность. Реализовано это в виде основного потока торговой логики и функций обратного вызова(«колбэков»).

- есть возможность писать и подключать библиотеки на других языках, сам Lua кстати написан на Си

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

- синтаксис языка немного похож на JavaScript и C#, отсюда удобство работы с операторами переменными и т.д.

- скрипты обычно интерпретируются, а не компилируются. QLua обладает JIT-компиляторами так как в его основе лежит язык Lua. Что позволяет прятать исходный код, в то время как у Qpile он всегда открыт. Ссылка как компилятор для скрытия исходного кода Qlua


3. Написание индикаторов на QLua.

(1) Пользовательский индикатор

Как уже упоменалось выше в рамках терминала Quik существует два скриптовых языка это: Qpile и QLua. Qpile являестя более ранним языком, который появился в Quik и на нем нельзя было писать индикаторы. Позже разработчики расширили функционал и добавили QLua, в рамках которого стало возможным писать индикаторы. В данной части статье пойдет речь о создании индикатора под Quik.

Индикатор на QLua представляет из себя файл с расширением *.lua или *.luac и визуально ничем не отличается от робота. Исключением является его месторасположение. Для того чтобы Quik увидел индикатор его надо разместить в папке LuaIndicators. Папка LuaIndicators должна храниться в корне папки Квика , если ее там нет , то ее надо создать. Для того чтобы быстро открыть папку Квика надо нажать на его ярлык на рабочем столе правой кнопкой мыши и выбрать «Расположение файла».

И так перейдем к созданию индикатора. Для этого нам понадобится любой текстовый редактор типа Notepade.

Индикатор на QLua для Quik состоит из трех основных частей.

Первая часть это объявление и заполнение глобального массива Settings , который содержит в себе переменные параметров индикатора, тип линии для отображения, толщину, цвет. В массиве Settings располагаются пользовательские переменные для изменения параметров индикатора из пользовательского интерфейса Квика.

Вторая часть это функция Init , она запускается один раз при инициализации индикатора. Возвращает количество линий индикатора.

Третья часть функция OnCalculate(index) получает на вход номер свечи. Она запускается интерпретатором столько раз сколько свечей на графике и возвращает численное значение индикатора. Если линий несколько в индикаторе , то несколько численных значений. Для удобства понимания кода там будут комментарии. Для комментирования строки в QLua используется «--». Все содержимое после двух дефисов интерпретатором кода на QLua не воспринимается.

--[[  Многострочные комментарии располагаются между символами двух дефисов и квадратных скобок  ]]--      
Settings=              
        {                          
            Name = "Channel",           -- название индикатора
            period=5,                   -- параметр индикатора                          
            line=                                     
                {                               
                    {  
                        Name = "High",
                        Type =TYPE_LINE,
                        Width = 1,
                        Color = RGB(120,90, 140)
                    },
                    {
                        Name = "Low",
                        Type =TYPE_LINE,
                        Width = 1,
                        Color = RGB(120,90,140)
                    }
                }
       }

Здесь мы описали массив настроек, ввели название индикатора, параметры описали параметры двух линий индикатора Именно эти параметры будут доступны в пользовательском индикаторе для изменения при загрузке индикатора на Qlua в Quik

Настройка индикатора в Quik

  • (1)Название индикатора
  • (2)Установка значения для переменной period
  • (3)Установка цвета по умолчанию для линии High
  • (4)Название линии индикатора , которое мы прописали в ячейке «line» нашего листа настроек
  • (5)Тип линии который мы установили для использования по умолчанию Type =TYPE_LINE
  • (6)Толщина линии индикатора Width = 1
function
Init()
            return 2
end
-- выводить на график будем две линии
 
--[[
В функции OnCalculate
мы рассчитаем индикатор Price Channel – это две линии построенные по
экстремумам значений свечей за определенное количество баров. То есть первое
значение, которое будем рассчитывать это максимальное значение за period в
нашем случае он равен пяти, а второе значение, которое будет возвращать функция
это минимальное значение за period. Итак приступим к расчету.
]]--

function OnCalculate(index)
    local high=0
    local low=0
    Period = math.floor(Settings.period)

    if index<=Period then
       high=H(index)
       low=L(index)
       for i=index , 1 , -1 do
           if H(i)>high then high=H(i) end
           if low>L(i) then low=L(i) end
       end
    else
       high=H(index)
       low=L(index)
       for i=index , (index+1)-Period , -1 do
           if H(i)>high then high=H(i) end
           if low>L(i) then low=L(i) end
       end
    end
    return high , low
end

Создаем в редакторе файл с расширением lua записываем в него код и сохраняем его в папке LuaIndicators , после этого у нас появится новый индикатор и его можно будет добавить к себе на график как стандартный индикатор Quik (1)

Пользовательский индикатор на QLua

Ссылка для скачивания исходного кода индикатора на QLua


4. Написание робота на QLua под Quik

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

И так приступим. Напишем робота с простым алгоритмом. Рассчитаем внутри робота скользящую среднюю и будем принимать решение о покупке/продаже одного лота по инструменту исходя из количества в клиентском портфеле при закрытии прошлого бара ниже/выше среднего значения посчитанного по закрытию минутных баров инструмента. То есть при пересечении close бара средней снизу вверх проверим количество контрактов в портфеле по инструменту, и если оно меньше одного , то докупим разницу до одного контракта в лонг. Обратная ситуация при пересечении close закрытого бара среднего значения сверху вниз. Отправим транзакцию для получения одного лота в шорт по клиентскому портфелю.

-- переменная для прырывания цикла при срабатывании функции обратного вызова OnStop

is_run = true

-- присванивание переменным начальных базовых значений торгового алгоритма

Account = "NL0011100043"     -- торговый счет
Class_Code = "QJSIM"         -- класс торгуемого инструмента
Sec_Code = "SBER"            -- код торгуемого инструмента
TF = INTERVAL_M1             -- торговый таймфрейм
g_lots = 1                   -- количество торгуемых лот

function main()
    -- подписываемся на получение данных свечей по инструменту в массив ds
    ds = CreateDataSource(Class_Code, Sec_Code, TF)
    while is_run do
          sleep(1000)            -- обрабатываем цикл с задержкой 1сек.
          ds:SetEmptyCallback()  -- обновляем данные по инструменту в массиве ds
          local serv_time=tonumber(timeformat(getInfoParam("SERVERTIME"))) -- помещене в переменную времени сервера в формате HHMMSS                  
          if isConnected()==1 and serv_time>=10000 and serv_time<235000 then -- проверка наличия соеденения с сервером и поподания в торговое окно
             -- место для размещения торговой логики
             -- расчет значения скользящей средней
             local SMA = 0
             local period = 20
             local count_candle = ds:size()
             if count_candle>20 then
                local sum = 0
                for i=0 , period-1 do                                                  
                    sum = sum + ds:C(count_candle-i)
                end
                SMA = sum / period
                local lots = get_lots()             

                -- получение количества лот в клиентском портфеле по инструменту
                if ds:C(count_candle)>SMA and lots<g_lots then
                    -- получение минимального шага цены для организации величины проскальзываня в ордере
                    local step=tonumber(getParamEx(Class_Code, Sec_Code, "SEC_PRICE_STEP").param_value)
                    local price_order = ds:C(count_candle)+(step*20)
                    -- цена для ордера будет = цена закрытия + 20-ть минимальных шагов инст.
                    send_order("B", math.abs(g_lots-lots) , price_order)
                end
                if ds:C(count_candle)<SMA and lots>(g_lots*(-1)) then
                    -- получение минимального шага цены для организации величины проскальзываня в ордере
                    local step=tonumber(getParamEx(Class_Code, Sec_Code, "SEC_PRICE_STEP").param_value)
                    local price_order = ds:C(count_candle)-(step*20)    
                    -- цена для ордера будет = цена закрытия - 20-ть минимальных шагов инст.
                    send_order("S", math.abs(g_lots+lots) , price_order)
                end
             end                  
           end
    end
end 

-- функция возвращает количество лот в клиентском портфеле по заданному инструменту
function get_lots()
    local lots = 0
    local n = getNumberOf("futures_client_holding")
    local futures_client_holding={}                    
    for i=0,n-1 do             
       futures_client_holding = getItem("futures_client_holding", i)
       if tostring(futures_client_holding["sec_code"])==Sec_Code then
          lots=tonumber(futures_client_holding["totalnet"])
       end
    end      
    return lots
end
----------------------

-- отправка транзакции
function send_order(operation, quantity, price)       
    -- получение минимального шага цены для округления цены отправляемого ордера
    local step=tonumber(getParamEx(Class_Code, Sec_Code, "SEC_PRICE_STEP").param_value)
    local trans_params = 
          {
            CLIENT_CODE = Account,
            CLASSCODE = Class_Code,
            SECCODE = Sec_Code,
            ACCOUNT = Account,
            TYPE = "L",
            TRANS_ID = tostring(1),
            OPERATION = tostring(operation),
            QUANTITY = tostring(math.abs(quantity)),
            PRICE = tostring(math.floor(tonumber(price)/step)*step),  -- округление цены при отправлении транзакции
            ACTION = "NEW_ORDER" 
          }
    local res = sendTransaction(trans_params)
    if string.len(res) ~= 0 then
        message('Error: '..res,3)
        return 0
    else
        return trans_id
    end      
end

function OnStop(stop_flag)              
     is_run=false
     stop_flag=1
     ds:Close() 
end


function timeformat(time_unf)
     local in1, in2=0,0
     local time_form=0      
     in1=string.find(time_unf,":" , 0)
     if in1~=nil and in1~=0 then
        in2=string.find(time_unf,":" , in1+1) 
        time_form=string.sub(time_unf, 0 ,in1-1)..string.sub(time_unf, in1+1 ,in2-1)..string.sub(time_unf, in2+1 ,string.len(time_unf))
     end
     return time_form
end

После редактирования кода сохраняем его в удобном для вас месте с расширением lua. Для запуска робота надо зайти в Сервисы ->Lua скрипты


Запуск робота на QLua

  • (1)Добавляем скрипт из сохраненнго места
  • (2)Проверяем его появление в таблице запущенных скриптов, после запуска появится зеленая стрелка индицирующая работу скрипта
  • (3)Запускаем его в работу
  • (4)Проверяем в поле отсутствие ошибок при работе

Ссылка для скачивания исходного кода робота на QLua


5. Заключение

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

В рамках торгового робота написанного как пример выше, мы получили готовую механическую торговую систему в сто строк кода. Что является очень хорошим результатом.

Для написания индикаторов в Квик, Qlua является единственно возможным вариантом.

В общем - торгуйте алгоритмами. Используйте хорошие технологии!

13:53
7224

3 комментария

19:22
Здорово и понятно описано! Спасибо! Недавно стал использовать Qlua, но возник вопрос выбора визуальной библиотеки. Подскажите какая на ваш взгляд лучше. Свою писать пока никак.
Приветствую. Как вариант VCLua.
Я много чего пробовал, но в конце концов отказался от встроенных библиотек для визуализации. Сам пишу на СиШарп окна. В wpf.
o-s-a.net/training.html у меня в платных примерах есть всё. Вэлком.
10:23
Спасибо! Подтвердили мой выбор. Немного подзаработаю ботом и куплю ваши примеры)