admin@guru-net.biz

Phone : +7 903 662-35-39

Типы заявок в QUIK: от теории к практике

В терминале QUIK доступно множество типов заявок, позволяющих гибко управлять позициями и автоматизировать торговлю. В этой статье мы разберем все виды заявок, ответим на частые вопросы трейдеров и приведем универсальный пример для программной установки через Lua-скрипты.

📌 Основные типы заявок

1️⃣ Лимитная заявка (limit)

Лимитная заявка — это ордер с заранее заданной ценой, по которой вы готовы совершить сделку. Она попадает в стакан заявок и ожидает встречного предложения. Используется для входа в рынок по указанной цене.

  • Появляется в таблице «orders»
  • Исполняется при достижении цены PRICE
  • Может исполняться частично

2️⃣ Рыночная заявка (limit_market)

Рыночная заявка исполняется мгновенно по лучшей доступной цене. Для фьючерсов (SPBFUT) используется минимальная или максимальная цена.

  • Появляется в таблице «orders»
  • Исполняется мгновенно
  • Не гарантирует точную цену входа

3️⃣ Стоп-лимит (stop-limit)

Стоп-лимит — заявка для входа в рынок на пробой уровня. При достижении цены активации выставляется лимитная заявка.

  • Появляется в таблице «stop_orders»
  • Для BUY: PRICE >= STOPPRICE
  • Для SELL: PRICE <= STOPPRICE

4️⃣ Тейк-профит (take)

Тейк-профит — заявка для фиксации прибыли по открытой позиции. Работает по принципу «скользящего» ордера.

  • Появляется в таблице «stop_orders»
  • Требует номер активной базовой заявки (order_num)
  • При достижении STOPPRICE и откате на OFFSET выставляется заявка

5️⃣ Комбинированная заявка (take-stop)

Тейк-профит и стоп-лимит — одна заявка управляет и тейком, и стопом. При срабатывании одного условия, второе автоматически отменяется.

  • Появляется в таблице «stop_orders»
  • MARKET_TAKE_PROFIT = YES (по умолчанию)
  • Может быть с привязкой к базовой заявке или без

6️⃣ Тейк-профит для входа (take-entry)

Тейк-профит для входа — специальный тип для входа в рынок на отскоке. Не требует базовой заявки.

  • Появляется в таблице «stop_orders»
  • Не требует order_num
  • Используется как TAKE_PROFIT_STOP_ORDER

🔍 Ответы на частые вопросы

❓ Можно ли выставить заявку на покупку/продажу с одновременной установкой стоп-лосса и тейк-профита одной заявкой?

Да, можно. В QUIK для этого существует тип заявки «Тейк-профит и стоп-лимит» (take-stop). Это условная заявка, которая одновременно задает оба уровня. Как только одно из условий сработает, второе автоматически отменяется системой.

❓ Где задается цена входа в такой заявке?

Цена входа задается отдельно. Сама стоп-заявка «Тейк-профит и стоп-лимит» не открывает позицию — она управляет уже открытой позицией. Цена входа — это параметр PRICE в лимитной заявке, которая открывает позицию. Стоп-заявка лишь автоматически управляет уже открытой позицией.

Последовательность действий:

  1. Выставляется лимитная заявка на вход (PRICE = цена входа)
  2. После ее исполнения выставляется стоп-заявка «Тейк-профит и стоп-лимит»

❓ Можно ли использовать заявку «Тейк-профит» для входа в рынок?

Да, можно. Для этого используется тип заявки TAKE_PROFIT_STOP_ORDER (в нашем коде — «take-entry»). В этом случае:

  • Не требуется базовая заявка (order_num не нужен)
  • Заявка появляется в таблице «stop_orders»
  • При достижении STOPPRICE и откате на OFFSET выставляется заявка

Важно: Для входа на отскоке не используйте тип ACTIVATED_BY_ORDER_TAKE_PROFIT_STOP_ORDER — он предназначен только для уже открытых позиций и требует номера базовой заявки.

❓ Какие параметры нужны для комбинированной заявки (take-stop)?

Для заявки типа «take-stop» требуется следующий набор параметров:

local order_param = {
    type_order = "take-stop",
    take = 274.68,        -- Тейк-профит
    stop = 278.29,        -- Стоп-лосс
    volume = 1,
    price = 272.00,       -- Цена входа (для лимитной заявки)
    comment = "TP_1_buy_270"
}

