Выставление и снятие лимиторованной заявки в торговую систему.

С помощью языка Lua в систему QUIK можно отправлять все виды заявок, включая все разновидности стоп-заявок. Заявки в торговую систему QUIK отправляются с помощью специальной функции sendTransaction(). Функция sendTransaction() является мощным инструментом работы с заявками. Все параметры настройки заявок, которые есть в окне «Ввод заявки»

и окне «Стоп-заявка»,

можно настроить с помощью языка Lua. Параметров настройки заявок большое количество, но скорее всего, большая часть из них вам не понадобится. Основную часть параметров мы рассмотрим в течении ближайшего времени, но если какие-то параметры будут пропущены вы всегда сможете обратиться к справочному списку с параметрами заявок. Все параметры заявок указаны в справочном руководстве к терминалу QUIK, файл «6 Работа с другими приложениями.pdf» и в отдельном файле к данному курсу.

С начала мы рассмотрим выставление лимитированной заявки. Для справки, лимитированной заявкой, называется заявка на покупку или продажу по определенной цене. Приступим к написанию кода. Создадим новый скрипт под названием «017 Лимитированная заявка.lua».

Для выставления заявки в торговую систему и отслеживания результата ее исполнения нам потребуется скрипт, который будет выполняться не единожды, а некоторое количество времени. Т.е. терминал QUIK будет многократно проходить все строки кода с заданной периодичностью до того момента пока мы его не остановим кнопкой «Остановить» в окне «Доступные скрипты» или пока из скрипта не придет команда на его остановку. Подобное мы уже делали в разделе «Интерпретатор языка Lua», когда мы рассматривали скрипт «014 Пользовательские таблицы.lua», напомню, там мы выводили в таблицу QUIK параметры фьючерса RIZ7 с интервалом 2 раза в секунду (500 мс.). Скопируем из файла «014 Пользовательские таблицы.lua» первые 5 строк и пропишем основную функцию main.

Напомню, первые 5 строк отвечают за остановку скрипта. Функция OnStop() вызывается терминалом QUIK при нажатии кнопки «Остановить» в окне «Доступные скрипты».

И, так функция sendTransaction().

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

Поскольку функция sendTransaction() принимает на вход таблицу, то давайте сначала создадим таблицу, а потом уже вызовем функцию sendTransaction() и рассмотрим ее подробнее.

В разделе «Таблицы с текстовыми ключами к строкам» мы уже заполняли таблицу, подходящую для использования в функции sendTransaction().

Создадим таблицу с именем LimitOrderBuy, создадим в ней строки с определенными ключами и заполним эти строки значениями.

Рассмотрим подробнее строки таблицы. Строка таблицы с ключом «ACTION» содержит указание, какое действие совершить, «NEW_ORDER» означает выставить новую заявку. Строка «ACTION» может принимать 17 видов значений, часть из них мы будем рассматривать, часть нет, все значения, принимаемые данной строкой, представлены в справочном руководстве к терминалу QUIK, файл «6 Работа с другими приложениями.pdf» и в отдельном файле к данному курсу.

Строка с ключом «ACCOUNT» содержит номер торгового счета в данном примере счет «SPBFUT00000». Номер своего торгового счета Вы можете посмотреть в окне «Ограничения по клиентским счетам», колонка «Торговый счет» или в окне «Клиентский портфель», колонка «Код клиента». Чтобы открыть окна «Клиентский портфель» и «Ограничения по клиентским счетам» воспользуйтесь основным меню терминала QUIK пункт «Создать окно». Если в меню «Создать окно» нет нужных пунктов откройте пункт «Все типы окон…» и выберете необходимые окна.

Строка с ключом «OPERATION» содержит вид операции, покупка «B» или продажа «S».Строка с ключом «CLASSCODE» содержит код класса для бумаги, все фьючерсы имеют код класса «SPBFUT».

Строка с ключом «SECCODE» содержит код инструмента, в данном примере «RIZ7».

Cтрока с ключом «PRICE», содержит цену инструмента, в данном примере tostring(100000). Цена в обязательном порядке должна быть передана в виде строкового значения, поэтому используется преобразование числового значения в строковое. Все параметры, передающиеся в функцию sendTransaction(), должны быть строкового типа. Для теста, выставим цену на покупку значительно ниже, чем текущая рыночная цена.

Строка с ключом «QUANTITY» содержит объем сделки в лотах, в данном примере лот. Тип значения строковый.

Строка с ключом «TRANS_ID» содержит идентификатор транзакции, для примера идентификатор равняется 1. Данный идентификатор должен быть уникальным для всех ваших транзакций. Используя данный идентификатор, мы будем находить номер заявки, который ей присвоила система QUIK, а номер заявки нам потребуется для снятия заявки или проверки ее состояния.
Таблица для отправки заявки в торговую систему готова. Создадим новую переменную с именем Err_Order и вызовем функцию sendTransaction() одновременно передав в нее таблицу LimitOrderBuy.

