Комментарии пользователя YouScriptor.com (вайб-хайринг)
— == QUICk/QLUA: авто-перестановка лимитной покупки на +1 тик от лучшего бида ==
— -------------------------
— CONFIG: настроить под себя
— -------------------------
local CONFIG = {
CLASS_CODE = «TQBR», — класс инструмента (акции T+ обычно TQBR)
SEC_CODE = «GAZP», — тикер
ACCOUNT = «L01-00000F00»,-- торговый счет (trdaccid для T+)
CLIENT_CODE = "", — клиентский код (может быть пустым)
QUANTITY = 100, — лот/кол-во
BROKERREF = «autobid+1», — пометка брокеру (необязательно)
ALLOW_CROSS = false, — true чтобы разрешить пересечение спреда (мгновенное исполнение)
MIN_REPLACE_MS= 800, — не чаще чем раз в N мс (анти-флуд)
PRICE_ROUND = 6 — точность округления цены
}
— -------------------------
— Внутренние переменные
— -------------------------
local last_action_ms = 0
local current_order_num = nil — Биржевой номер активной заявки
local current_price = nil — Текущая цена активной заявки
local price_step = nil — Шаг цены инструмента
local best_bid, best_ask = nil, nil
local running = true
— -------------------------
— Утилиты
— -------------------------
local function now_ms()
return os.time() * 1000 + (getInfoParam(«LOCAL_TIME_MCS») ~= "" and math.floor(tonumber(getInfoParam(«LOCAL_TIME_MCS»))/1000)%1000 or 0)
end
local function to_num(x)
if x == nil then return nil end
local n = tonumber(tostring(x):gsub(",", "."))
return n
end
local function round_price(p)
if not p then return nil end
local scale = 10^CONFIG.PRICE_ROUND
return math.floor(p*scale + 0.5)/scale
end
local function fetch_price_step()
— В QUIK параметр шага цены: «SEC_PRICE_STEP»
local p = getParamEx(CONFIG.CLASS_CODE, CONFIG.SEC_CODE, «SEC_PRICE_STEP»)
if p and p.param_type ~= 0 and p.param_value ~= "" then
return to_num(p.param_value)
end
— запасной путь (иногда брокеры публикуют STEPPRICe/PRICESTEP)
local p2 = getParamEx(CONFIG.CLASS_CODE, CONFIG.SEC_CODE, «PRICESTEP»)
if p2 and p2.param_type ~= 0 and p2.param_value ~= "" then
return to_num(p2.param_value)
end
message(«Не удалось определить SEC_PRICE_STEP — проверьте настройки инструмента», 3)
return nil
end
local function read_l2_best()
local book = getQuoteLevel2(CONFIG.CLASS_CODE, CONFIG.SEC_CODE)
if not book then return nil, nil end
local bid = nil
if book.bid_count and book.bid_count > 0 then
bid = to_num(book.bid[1].price)
end
local ask = nil
if book.offer_count and book.offer_count > 0 then
ask = to_num(book.offer[1].price)
end
return bid, ask
end
local function desired_price()
if not best_bid or not price_step then return nil end
local p = best_bid + price_step
if not CONFIG.ALLOW_CROSS and best_ask and p >= best_ask then
— Чуть ниже оффера, чтобы не сделать рыночную покупку
if best_ask — price_step > 0 then
p = best_ask — price_step
else
— если шаг не позволяет опуститься — остаёмся на бест бид + шаг (может исполниться)
end
end
return round_price(p)
end
local function can_replace()
return (now_ms() — last_action_ms) >= CONFIG.MIN_REPLACE_MS
end
local function kill_current_order()
if not current_order_num then return end
local t = {
[«TRANS_ID»] = tostring( os.time()… «01» ),
[«ACTION»] = «KILL_ORDER»,
[«CLASSCODE»] = CONFIG.CLASS_CODE,
[«SECCODE»] = CONFIG.SEC_CODE,
[«ORDER_KEY»] = tostring(current_order_num)
}
sendTransaction(t)
last_action_ms = now_ms()
end
local function place_order(price)
local t = {
[«TRANS_ID»] = tostring( os.time()… «02» ),
[«ACTION»] = «NEW_ORDER»,
[«CLASSCODE»] = CONFIG.CLASS_CODE,
[«SECCODE»] = CONFIG.SEC_CODE,
[«ACCOUNT»] = CONFIG.ACCOUNT,
[«CLIENT_CODE»]= CONFIG.CLIENT_CODE,
[«OPERATION»] = «B», — Покупка
[«PRICE»] = string.format("%."..CONFIG.PRICE_ROUND..«f», price),
[«QUANTITY»] = tostring(CONFIG.QUANTITY),
[«TYPE»] = «L», — Лимитка
[«BROKERREF»] = CONFIG.BROKERREF
}
sendTransaction(t)
last_action_ms = now_ms()
end
local function sync_order()
if not can_replace() then return end
local want = desired_price()
if not want then return end
if current_price and math.abs(current_price — want) < 1e-10 then
return — уже на нужной цене
end
— Убираем старую и ставим новую
if current_order_num then
kill_current_order()
end
place_order(want)
end
— -------------------------
— Callbacks
— -------------------------
function OnInit()
price_step = fetch_price_step()
best_bid, best_ask = read_l2_best()
message(string.format(«Автобид запущен: %s/%s, шаг=%.10f, cross=%s»,
CONFIG.CLASS_CODE, CONFIG.SEC_CODE, price_step or -1,
tostring(CONFIG.ALLOW_CROSS)), 1)
end
function OnQuote(class_code, sec_code)
if not running then return end
if class_code ~= CONFIG.CLASS_CODE or sec_code ~= CONFIG.SEC_CODE then return end
best_bid, best_ask = read_l2_best()
sync_order()
end
function OnOrder(order)
if not running then return end
if order.class_code ~= CONFIG.CLASS_CODE or order.sec_code ~= CONFIG.SEC_CODE then return end
if order.trans_id and order.flags then
— flags битовый; 0x1=покупка; 0x2=продажа; 0x4=активна и т.д.
— Отслеживаем размещение и изменение своей лимитки
if order.brokerref == CONFIG.BROKERREF and order.account == CONFIG.ACCOUNT then
— Сохраняем актуальную заявку
if bit and bit.band(order.flags, 0x4) ~= 0 then
current_order_num = order.order_num
current_price = to_num(order.price)
else
— Заявка снята/исполнена
if current_order_num == order.order_num then
current_order_num = nil
current_price = nil
end
end
end
end
end
function OnTrade(trade)
— Если частично/полностью исполнилась — в зависимости от вашей логики
— можно сразу выставлять заново оставшийся объём на +тик
end
function OnStop(sign)
running = false
— По желанию можно снять остаток:
— if current_order_num then kill_current_order() end
message(«Автобид остановлен», 1)
end