В предыдущем примере торгового робота мы рассматривали выставление двух отдельных стоп-заявок типа стоп-лимит и тейк-профит для фьючерса РТС в терминале QUIK данные заявки можно объединить в одну. Существует такой тип стоп-заявки как «Тейк-профит и стоп-лимит». Далее рассмотрим выставление заявки данного типа, для фьючерса на обыкновенные акции ПАО «Сбербанк».

Условие работы скрипта будут следующие. Будет выставлена заявка на продажу фьючерса, если цена последней сделки меньше минимальной цены предыдущей минуты. Для этого нам понадобится минутный график цены фьючерса. После исполнения заявки, выставится стоп-заявка типа Тейк-профит и Стоп-лимит. Стоп-цена для Стоп-лимита будет на 0,1% выше цены входа в позицию, при исполнении Стоп-лимита будет выставлена рыночная заявка на покупку. Стоп-цена для Тэйк-профита, после которой начнется расчет отступа цены от минимума будет на 0,1% ниже цены входа в позицию, отступ от минимума будет равен 0,1%, после того как цена пройдет от минимума 0,1% будет выставлена рыночная заявка на покупку.

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

В качестве инструмента будем использовать ближайший фьючерс на обыкновенные акции ПАО «Сбербанк». На данный момент это SRH8.

Создадим новый файл скрипта для торгового робота и назовем его «019 Стоп-заявка типа Тэйк-профит и Стоп-лимит.lua».

Запишем строки скрипта, отвечающие за остановку торгового робота.

Далее по аналогии с предыдущим торговым роботом добавим переменные, которые понадобиться в скрипте.

Строка кода 6. Идентификатор для выставления заявки на продажу. Кодировка идентификатора изменится, поскольку мы поменяли инструмент и направление сделки. Первое число «11» будет означать фьючерс на акции «Сбербанка», далее идет число «2» означающий операцию продажи и «0001» – порядковый номер транзакции.
Строка кода 7. Идентификатор для выставления стоп-заявки типа Тейк-профит и стоп-лимит.
Строка кода 8. Переменная для статуса транзакции заявки на продажу.
Строка кода 9. Переменная для статуса транзакции стоп-заявки Тейк-профит и Стоп-лимит.
Строка кода 10. Переменная для информационного сообщения транзакции заявки на продажу.
Строка кода 11. Переменная для информационного сообщения транзакции стоп-заявки Тэйк-профит и Стоп-лимит.
Строка кода 12. Переменная для номера заявки на продажу. Используя номер заявки, будет определяться ее состояние.
Строка кода 13. Переменная для номера стоп-заявки Тэйк-профит и Стоп-лимит.
Строка кода 14. Переменная для цены сделки. Цена сделки будет использоваться при расчетах цен Тэйк-профит и Стоп-лимит.
Строка кода 15. Переменная, в которую запишется состояние заявки на продажу. После того как состояние заявки изменится на «Исполнена», скрипт отправит транзакцию на выставление стоп-заявки Тэйк-профит и Стоп-лимит.

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

Добавим функцию OnTransReply() в наш скрипт. Функция OnTransReply() является функцией интерпретатора QLua.

Далее нам понадобится функция обратного вызова OnOrder() которая вызывается терминалом QUIK при поступлении новой заявки или изменении состояния существующей. В этой функции в переменную State_S_Order будет записано строковое значение «Исполнена» в случае исполнения заявки на продажу при входе в позицию.

Далее нам понадобится функция обратного вызова OnTrade(), которая вызывается терминалом QUIK после совершения сделки. В теле данной функции мы запишем цену сделки в переменную Trade_S.

Далее, для записи результатов работы скрипта торгового робота добавим пользовательскую функцию ResTransaction, скопируем ее из предыдущего примера скрипта. Код этой функции универсален и не требует корректировок. Будет необходимо лишь поменять имя файла, в который запишется информация. Изменим имя файла на «LogTakeAndStop.txt».

Вспомогательные функции торгового робота готовы, запишем основную функцию main().

В тело функции main() добавим переменные необходимые для работы скрипта торгового робота.

