_sk_
_sk_ личный блог
03 апреля 2020, 15:06

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

Иногда хочется наблюдать за ситуациями, когда участники торгов исполняют по рынку крупные заявки. Конечно, можно смотреть на обычную ленту обезличенных сделок с настроенными фильтрами на размер сделки, но ведь можно написать специальный 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
25 Комментариев
  • Maxone
    03 апреля 2020, 15:29
    Круто! Спасибо! Еще бы разобраться во всем)))
  • Hired
    03 апреля 2020, 15:29
    спасибо! полезно!
  • Виктор Суржиков
    03 апреля 2020, 16:01
    А для склейки какие критерии используете можно полюбопытствовать?
      • Виктор Суржиков
        03 апреля 2020, 16:44
        _sk_, если я правильно понял склеиванию подлежат сделки по следующим критериям:
        1) одинаковый инструмент
        2)одинаковое направление
        3)одинаковое время сек.
        4)одинаковое время мсек
        Верно?
          • Виктор Суржиков
            03 апреля 2020, 17:24
            _sk_,  я так понимаю можно сделать 1 бумага-один скрипт, и запускать их под разными именами?
              • Виктор Суржиков
                04 апреля 2020, 11:52
                _sk_, 

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

  • Денис Михайлов
    03 апреля 2020, 23:21
    На самом деле крутая штука! Спасибо. Для скальперов очень полезно будет думаю)
  • Вячеслав Кузнецов
    04 апреля 2020, 07:46
    Для скальперов есть привод Qscalp где это реализовано
  • vikgon
    04 апреля 2020, 13:28
    присоединяюсь к просьбе можно работающий пример для любой бумаги из ммвб 
    • Виктор Суржиков
      06 апреля 2020, 12:21
      _sk_, , большое спасибо, запустил.
      Поэкспериментировал  методом тыка с заливкой, не подскажите какой из 3х параметров нужно изменять чтобы цвет стал более насыщенным?
  • tefl
    21 января 2021, 20:24
    Интересная тема. А возможно ли из этой таблицы вытянуть горизонтальные объемы? Если да, то как?
  • DennyV8
    10 августа 2023, 10:48
    Годная вещь, искал такую, еще бы эти крупные сделки на график в Quik выводить, попробую сделать.
  • Matrix_34
    04 октября 2023, 14:08
    Добрый день. Как его настроить на фьючерс газа?

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн