Блог им. _sk_

QLua: таблица крупных "склеенных" обезличенных сделок

    • 03 апреля 2020, 15:06
    • |
    • _sk_
  • Еще
Иногда хочется наблюдать за ситуациями, когда участники торгов исполняют по рынку крупные заявки. Конечно, можно смотреть на обычную ленту обезличенных сделок с настроенными фильтрами на размер сделки, но ведь можно написать специальный QLua-скрипт, который будет отбирать сделки, являющиеся результатом исполнения.

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

Если суммарный объём не менее какой-то границы, которую можно задать для каждого инструмента индивидуально, такие «склеенные» сделки выводятся в таблице. В ней указаны:
— суммарный объём;
— количество обезличенных сделок, которые были склеены;
— начальная цена и конечная цена;
— проскальзывание (разность этих цен).

Скриншот



Ниже приведён код самого скрипта. Надеюсь, кому-то он поможет в торговле или изучении программирования на QLua на данном примере.

Успехов в торговле!

--
-- Вывод таблицы крупных "склеенных" обезличенных сделок по набору инструментов.
--

-- Для каждого инструмента, идентифицируемого кодом класса и кодом инструмента,
-- задаётся положительная граница размера крупной обезличенной сделки в лотах/контрактах.
local bounds = {
    ["TQBR:SBER"] = 500,
    ["TQBR:GAZP"] = 200,
    ["SPBFUT:SiM0"] = 100,
    ["SPBFUT:RIM0"] = 50,
}

local isInterrupted = false -- флаг прерывания для завершения работы скрипта
local tableId -- информационная таблица

local SELL_FLAG = 1
local BUY_FLAG = 2

local prevTrade -- информация о предыдущей обезличенной сделке
local currTrade -- информация о текущей обезличенной сделке
local mergedTrade = {} -- информация о "склееной" сделке
local largeTradesCount = 0 -- количество крупных "склеенных" сделок
local largeTrades = {} -- массив крупных "склеенных" сделок
local printCount = 1 -- номер крупной "склеенной" сделки, которую нужно вывести в таблицу

function OnStop()
    if tableId then
        DestroyTable(tableId)
        tableId = nil
    end
    isInterrupted = true
end

function OnAllTrade(allTrade)
    prevTrade = currTrade
    currTrade = allTrade

    local key = allTrade.class_code .. ":" .. allTrade.sec_code
    local bound = bounds[key]
    if type(bound) ~= "number" then
        return
    end

    local buySell = 0
    if bit.band(currTrade.flags, BUY_FLAG) == BUY_FLAG then
        buySell = 1
    elseif bit.band(currTrade.flags, SELL_FLAG) == SELL_FLAG then
        buySell = -1
    end
    -- Эвристика buySell
    if prevTrade then
        if currTrade.class_code == prevTrade.class_code
                and currTrade.sec_code == prevTrade.sec_code
                and currTrade.trade_num == prevTrade.trade_num + 1
                and (buySell > 0 and prevTrade.buySell > 0 and currTrade.price >= prevTrade.price or buySell < 0 and prevTrade.buySell < 0 and currTrade.price <= prevTrade.price)
                and currTrade.datetime.ms == prevTrade.datetime.ms
                and os.time(currTrade.datetime) == os.time(prevTrade.datetime)
        then
            buySell = buySell * 2
        end
    end
    currTrade.buySell = buySell

    if buySell == 1 or buySell == -1 then
        mergedTrade.datetime = currTrade.datetime
        mergedTrade.tradeNum = currTrade.trade_num
        mergedTrade.price1 = currTrade.price
        mergedTrade.price2 = currTrade.price
        mergedTrade.prevVolume = 0
        mergedTrade.currVolume = currTrade.qty * buySell
        mergedTrade.count = 1
    else
        mergedTrade.price2 = currTrade.price
        mergedTrade.prevVolume = mergedTrade.currVolume
        mergedTrade.currVolume = mergedTrade.currVolume + currTrade.qty * buySell / 2
        mergedTrade.count = mergedTrade.count + 1
    end

    if math.abs(mergedTrade.currVolume) >= bound then
        if math.abs(mergedTrade.prevVolume) < bound then
            -- Новая крупная "склеенная" обезличенная сделка
            largeTrades[largeTradesCount + 1] = {
                time = os.date("%Y-%m-%d %X", os.time(mergedTrade.datetime)) .. string.format(".%03d", mergedTrade.datetime.ms),
                classCode = currTrade.class_code,
                secCode = currTrade.sec_code,
                tradeNum = mergedTrade.tradeNum,
                price1 = mergedTrade.price1,
                price2 = mergedTrade.price2,
                volume = mergedTrade.currVolume,
                count = mergedTrade.count,
            }
            largeTradesCount = largeTradesCount + 1
        else
            -- Обновление информации о последней крупной "склееннщй" обезличенной сделке
            local largeTrade = largeTrades[largeTradesCount]
            largeTrade.price2 = mergedTrade.price2
            largeTrade.volume = mergedTrade.currVolume
            largeTrade.count = mergedTrade.count
        end
    end
