alfacentavra
alfacentavra личный блог
11 октября 2023, 11:40

Qlua: работа с заявками (часть 3).

Сегодня завершаем работу с заявками:

Функция OnTransReply
Функция OnOrder
Получение остатка по заявке, контроль исполнения полного объема
Таблица транзакций
Общая логика выставления лимитной заявки в стакане

В предыдущих примерах мы закладывали на обработку заявки небольшой таймаут (в пределах от 300 мс до 1 секунды), но правильнее отслеживать результат по коллбэкам, т.к. это время может быть и менее 300 мс, а может затянуться (по разным причинам) на секунды. Поможет нам в этом отслеживании функции обратного вызова OnTransReply и OnOrder.

Если отправляя заявку через sendTransaction мы на старте могли получить ошибку на стороне терминала (неправильно указанный торговый код/инструмент/класс рынка), то через OnTransReply мы получаем результат обработки нашей транзакции на сервере брокера (например ошибка при выставлении заявки из-за отсутствия подключения, либо у клиента нет прав на отправку транзакции конкретного типа, либо заявка не проходит по лимитам и пр.).

Функция OnTransReply возвращает ответ на транзакцию, выставленную средствами qlua. При этом коллбэк придет по заявкам всех запущенных в терминале скриптов, поэтому нужно ставить дополнительные условия, если мы хотим отследить статус нашей заявки. Заявки, выставленные в терминале вручную не будут обработаны этой функцией (транзакции скриптов имеют trans_id, поэтому каждый раз в таком случае функция будет вызываться, а выставленные вручную заявки trans_id не имеют, поэтому коллбэк не сработает).

Чтобы получить таблицу возвращаемых значений находим функцию в файле QLUA.chm:

 Qlua: работа с заявками (часть 3).

И по ссылке проваливаемся в таблицу с описанием транзакций:

Qlua: работа с заявками (часть 3).

Напишем небольшой пример:

Qlua: работа с заявками (часть 3).

Выставим скриптом заявку по цене на 30 копеек ниже последней сделки:

price_order  = tonumber(getParamEx(class, tiker, "LAST").param_value) - 0.3

Функция newtransaction остается без изменений, в main среди прочего поставим фиксацию времени начала работы скрипта, чтобы отслеживать скорость получения коллбэка (time2-time1):

Qlua: работа с заявками (часть 3).

Запустим скрипт на выходе получим серию сообщений:

Qlua: работа с заявками (часть 3).

Здесь мы получили ответ от OnTransReply всего за 56 мс, но далеко не всегда это бывает настолько оперативно.

Запустим параллельно c текущим скриптом, алгоритм, выставляющий заявки по стакану из прошлой статьи, предварительно заменим в нем переменную с названием скрипта и выводимые к заявке комментарии:

progname = "sndTrnsctn_two"
commentid = "sndTr_v2/"

Несмотря на то, что мы отслеживаем OnTransReply в первом скрипте мы получим коллбэки и по заявкам второго:

Qlua: работа с заявками (часть 3).

Т.к. выставлял заявки второй скрипт, то мы в первом не получили точное time1 sendTransaction для оценки скорости получения коллбэка (отсюда и такие большие значения времени отклика), но можем посмотреть какая получилась временная разница между каждым OnTransReply: 12,278 – 11,931 = 0,347 сек и 11,931 – 11,656 = 0,275 сек.

Чтобы отлавливать только заявки текущего скрипта, необходимо либо иметь уникальную для каждого скрипта нумерацию транзакций (например при условии, что каждый скрипт не выставляет более 100 000 заявок в день: первый скрипт выставляет заявки с нумерацией от 1 и далее, второй от 100000, третий от 200000 и т.д.), либо сделать её единой для всех скриптов (например с использованием файла, куда можно сохранять эти id с указанием какой скрипт и когда отправил транзакцию) и отфильтровывать коллбэки по нужным id, либо добавить условия совпадения по комментарию (brokerref). Выберем последний вариант для нашего примера:

Qlua: работа с заявками (часть 3).

Теперь при запуске мы будем отфильтровывать коллбэки по своим транзакциям (при условии, что другие алгоритмы имеют отличные от текущего brokerref):

Qlua: работа с заявками (часть 3).

