При торговле с помощью роботов в терминале QUIK рано или поздно встаёт вопрос об ограничении количества отправляемых в секунду транзакций, чтобы не начинались ошибки вида: «Превышен лимит отправки транзакций для данного логина».
Простой способ, когда вводятся ограничения на уровне каждого торгового робота, приводит к ситуациям, когда заявки отправляются медленно из-за этого ограничения, а свободная пропускная способность ещё есть. Для введения
общего ограничения для всех роботов сразу нужно использовать какой-то общий ресурс. В качестве такого ресурса может выступать база данных, собственная dll или что-то ещё. Но чистый QLua не позволяет использовать базу данных без каких-либо внешних библиотек, а dll не всякий умеет писать. К счастью, существует реализация ограничителя на базе файловой системы с помощью чистого QLua.
Создаётся некая папка D:\throttle\, где работает ограничитель интенсивности. Во время работы QLua-скрипты формируют в этой папке столько файлов, сколько транзакций в секунду разрешено, например 10. При каждой попытке послать транзакцию скрипты, грубо говоря, конкурируют за эти файлы. Если ресурса хватило, то транзакция отправляется, если нет, то скрипт ждёт 10 мс и повторяет попытку заново.
Создание ограничителя, предоставляемого модулем Throttle.lua, производится командами
local Throttle = require("Throttle") -- указать путь к файлу модуля
local throttle = Throttle:new("D:/throttle/", 10)
Цикл проверки/ожидания ресурса и последующая отправка транзакции выглядит примерно так:
while not throttle:isAllowed() do
sleep(10)
end
sendTransaction(...)
Приятным свойством данного подхода является то, что все торговые скрипты, работающие с общей папкой, будут иметь общее ограничение, вне зависимости от числа применяемых терминалов QUIK. В принципе, папку D:\throttle\ можно даже разместить на сетевом диске, тогда терминалы QUIK с роботами с разных компьютеров будут иметь общее ограничение по количеству транзакций.
Наконец, вот код модуля Throttle.lua:
---
--- Ограничитель интенсивности на базе файловой системы.
---
--- В папке folder создаются служебные файлы, содержащие штампы с идентификатором объекта и текущим временем.
--- Функция isAllowed() выдаёт разрешение или запрещает использование ресурса.
--- Количество используемых служебных файлов capacity обеспечивает ограничение интенсивности уровнем не более
--- чем capacity разрешений в секунду.
---
--- Алгоритм реализации использует датчик случайных чисел и не гарантирует, что на практике уровень интенсивности
--- будет в точности равен capacity разрешений в секунду при высоких нагрузках.
---
local M = {}
--- Конструктор.
-- @param self объект
-- @param folder имя папки со служебными файлами; оканчивается символом '/'
-- @param capacity количество используемых служебных файлов
local function new(self, folder, capacity)
os.execute("mkdir \"" .. folder .. "\"")
local object = {
folder = folder,
capacity = capacity,
id = math.random(1000000, 9999999)
}
setmetatable(object, self)
self.__index = self
return object
end
M.new = new
local function getFilename(self)
return self.folder .. string.format("%03d.dat", math.random(1, self.capacity))
end
local function getStamp(self)
return self.id .. ":" .. os.time()
end
local function getIdTime(stamp)
if type(stamp) ~= "string" then
return nil, nil
end
local i = string.find(stamp, ":", 1, true)
if i then
return tonumber(string.sub(stamp, 1, i - 1)), tonumber(string.sub(stamp, i + 1))
else
return nil, nil
end
end
--- Узнать, разрешено ли использование ресурса.
-- @param self объект
-- @return true/false
local function isAllowed(self)
local filename = getFilename(self)
local file = io.open(filename, "r")
if file then
local stamp = file:read()
file:close()
local id, time = getIdTime(stamp)
if id == nil or time == nil then
return false
end
if time == os.time() then
return false
end
end
file = io.open(filename, "w+")
if file then
local stamp1 = getStamp(self)
file:write(stamp1)
file:close()
file = io.open(filename, "r")
if file then
local stamp2 = file:read()
file:close()
return stamp1 == stamp2
else
return false
end
else
return false
end
end
M.isAllowed = isAllowed
return M
Успехов в торговле!
Lua реально выучить новичку самостоятельно? Просто тупо документацию из квика по языку читать?
А это на чьей стороне ограничение? И оно чем-то грозит, кроме того, что вылезающие за лимит в моменте транзакции отклонятся?
www.banki.ru/services/responses/bank/response/10461425/
Транзакции будут отклоняться. Чтобы не было ошибок подобного рода и нужен ограничитель.
Мрак какой-то.
Приоритетов итп похоже нету никаких, кто первый встал, того и тапки.
Стало даже немножко страшно за тех, кто в таких инфраструктура бабки крутит…