end

local function rgb(color)
    local r, g, b = string.match(color, "#(%x%x)(%x%x)(%x%x)")
    return RGB(tonumber(r, 16), tonumber(g, 16), tonumber(b, 16))
end

local function displayInfoTable()
    if tableId and IsWindowClosed(tableId) then
        isInterrupted = true
    else
        local count = largeTradesCount
        for rowId = printCount, count do
            while GetTableSize(tableId) < rowId do
                InsertRow(tableId, -1)
            end
            local largeTrade = largeTrades[rowId]
            SetCell(tableId, rowId, 1, largeTrade.time)
            SetCell(tableId, rowId, 2, largeTrade.classCode)
            SetCell(tableId, rowId, 3, largeTrade.secCode)
            SetCell(tableId, rowId, 4, tostring(largeTrade.tradeNum), largeTrade.tradeNum)
            SetCell(tableId, rowId, 5, tostring(largeTrade.price1), largeTrade.price1)
            SetCell(tableId, rowId, 6, tostring(largeTrade.price2), largeTrade.price2)
            SetCell(tableId, rowId, 7, tostring(largeTrade.volume), largeTrade.volume)
            SetCell(tableId, rowId, 8, tostring(largeTrade.count), largeTrade.count)
            SetCell(tableId, rowId, 9, tostring(largeTrade.price2 - largeTrade.price1), largeTrade.price2 - largeTrade.price1)
            if largeTrade.volume > 0 then
                SetColor(tableId, rowId, QTABLE_NO_INDEX, rgb("#E8FFE8"), rgb("#000000"), rgb("#E8FFE8"), rgb("#000000"))
            elseif largeTrade.volume < 0 then
                SetColor(tableId, rowId, QTABLE_NO_INDEX, rgb("#FFE8E8"), rgb("#000000"), rgb("#FFE8E8"), rgb("#000000"))
            end
        end
        if printCount < count then
            printCount = count
        end
    end
end

function main()
    tableId = AllocTable()
    AddColumn(tableId, 1, "Дата/Время", true, QTABLE_STRING_TYPE, 25)
    AddColumn(tableId, 2, "Класс", true, QTABLE_CACHED_STRING_TYPE, 10)
    AddColumn(tableId, 3, "Инструмент", true, QTABLE_CACHED_STRING_TYPE, 10)
    AddColumn(tableId, 4, "Номер сделки", true, QTABLE_INT_TYPE, 20)
    AddColumn(tableId, 5, "Нач.Цена", true, QTABLE_DOUBLE_TYPE, 12)
    AddColumn(tableId, 6, "Кон.Цена", true, QTABLE_DOUBLE_TYPE, 12)
    AddColumn(tableId, 7, "Кол-во", true, QTABLE_INT_TYPE, 6)
    AddColumn(tableId, 8, "Частей", true, QTABLE_INT_TYPE, 6)
    AddColumn(tableId, 9, "Проскальзывание", true, QTABLE_DOUBLE_TYPE, 20)
    CreateWindow(tableId)
    SetWindowCaption(tableId, "Крупные склеенные обезличенные сделки")
    while not isInterrupted do
        displayInfoTable()
        sleep(200)
    end
end
  • обсудить на форуме:
  • Quik Lua
★36
25 комментариев
Бизне$$ Ангел, примерно так.
Скриншот


avatar
Бизне$$ Ангел, добавил скриншот в основной пост. Спасибо за подсказку!
avatar
Круто! Спасибо! Еще бы разобраться во всем)))
avatar
Maxone, использование-то простое: копируем код в блокнот и сохраняем в файл с расширением .lua. Потом в терминале QUIK в меню Lua-скриптов добавляем скрипт и запускаем. Должно работать из «коробки». Если настройки не устраивают, то дописываем свои по образу и подобию.

Если же хочется разобраться с тем, как это работает, то надо вдумчиво почитать код.
avatar
спасибо! полезно!
avatar
А для склейки какие критерии используете можно полюбопытствовать?
Виктор Суржиков, инструмент в текущей сделке совпадает с инструментом в предыдущей сделке, направление сделки то же самое, время сделки совпадает с точностью до миллисекунд, и цена при покупке растёт, а при продаже — падает.
avatar
_sk_, если я правильно понял склеиванию подлежат сделки по следующим критериям:
1) одинаковый инструмент
2)одинаковое направление
3)одинаковое время сек.
4)одинаковое время мсек
Верно?
Виктор Суржиков, плюс ещё номера обезличенных сделок идут подряд и цена при покупке стоит на месте или растёт и при продаже стоит на месте или падает.
avatar
_sk_,  я так понимаю можно сделать 1 бумага-один скрипт, и запускать их под разными именами?
Виктор Суржиков, можно. В этом случае лучше создать несколько файлов типа

bounds = {
    [«TQBR:SBER»] = 2000,
}
dofile(«LargeTrades.lua») — вызов самого скрипта; указать путь к файлу

а в коде LargeTrades.lua убрать строки, которые задают bounds.
avatar
_sk_, 

Здравствуйте. Я по поводу вашего поста о таблице обезличенных сделок с агрегацией.
Собственно у меня это реализовано в эксель. Условия я подглядел у Морошкина.
Правда условие о номерах сделок опущено. Это трудно сделать в VBA.
Я честно сказать не разработчик, заказывал фрилансеру.
Вы не могли бы показать мне пример кода для одной бумаги. У себя попробовать, чтобы был рабочий.
Готов отблагодарить разумной суммой денег.

На самом деле крутая штука! Спасибо. Для скальперов очень полезно будет думаю)
Для скальперов есть привод Qscalp где это реализовано
присоединяюсь к просьбе можно работающий пример для любой бумаги из ммвб 
avatar
можно работающий пример для любой бумаги из ммвб

Ну, тут же всё просто.
1) Сохраняем код в файл LargeTrades.lua
2) В начале файла ищем строки, которые задают размер заявки.

Сейчас эти строки выглядят так:
    ["TQBR:SBER"] = 500,
    ["TQBR:GAZP"] = 200,
    ["SPBFUT:SiM0"] = 100,
    ["SPBFUT:RIM0"] = 50,
Оставляем только строку для Сбербанка (первая), остальные — удаляем.
3) Файл сохраняем ещё раз.

В результате будет вам скрипт, который только сделки для Сбербанка показывает.
avatar
_sk_, , большое спасибо, запустил.
Поэкспериментировал  методом тыка с заливкой, не подскажите какой из 3х параметров нужно изменять чтобы цвет стал более насыщенным?
Надо менять #E8FFE8 (зелёный) и #FFE8E8 (красный) на что-то другое. Например, если заменить на #C0FFC0 и #FFC0C0 соответственно, то будут более насыщенные цвета. Подбираете 16-ричный код на свой вкус.
avatar
Интересная тема. А возможно ли из этой таблицы вытянуть горизонтальные объемы? Если да, то как?
avatar
Годная вещь, искал такую, еще бы эти крупные сделки на график в Quik выводить, попробую сделать.
avatar
Добрый день. Как его настроить на фьючерс газа?
avatar

теги блога _sk_

....все тэги



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