Получили OnTransReply только по 1 заявке текущего скрипта, по остальным второй скрипт вывел минимальную информацию о выставленных заявках, коллбэки отфильтровались.

В штатном режиме работы терминала и устойчивой связи ответы от OnTransReply приходят всегда. Однако бывают редкие ситуации (например краткосрочная потеря связи), когда этот коллбэк может не сработать, либо прийти, но сперва без order_num, что отмечено в самой документации (см.выше в скринах сноску со звездочкой, что параметр может иметь значение nil), т.к. это ответ сервера брокера на обработку транзакции и первый раз он может прийти раньше, чем ответ с сервера биржи.  В этих случаях для гарантированного отслеживания статуса своей заявки нам пригодится функция обратного вызова OnOrder.


OnOrder

В отличие от OnTransReply, которая срабатывает на транзакции скрипта (новая заявка / замена текущей / снять заявку и пр.), функция обратного вызова OnOrder срабатывает уже по факту обработки и регистрации новой заявки, удалении старой или изменении параметров существующей.

Если мы получили от OnTransReply номер заявки и status == 3 это значит, что она зарегистрирована. Это приведет к срабатыванию OnOrder и появлению соответствующей записи в таблице заявок. В отличие от OnTransReply здесь нет параметра status, т.е. если наша транзакция нормально обработалась (без ошибок) и уже появилась новая строка в таблице заявок, то мы можем быть спокойны, что заявка выставилась и далее отслеживать её исполнение (иногда это проходит практически одновременно). Но об этом поговорим уже в следующей статье

Обычно коллбэк по OnOrder приходит следом за OnTrasReply. Однако разработчики терминала делают оговорку, что это не всегда так: в связи с тем, что разные биржи предоставляют интерфейс подключения к своим системам по разным протоколам и схемам, отбивки по заявкам могут доставляться не синхронизированными потоками, а иногда случаются еще и факты потери пакетов. Таким образом возможны ситуации, когда первым будет вызван OnOrder, а не OnTransReply. И в каких-то еще более редких случаях может сработать коллбэк OnOrder при том, что мы не дождемся ответа от OnTransReply. Это нужно учитывать в своем алгоритме (например закладывая альтернативы получения статуса по транзакции: либо ответа от OnTransReply, либо от OnOrder в зависимости от того, что придёт раньше).

По аналогии с поиском параметров у OnTransReply находим в QLUA.chm функцию OnOrder:

Qlua: работа с заявками (часть 3).

Проваливаемся в таблицу с параметрами заявки:

Qlua: работа с заявками (часть 3).

Для сравнения скорости срабатывания этих двух функций запустим такой пример:

Qlua: работа с заявками (часть 3).

Получаем:

Qlua: работа с заявками (часть 3).

Чаще всего приходит один ответ по транзакции, однако коллбэк от OnOrder может срабатывать несколько раз. Как видим из примера сперва пришел ответ с trn_id = 0.

Чтобы избежать повторов достаточно добавить в код условие, что мы отфильтровываем коллбэки если trn_id = 0 (пропускаем ответы с неполными данными), а также last_id_trans ~= trn_id (отсекаем дублирования, отрабатываем только транзакции с новыми id, где last_id_trans переменная, в которой мы будем фиксировать id последней транзакции).

 

Получение остатка по заявке

Проверить полное исполнение по заявке, либо получить текущий неисполненный остаток можно через параметр balance OnOrder.

Для примера напишем скрипт, который будет выставлять заявку на покупку 100 лот SBER, если в стакане объем лучшего и следующего за ним оффера станет меньше 50. Помним, что нам в этом случае нужен будет открытый стакан по самому инструменту, чтобы скрипт нормально работал.

С помощью функции обратного вызова OnQuote получим нужные объемы и котировку из стакана заявок:

Qlua: работа с заявками (часть 3).

Выставляемся по цене offer2 (более дорогая цена из этих двух):

Qlua: работа с заявками (часть 3).
Запускаем, видим, что на лучшем оффере и следующем уровне суммарно было 27 лотов, поэтому в таком количестве и прошла первая сделка:

Qlua: работа с заявками (часть 3).

