Блог им. _sk_

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

    • 25 января 2022, 09:43
    • |
    • _sk_
  • Еще
Недавно ко мне обратился один из смартлабовцев с просьбой доработать скрипт из поста https://smart-lab.ru/blog/610116.php, чтобы можно было более гибко подходить к раскраске выводимой там таблицы крупных «склеенных» сделок. Я решил проделать эту работу и выложить сюда модернизированный скрипт.

Настройки раскраски таблицы производятся в самом скрипте. Я сделал какие-то настройки для светлой темы терминала, может быть, весьма далёкие от ваших идеалов. Каждый пользователь пусть настраивает сам на свой вкус через палитру RGB (для каждого из трёх основных цветов нужно выбрать интенсивности от 0 до 255), редактируя строки в начале основного скрипта.

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

Пример файла настроек LargeMergerTrades_Demo.lua для акций Сбербанка и фьючерса SiH2. Обратите внимание, что его формат поменялся по сравнению с первоначальной версией из-за расширения функционала.

--
-- Вывод таблицы крупных "склеенных" обезличенных сделок по набору инструментов.
--
-- Для каждого инструмента, идентифицируемого кодом класса и кодом инструмента,
-- задаются следующие значения:
-- largeBound = положительная граница размера крупной склеенной сделки в лотах/контрактах;
-- hugeBound = положительная граница размера очень крупной склеенной сделки в лотах/контрактах;
-- slipPercent = размер проскальзывания в процентах, которое считается большим;
-- manyBound = граница количества сделок в "слишком многосоставной" склеенной сделке.
--

settings = {
    ["TQBR:SBER"] = {
        largeBound = 500,
        hugeBound = 2000,
        slipPercent = 0.03,
        manyBound = 25,
    },
    ["SPBFUT:SiH2"] = {
        largeBound = 50,
        hugeBound = 200,
        slipPercent = 0.03,
        manyBound = 25,
    },
}

dofile(getScriptPath() .. "/LargeMergedTrades.lua")

Ниже приведён сам скрипт LargeMergedTrades.lua, который нужно положить в ту же папку, что и файл с настройками LargeMergerTrades_Demo.lua.

--
-- Вывод таблицы крупных "склеенных" обезличенных сделок по набору инструментов.
--
-- Для каждого инструмента, идентифицируемого кодом класса и кодом инструмента,
-- задаются следующие значения:
-- largeBound = положительная граница размера крупной склеенной сделки в лотах/контрактах;
-- hugeBound = положительная граница размера очень крупной склеенной сделки в лотах/контрактах;
-- slipPercent = размер проскальзывания в процентах, которое считается большим;
-- manyBound = граница количества сделок в "слишком многосоставной" склеенной сделке.
--
-- Пример файла настроек, из которого вызывается основной скрипт:
-- settings = {
--    ["TQBR:SBER"] = {
--      largeBound = 500,
--      hugeBound = 2000,
--      slipPercent = 0.03,
--      manyBound = 25,
--    },
--    ["SPBFUT:SiH2"]  = {
--      largeBound = 50,
--      hugeBound = 200,
--      slipPercent = 0.03,
--      manyBound = 25,
--    },
-- }
-- dofile(getScriptPath() .. "/LargeMergedTrades.lua")
--

--
-- Цветовые настройки: FG -- цвет символов, BG -- цвет фона
--

-- Цветовые настройки для больших покупок
local LARGE_BUY_FG_COLOR = RGB(216, 255, 216)
local LARGE_BUY_BG_COLOR = RGB(0, 0, 0)
-- Цветовые настройки для очень больших покупок
local HUGE_BUY_FG_COLOR = RGB(160, 255, 160)
local HUGE_BUY_BG_COLOR = RGB(0, 0, 0)
-- Цветовые настройки для больших проскальзываний при покупках
local SLIP_BUY_FG_COLOR = RGB(128, 255, 128)
local SLIP_BUY_BG_COLOR = RGB(0, 0, 0)
-- Цветовые настройки для большого количества сделок в склеенной сделке при покупке
local MANY_BUY_FG_COLOR = RGB(64, 255, 64)
local MANY_BUY_BG_COLOR = RGB(0, 0, 0)

-- Цветовые настройки для больших продаж
local LARGE_SELL_FG_COLOR = RGB(255, 216, 216)
local LARGE_SELL_BG_COLOR = RGB(0, 0, 0)
-- Цветовые настройки для очень больших продаж
local HUGE_SELL_FG_COLOR = RGB(255, 160, 160)
local HUGE_SELL_BG_COLOR = RGB(0, 0, 0)
-- Цветовые настройки для больших проскальзываний при продажах
local SLIP_SELL_FG_COLOR = RGB(255, 128, 128)
local SLIP_SELL_BG_COLOR = RGB(0, 0, 0)
-- Цветовые настройки для большого количества сделок в склеенной сделке при продаже
local MANY_SELL_FG_COLOR = RGB(255, 64, 64)
local MANY_SELL_BG_COLOR = RGB(0, 0, 0)

local SELL_FLAG = 1
local BUY_FLAG = 2

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

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

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

function OnAllTrade(allTrade)
    prevTrade = currTrade
    currTrade = allTrade

    local key = allTrade.class_code .. ":" .. allTrade.sec_code
    local t = settings[key]
    if type(t) ~= "table" then
        return
    end
    local largeBound = t.largeBound

    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) >= largeBound then
        if math.abs(mergedTrade.prevVolume) < largeBound 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

--- Строковое представление для данного числа.
-- @param x число
-- @return строка, представляющая число с некоторой точностью
local function roundStringValue(x)
    if x == 0 then
        return "0"
    end
    local s = x > 0 and 1 or -1
    x = x * s
    local p = math.log(x, 10)
    p = math.floor(p - 3)
    if p >= 0 then
        return tostring(math.floor(0.5 + x) * s)
    else
        return string.format("%." .. (-p) .. "f", x * s)
    end
end

local tointeger = math.tointeger or (function(x) return x end)

--- Сделать вещественное число целым, если это возможно.
-- @param x вещественное число
-- @return целое число или исходное вещественное число, если преобразование невозможно
local function tryInt(x)
    return tointeger(x) or x
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(tryInt(largeTrade.volume)), tryInt(largeTrade.volume))
            SetCell(tableId, rowId, 8, tostring(largeTrade.count), largeTrade.count)
            SetCell(tableId, rowId, 9, roundStringValue(largeTrade.price2 - largeTrade.price1), largeTrade.price2 - largeTrade.price1)
            local t = settings[largeTrade.classCode .. ":" .. largeTrade.secCode]
            local isHuge = math.abs(largeTrade.volume) >= t.hugeBound
            local isMany = largeTrade.count >= t.manyBound
            local isSlippery = math.abs(largeTrade.price2 / largeTrade.price1 - 1) >= t.slipPercent / 100.0
            if largeTrade.volume > 0 then
                if isHuge then
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, HUGE_BUY_FG_COLOR, HUGE_BUY_BG_COLOR, HUGE_BUY_FG_COLOR, HUGE_BUY_BG_COLOR)
                else
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, LARGE_BUY_FG_COLOR, LARGE_BUY_BG_COLOR, LARGE_BUY_FG_COLOR, LARGE_BUY_BG_COLOR)
                end
                if isMany then
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, MANY_BUY_FG_COLOR, MANY_BUY_BG_COLOR, MANY_BUY_FG_COLOR, MANY_BUY_BG_COLOR)
                end
                if isSlippery then
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, SLIP_BUY_FG_COLOR, SLIP_BUY_BG_COLOR, SLIP_BUY_FG_COLOR, SLIP_BUY_BG_COLOR)
                end
            elseif largeTrade.volume < 0 then
                if isHuge then
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, HUGE_SELL_FG_COLOR, HUGE_SELL_BG_COLOR, HUGE_SELL_FG_COLOR, HUGE_SELL_BG_COLOR)
                else
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, LARGE_SELL_FG_COLOR, LARGE_SELL_BG_COLOR, LARGE_SELL_FG_COLOR, LARGE_SELL_BG_COLOR)
                end
                if isMany then
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, MANY_SELL_FG_COLOR, MANY_SELL_BG_COLOR, MANY_SELL_FG_COLOR, MANY_SELL_BG_COLOR)
                end
                if isSlippery then
                    SetColor(tableId, rowId, QTABLE_NO_INDEX, SLIP_SELL_FG_COLOR, SLIP_SELL_BG_COLOR, SLIP_SELL_FG_COLOR, SLIP_SELL_BG_COLOR)
                end
            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

Успехов в использовании скрипта и профитов в вашей торговле!

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


  • обсудить на форуме:
  • Quik Lua
★14
13 комментариев
спасибо за продвижение открытого кода в области торгов

скрин бы к такому показать как выглядит — когнетивней быстрей понять смысл
Олайвир Стокс, выглядит примерно так же, как и в исходном скрипте из первого поста.
avatar
Олайвир Стокс, добавил скриншот.
avatar
_sk_, 
спасибо
_sk_,  Вы уж извините, а как это всё установить в терминале, например на ВТБ ?  Спасибо.
avatar
Анатолий,
1) в папке терминала QUIK создаёте подпапку Lua, в неё кладёте приведённые в посте 2 файла в кодировке Windows CP-1251 (иначе QUIK не поймёт);
2) в терминале вызываете меню Lua-скриптов, добавляете туда файл LargeMergerTrades_Demo.lua и запускаете его.
avatar
_sk_, здравствуйте, а возможно в таблицу добавить отображение айсберг ордеров на уровнях?
Например сравнить какой объём был проторгован маркет ордерами на каждом ценовом уровне с максимальным лимитником встречного направления на этом же уровне.
avatar
Благодарю…
avatar
Спасибо.
avatar
А есть такой скрипт, чтобы сделки крупные в течение дня видеть?
avatar
Анатолий, так это и есть этот скрипт. Посмотрите внимательнее, чем темнее цвет, тем крупнее сделка.
Доброго всем дня! Сделал все как написано, но когда запускаю скрипт открывается пустое окно, в файле настроек указываю фьючерс SPBFUT:SiU2. Что я делаю неправильно?
Здравствуйте. А можно-ли как-то данные из таблицы вытащить в файл, типа вывод через DDE?
avatar

теги блога _sk_

....все тэги



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