AlexGood
AlexGood Ответы на вопросы
27 октября 2025, 20:18

Друзья, у кого-нибудь есть скрипт для КВИК, который переставляет заявку трейдера всегда на 1 тик выше лучшего бида?😏

Друзья, у кого-нибудь есть скрипт для КВИК, который переставляет заявку трейдера всегда на 1 тик выше лучшего бида?😏
20 Комментариев
  • YouScriptor.com (вайб-хайринг)
    27 октября 2025, 20:20
    chatgpt попроси написать
  • YouScriptor.com (вайб-хайринг)
    27 октября 2025, 20:24

    — == 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

      • Rostislav Kudryashov
        27 октября 2025, 21:08
        AlexGood, 21:00 Не поленись рассказать о результатах попробования.
      • YouScriptor.com (вайб-хайринг)
        27 октября 2025, 21:48
        AlexGood, кодировку проверьте
      • YouScriptor.com (вайб-хайринг)
        27 октября 2025, 21:49
        AlexGood, 

        Это из-за «красивых» символов, которые попали при копипасте: в коде вместо обычных дефисов - оказались длинные тире (в cp1251 это байт 151), и Lua ругается: unexpected symbol near '<\151>'.

        Что сделать быстро:

        1. Открой файл в обычном редакторе (Notepad++ / VS Code).

        2. Замените все “косые кавычки” и длинные тире на обычные ASCII символы:

          • и -

          • « »"

        3. Сохраните файл как UTF-8 (без BOM) или ANSI (Windows-1251).

        4. Убедитесь, что комментарии начинаются ровно -- (две короткие черты), не .

        Ниже — «чистая» ASCII-версия скрипта (минимум зависимостей), под TQCB/облигации. Скопируйте целиком; в CONFIG поставьте ваш CLASS_CODE=«TQCB» и SEC_CODE=«RU000…».

         -- QUIK/QLUA: держим лимитную покупку на 1 тик выше лучшего бида -- ВНИМАНИЕ: файл должен быть в чистом ASCII/UTF-8 без BOM. Все дефисы обычные "-". local CONFIG = { CLASS_CODE = "TQCB", -- для облигаций MOEX T+ обычно TQCB SEC_CODE = "RU0000000000", -- ваш тикер ACCOUNT = "L01-00000F00", -- trdaccid для T+ CLIENT_CODE = "", -- если требуется брокером QUANTITY = 1, -- размер заявки (лот) BROKERREF = "autobid+1", ALLOW_CROSS = false, -- не пересекаем спред MIN_REPLACE_MS = 800, -- антифлуд PRICE_ROUND = 6 -- точность цены (под облиги можно 4..6) } -- state 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 -- utils local function now_ms() local mcs = getInfoParam("LOCAL_TIME_MCS") local ms = 0 if mcs ~= "" then ms = math.floor(tonumber(mcs) / 1000) % 1000 end return os.time() * 1000 + ms end local function to_num(x) if x == nil then return nil end local s = tostring(x):gsub(",", ".") return tonumber(s) 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() local p = getParamEx(CONFIG.CLASS_CODE, CONFIG.SEC_CODE, "SEC_PRICE_STEP") if p and p.param_value ~= "" then return to_num(p.param_value) end local p2 = getParamEx(CONFIG.CLASS_CODE, CONFIG.SEC_CODE, "PRICESTEP") if p2 and p2.param_value ~= "" then return to_num(p2.param_value) end message("SEC_PRICE_STEP not found. Check instrument params.", 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 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("AutoBid started " .. CONFIG.CLASS_CODE .. "/" .. CONFIG.SEC_CODE, 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.brokerref == CONFIG.BROKERREF and order.account == CONFIG.ACCOUNT then if order.flags and (order.flags % 8) >= 4 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 function OnStop(sign) running = false message("AutoBid stopped", 1) end

        Если снова появится такая ошибка, быстро проверить:

        • В Notepad++: Поиск → Заменить → Включить регулярные выражения →
          Найти: [^\x00-\x7F] → Заменить на пусто → «Заменить все». Это уберет не-ASCII.

        • Убедитесь, что все комментарии начинаются --, а не каким-то похожим символом.

        Если хочешь, добавлю «бережный» режим: переставлять только если мы перестали быть лучшими, чтобы не терять очередь.

           
  • Viacheslav Ivanenkov
    27 октября 2025, 21:06
    Может я  не понял вопрос, но вроде это базовый функционал в Квике
    Заявки тейк-профит именно так и работают, постоянно переставляясь все выше по тренду
     
  • Viacheslav Ivanenkov
    28 октября 2025, 09:59
    Тейк сам по себе конечно не переставляется так как это всего лишь условие порожденя лимитки или рыночной заявки


    Здесь не написано что происходит при повышении цены
    А происходит следущее — от последней максимальной цены сделки тейк следит куда идет цена, если вниз — то при достижении отступа срабатывает условие(как написано в справке)
    А вот если цена идет вверх то тейк продолжает следить и постоянно увеличивает значение  «последней максимальной цены» постоянно сдвигаясь вверх следом за ценой последней сделки, считайте что условие срабатывания тейка постоянно пересчитывается в сторону увеличения

    И сработает тейк только когда тренд изменится  и цена начнет снижаться

    Не знаю что делает упомянутый скрипт но по описанию похоже что то же самое что и сам тейк, иными словами — ничего 
  • Eugene Bright
    28 октября 2025, 17:08
    У меня данная процедура состоит из 2 частей:
    1. постоянная проверка списка активных и нереализованных ордеров или их частей, т.е. контроль нереализованного остатка, в таблице Заявки. Признак — ненулевой balance в параметрах и соответствующий bit.test() по id заявки.
    2. Поскольку бот — это цикл с интервалом времени sleep(), то проверка п.1 происходит каждый раз после этого интервала.
    3. Если bit.test() показал, что есть нереализованный остаток, то эта заявка сначала копируется в отдельную внешнюю БД (для чего — это отдельный разговор),
    4. Эта заявка с новым ID и новым объемом (в размере нереализованного остатка старой Заявки) получает данные цены из Таблицы Параметров Торгов (параметры BID, OFFER, LAST — соответствующие цены в данный момент) и перевыставляется с учетом нужного изменения (± от нужного параметра цены) и с учетом шага цены для данного инструмента.

    Работоспособность проверена лично. Зуб даю.))

    Весь алгоритм (без кода lua) представлен в посте в моем СЛ-блоге. Блог небольшой, можно найти быстро «Коллбэки своими руками».
  • Rostislav Kudryashov
    28 октября 2025, 19:22
    В Quik Lua состояние стакана запрашивается функцией getQuoteLevel2().
    Эту функцию надо вставить в обработчик изменения стакана OnQuote(). Это функция обратного вызова.
    И уповать, что послав цену на один тик сдвинутую от лучшей цены стакана, эта заявка придёт на биржу с тем же сдвигом от текущей на момент прихода лучшей цены стакана.
    При интенсивных торгах никто не гарантирует, что на это хватит скорости интернета и поворотливости сервера брокера.
    При этом нелишне отследить, что случилось с предыдушей заявкой.

    Всё это есть  в YouScriptor.com (вайб-хайринг)  Вчера в 20:24
    Только скинуто в ленту неаккуратно. И без внутриблочных отступов почти нечитаемо.
    Чтобы заработало, надо понимать, что к чему.
  • Мультитрендовый
    29 октября 2025, 02:56
     Я думал пост будет, Друзья, у кого нить есть скрипт для квик, который делает 30% годовых?! А тут скучно(

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн