Блог им. morefinances
Сегодня:
Работа с биржевым стаканом через getQuoteLevel2
Особенность нумерации в стакане заявок терминала квик
Работа через функцию обратного вызова OnQuote
Примеры работы со стаканом из скрипта
Сравнение реализации одного алгоритма через разные функции
Из таблицы текущих торгов мы можем получать большой перечень данных, в т.ч. по лучшим ценам спроса и предложения, из которых желающие получат спрэд по выбранному инструменту. Однако иногда нужно заглянуть именно в биржевой стакан. Это, например, пригодится нам далее при выставлении заявок.
Работать с биржевым стаканом можно через getQuoteLevel2 и функцию обратного вызова OnQuote.
Функция getQuoteLevel2 возвращает 2 массива котировок (bid и offer) и 2 значения: количество бидов в стакане (bid_count) и количество офферов (offer_count). Чтобы нам не было скучно разработчики терминала решили последних 2 параметра передавать в виде строки, поэтому при работе их нужно перевести в числа (через tonumber).
Массивы bid и offer содержат цены (price) и количество (quantity) по каждому уровню заявок стакана. Их также нужно будет предварительно перевести в число.
Логичнее всего было сделать нумерацию в стакане от границы покупок/продаж, но разработчики и здесь решили сделать нашу жизнь интереснее: в стакане используется нумерация снизу вверх как по покупателям, так и по продавцам. Нумерация строк начинается с 1, а заканчивается у продавцов offer_count, у покупателей bid_count:
Т.о. лучший оффер находится на 1ом значении, а лучший спрос на значении bid_count.
Выведем лучшую цену и количество по спросу и предложению:
Предварительно откроем стакан по Сбербанку. Запускаем скрипт, получаем:
Теперь закроем (крестиком) стакан по SBER и повторно запустим скрипт. Получаем:
Вернем стакан по Сбербанку обратно в терминал и постараемся вывести все значения по ценам и количеству:
Подсчитаем на сколько поднимется цена, если мы выставим заявку на покупку SBER на 10 млн.руб.:
Файл: https://github.com/morefinances/qlua/blob/main/getQuoteLevel3.lua
В качестве самостоятельного упражнения можете попробовать сделать аналогичный расчет в обратную сторону (для продаж).
Функция обратного вызова OnQuote.
Отдает класс и тикер бумаги по которому прошли изменения в стакане при любом изменении последнего:
function OnQuote(class, tiker) if class == "TQBR" and tiker == "SBER" then … end end
Если у вас открыто несколько стаканов заявок по разным инструментам, то функция будет отрабатывать изменения по всем, поэтому рекомендую закрыть все ненужные, иначе при большом количестве открытых стаканов терминал со временем может начать подтормаживать.
Выведем лучшие цены покупок и продаж через данную функцию:
Запускаем. Как видим есть строки, в которых значения дублируются:
Это связано с тем, что наш код выдает сообщение при любом изменении стакана (а не только лучших цен/количества покупок/продаж).
Подправим скрипт, чтобы сообщение выводилось только если изменения связаны с лучшими ценами и количеством BID / OFFER:
Запускаем, наблюдаем, что задвоений нет, каждая строка уникальна:
Сумма по всем заявкам офферов и бидов.
Сделаем расчет произведения количества на цену по каждому уровню стакана с выводом общей суммы как по продажам, так и по покупкам. После чего найдем коэффициента 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
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
Таблица с функцией обратного вызова работает гораздо более динамично, т.к. отрабатывает любые изменения в стакане (правая таблица):
Однако при резких движениях цены (как следствие и динамике в стакане) второй скрипт (реализация через OnQuote) может потреблять на два порядка больше ресурсов:
Учтите это при работе с функциями обратного вызова.
В качестве самостоятельного упражнения возьмите любой из сегодняшних скриптов и добавьте сохранение с заданной периодичностью данных стакана заявок в файл.
В следующей статье начнем работу с заявками и сделками. Предварительно необходимо будет открыть демосчет с доступом к демотерминалу 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