Строка кода 63. Переменная, содержащая значение в процентах для расчета стоп-цены для Стоп-лимита.
Строка кода 64. Переменная, в которую запишется стоп-цена для Стоп-лимита. После того как цена дойдет до указанного значения выставится рыночная заявка на покупку.
Строка кода 65. Переменная, содержащая значение в процентах для расчета стоп-цены для Тэйк-профита.
Строка кода 66. Переменная, в которую запишется стоп-цена для Тэйк-профита. После того как цена дойдет до этого значения начнется расчет отступа цены от минимума.
Строка кода 67. Переменная, содержащая значение в процентах отступа цены от минимума для стоп-заявки Тэйк-профит. После того как, Тэйк-профит начнет расчет отступа от минимума и цена поднимется на указанное значение, будет отправлена рыночная заявка на покупку.
Строка кода 68. Переменная, содержащая код торгового инструмента.
Строка кода 69. Переменная, содержащая идентификатор графика. Данный идентификатор будет прописан в свойствах графика цены фьючерса SRH8. Используя данный идентификатор мы будем обращаться к графику с помощью функции getCandlesByIndex(). Работа функции рассматривалась в разделе «13. Данные с графиков цены и индикаторов.».
Строка кода 70. Переменная, содержащая номер торгового счета.
Строка кода 71. Переменная, которая будет использоваться в качестве индикатора входа в позицию, после того как заявка на продажу будет отправлена в систему переменная поменяет свое значение на «YES». Если переменная будет равна «YES», то это будет указанием скрипту отправить транзакцию на выставления стоп-заявки типа Тэйк-профит и Стоп-лимит.
Строка кода 72. Переменная, которая будет использоваться в качестве индикатора выставления стоп-заявки типа Тэйк-профит и Стоп-лимит. После того как транзакция будет отправлена в систему, переменная поменяет свое значение на «YES», это будет указанием скрипту более не выставлять стоп-заявки.

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

По условию скрипт должен отправить заявку на продажу фьючерса при снижении текущей цены ниже, чем цена предыдущей свечи на графике, чтобы реализовать условия, запишем цикл, который будет повторять блок кода с отслеживанием текущей цены. Цикл будет повторяться до тех пор, пока переменная stopped == false. Переменная stopped поменяет свое значение на true в двух случаях. Первый случай, если скрипт будет остановлен вручную с помощью кнопки «Остановить» в окне «Доступные скрипты». Второй случай, после того как будет осуществлен вход в позицию и выставлена стоп-заявка типа Тэйк-профит и Стоп-лимит, пропишем в скрипте строку кода в которой изменим значение в переменной stopped на true, скрипт будет остановлен.

Добавим код цикла в скрипт торгового робота.

Создадим переменную, в которой будет содержаться цена последней сделки. С помощью функции getParamEx() запишем в нее значение цены последней сделки, значение будет обновляться при каждой итерации цикла. Работа функции getParamEx() была описана в разделе «12. Интерпретатор языка Lua для терминала QUIK. «

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

Откроем график цены инструмента и установим минутный интервал. Для работы функции getCandlesByIndex(), нам потребуется прописать идентификатор. Пропишем идентификатор, как в переменной Graph_ID, строка кода 69. Также для работы функции getCandlesByIndex(), нам потребуется переменная, которая будет содержать количество свечек отображенных на графике. Создадим новую переменную с именем NumCandles, с помощью функции getNumCandles() запишем в нее количество свечей на графике. Работа функции getNumCandles() была рассмотрена в разделе «13 Данные с графиков цены и индикаторов.».

В строке кода 75 в переменную NumCandles, записали количество строк на графике с идентификатором Graph_ID.

В строке кода 76, была создана таблица с именем SRPrice, и две переменные NSR и SRName. Функция getCandlesByIndex() вернула следующие значения:

— в таблицу SRPrice были записаны цены с графика, имеющего идентификатор, содержащийся в переменной Graph_ID;

— в переменную NSR, функция записала количество свечек на графике (количество свечек на графике равняется количеству строк в таблице SRPrice);

— в переменную SRName была записана легенда график.

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

Указывая индекс строки в таблице SRPrice, равный NSR-2, мы обращаемся к предпоследней свече графика. В переменной NSR записано количество свечек на графике. Нумерация строк в таблице SRPrice начинается с ноля, последняя строка имеет номер NSR-1, соответственно предпоследняя NSR-2.

(Для справки. Цену последней сделки можно было получить точно таким же образом, как и минимальную цену предпоследней свечи. Код строки выглядит так LastPrice = SRPrice[NSR-1].close. Но, для примера, был продемонстрирован способ получения цены последней сделки при помощи функции QLua с именем getParamEx().)

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

Запишем условие в скрипт торгового робота.

Строка кода 78. Условие, если цена последней сделки меньше минимальной цены предыдущей свечи и входа в позицию не было, то условие выполняется. Обратите внимание, выполнено явное преобразование типов переменных LastPrice и LowPrevCandle, для корректной работы скрипта.

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

Строка кода 79. Переменная EnterInPos меняет свое значение на «YES», поскольку условие выполнилось.
Строки кода 80. Таблица, содержащая параметры для заявки на продажу.
Строка кода 81. Вид транзакции в данном случае — выставление новой заявки.
Строка кода 82. Номер торгового счета.
Строка кода 83. Вид операции «B» – покупка, «S» продажа.
Строка кода 84. Класс бумаги.
Строка кода 85. Код инструмента, в данном случае код инструмента прописан в переменной SecCode.
Строка кода 86. Цена для заявки, если указана цена «0», и строка [«TYPE»] = «M» имеет значение «M», то выставляется рыночная заявка.
Строка кода 87. Количество лотов (контрактов) в заявке. Оператор tostring() преобразует числовое значение в строковое. Все поля таблицы для выставления заявок должны иметь строковое значение.
Строка кода 88. Идентификатор транзакции.
Строка кода 89. Тип заявки, значение «M» означает, что выставить рыночную заявку, т.е. исполнить заявку немедленно по текущей рыночной цене.

Строка кода 91. Транзакция отправляется в систему с помощью функции sendTransaction(), в переменную Err_Order запишется ошибка отправки транзакции.
Строки кода 92 – 94. Цикл ожидания выполнения транзакции. Цикл исполняется, пока статус выполнения транзакции равен nil и переменная содержащая ошибку транзакции не равна пустой строке. Если транзакция будет отправлена успешно, то переменная содержащая статус изменит свое значение и цикл остановится. Если транзакция не будет отправлена, то в переменную с ошибкой транзакции запишется сообщение об ошибке и цикл остановится.
Строка кода 95. Вызываем пользовательскую функцию ResTransaction() и передаем в нее переменные содержащие информацию о транзакции.
Строка кода 96. Условие проверяющее, равен ли статус транзакции 3, в случае равен, запускается цикл ожидания исполнения заявки.
Строки кода 97 – 99. Цикл ожидания исполнения заявки, если статус транзакции 3 и заявка рыночная, то она должна исполниться, поэтому ожидаем ее исполнения. После того как заявка будет исполнена QUIK вызовет функцию обратного вызова OnOrder(), в теле этой функции в переменную State_S_Order запишется значение «Исполнена».

После того как заявка будет исполнена, по условию необходимо выставить стоп-заявку типа Тэйк-профит и Стоп-лимит.

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

Добавим условие проверки.

Строка кода 102. Условие проверяет, исполнена ли заявка и есть ли выставленная стоп-заявка типа Тэйк-профит и Стоп_лимит. Если заявка исполнилась и стоп-заявка еще не выставлена, то скрипт переходит во внутрь условия для отправки транзакции.

Первой строкой в теле условия, будет строка, в которой переменной TakeStop будет присвоено значение «YES», говорящее о том, что условие выполнилось.

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

Строка кода 104. В строке вычисляется стоп-цена для Стоп-лимита. Цена сделки Trade_S увеличивается на заданный процент Stop_Percent. Цену сделки Trade_S мы получили из функции OnTrade(), функция была вызвана терминалом QUIK, после того как заявка исполнилась.
Строка кода 105. В строке отбрасывается дробная часть стоп-цены для Стоп-лимита, поскольку шаг цены фьючерса равен целому числу.
Строка кода 106. В строке вычисляется стоп-цена для Тэйк-профита (цена начала расчета Тэйк-профита). Цена сделки Trade_S уменьшается на заданный процент Take_Percent.
Строка кода 107. В строке отбрасывается дробная часть стоп-цены для Тэйк-профита, поскольку шаг цены фьючерса равен целому числу.

Теперь у нас есть все значения для заполнения таблицы с параметрами транзакции для стоп-заявки типа Тэйк-профит и Стоп-лимит. Заполним ее.

Практически все строки в этой таблицы Вам уже известны, за исключением нескольких.

Строка кода 109. Вид транзакции в данном случае — выставление новой стоп заявки.
Строка кода 110. Тип стоп-заявки, в данном случае стоп-заявка типа Тэйк-профит и Стоп_лимит.
Строка кода 111. Номер торгового счета.
Строка кода 112. Вид операции «B» – покупка, «S» продажа.
Строка кода 113. Класс бумаги.
Строка кода 114. Код инструмента, в данном случае код инструмента прописан в переменной SecCode.
Строка кода 115. Количество лотов (контрактов) в заявке. Оператор tostring() преобразует числовое значение в строковое. Все поля таблицы для выставления заявок должны иметь строковое значение.
Строка кода 116. Идентификатор транзакции.
Строка кода 117. Строка, содержащая стоп-цену. В данном случае это стоп-цена для Тейк-профита на покупку. После того как цена инструмента дойдет до этого значения на сервере начнется расчет на сколько рыночная цена выросла от минимального значения рыночной цены, т.е. минимум цены после начала расчета плюс текущая цена. Если эта разница превысит значение указанное в строке «OFFSET» – строка кода 122, то в систему будет отправлена заявка на покупку.
Строка кода 118. Строка, содержащая стоп цену. В данном случае это стоп-цена для Стоп_лимита на покупку. После того как цена инструмента дойдет до этого значения в систему будет отправлена рыночная заявка на покупку.
Строка кода 119. Строка указывает, что в случае исполнения Стоп-лимита выставить рыночную заявку.
Строка кода 120. Строка указывает, что в случае исполнения Тэйк-профита выставить рыночную заявку.
Строка кода 121. Содержит единицы измерения отступа. Возможные значения «PERCENTS» – в процентах (шаг изменения – одна сотая процента), «PRICE_UNITS» – в параметрах цены (шаг изменения равен шагу цены по данному инструменту).
Строка кода 122. Содержит значение отступа от максимума или минимума в зависимости от направлении заявки Тэйк-профит.
Строка кода 123. Срок действия стоп-заявки. В данном случае «GTC» означает до отмены. Еще возможны значения «TODAY» – до окончания текущей торговой сессии или указываем определенную дату в формате «ГГГГММДД»
Строка кода 124. Строка, содержащая код клиента или комментарий к заявке. В данном случае в строку выведен комментарий, а именно идентификатор транзакции. Не обязательная строка, но очень полезная. При большом количестве различных заявок в системе используется для визуальной идентификации в окне QUIK «Таблица стоп-заявок».

Таблица заполнена, отправим транзакцию в торговую систему QUIK и дождемся ее принятия, с помощью цикла. Далее запишем результат транзакции с помощью функции ResTransaction() и остановим скрипт изменив значение переменной stopped.

Строка кода 126. Создается переменная, в которую запишется ошибка отправки транзакции, одновременно вызывается функция QLua которая отправляет транзакцию в торговую систему.
Строки кода 127 – 129. Цикл ожидания выполнения транзакции. Цикл исполняется, пока статус выполнения транзакции равен nil и переменная содержащая ошибку транзакции не равна пустой строке. Если транзакция будет отправлена успешно, то переменная содержащая статус изменит свое значение и цикл остановится. Если транзакция не будет отправлена, то в переменную с ошибкой транзакции запишется сообщение об ошибке и цикл остановится.
Строка кода 130. В строке вызывается пользовательская функция ResTransaction() с передачей в нее переменные содержащие информацию о транзакции.
Строка кода 131. В переменную stopped присваивается значение true для того чтобы остановить цикл while stopped == false do. Цикл выполнил свою функцию, дальнейшая его работа не требуется. После остановки цикла остановится и весь скрипт.

Скрипт, торгового робота, почти готов, осталось лишь добавить одну строку кода. Этой строкой будет пауза выполнения скрипта для цикла while stopped == false do. Остановка скрипта используется для ожидания обновления цены в терминале QUIK. В случае активных торгов можно уменьшить время ожидания и наоборот. Это необходимый параметр, без него скрипт будет выполняться слишком быстро, почти на максимальных скоростях Вашего процессора.

Сохраним и запустим скрипт торгового робота в терминале QUIK.

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

На этом мы закончим рассматривать выставление заявок для фьючерсов.

В терминале QUIK существует еще несколько видов стоп-заявок. Если они Вам понадобятся, то Вы без труда сможете самостоятельно реализовать из в коде, взяв за основу рассмотренные примеры и воспользовавшись документом под названием «6 Работа с другими приложениями.pdf».

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

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