Разберем строку кода 18. Данной строкой мы вызываем функцию sendTransaction() и передаем в нее таблицу LimitOrderBuy, содержащую параметры заявки. Функция sendTransaction() отправляет транзакцию в торговую систему, если в результате транзакции произошла ошибка, то функция sendTransaction() вернет описание ошибки в переменную Err_Order, если ошибок нет, то переменная Err_Order будет содержать пустую строку.

Пока что не будем запускать скрипт. Для тестирования работы скрипта нам требуется выставить ламинированную заявку и не требуется ее исполнение. Конечно, мы можем вручную прописать значение цены, поставив его ниже текущей цены инструмента, но гораздо познавательнее будет сделать цену заявки расчетную исходя из текущей рыночной цены. Для этого возьмем цену последней сделки из таблицы «Текущие торги» при помощи функции getParamEx() и вычтем из нее определенное значение, например 2000 пунктов. Работу функции getParamEx() мы рассматривали в разделе «Интерпретатор языка Lua для терминала QUIK». Объявим две переменные и присвоим им значения, первая переменная SecCode будет содержать код инструмента, вторая переменная PriceRTSBuy будет содержать цену последней сделки. Переменные объявим выше строки с таблицей LimitOrderBuy.

Далее изменим две строки в таблице с параметрами заявки. Первая это код инструмента (строка кода 15), вместо прямого ввода кода инструмента вставим переменную, содержащую код, вторая вместо фиксированного значения цены вставим расчетное (строка кода 16), а именно из переменной содержащей цену последней сделки вычтем 2000.

Скрипт для заявки на покупку готов, сохраним его.Откроем таблицу заявок в терминале QUIK. Меню «Создать окно» пункт «Заявки». Настроим колонки окна. Нам понадобятся следующие колонки: Номер, Выставлена (время), Бумага, Операция, Счет, Цена, Количество, Комментарий, Состояние, ID транзакции.

Дополнительно настроим вывод окна сообщений на экран. Меню «Система» пункт меню «Настройки» пункт «Основные настройки». В открывшемся окне «Настройки клиентского места», в левом списке выберем пункт «Сообщения», в правой части окна отметим пункт «Показывать Окно сообщений». В окно сообщений мы будем выводить информацию по работе скрипта.

Рассмотрим следующий случай, если в результате транзакции произошла ошибка, например, мы ввели некорректный торговый счет, то в переменной Err_Order будет содержаться описание ошибки нам необходимо будет увидеть данное описание. Для этого воспользуемся функцией интерпретатора QLua под именем message(). Функция message() выводит на экран окно сообщения. Добавим соответствующую строку в наш код.

Сохраним скрипт. В данном коде в таблице LimitOrderBuy прописан некорректный торговый счет, не меняя его значения, запустим скрипт.

У нас появилось окно сообщений, в котором отображается ошибка нашей транзакции. Если транзакция пройдет успешно, то отобразится пустое окно сообщений. Введем действующий торговый счет в строке кода 12 и запустим скрипт.

В результате выполнения скрипта, в торговую систему была выставлена заявка с указанными параметрами. Терминал QUIK выдал окно сообщения, подтверждающее выставление новой заявки. Сама заявка отобразилась в окне «Таблица заявок». На данный момент заявка выставлена, но не исполнена. Сейчас, исполнение заявки не требуется, снимем ее вручную комбинацией клавиш Ctrl+D или через контекстное меню «Снять заявку».

Для контроля состояния заявки и возможности ее снять с помощью языка Lua необходимо, знать номер заявки который отображается в столбце «Номер» таблицы «Таблица Заявок». Номер заявки мы можем получить, используя ее идентификатор и функцию обратного вызова OnTransReply(). Функция OnTransReply() вызывается терминалом QUIK в случае получения клиентской транзакции. Функция возвращает таблицу с описанием параметров транзакции. В документе «Интерпретатор языка Lua.pdf» в пункте «4.24 Транзакции» содержится полное описание параметров транзакции. В наших примерах мы будем использовать только некоторые из них.

Добавим функцию OnTransReply() в наш скрипт.

Функция OnTransReply() вызывается при любой транзакции, отправленной из скрипта, если у нас одновременно может отправляться несколько транзакций, то нам необходимо определить по какой именно транзакции была вызвана функция OnTransReply(). Для этого мы можем использовать уникальный идентификационной номер, присвоенный заявки при отправки ее в систему. Идентификационный номер мы прописывали в таблице с параметрами заявки строка «TRANS_ID». В предыдущем примете мы прописали идентификатор равный 1, но это было лишь для первичного тестирования выставления заявки. В рабочих торговых роботах так не пойдет.

Идентификатор играет важную роль в управлении заявками, так что над ним придется поработать. Объявим переменную, которая будет содержать идентификатор заявки. Переменную необходимо объявить выше функции OnTransReply() и соответственно выше функции main(). Это необходимо для того чтобы переменная содержащая идентификатор была видна в функциях OnTransReply() и main(). Назовем переменную ID_B (ID — идентификатор B — покупка) и присвоим ей значение 0.