При этом в транзакцию передаются:

  • STOPPRICE = take (цена активации тейк-профита)
  • STOPPRICE2 = stop (цена активации стоп-лосса)
  • PRICE = цена для лимитной заявки стоп-лосса

❓ Что делать, если возникает ошибка «Неправильно указан номер базовой заявки»?

Эта ошибка возникает, когда вы пытаетесь создать заявку типа ACTIVATED_BY_ORDER_TAKE_PROFIT_AND_STOP_LIMIT_ORDER (по исполнению), но базовая заявка уже исполнена.

Важное правило: Заявки «по исполнению» могут быть созданы только до исполнения базовой заявки. Как только базовая заявка получает статус «Исполнена», привязать к ней стоп-заявку уже невозможно.

Решение:

  1. Выставляйте стоп-заявку до исполнения базовой заявки
  2. Используйте OnOrder для получения номера базовой заявки (вместо OnTransReply)
  3. Если базовая заявка уже исполнена, используйте обычную заявку TAKE_PROFIT_AND_STOP_LIMIT_ORDER без BASE_ORDER_KEY

💻 Универсальный метод для установки заявок

Ниже приведен универсальный метод Transaction, который поддерживает все описанные типы заявок.

--=================================================================
-- УСТАНОВКА ЗАЯВКИ В СИСТЕМУ TRANSACTION
-- 
-- Параметры:
--   operation   - тип операции: "buy" или "sell"
--   order_param - таблица с параметрами заявки:
--     volume  - объем (обязательный)
--     comment - комментарий (макс 12 символов)
--     price   - цена (для лимитных заявок)
--     last    - цена последней сделки (для limit_market)
--     take    - цена тейк-профита
--     stop    - цена стоп-лосса
--     order_num - НОМЕР БАЗОВОЙ ЗАЯВКИ (для ACTIVATED_BY_ORDER)
--                 ВНИМАНИЕ: базовая заявка ДОЛЖНА БЫТЬ АКТИВНОЙ!
--     
--   type_order - тип заявки (обязательный):
--     
--     === ДЛЯ ВХОДА В РЫНОК ===
--     "limit"        - лимитная заявка по указанной цене
--     "limit_market" - рыночная заявка (исполняется мгновенно)
--     "stop-limit"   - стоп-лимит на пробой уровня
--     
--     === ДЛЯ УПРАВЛЕНИЯ ОТКРЫТЫМИ ПОЗИЦИЯМИ ===
--     "stop"         - стоп-лосс (требует price)
--     "take"         - тейк-профит (требует order_num - АКТИВНОЙ заявки!)
--     "take-stop"    - комбинированная заявка (тейк + стоп)
--                      Если order_num указан - используется как ACTIVATED_BY_ORDER
--                      (базовая заявка должна быть АКТИВНОЙ!)
--     
--     === ДЛЯ ВХОДА НА ОТСКОКЕ ===
--     "take-entry"   - тейк-профит для входа в рынок (НЕ требует order_num)
--
-- Возвращаемые значения:
--   success (boolean) - true при успешной отправке
--   id_transact (number) - идентификатор транзакции
--=================================================================
function TransactionClass:Transaction(operation, order_param)
    local func_name = ":Transaction"
    
    -- Автокоррекция счета (L01-00000F00 вместо L01-0000F00)
    if self.account and string.find(self.account, "L01") then
        if string.match(self.account, "L01%-%d+") then
            local num_part = string.match(self.account, "L01%-(%d+)")
            if num_part and #num_part == 4 then
                self.account = "L01-00000F00"
                self.logging_obj:info(
                    string.format("Счет скорректирован: L01-0000F00 -> L01-00000F00"),
                    self.name_class .. func_name
                )
            end
        end
    end
    
    -- Получаем ID транзакции
    local id_transact = self:GetId()
    
    -- Количество знаков после запятой
    local digits_str = tostring(self.digits)
    
    -- Логирование входных параметров
    local msg = string.format("******************\nТаблица order_param")
    self.logging_obj:info(msg, self.name_class .. func_name)
    self.logging_obj:print_table(order_param, "order_param")
    
    -- Формируем базовую таблицу транзакции
    local transact_table = {
        ["TRANS_ID"] = id_transact,
        ["CLASSCODE"] = self.class,
        ["SECCODE"] = self.name,
        ["QUANTITY"] = string.format("%.0f", NumberClass.round(order_param.volume, 0)),
        ["ACCOUNT"] = self.account,
        ["CLIENT_CODE"] = order_param.comment or "",
        ["EXECUTION_CONDITION"] = "PUT_IN_QUEUE",
    }
    
    -- Определяем операцию
    if operation == "sell" then
        transact_table["OPERATION"] = "S"
    else
        transact_table["OPERATION"] = "B"
    end
    
    --=================================================================
    -- ОБРАБОТКА ТИПОВ ЗАЯВОК
    --=================================================================
    
    -- 1. ЛИМИТНАЯ ЗАЯВКА (вход по указанной цене)
    if order_param.type_order == "limit" then
        transact_table["ACTION"] = "NEW_ORDER"
        transact_table["PRICE"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.price, self.digits))
    
    -- 2. ЛИМИТНАЯ ЗАЯВКА ПО РЫНОЧНОЙ ЦЕНЕ
    elseif order_param.type_order == "limit_market" then
        transact_table["ACTION"] = "NEW_ORDER"
        transact_table["TYPE"] = "M"
        
        if self.class == "SPBFUT" then
            if operation == "sell" then
                transact_table["PRICE"] = string.format("%."..digits_str.."f",
                    NumberClass.round(self:GetMinPrice(order_param.last), self.digits))
            else
                transact_table["PRICE"] = string.format("%."..digits_str.."f",
                    NumberClass.round(self:GetMaxPrice(order_param.last), self.digits))
            end
        else
            transact_table["PRICE"] = string.format("%."..digits_str.."f",
                NumberClass.round(0, self.digits))
        end
    
    -- 3. СТОП-ЛИМИТ (вход на пробой)
    elseif order_param.type_order == "stop-limit" then
        transact_table["ACTION"] = "NEW_STOP_ORDER"
        transact_table["STOPPRICE"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.price, self.digits))
        
        if operation == "sell" then
            transact_table["PRICE"] = string.format("%."..digits_str.."f",
                NumberClass.round(order_param.price - self.offset * self.step_price, self.digits))
        else
            transact_table["PRICE"] = string.format("%."..digits_str.."f",
                NumberClass.round(order_param.price + self.offset * self.step_price, self.digits))
        end
    
    -- 4. СТОП-ЛОСС (управление открытой позицией)
    elseif order_param.type_order == "stop" then
        transact_table["ACTION"] = "NEW_STOP_ORDER"
        transact_table["STOP_ORDER_KIND"] = "STOP_LIMIT_ORDER"
        transact_table["PRICE"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.price, self.digits))
        transact_table["STOPPRICE"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.stop, self.digits))
        
        -- Если указан номер заявки-условия (должна быть АКТИВНОЙ!)
        if order_param.order_num ~= nil then
            transact_table["STOP_ORDER_KIND"] = "ACTIVATED_BY_ORDER_STOP_LIMIT_ORDER"
            transact_table["BASE_ORDER_KEY"] = order_param.order_num
            transact_table["USE_BASE_ORDER_BALANCE"] = "YES"
            transact_table["ACTIVATE_IF_BASE_ORDER_PARTLY_FILLED"] = "YES"
        end
    
    -- 5. ТЕЙК-ПРОФИТ (управление открытой позицией)
    --    ВНИМАНИЕ: ТРЕБУЕТСЯ активная базовая заявка (order_num)
    elseif order_param.type_order == "take" then
        transact_table["ACTION"] = "NEW_STOP_ORDER"
        transact_table["STOP_ORDER_KIND"] = "ACTIVATED_BY_ORDER_TAKE_PROFIT_STOP_ORDER"
        transact_table["BASE_ORDER_KEY"] = order_param.order_num
        transact_table["USE_BASE_ORDER_BALANCE"] = "YES"
        transact_table["ACTIVATE_IF_BASE_ORDER_PARTLY_FILLED"] = "YES"
        
        -- Цена активации тейк-профита
        transact_table["STOPPRICE"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.take, self.digits))
        
        -- Отступ (OFFSET) - для расчета отката
        local offset_val = self.offset_take
        if offset_val == 0 and self.step_price > 0 then
            offset_val = self.step_price * 5
        end
        transact_table["OFFSET"] = string.format("%."..digits_str.."f",
            NumberClass.round(offset_val, self.digits))
        transact_table["OFFSET_UNITS"] = "PRICE_UNITS"
        
        -- Защитный спред (SPREAD)
        local spread_val = self.spread_take
        if spread_val == 0 and self.step_price > 0 then
            spread_val = self.step_price * 5
        end
        transact_table["SPREAD"] = string.format("%."..digits_str.."f",
            NumberClass.round(spread_val, self.digits))
        transact_table["SPREAD_UNITS"] = "PRICE_UNITS"
        
        -- Рыночный тейк-профит по умолчанию
        transact_table["MARKET_TAKE_PROFIT"] = "YES"
    
    -- 6. КОМБИНИРОВАННАЯ ЗАЯВКА (тейк-профит + стоп-лосс)
    --    Если указан order_num - используется как ACTIVATED_BY_ORDER (базовая заявка должна быть АКТИВНОЙ!)
    --    Если order_num НЕ указан - работает как обычная TAKE_PROFIT_AND_STOP_LIMIT_ORDER
    elseif order_param.type_order == "take-stop" then
        transact_table["ACTION"] = "NEW_STOP_ORDER"
        
        -- Если есть номер базовой заявки - используем тип "по исполнению"
        if order_param.order_num ~= nil then
            transact_table["STOP_ORDER_KIND"] = "ACTIVATED_BY_ORDER_TAKE_PROFIT_AND_STOP_LIMIT_ORDER"
            transact_table["BASE_ORDER_KEY"] = order_param.order_num
            transact_table["USE_BASE_ORDER_BALANCE"] = "YES"
            transact_table["ACTIVATE_IF_BASE_ORDER_PARTLY_FILLED"] = "YES"
        else
            transact_table["STOP_ORDER_KIND"] = "TAKE_PROFIT_AND_STOP_LIMIT_ORDER"
        end
        
        -- Цена активации тейк-профита
        transact_table["STOPPRICE"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.take, self.digits))
        
        -- Цена активации стоп-лосса
        transact_table["STOPPRICE2"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.stop, self.digits))
        
        -- Цена для лимитной заявки стоп-лосса
        if operation == "sell" then
            transact_table["PRICE"] = string.format("%."..digits_str.."f",
                NumberClass.round(order_param.stop + self.offset * self.step_price, self.digits))
        else
            transact_table["PRICE"] = string.format("%."..digits_str.."f",
                NumberClass.round(order_param.stop - self.offset * self.step_price, self.digits))
        end
        
        -- Отступ для тейк-профита
        local offset_val = self.offset_take
        if offset_val == 0 and self.step_price > 0 then
            offset_val = self.step_price * 5
        end
        transact_table["OFFSET"] = string.format("%."..digits_str.."f",
            NumberClass.round(offset_val, self.digits))
        transact_table["OFFSET_UNITS"] = "PRICE_UNITS"
        
        -- Защитный спред
        local spread_val = self.spread_take
        if spread_val == 0 and self.step_price > 0 then
            spread_val = self.step_price * 5
        end
        transact_table["SPREAD"] = string.format("%."..digits_str.."f",
            NumberClass.round(spread_val, self.digits))
        transact_table["SPREAD_UNITS"] = "PRICE_UNITS"
        
        -- Используем рыночный тейк-профит по умолчанию
        transact_table["MARKET_TAKE_PROFIT"] = "YES"
        transact_table["MARKET_STOP_LIMIT"] = "NO"
    
    -- 7. ТЕЙК-ПРОФИТ ДЛЯ ВХОДА В РЫНОК (на отскоке)
    --    НЕ требует базовой заявки (order_num не используется)
    elseif order_param.type_order == "take-entry" then
        transact_table["ACTION"] = "NEW_STOP_ORDER"
        transact_table["STOP_ORDER_KIND"] = "TAKE_PROFIT_STOP_ORDER"
        
        -- Цена активации тейк-профита (уровень для входа)
        transact_table["STOPPRICE"] = string.format("%."..digits_str.."f",
            NumberClass.round(order_param.take, self.digits))
        
        -- Отступ (OFFSET)
        local offset_val = self.offset_take
        if offset_val == 0 and self.step_price > 0 then
            offset_val = self.step_price * 5
        end
        transact_table["OFFSET"] = string.format("%."..digits_str.."f",
            NumberClass.round(offset_val, self.digits))
        transact_table["OFFSET_UNITS"] = "PRICE_UNITS"
        
        -- Защитный спред (SPREAD)
        local spread_val = self.spread_take
        if spread_val == 0 and self.step_price > 0 then
            spread_val = self.step_price * 5
        end
        transact_table["SPREAD"] = string.format("%."..digits_str.."f",
            NumberClass.round(spread_val, self.digits))
        transact_table["SPREAD_UNITS"] = "PRICE_UNITS"
        
        -- Рыночный тейк-профит по умолчанию
        transact_table["MARKET_TAKE_PROFIT"] = "YES"
    end
    
    -- Логирование финальной таблицы транзакции
    self.logging_obj:info("******************\nТаблица transact_table", self.name_class .. func_name)
    self.logging_obj:print_table(transact_table, "transact_table")
    self.logging_obj:info("******************", self.name_class .. func_name)
    
    -- Получаем количество заявок до отправки
    local num_orders = getNumberOf("orders") + getNumberOf("stop_orders")
    
    -- Отправляем транзакцию
    local error_transaction = sendTransaction(transact_table)
    
    if error_transaction ~= "" then
        self.logging_obj:error(
            string.format("%s Ошибка '%s' при выставлении заявки '%s' '%s' на объем %f",
                self.name, error_transaction, order_param.type_order, operation, order_param.volume),
            self.name_class .. func_name
        )
        return false, 0
    else
        -- Проверяем, что заявка действительно появилась
        sleep(500)
        local num_orders_curr = getNumberOf("orders") + getNumberOf("stop_orders")
        
        if num_orders_curr <= num_orders then
            self.logging_obj:error(
                string.format("%s Требуется проверка установки заявки '%s' '%s' (ID: %d)",
                    self.name, order_param.type_order, operation, id_transact),
                self.name_class .. func_name
            )
            return false, 0
        end
        
        self.logging_obj:info(
            string.format("%s Успешно отправлена заявка '%s' '%s' (ID: %d)",
                self.name, order_param.type_order, operation, id_transact),
            self.name_class .. func_name
        )
        return true, id_transact
    end
end

📝 Примеры вызова функции

🔹 Вход в рынок по лимитной заявке

local order_param = {
    type_order = "limit",
    price = 100.50,
    volume = 1,
    comment = "TP_1_buy_100.50"
}
local success, id = transaction_obj:Transaction("buy", order_param)

🔹 Вход на отскоке через тейк-профит

local order_param = {
    type_order = "take-entry",
    take = 105.00,        -- Уровень для входа
    volume = 1,
    comment = "TP_2_buy_105"
}
local success, id = transaction_obj:Transaction("buy", order_param)

🔹 Комбинированная заявка для открытой позиции

local order_param = {
    type_order = "take-stop",
    take = 105.00,        -- Тейк-профит
    stop = 95.00,         -- Стоп-лосс
    price = 100.00,       -- Цена входа
    volume = 1,
    comment = "TP_3_buy_100",
    order_num = 1234567   -- Номер АКТИВНОЙ базовой заявки
}
local success, id = transaction_obj:Transaction("sell", order_param)

🔹 Восстановление комбинированной заявки (без привязки)

-- Используется, когда базовая заявка уже исполнена
local order_param = {
    type_order = "take-stop",
    take = 105.00,
    stop = 95.00,
    price = 100.00,
    volume = 1,
    comment = "TP_3_buy_100"
    -- order_num не передается!
}
local success, id = transaction_obj:Transaction("sell", order_param)

🎯 Ключевые выводы

  • Для входа в рынок используйте типы: «limit», «limit_market», «stop-limit» или «take-entry»
  • Для управления позицией используйте типы: «take», «stop» или «take-stop»
  • Заявки «по исполнению» (ACTIVATED_BY_ORDER) требуют АКТИВНУЮ базовую заявку
  • order_num должен быть получен ДО исполнения базовой заявки (через OnOrder)
  • Если базовая заявка уже исполнена — используйте обычную заявку без BASE_ORDER_KEY
  • Комбинированная заявка (take-stop) устанавливается одной транзакцией и управляет и тейком, и стопом
Categories:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Related Posts :-