Блог им. morefinances

Qlua: работа с биржевым стаканом.

Сегодня:

Работа с биржевым стаканом через getQuoteLevel2
Особенность нумерации в стакане заявок терминала квик
Работа через функцию обратного вызова OnQuote
Примеры работы со стаканом из скрипта
Сравнение реализации одного алгоритма через разные функции

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

Работать с биржевым стаканом можно через getQuoteLevel2 и функцию обратного вызова OnQuote.

Функция getQuoteLevel2 возвращает 2 массива котировок (bid и offer) и 2 значения: количество бидов в стакане (bid_count) и количество офферов (offer_count). Чтобы нам не было скучно разработчики терминала решили последних 2 параметра передавать в виде строки, поэтому при работе их нужно перевести в числа (через tonumber).

Массивы bid и offer содержат цены (price) и количество (quantity) по каждому уровню заявок стакана. Их также нужно будет предварительно перевести в число.

Логичнее всего было сделать нумерацию в стакане от границы покупок/продаж, но разработчики и здесь решили сделать нашу жизнь интереснее: в стакане используется нумерация снизу вверх как по покупателям, так и по продавцам. Нумерация строк начинается с 1, а заканчивается у продавцов offer_count, у покупателей bid_count:

Qlua: работа с биржевым стаканом.

Т.о. лучший оффер находится на 1ом значении, а лучший спрос на значении bid_count.

Выведем лучшую цену и количество по спросу и предложению:

Qlua: работа с биржевым стаканом.

Предварительно откроем стакан по Сбербанку. Запускаем скрипт, получаем:

Qlua: работа с биржевым стаканом.

Теперь закроем (крестиком) стакан по SBER и повторно запустим скрипт. Получаем:

Qlua: работа с биржевым стаканом.

Вернем стакан по Сбербанку обратно в терминал и постараемся вывести все значения по ценам и количеству:

Qlua: работа с биржевым стаканом.

Получаем после запуска:
Qlua: работа с биржевым стаканом.
 

Подсчитаем на сколько поднимется цена, если мы выставим заявку на покупку SBER на 10 млн.руб.:

 Qlua: работа с биржевым стаканом.

Qlua: работа с биржевым стаканом.
Файл: https://github.com/morefinances/qlua/blob/main/getQuoteLevel3.lua 

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

 

Функция обратного вызова OnQuote.

Отдает класс и тикер бумаги по которому прошли изменения в стакане при любом изменении последнего:

function OnQuote(class, tiker)
  if class == "TQBR" and tiker == "SBER" then
    …
  end
end

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

Выведем лучшие цены покупок и продаж через данную функцию:

Qlua: работа с биржевым стаканом.

Запускаем. Как видим есть строки, в которых значения дублируются:

Qlua: работа с биржевым стаканом.

Это связано с тем, что наш код выдает сообщение при любом изменении стакана (а не только лучших цен/количества покупок/продаж).

Подправим скрипт, чтобы сообщение выводилось только если изменения связаны с лучшими ценами и количеством BID / OFFER:

Qlua: работа с биржевым стаканом.

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

Qlua: работа с биржевым стаканом.
 

Сумма по всем заявкам офферов и бидов.

Сделаем расчет произведения количества на цену по каждому уровню стакана с выводом общей суммы как по продажам, так и по покупкам. После чего найдем коэффициента bid/offer как сумму всех продаж в руб. к аналогичной сумме покупок в руб. Результат выведем в отдельную таблицу и сравним 2 реализации этого алгоритма: с помощью getQuoteLevel2 и через функцию обратного вызова OnQuote.

Реализация через getQuoteLevel2:

function OnInit()
SumOffer = 0
SumBid = 0
end

function main()
do_it = true

if m_t==nil then
m_t=AllocTable()
AddColumn(m_t, 1, "Продажи", true, QTABLE_INT_TYPE, 12)
AddColumn(m_t, 2, "Покупки", true, QTABLE_INT_TYPE, 12)
AddColumn(m_t, 3, "bid/offer", true, QTABLE_DOUBLE_TYPE, 10)
CreateWindow(m_t)
SetWindowPos(m_t, 500, 447, 300, 110)
SetWindowCaption(m_t, "bid/offer анализ")
InsertRow(m_t,-1)
end

while do_it do
quotelvl = getQuoteLevel2("TQBR", "SBER")

if quotelvl then
if quotelvl.offer then

for i = quotelvl.offer_count, 1, -1 do
offer = tonumber(quotelvl.offer[i].price)
quant = tonumber(quotelvl.offer[i].quantity)
SumOffer = SumOffer + 10 * offer * quant
end

end

if quotelvl.bid then

for i = quotelvl.bid_count, 1, -1 do
bid = tonumber(quotelvl.bid[i].price)
quant = tonumber(quotelvl.bid[i].quantity)
SumBid = SumBid + 10 * bid * quant
end
end

SetCell(m_t, 1, 1, tostring(string.format("%.0f",SumOffer)))
SetCell(m_t, 1, 2, tostring(string.format("%.0f",SumBid)))

if SumBid > 0 then
a = SumOffer / SumBid
else
a = " "
end

SetCell(m_t, 1, 3, tostring(string.format("%.2f",a)))

SumOffer = 0
SumBid = 0
end

sleep(1000)
end
end

Реализация через OnQuote:

function OnInit()
SumOffer = 0
SumBid = 0
end
function OnStop()
do_it = false
end
function OnQuote(class, tiker)
if class == "TQBR" and tiker == "SBER" then

quotelvl = getQuoteLevel2("TQBR", "SBER")
if quotelvl and m_t then
if quotelvl.offer then

for i = quotelvl.offer_count, 1, -1 do
offer = tonumber(quotelvl.offer[i].price)
quant = tonumber(quotelvl.offer[i].quantity)
SumOffer = SumOffer + 10 * offer * quant
end

end


if quotelvl.bid then

for i = quotelvl.bid_count, 1, -1 do
bid = tonumber(quotelvl.bid[i].price)
quant = tonumber(quotelvl.bid[i].quantity)
SumBid = SumBid + 10 * bid * quant
end
end

SetCell(m_t, 1, 1, tostring(string.format("%.0f",SumOffer)))
SetCell(m_t, 1, 2, tostring(string.format("%.0f",SumBid)))

if SumBid>0 then
a = SumOffer / SumBid
else
a = " "
end

SetCell(m_t, 1, 3, tostring(string.format("%.2f",a)))

SumOffer = 0
SumBid = 0

end
end

end
function main()
do_it = true
if m_t==nil then
m_t=AllocTable()
AddColumn(m_t, 1, "Продажи", true, QTABLE_INT_TYPE, 12)
AddColumn(m_t, 2, "Покупки", true, QTABLE_INT_TYPE, 12)
AddColumn(m_t, 3, "bid/offer", true, QTABLE_DOUBLE_TYPE, 10)
CreateWindow(m_t)
SetWindowPos(m_t, 400, 447, 300, 110)
SetWindowCaption(m_t, "bid/offer анализ")
InsertRow(m_t,-1)
end
while do_it do
sleep(1000)
if m_t==nil then OnStop() end
end
end


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

Qlua: работа с биржевым стаканом.

Однако при резких движениях цены (как следствие и динамике в стакане) второй скрипт (реализация через OnQuote) может потреблять на два порядка больше ресурсов:

Qlua: работа с биржевым стаканом.

Учтите это при работе с функциями обратного вызова.

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

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

Теги: qlua для начинающих, кружок авиамоделизма.


Ранее:

Qlua: введение
Доля клиентов, использующих алгоритмическую торговлю
«Кружок авиамоделизма»
Разные торговые терминалы, почему Quik
Основной функционал qlua
smart-lab.ru/blog/917696.php

Настраиваем торговый терминал и редактор кода
Установка торгового терминала
Подготовка терминала
Вкладки в терминале
Сохранение и загрузка настроек
Таблица системных сообщений
Отключение окна сообщений
Редактор Notepad++
Настройка русского языка в редакторе
Выбор синтаксиса языка и кодировки
Цветовые настройки
Дублирующий просмотр
Запуск первого скрипта
smart-lab.ru/blog/918869.php

Основы qlua, часть 1:
message, конкатенация
фильтрация по сообщениям в терминале
PrintDbgStr, комментарии
типы данных, type
операции с числами
операции со строками
операции с таблицами
условные операторы
smart-lab.ru/blog/920031.php

Основы qlua, часть 2:
Циклы for … do … end, while do … end, repeat … until.
sleep, break, goto.
как пройти весь массив циклом, как пройти таблицу по ключам
локальные и глобальные переменные, функции
получение даты и времени
получение данных через getInfoParam
smart-lab.ru/blog/921366.php

Qlua: структура скрипта.
Структура типового скрипта qlua с примерами.
Обработка скриптом «обрыва связи» с сервером и возобновления работы.
Работа с файлами: запись, перезапись и чтение файла.
getScriptPath, getWorkingFolder
smart-lab.ru/blog/922044.php

Qlua: получение данных из таблицы текущих торгов, создание таблиц в торговом терминале.
Получение биржевых данных через функцию getParamEx
Выгрузка списка параметров функции getParamEx через DDE из торгового терминала
Создание пользовательских таблиц в торговом терминале
smart-lab.ru/blog/923365.php

Qlua: работа с таблицами (продолжение). Пишем своего советника (начало).
Интегрируем таблицы в структуру скрипта qlua.
Удаляем таблицы через DestroyTable.
Останавливаем скрипт через IsWindowClosed.
Обработка события закрытия таблицы через коллбэк.
Работа с цветом SetColor, Highlight, SetSelectedRow.
Пишем простого советника.
smart-lab.ru/blog/924710.php

Qlua: дополняем скрипт советника таймингом:
Устанавливаем время старта работы скрипта,
Ставим тайминг на получение сигналов на вход,
Устанавливаем таймер на приостановку скрипта.
smart-lab.ru/blog/925421.php

Qlua советник: дополняем сигналами на закрытие позиции, таблицей для вывода данных и расчетом финансового результата по позициям.
Дополняем сигналами на закрытие позиции.
Создаем дополнительную таблицу для вывода данных.
Делаем расчет финансового результата.
smart-lab.ru/blog/926972.php

Qlua: завершаем апгрейд советника:
Пропуск «поздних» сигналов на старте.
Обработка советником обрыва связи.
Сохранение сигналов и логов в файл.
smart-lab.ru/blog/927748.php

Qlua: пишем скринер акций Московской биржи
smart-lab.ru/blog/928152.php

Qlua: получение данных биржевых свечей с сервера брокера, обработка данных, пишем скрипт выгрузки котировок
Функция CreateDataSource
Получение количества свечек данных
Пауза для подгрузки данных
Получение по инструменту OPEN, HIGH, LOW, CLOSE, VOLUME
Обработка времени и даты
Закрытие источника данных
Примеры: получение данных последних 10 свечей, выгрузка новой минутной свечки после её закрытия, текущее значение простой средней SMA10 по минуткам
Простой скрипт выгрузки котировок
smart-lab.ru/blog/929905.php

Qlua: получение данных с графиков терминала.
Идентификатор инструмента
Получаем количество свечей через getNumCandles
Получаем свечные данных через getCandlesByIndex
Читаем данные с индикатора SMA
Данные с верхней и нижней линии Price Channel
Графики с таблицы текущих торгов.
Сравнение получение данных через CreateDataSource и через getCandlesByIndex
smart-lab.ru/blog/931408.php

Qlua: работа с метками, пишем торгового советника на индикаторах.
Вывод текста на график
Вывод графических сигналов
Удаление меток с графика
Торговый советник на индикаторах
Удаление данных вечерней/утренней сессии с графика.
smart-lab.ru/blog/933582.php

Qlua: работа с лентой всех сделок.
Что такое таблица обезличенных сделок.
Настройка таблицы в терминале.
Что делать, если таблица открылась, но она пустая.
Вывод данных с таблицы по DDE.
Работа с таблицей обезличенных сделок через скрипт qlua с примерами.
Пишем советника, показывающего на графике крупных игроков.
smart-lab.ru/blog/935919.php

Qlua: работа с лентой всех сделок (часть 2).
Различия данных ленты всех сделок и биржевого стакана.
Большие покупки и продажи в ленте сделок и динамика цены.
Альтернативные варианты поиск крупных игроков по ленте сделок.
smart-lab.ru/blog/938053.php

Qlua: дополняем скринер акций статистикой, лидерами роста и падения.
Добавляем статистику по акциям роста и падения.
Составляем TOP лидеров роста и падения.
Быстрый поиск акций по тикеру в терминале.
smart-lab.ru/blog/938450.php

 

  • обсудить на форуме:
  • Quik Lua
★19
4 комментария
Спасибо большое за работу.
avatar
avatar
Здравствуйте! Такой вопрос… Запускаю первый скрипт в цикле, но при отсутствии оффера или бида в стакане сразу выдает ошибку (attempt to index a nil value (field '?')). Как можно исправить?
avatar
Алексей, в самом первом примере есть ответвление else которое выводит сообщение, если нет данных по спросу или предложению. Добавьте эту часть в свой код, всё должно получиться.
avatar

теги блога alfacentavra

....все тэги



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