Переменная есть, но ее значение 0 нас не устроит. Если в нашем терминале будет несколько торговых роботов, то необходимо предусмотреть уникальность идентификатора заявки. Для этого придумаем кодировку для заявок, отправляемых нашим роботом. Например, первоначально идентификатор будет иметь значение 1010001, что это значит. Образно разобьем число на части. Первое число 10 будет означать инструмент, по которому торговый робот отправляет заявки, в данном случае это RIZ7. Второе число 1 будет означать операцию покупки (если операция продажа, то значение будет 2). Третье значение 0001 это наш порядковый номер идентификатора, который будет увеличиваться на единицу при каждой последующей отправленной заявке.

Также нам потребуются переменные, в которых будут содержать параметры транзакции, которые вернет функция OnTransReply(). А именно:- Номер заявки;- Статус заявки;- Сообщение отправляемое системой при обработке заявки. Может содержать подтверждение выставления заявки или причину отказа в выставлении заявки;- Сообщение об ошибке отправки транзакции. Данная переменная уже есть в нашем скрипте, но нам потребуется ее вынести выше функции main() для ее видимости во всем скрипте. Создадим соответствующие переменные:

Как уже говорилось ранее функция OnTransReply() вызывается терминалом QUIK при получении от клиента транзакции. Терминал QUIK передает в функцию таблицу содержащую параметры транзакции. Мы будем использовать следующие строки со следующими ключами:

— order_num (номер заявки)
— status (статус заявки)
— result_msg (сообщение с результатом обработки заявки)

В строке кода 12 мы прописали функцию OnTransReply(order) с параметром (order). order – это имя таблицы в которую функция OnTransReply() вернет таблицу с параметрами, следовательно именно их этой таблицы мы будем брать строки с нужными ключами. Присвоим переменным NunOrderB, StatusOrderB, msgOrderB соответствующие строки из таблицы order.

Как уже говорилось ранее, у нас единовременно может отправляться несколько заявок, а нам необходимо узнать параметры конкретной. Для этого введем проверку. В качестве проверочного параметра будем использовать наш идентификатор заявки, который мы указываем при отправке транзакции в систему. Запишем условие проверки в наш код.

Теперь после выполнения функции sendTransaction(LimitOrderBuy) и отправки транзакции на сервер, терминал QUIK вызовет функцию OnTransReply(order), в случае соблюдения условия if order.trans_id == ID_B then, в соответствующих переменных будут необходимые значения. В распоряжении скрипта будет статус транзакции, номер заявки, присвоенный системой (если заявка выставлена) и сообщение о результате транзакции.

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

Рассмотрим вариант, когда транзакция выполняется и терминал вызывает функцию OnTransReply(order). Еще раз этапы прохождения транзакции. Первый этап вызов функции sendTransaction(LimitOrderBuy), второй этап прием транзакции сервером QUIK, третий этап формирование заявки на сервере и присвоение ей номера, четвертый этап вызов функции OnTransReply(order). Все эти действия занимают некоторое время, это время может варьироваться в зависимости от состояния канала связи и загруженности серверов брокера. В любом случае время прохождения всех этапов больше, чем время выполнения скрипта на компьютере, поэтому мы не можем сразу после выполнения функции sendTransaction(LimitOrderBuy), прописать строку с проверкой состояния транзакции. Для проверки состояния транзакции нам необходимо записать цикл, который будет с заданной периодичностью проверять состояние нашей транзакции, в тоже время бесконечно этот цикл выполняться не может, значит необходимо предусмотреть условие остановки цикла. Цикл прекратит выполняться при соблюдении одного из условий. Первое если заявка будет выставлена тогда, в переменную StatusOrderB будет записан статус принятия заявки. Второе если транзакция не будет выполнена, то в переменную Err_Order будет записан текст ошибки отправки транзакции. Это два взаимоисключающих условия. Если в переменной StatusOrderB будет статус выставления заявки, то это означает что транзакция прошла успешно и переменная Err_Order будет содержать пустую строку. Если транзакция не была принята сервером, то в переменной Err_Order будет сообщение об ошибке и соответственно заявка выставлена не будет и переменная StatusOrderB будет nil. Из этого следует условие цикла. Цикл выполняется условие, когда переменная StatusOrderB равна nil и переменная Err_Order содержит пустую строку. В теле цикла не будет выполняться какой-либо код, кроме остановки выполнения скрипта на определенное время, установим время в 10 миллисекунд.

(Поясню по переменной Err_Order. Переменная будет содержать именно пустую строку, а не nil, как может показаться на первый взгляд, поскольку функция sendTransaction(), в любом случае возвращает в переменную Err_Order строку, разница лишь в том есть ли в этой строке информация об ошибке, если ошибки нет, то соответственно строка пустая.)

Создадим цикл проверки состояния транзакции.

(Во время написания этого раздела у фьючерса RIZ7 истек срок обращения, поэтому я перешел на ближайший фьючерс RIH8, изменив в строке кода 21, значение переменной SecCode. И вообще за время написания этого курса много фьючерсов истекло, так что если где-то в тексте или на изображениях увидите расхождение в наименовании фьючерсов, то не обращайте внимания, суть от этого не меняется)

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

Для наглядности выполнения транзакции выведем на экран окно сообщения, содержащее статус транзакции. Если транзакция выполнена, выведем основную информацию по ней. Из таблицы с описанием параметров транзакции мы видим, что если транзакция выполнена, то переменная StatusOrderB должна содержать значение 3. (Таблица «Описание параметров транзакции», есть в документе «Интерпретатор языка Lua.pdf»). Запишем данное условие, если StatusOrderB == 3, то выводить окно сообщения, содержащее идентификатор транзакции ID_B, номер заявки NumOrderB, статус транзакции StatusOrderB и информационное сообщение по транзакции. В случае если StatusOrderB не равно 3, то добавим проверку равно ли значение переменной StatusOrderB значению nil, если равно, то выведем на экран соответствующее сообщение. В случае если переменная StatusOrderB не равна 3 и не равна nil, то это означает, что сервер транзакцию получил, но не выполнил, по какой-то причине, чтобы узнать эту причину выведем в окно сообщения соответствующую информацию. Код вставим выше строки, в которой идентификатор ID_B увеличивается на единицу, иначе в сообщении будет указан неверный номер идентификатора.

В результате получившийся скрипт может отправлять транзакцию в торговую систему, в случае успешного выполнения транзакции выставлять заявку с указанными параметрами и выводить информацию, если транзакция не выполнена.

Сохраним и запустим скрипт.

Я специально оставил некорректный торговый счет для демонстрации работы скрипта в случае не выставления заявки. В сообщении отобразилась информация о не выставленной заявке и причина отказа. Далее пропишем корректный торговый счет и заново запустим скрипт.

В результате выполнения скрипта была выставлена лимитированная заявка на покупку фьючерсного контракта RIH8 с ценой покупки меньше текущей на 2000 пунктов. После того как заявка была принята системой отработала функция OnTransReply(), и в созданные нами переменные была записана информация по этой заявке. Для дальнейшей работой с выставленной заявкой нам обязательно потребуется ее номер, который записан в переменную NumOrderB. По номеру заявки мы можем найти ее в таблице заявок и определить состояние. Также, номер заявки обязательно прописывается при отправке транзакции на снятие заявки.

Первым делом мы найдем нашу заявки в таблице заявок и запишем номер строки таблицы в переменную.

Для того чтобы обратиться к таблице с заявками используется функция интерпретатора QLua под именем getItem(), функция возвращает таблицу Lua с данными из определенной строки указанной таблицы QUIK. Функция getItem() позволяет взять данные не только из окна таблицы «Таблица заявок», но еще из нескольких таблиц. Список таблиц, к которым getItem() предоставляет доступ, содержится в документе «Интерпретатор языка Lua.pdf», пункт «3.1.5 Таблицы, используемые в функциях «getItem», «getNumberOf» и «SearchItems»». «Таблицы заявок» имеет имя «orders».

Создадим новую, пустую таблицу Lua с именем orderRTSB в, которую будем записывать значения возвращаемые функцией getItem().

Вызовем функцию getItem() и поместим возвращаемые значения в таблицу orderRTSB.

Рассмотрим формат вызова функции getItem(). В функцию мы передали имя таблицы «orders» и номер строки таблицы 0. (Нумерация строк в таблице начинается с нуля.) Функция в свою очередь вернет нам запрашиваемую строку в виде таблицы.

Определенная особенность QUIK. В строке с индексом 0 не бывает заявок эта строка пустая. Если у нас в таблице заявок будет лишь одна заявка, то она будет в строке с индексом 1, а не 0, хотя строка с индексом 0 существует. Когда мы будем проходить по таблице заявок циклом, мы все равно будем просматривать строку 0, просто чтобы не запутаться в коде.

Пока что все хорошо, но мы не знаем в какой строке таблицы «Таблицы заявок» находится наша заявка. Следовательно, сначала нам нужно определить номер строки, в которой записана заявка.

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

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

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

Исходя из выше сказанного, у нас будет два цикла, один вложенный в другой. Вложенный цикл будет служить для поиска нужной заявки в таблице заявок. Внешний цикл будет повторять вложенный до того момента пока вложенный цикл не найдет требуемую заявку.

Перед тем как прописывать циклы создадим вспомогательные переменные.

Создадим переменную под именем NumberOrders, в которой будет содержаться количество строк в таблице заявок. В этой же строке запишем в нее количество строк в таблице заявок при помощи известной нам функции getNumberOf().