Далее по мере изменения стакана заявка исполнилась в полном объеме за следующие 4 сделки. При этом отбивка по транзакции была только одна в самом начале, т.к. по ней зарегистрировалась наша заявка, а уже все изменения по заявке приходили через OnOrder. Неисполненный остаток видим через balance.

Однако мы могли и не получить полное исполнение, например после покупки 27 лот, которые в моменте в стакане при текущих исходных данных, котировки по инструменту могли резко подняться и мы провалились бы в середину бидов, не получив исполнения до конца торговой сессии. В крайней ситуации, когда за время выставления заявки прошел резкий импульс вверх мы могли бы не получить даже исполнения на 27 лот. С одной стороны, можно работать не лимитными заявками, а кидать заявки по рынку. Но этот подход имеет достаточно весомую обратную сторону – цена входа в этом случае может сильно отличаться от текущей (offer2), особенно в период волатильности или другая ситуация — стакан по инструменту сильно разряжен, а нам нужно взять какой-то объем. Как можно решать такую ситуацию рассмотрим ниже.

В таблице заявок в текущем примере мы увидим 1 заявку на 100 лот, в таблице сделок 5 сделок с разными объемами (еще есть таблица транзакций, где также можно посмотреть статус по транзакции, т.е. отследить весь маршрут нашего запроса на выставление заявки и её исполнение: транзакция -> заявка -> сделка). С таблицей сделок также можно работать средствами qlua, это мы рассмотрим уже в следующей статье.

Таблица транзакций.

Если заявки можно найти в таблице заявок, то наши транзакции можно посмотреть в соответствующей таблице транзакций.

Открыть её можно через Создать окно / Таблица транзакций (если там не нашли, то добавляем через: Создать окно / Настроить меню / Таблица транзакций / Добавить / ОК ).

Qlua: работа с заявками (часть 3).

Небольшое описание полей таблицы можно найти в хелпе терминала (F1, в поиске: «Таблица транзакций»):
Qlua: работа с заявками (часть 3).

Кому-то эта таблица может пригодиться, чтобы разобраться с проблемами выставления заявок через скрипт, если таковые будут. Если после анализа таблицы транзакций и таблицы сообщений ясность не появилась, то единственный выход  – писать на форум разработчиков (forum.quik.ru). Отвечают там не быстро (порой ответ можно ждать 5-10 дней, за это время возможно уже и сами определите проблему), но пока это единственный вариант помощи со стороны разработчиков терминала, иногда быстрее и по делу отвечают форумчане, хотя различных троллей и «немного умалишенных» там тоже хватает (как и на любом форуме).


Общая логика выставления лимитной заявки в стакане

С одной стороны, можно работать рыночными заявками и понимать, что после выставления заявки нас ждет однозначное исполнение. Однако в каких-то моментах нас будут ждать резкие импульсы и, как следствия, гораздо менее выгодные цены (но и в каких-то могут ждать проскальзывания в нашу сторону и более выгодная цена входа). К тому же, если нам необходимо войти не 1 лотом, а большим объемом, то нужно будет фиксировать цены сделок и считать средние, плюс мы можем попасть на разряженный стакан, что сильно ухудшит эту среднюю цену входа.

Лично мне удобнее работать через лимитки, больше контроля, исключаю все проскальзывания, понимаю по каким уровням я вхожу, даже если котировки стакана немного сместились. Так можно брать по цене не 2 уровня оффера, как в примере выше, а брать по 3 или для еще большей гарантии по 5 уровню. Если стакан за время регистрации заявки не сдвинулся исполнение всё равно проходит по лучшему офферу. Если же стакан немного «передвинется», то в большинстве случаев заявка исполнится, т.к. изначально выставились повыше. Но даже если ориентироваться на 5 уровень рано или поздно будет тот самый момент волатильности, когда заявка выставилась, при этом котировки за время обработки транзакции «убежали». Тогда по истечении таймаута (например 10-15 секунд) алгоритм анализирует новые цены стакана и принимается решение нужно ли перевыставляться: если цена и прочие условия для входа еще соответствуют условиям сигнала стратегии, то текущая заявка снимается и повторно выставляется по 5 уровню обновленного стакана.

Тема работы с заявками объемная и мы рассмотрели далеко не всё, но этого достаточно для первичного знакомства, в следующей статье мы начнем работу со сделками.

Теги: qlua для начинающих, кружок авиамоделизма.

Ранее:

Qlua: введение
Доля клиентов, использующих алгоритмическую торговлю
«Кружок авиамоделизма»
Разные торговые терминалы, почему Quik
Основной функционал qlua
smart-lab.ru/blog/917696.php

Настраиваем торговый терминал и редактор кода
Установка торгового терминала
Подготовка терминала
Вкладки в терминале
Сохранение и загрузка настроек
Таблица системных сообщений
Отключение окна сообщений
Редактор Notepad++
Настройка русского языка в редакторе
Выбор синтаксиса языка и кодировки
Цветовые настройки
Дублирующий просмотр
Запуск первого скрипта
smart-lab.ru/blog/918869.php

Основы qlua, часть 1:
message, конкатенация
фильтрация по сообщениям в терминале
PrintDbgStr, комментарии
типы данных, type
операции с числами
операции со строками
операции с таблицами
условные операторы
smart-lab.ru/blog/920031.php

Основы qlua, часть 2:
Циклы for … do … end, while do … end, repeat … until.
sleep, break, goto.
как пройти весь массив циклом, как пройти таблицу по ключам
локальные и глобальные переменные, функции
получение даты и времени
получение данных через getInfoParam
smart-lab.ru/blog/921366.php

Qlua: структура скрипта.
Структура типового скрипта qlua с примерами.
Обработка скриптом «обрыва связи» с сервером и возобновления работы.
Работа с файлами: запись, перезапись и чтение файла.
getScriptPath, getWorkingFolder
smart-lab.ru/blog/922044.php

Qlua: получение данных из таблицы текущих торгов, создание таблиц в торговом терминале.
Получение биржевых данных через функцию getParamEx
Выгрузка списка параметров функции getParamEx через DDE из торгового терминала
Создание пользовательских таблиц в торговом терминале
smart-lab.ru/blog/923365.php

Qlua: работа с таблицами (продолжение). Пишем своего советника (начало).
Интегрируем таблицы в структуру скрипта qlua.
Удаляем таблицы через DestroyTable.
Останавливаем скрипт через IsWindowClosed.
Обработка события закрытия таблицы через коллбэк.
Работа с цветом SetColor, Highlight, SetSelectedRow.
Пишем простого советника.
smart-lab.ru/blog/924710.php

Qlua: дополняем скрипт советника таймингом:
Устанавливаем время старта работы скрипта,
Ставим тайминг на получение сигналов на вход,
Устанавливаем таймер на приостановку скрипта.
smart-lab.ru/blog/925421.php

Qlua советник: дополняем сигналами на закрытие позиции, таблицей для вывода данных и расчетом финансового результата по позициям.
Дополняем сигналами на закрытие позиции.
Создаем дополнительную таблицу для вывода данных.
Делаем расчет финансового результата.
smart-lab.ru/blog/926972.php

Qlua: завершаем апгрейд советника:
Пропуск «поздних» сигналов на старте.
Обработка советником обрыва связи.
Сохранение сигналов и логов в файл.
smart-lab.ru/blog/927748.php

Qlua: пишем скринер акций Московской биржи
smart-lab.ru/blog/928152.php

Qlua: получение данных биржевых свечей с сервера брокера, обработка данных, пишем скрипт выгрузки котировок
Функция CreateDataSource
Получение количества свечек данных
Пауза для подгрузки данных
Получение по инструменту OPEN, HIGH, LOW, CLOSE, VOLUME
Обработка времени и даты
Закрытие источника данных
Примеры: получение данных последних 10 свечей, выгрузка новой минутной свечки после её закрытия, текущее значение простой средней SMA10 по минуткам
Простой скрипт выгрузки котировок
smart-lab.ru/blog/929905.php

Qlua: получение данных с графиков терминала.
Идентификатор инструмента
Получаем количество свечей через getNumCandles
Получаем свечные данных через getCandlesByIndex
Читаем данные с индикатора SMA
Данные с верхней и нижней линии Price Channel
Графики с таблицы текущих торгов.
Сравнение получение данных через CreateDataSource и через getCandlesByIndex
smart-lab.ru/blog/931408.php

