Иногда хочется наблюдать за ситуациями, когда участники торгов исполняют по рынку крупные заявки. Конечно, можно смотреть на обычную ленту обезличенных сделок с настроенными фильтрами на размер сделки, но ведь можно написать специальный 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
Если же хочется разобраться с тем, как это работает, то надо вдумчиво почитать код.
1) одинаковый инструмент
2)одинаковое направление
3)одинаковое время сек.
4)одинаковое время мсек
Верно?
bounds = {
[«TQBR:SBER»] = 2000,
}
dofile(«LargeTrades.lua») — вызов самого скрипта; указать путь к файлу
а в коде LargeTrades.lua убрать строки, которые задают bounds.
Здравствуйте. Я по поводу вашего поста о таблице обезличенных сделок с агрегацией.
Собственно у меня это реализовано в эксель. Условия я подглядел у Морошкина.
Правда условие о номерах сделок опущено. Это трудно сделать в VBA.
Я честно сказать не разработчик, заказывал фрилансеру.
Вы не могли бы показать мне пример кода для одной бумаги. У себя попробовать, чтобы был рабочий.
Готов отблагодарить разумной суммой денег.
Ну, тут же всё просто.
1) Сохраняем код в файл LargeTrades.lua
2) В начале файла ищем строки, которые задают размер заявки.
Сейчас эти строки выглядят так:
Оставляем только строку для Сбербанка (первая), остальные — удаляем.
3) Файл сохраняем ещё раз.
В результате будет вам скрипт, который только сделки для Сбербанка показывает.
Поэкспериментировал методом тыка с заливкой, не подскажите какой из 3х параметров нужно изменять чтобы цвет стал более насыщенным?