Создадим переменную под именем NumOrderTable и присвоим ей значение 0. Это вспомогательная переменная, которая будет использоваться в условии цикла. В эту переменную мы будем записывать номер заявки из текущей строки таблицы заявок. В условии цикла мы будем сравнивать значение этой строки и номер искомой заявки.

Также нам потребуется переменная, в которую мы запишем номер строки таблицы заявок, в которой содержится искомая заявка, создадим ее под именем NumStringTable.

Хорошо, переменные готовы, но для записи циклов пока рановато. Перед тем как прописать циклы необходимо прописать условия, при которых они будут выполняться. Циклы будут выполняться, только если в таблице заявок есть хотя бы одна заявка, т.е. переменная NumberOrders больше нуля и второе условие если наша заявка принята торговой системой, эта информация содержится в переменной StatusOrderB, если переменная равна «3», то заявка принята торговой системой. Нет никакого смысла искать заявку в пустой таблице или многократно пролистывать таблицу заявок в поиске той, которую не приняла торговая система.

Обратите внимание строка кода orderRTSB = getItem(«orders», 0) постепенно смещается в низ скрипта и теперь находится в условии. Напомню, этой строкой кода мы берем строку из таблицы заявок. Возможно, мы ее рановато записали, но ничего. Она и далее будет смещаться и, в конце концов, окажется в теле вложенного цикла.

Теперь можем приступить к записи первого цикла. Этот цикл отвечает за многократный перебор строк в таблице заявок. Цикл будет исполняться до тех пор, пока в переменную NumStringTable не будет присвоен номер строки таблицы заявок, в которой содержится искомая заявка, и переменная stopped содержит значение false. Добавим в тело цикла паузу в 10 миллисекунд, хотя это не обязательно. Напомню, переменная stopped меняет свое значение на true, когда мы нажимаем кнопку «Остановить» в окне «Доступные скрипты». Если будет соблюдено одно из вышеуказанных условий, то цикл остановится.

Перед записью вложенного цикла, который будет перебирать строки в таблице заявок, добавим вспомогательные строки кода. Еще раз запросим количество строк в таблице заявок и поместим значение в переменную NumberOrders. Поясню зачем, первый раз (строка кода 49) мы запрашивали их количество для проверки, не пустая ли таблица заявок. Во второй раз мы запрашиваем количество строк для вложенного цикла, а конкретнее для управляющей переменной вложенного цикла. Создадим управляющую переменную «i» для вложенного цикла и присвоим ей значение из переменной NumberOrders минус 1. Поскольку нумерация строк в таблице заявок начинается с нуля, то количество строк в таблице заявок будет на единицу больше индекса последней строки, именно поэтому мы и вычитаем единицу из переменной NumberOrders.

Теперь мы готовы записать вложенный цикл, который будет перебирать строки в таблице заявок в поисках нужной заявки. Идентифицировать нужную заявку мы будем по ее номеру. Номер нашей заявки хранится в переменной NumOrderB. Напомню, функция OnTransReply() вернула нам номер выставленной заявки и записала его в эту переменную, строка кода 15. Цикл будет исполняться до тех пор пока соблюдаются два условия. В первом условии сравниваются, номер заявки из таблицы заявок, переменная NumOrderTable, с номером нашей заявки, переменная NumOrderB, если номера совпадают, то цикл останавливается. Во втором условии проверяется индекс строки в таблице заявок, если индекс строки становится меньше нуля, то цикл останавливается, индекс строки содержится в переменной «i».

Запишем цикл.

Цикл перебирает строки пока не найдет заявку и пока не кончатся строки в таблице заявок.

Хорошо. Посмотрим на строку кода 56. В данной строке функция getItem() возвращает таблицу orderRTSB содержащую значения в строке с индексом 0 таблицы заявок. За индекс строки отвечает переменная «i», заменим 0 на переменную «i».

Далее запишем еще несколько строк в теле вложенного цикла, а ниже разберем их работу.

Строка кода 56. В таблицу orderRTSB записываются данные из строки под индексом «i», таблицы заявок, с помощью функции getItem().

Строка кода 57. В переменную NumOrderTable записывается номер заявки из таблицы orderRTSB. (Ключи к строкам таблицы заявок прописаны в документе «Интерпретатор языка Lua.pdf» пункт «4.5 Заявки». Для строки с номером заявки используется ключ order_num.)

Строка кода 58. Условие, в котором сравниваются две переменные NumOrderTable и NumOrderB, если условие верно, то в переменную NumStringTable записывается индекс строки таблицы заявок в которой записана искомая заявка, строка кода 59.

Строка кода 61. Переменная «i» уменьшается на единицу, тем самым осуществляется переход на одну строку выше в таблице заявок.

После того как выше записанные циклы прекратят выполняться в нашем распоряжении будет индекс строки таблицы заявок, в которой записана заявка, отправленная из этого скрипта.

Теперь, зная номер строки таблицы заявок, мы можем проверить исполнена заявка или нет. Состояние заявки, в отличие от остальных параметров заявки содержится в виде битовых флагов. Операции с битовыми данными сложнее для понимания, поскольку мы привыкли оперировать десятичными числами и переменными. На самом деле для работы с заявками нам нет необходимости досконально разбираться, как работать с битовыми данными, нам достаточно знать, как определить состояние заявки и еще некоторые параметры, которые также содержаться в виде битовых флагов. Описание битовых флагов содержится в документе «Интерпретатор языка Lua.pdf», так же здесь прописаны функции для работы с битовыми данными. В нашем курсе мы разберем работу с тремя первыми битами, в которых содержится информация о состоянии и направлении заявки.

Исходя из раздела номер 5, документа «Интерпретатор языка Lua.pdf», бит номер 0 указывает, активна заявка или нет. Для того чтобы обратиться к определенному биту будем использовать функцию битового «and» под названием bit.band() в функцию мы передаем набор битовых флагов, взятых из определенной заявки и адрес нужного нам бита.

Вызов функции будет выглядеть следующим образом:

bit.band(orderRTSB.flags, 0x1)

Сначала прописываем функцию битового «and», затем передаем в нее строку, содержащую битовые флаги из таблицы, содержащей информацию по заявке, затем указываем адрес бита, к которому хотим обратиться. В результате функция вернет числовое значение. В случае с битом номер 0, который сообщает активна заявка или нет, если функция вернет значение 0 то заявка не активна иначе активна. Это мы рассмотрели обращение к биту 0.

Для обращению в биту 1, пропишем следующий код

bit.band(orderRTSB.flags, 0x2)

В бите 1 указывается, снята заявка или нет. Если бит 1 равен нулю, то заявка не снята. Если бит 1 не равен 0 то заявка либо активна, либо исполнена.

Обратимся к биту 2

bit.band(orderRTSB.flags, 0x4)

В бите 2 указывается заявка на покупку или продажу. Если бит 2 равен ноль, то это заявка на покупку иначе заявка на продажу.

В битовых флагах нет флага указывающего, исполнена заявка или нет, но есть флаги, указывающие, активна заявка или нет и снята заявка или нет. Используя комбинацию этих двух флагов, мы можем узнать исполнена наша заявка или нет. Если бит 0 равен 0 и бит 1 равен 0, то заявка исполнена. Логика следующая. Бит 0 равен 0 значит заявка не активна, далее бит 1 равен 0 значит заявка не снята, следовательно, она исполнена, четвертого не дано. Статус заявки может принимать следующие значения «Активна», «Исполнена» и «Снята».

Для наглядности работы битовых флагов создадим окно таблицы, в которое выведем значения битовых флагов, по заявке. Как создавать окна таблиц мы рассматривали в разделе «Интерпретатор языка Lau для терминала QUIK.» Вставим строки описывающие окно таблицы в начале функции main(). У нас будет три колонки, в которых будут отображаться значения первых трех битов соответственно. Назовем таблицу «Битовые флаги».

В предыдущем цикле мы нашли номер строки таблицы заявок, в которой записана наша заявка, номер строки содержится в переменной NumStringTable. Считаем из таблицы заявок соответствующую строку и запишем ее в таблицу orderRTSB.

Введем значения битов в соответствующие колонки окна таблицы.

Если в таком виде запустить скрипт, то он выполниться одни раз и остановиться. Для наглядности изменения битовых флагов добавим цикл, который будет с паузой в одну секунду выводить состояние битовых флагов, до того момента пока его не остановят вручную.

Используем уже известную переменную stopped, которая определена в начале скрипта и меняет свое значение после нажатия кнопки «Остановить» в окне «Доступные скрипты».

Добавились строки кода 74, 79, 80. Сохраним и запустим скрипт.

В левом верхнем углу окна QUIK отобразилась таблица «битовые флаги». В соответствующих колонках выводится их состояние. На данный момент заявка актина и в таблице отображается значения бит 0 состояние 1, бит 1 состояние 0, бит 2 состояние 0.

Эти цифры говорят нам: заявка активна, заявка не снята, заявка на покупку. Обратите внимание на окно «Доступные скрипты». Скрипт «017 Лимитированная заявка.lua» запущен о чем свидетельствует значок Play напротив названия скрипта. Остановится скрипту не дает цикл, записанный в строках кода с 74 по 80.

Чтобы проверить, как изменяется состояние битовых флагов, снимем заявку вручную, через контекстное меню или клавишами Ctrl+D.

Теперь в таблице «Битовые флаги» отображается следующее: бит 0 состояние 0 заявка не активна, бит 1 состояние 2 заявка снята, бит 2 не изменился заявка на покупку.

Остановим скрипт вручную кнопкой «Остановить» в окне «Доступные скрипты» и закроем окно «Битовые флаги».

Посмотрим, что происходит с состоянием битов, когда заявка исполняется. Изменим цену заявки таким образом, чтобы она исполнилась. Поменяем знак «-» на «+», в строке кода 39.

Сохраним и запустим скрипт.

Заявка была выставлена скриптом и сразу исполнилась. Битовые флаги имеют следующее состояние: бит 0 значение 0 заявка не активна, бит 1 значение 0 заявка не снята, бит 2 значение 0 заявка на покупку. В итоге получается. Если заявка не активна и не снята, то она исполнена. Какого-то определенного бита отвечающего за статус заявка исполнена нет. Определить, что заявка исполнена можно только косвенно через бит 0 и бит 1.

Остановим скрипт вручную. Закроем окно «Битовые флаги».

На данный момент у нас имеется позиция в один контракт на RIH8. Закроем позицию с помощью нашего скрипта, а заодно посмотрим, как отображается заявка на продажу в таблице «Битовые флаги».

Изменим направление заявки с «B» на «S», в строке кода 36 и поменяем знак с «+» на «-» в строке кода 39, чтобы заявка исполнилась.

Сохраним и запустим скрипт.

В результате работы скрипта была выставлена заявка на продажу, поскольку цена заявки была указана ниже рыночной на 2000 пунктов, то она сразу исполнилась по рыночным ценам. Состояние заявки отразилась в окне «Битовые флаги». Бит 0 имеет состояние 0, т.е. заявка не активна. Бит 1 имеет состояние 0, т.е. заявка не снята. Бит 2 имеет состояние отличное от нуля, т.е. заявка на продажу.

Остановим скрипт вручную кнопкой «Остановить» в окне «Доступные скрипты».

Вернем наш код в исходное состояние. В строке кода 36 изменим операцию с продажи «S» на покупку «B». Тем самым будет выставляться заявка на покупку по цене ниже на 2000 пунктов, чем рыночная цена.

Далее рассмотрим снятие заявки, по ее номеру используя интерпретатор QLua. Для снятия заявки необходимо отправить соответствующую транзакцию на сервер QUIK. Процедура снятия заявки очень похожа на ее выставление, за исключением, что в поле «ATCION», указываем действие «KILL_ORDER».

Таблица, передаваемая в функцию sendTransaction() должна содержать поля причисленные ниже.Создадим и заполним таблицу, которую будем отправлять в функцию sendTransaction().Назовем таблицу killOrder, в таблице нам понадобятся уже известные строки «ATCION», «CLASSCODE», «SECCODE», «TRANS_ID» и новая строка «ORDER_KEY», которая будет содержать номер снимаемой заявки, в нашем случае эта строка будет содержать переменную NumOrderB. Поле «TRANS_ID» должно содержать уникальный идентификационный номер. Придумаем кодировку для идентификатора транзакций на снятие заявок. Например возьмем уже имеющийся идентификатор для выставления заявки и добавим в начало цифру 9, которая будет означать, что эта транзакция для снятия заявки, получится значение 91010001.

Созадим переменную, которая будет содержать идентификатор транзакции на снятие заявки, назовем ее ID_B_kill. Переменную пропишем в начале скрипта сразу за переменной ID_B, это необходимо для того чтобы, переменная была видна во всем скрипте.

Теперь мы можем заполнить все строки таблицы killOrder. Напомню все значения передаваемые в таблицу должны иметь строковый тип данных, соответственно числовой тип данных преобразуем в строковый с помощью функции tostring().

Нам необходимо снять заявку при условии, что она активна. Нам уже известно, что информация об активности заявки содержится в нулевом бите строки flags. Если состояние бита равно 0, то заявка не активна, иначе активна. Создадим соответствующее условие для проверки.

Закомментируем строки создания окна таблицы, в которое выводилось состояние битовых флагов, сейчас нам она не понадобится. Строки кода с 75 по 81

Ниже таблицы killOrder, заново запишем строку кода, которая считывает из таблицы заявок нужную нам заявку.

Добавим условие, которое проверяет, активна заявка или нет, для этой цели будем использовать битовые флаги, а конкретно бит 0, если значения бит 0 не равно нулю то заявка активна.

Отлично! Если заявка активна, то можно ее снимать. В теле условия запишем строку кода для отправки транзакции на сервер. Перед тем как вызывать функцию sendTransaction() создадим переменную в которую будет записана ошибка транзакции, если она конечно появится. Переменную назовем Err_KillOrder и пропишем ее в начале скрипта сразу за переменной Err_Order.

Вызовем функцию sendTransaction() в теле условия проверки активности заявки.

Сохраним и запустим скрипт.

В результате выполнения скрипта с начала заявка была выставлена, а потом сразу же снята.

Как уже говорилось ранее, транзакция по снятию заявки не отличается по своей сути от транзакции по выставлению заявки, и мы можем проверить ее статус и вывести сообщение с результатом транзакции. Различных транзакций может быть много и, чтобы для каждой из них не писать код проверки состояния создадим функцию, которая будет проверять состояние транзакций, выводить окно сообщения и записывать в файл информацию по ним.

Создадим функцию и назовем ее ResTransaction. Код функции запишем под функцией OnTransReply(). Функция будет принимать на вход идентификатор транзакции, номер заявки, статус транзакции, сообщение о результате транзакции. Назовем переменные для функции ResTransaction() следующими именами:

идентификатор транзакции – ID;
номер заявки – Num;
статус транзакции – Status;
информационное сообщение – MSG.

Вырежем код из строк кода начиная с 49 по 55 и вставим его в функцию ResTransaction(), изменим имена переменных на соответствующие в функции.

Вызовем функцию ResTransaction() и передадим в нее необходимые переменные. Вызов функции пропишем сразу после цикла ожидания принятия заявки торговой системой и перед изменением идентификатора транзакции.

Далее перейдем к транзакции снятия заявки из торговой системы.

На данном этапе определение статуса транзакции работает только для выставления заявки в торговую систему. Но нам может понадобиться проконтролировать транзакцию на снятие заявки. Для контроля транзакции снятия заявки объявим переменные, которые будут содержать статус транзакции, информационное сообщение и ошибку выполнения функции sendTransaction(). Переменные поместим в начало кода, в тоже место, где объявлялись аналогичные переменные для транзакции выставления заявки.

Далее, после того как транзакция на снятие заявки будет принята торговой системой, терминал QUIK автоматически вызовет функцию OnTransReply(). Для того чтобы заполнить вышеобъявленные переменные вставим в функцию OnTransReply(), условие, которое идентифицирует транзакцию на снятие заявки и запишем информацию по этой транзакции в соответствующие переменные. Нам понадобится статус транзакции – переменная StatusKillOrderB и информационное сообщение – переменная msgKillOrderB.

Также как и с транзакцией выставления заявки, транзакция снятия заявки происходит не мгновенно и нам потребуется цикл проверяющий статус транзакции, скопируем цикл из строк кода выставления заявки, вставим цикл в условие проверки состояние заявки и изменим соответствующие переменные в цикле.

После того, как цикл завершится, вызовем функцию ResTransaction(), и передадим в нее переменные, относящиеся к транзакции снятия заявки.

Сохраним и запустим скрипт.

Как и планировалось скрипт, отправляет транзакции и выводит по ним информацию в окно сообщения.

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

Добавим в функцию ResTransaction() запись в файл информации о транзакциях.

Запись данных в файл мы рассматривали в разделе «11. Запись и чтение файла».

Информацию о транзакциях будем записывать в файл с именем LogTransRTS.txt, находящийся в папке Lua на диске C:\\.Создадим переменную с именем FileName и запишем в нее путь к файлу.

Создадим переменную с именем FileOpen, которая будет содержать ссылку на открытый файл, в этой же строке откроем файл с параметром «а» – запись данных в конец файла.

Файл создан и открыт. Для того чтобы знать, когда совершалась транзакция, возьмем с сервера дату и время. Как взять дату и время из системы рассматривалось в разделе «12. Интерпретатор языка Lua для терминала QUIK. «.

Создадим переменную TradeDate в нее мы запишем торговую дату при помощи функции getInfoParam(). Создадим переменную ServerTime в нее мы запишем текущее время сервера при помощи той же функции getInfoParam().

Отлично! Теперь на надо сформировать строки с информацией, которую мы будем записывать в файл. Информация, записываемая в файл, будет идентична той, которая выводится в окно сообщения. И так, первый случай если Status == 3, то мы запишем сначала дату – переменная TradeDate, потом время – переменная ServerTime, потом идентификатор транзакции – переменная ID, потом номер заявки – переменная Num, потом статус заявки – переменная Status, потом информационное сообщение – переменная MSG.

Строка кода будет выглядеть следующим образом.

Следующий случай, если переменная Status равна nil. Мы сначала дату и время потом запишем текст предупреждения, идентификатор транзакции – переменная ID и сообщение об ошибке – переменная Err.

И последний третий случай если два предыдущих не подходят. Т.е. Переменная Status не равна nil, но и не равна 3. В файл запишем следующую строку. Дата, время и сообщение, в котором указывается причина отказа выставления заявки – переменная MSG. В конце этого условия закроем файл строкой FileOpen:close().

Получившуюся функцию мы можем использовать для любых наших транзакций.Сохраним и запустим скрипт. После остановки скрипта откроем файл LogTransRTS.txt в папке Lua на диске C:\\ и проверим записи.

Информация записалась в нужном формате. Таким образом, можно записывать любые параметры работы скрипта для контроля его состояния или сохранения истории работы, а также для отладки кода.

Далее перейдем к рассмотрению рыночных заявок, стоп-заявок типа Стоп-лимит и стоп-заявок типа Тэйк-профит.

Вы можете получить готовые коды скриптов с этого сайта.
Не тратьте свое время на ввод символов с клавиатуры, потратьте его лучше на создание своих собственных торговых роботов из блоков уже готового кода.