Недавно ко мне обратился один из смартлабовцев с просьбой доработать скрипт из поста
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
Успехов в использовании скрипта и профитов в вашей торговле!
скрин бы к такому показать как выглядит — когнетивней быстрей понять смысл
спасибо
1) в папке терминала QUIK создаёте подпапку Lua, в неё кладёте приведённые в посте 2 файла в кодировке Windows CP-1251 (иначе QUIK не поймёт);
2) в терминале вызываете меню Lua-скриптов, добавляете туда файл LargeMergerTrades_Demo.lua и запускаете его.
Например сравнить какой объём был проторгован маркет ордерами на каждом ценовом уровне с максимальным лимитником встречного направления на этом же уровне.