Qlua: работа с метками, пишем торгового советника на индикаторах.
Вывод текста на график
Вывод графических сигналов
Удаление меток с графика
Торговый советник на индикаторах
Удаление данных вечерней/утренней сессии с графика.
smart-lab.ru/blog/933582.php

Qlua: работа с лентой всех сделок.
Что такое таблица обезличенных сделок.
Настройка таблицы в терминале.
Что делать, если таблица открылась, но она пустая.
Вывод данных с таблицы по DDE.
Работа с таблицей обезличенных сделок через скрипт qlua с примерами.
Пишем советника, показывающего на графике крупных игроков.
smart-lab.ru/blog/935919.php

Qlua: работа с лентой всех сделок (часть 2).
Различия данных ленты всех сделок и биржевого стакана.
Большие покупки и продажи в ленте сделок и динамика цены.
Альтернативные варианты поиск крупных игроков по ленте сделок.
smart-lab.ru/blog/938053.php

Qlua: дополняем скринер акций статистикой, лидерами роста и падения.
Добавляем статистику по акциям роста и падения.
Составляем TOP лидеров роста и падения.
Быстрый поиск акций по тикеру в терминале.
smart-lab.ru/blog/938450.php

Qlua: работа с биржевым стаканом.
Работа с биржевым стаканом через getQuoteLevel2
Особенность нумерации в стакане заявок терминала квик
Работа через функцию обратного вызова OnQuote
Примеры работы со стаканом из скрипта
Сравнение реализации одного алгоритма через разные функции
smart-lab.ru/blog/940742.php

Qlua: работа с заявками (часть 1).
Зачем нужен демо терминал
Где открыть учебный счет
Выставление заявки в торговом терминале через скрипт
Делаем функцию выставления заявки по требуемым условиям
Карман заявок и tri-файлы
Параметры для заявок с примерами по разным рынкам
smart-lab.ru/blog/942481.php

Qlua: работа с заявками (часть 2).
Узнаем общее количество заявок
Функции getNumberOf и getItem
Как пройтись циклом по всем заявкам
Вывод активных заявок
Снять скриптом заявку
Снимаем все активные заявки скриптом
Снимаем только заявки, выставленные конкретным скриптом
smart-lab.ru/blog/944520.php

12 Комментариев
  • serg1209
    11 октября 2023, 12:22
    Спасибо за труд.
  • Eugene Bright
    11 октября 2023, 12:56
    Где ж ты, любезный, был лет 15 назад?
    Пришлось самому, без спец.образования, «рыть» весь мануал. Сколько седых волос нажито!!!
  • Socol
    11 октября 2023, 23:22
    Спасибо большое за материал и труды! ждал этого нового выпуска )
  • Vadim Ch
    14 октября 2023, 10:59
    Огромное спасибо за труд!
    Считаю, что надо собрать все ваши труды и выпустить книгу по практического использованию qLua
  • Сергей Иванов
    26 октября 2023, 12:55
    Спасибо большое за материал и труды! ждал этого нового выпуска )
  • tranquility
    20 декабря 2023, 20:39
    Хороший блог! Попал сюда пытаясь найти документацию какую-то по
    TABLE transaction
    Есть в природе описание полей этой таблицы? В частности, можно ли в qlua передвинуть выставленный ордер без ее снятия и создания нового?
      • tranquility
        03 февраля 2024, 16:05
        alfacentavra, большое спасибо за ответы. А вы не знаете, может есть другие подключения (например, через фирменное финамовское), где есть возможность нормально двигать заявки? А так, «доли секунды» звучит страшно, т.к. смысл двигать заявку в том, чтобы совершать меньше транзакций в секунду, иначе может появится риск попасть на штраф. Специально узнавал, у БКС если через квик больше 25 транзакций в секунду, то каждая новая оплачивается (там что-то от 2 до 5 рублей), а если частота доходит до 50, то происходит бан (временный, надо полагать). Ну, и по хорошему, одна транзакция занимает пинг к серверу плюс пинг от сервера (если торговля роботами ведется не параллельно по разным инструментам), это все равно больше чем потеря миллисекунд на одной более долгой транзакции MOVE_ORDERS.
  • Harah
    29 февраля 2024, 17:33
    Отличный блог. Проглотил все на одном дыхании. Жду продолжения.
    В частности по работе со Стопами.

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

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