Как написать робота для quik на lua

Честно говоря, не знаю, что Вам сказать, мне нужно как-то время выкроить, чтобы начать создавать этот курс, а я сейчас проект большой делаю, по завершении которого, навыки программирования не понадобятся для создания скриптов на QLua. Будет нужно только понимание алгоритмов и умение их создавать из блоков, т.е. будет конструктор роботов из функциональных, визуальных блоков, только в отличии от существующих решений в результате будут получатся не роботы, которые могут работать только на определенной платформе, а обычные скрипты QLua, которые можно будет запускать в терминале QUIK. Но первым этапом я сделаю и запущу именно редактор кода с полной поддержкой QLua синтаксиса, с подсказками, автозавершением кода и т.п. И если Вы захотите уже на этом этапе начать использовать проект, то навыки программирования все-таки понадобятся 🙂

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

  1. Хотелось бы конечно с самых азов. Но для начала желательно было бы узнать от знающего что, куда и в чем. Если не сложно, можно написать пост, в котором конкретно расписано по пунктам где писать код скрипта, синтаксис и т.д.???
    п.с. Может я конечно слеповат (прошу прощения сразу). Если есть что то похожее, то тыкните лицом в это как говориться.

    1. 🙂 в меню (слева) пункт «Инструменты»:
      для работы Вам понадопиться терминал QUIK с демо-доступом, чтобы не тестировать скрипты на реальных деньгах:
      https://quikluacsharp.ru/instruments/demo-dostup-quik-dlya-testirovaniya-skriptov-i-robotov-na-qlua-lua/
      чтобы писать скрипты Вам нужен редактор кода, для этого хорошо подойдет Notepad++:
      https://quikluacsharp.ru/instruments/instrumenty-dlya-razrabotki-torgovyh/

      О том, что такое переменные, арифметические операции, операторы сравнения, условные операторы, циклы и функции на данном сайте нет информации, т.к. эта информация касается всех языков программирования, а сайт о QLua.

      Если у Вас есть базовые знания программирования, то информацию о том, из чего состоит скрипт QLua Вы можете найти здесь:
      https://quikluacsharp.ru/qlua-osnovy/baza-skripta-v-qlua/
      здесь о том, как запустить скрипт в терминале QUIK:
      https://quikluacsharp.ru/quik-qlua/kak-zapustit-qlua-lua-skript-v-terminale-quik/

      со всеми особенностями синтакса QLua можно ознакомиться в разделе меню «QLua(Lua) основы»

      и, вобще, все пункты меню названы по принципу связи, т.е., например, в пункте «QUIK + QLua(Lua)» находится информация о взаимосвязях скрипта QLua и терминала QUIK.

      1. Спасибо за подсказку, буду изучать.

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

      Особого внимания заслуживает раздел «QLua(Lua основы)» -> «ФУНКЦИИ ОБРАТНОГО ВЫЗОВА, ВСТРОЕННЫЕ В QLUA», т.к. вся работа с терминалом строится на них, в данном разделе так же есть описания и примеры использования.

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

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

    3. Решил еще пример использования сайта привести 🙂
      Например, стоит у Вас задача: «Как в скрипте получить данные с графика», вы рассуждайте следующим образом:
      Между какими элементами взаимосвязь? С одной стороны скрипт QLua (данные нужно получить в него), с другой стороны терминал QUIK (данные нужно получить из него, т.к. график в нем), значит первоначальный раздел меню «QUIK + QLua(Lua)», открываете его, и видите в нем пункт «Обмен данными», открываете его и видите в нем решение Вашей задачи, пункт: «ПОЛУЧЕНИЕ В QLUA(LUA) ДАННЫХ ИЗ ГРАФИКОВ И ИНДИКАТОРОВ»

      Или, например, Вы на знаете как в QLua работать с массивами, Вы снова рассуждаете, массивы это что? Чисто скрипт, т.к. массивы не имеют никакого отношения к терминалу, значит раскрываете раздел меню «QLua(Lua) основы» и ищите там в названии пунктов слово «массив», находите пункт «ПЕРЕМЕННЫЕ, МАССИВЫ И ФУНКЦИИ В QLUA (LUA)»

      Можете еще вот этот комментарий прочитать https://quikluacsharp.ru/quik-qlua/skript-vyvodit-v-tablitsu-qlua-balans-pokupok-prodazh-poslednih-5-ti-1-minutnyh-svechej/#comment-861 , я в нем одной девушке как раз основы некоторые рассказывал, надеюсь поможет.

      1. спасибо

        1. Всегда пожалуйста, обращайтесь

Если робот остался в длинной позиции, указываем L_Pos = true, а S_Pos = false  и наоборот, если в короткой позиции, то указываем S_Pos = trueL_Pos = false. Если без позиции, указываем falsefalseЭти действия удобно делать, просто комментируя не нужные строки кода.  

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

Давайте поясню понятие Транзакция. Транзакция — это процесс отправки заявки на биржу. В момент транзакции отслеживается корректность параметров заявки, и проверяются доступные лимиты по объемам заявки. При отправке транзакции могут возникать два вида ошибок на двух разных этапах. Первый этап, на котором могут возникать ошибки, это в момент вызова функции sendTransaction  эта функция отправляет транзакцию на сервер брокера. На этом этапе, ошибки отслеживаются терминалом QUIK, и все они связаны с корректностью составленной таблицы с параметрами заявки. Какие это могут быть ошибки, например, не указали, объем заявки или не указан торговый счет, т.е. это ошибки в коде скрипта. Второй этап, на котором могут возникнуть ошибки, из-за которых заявка не будет выставлена на бирже, это этап проверки параметров заявки в торговой системе. Этот этап наступает после того как терминал QUIK принял Вашу транзакцию и отправил ее дальше. Тут могут возникнуть ошибки следующего характера. Например, превышен допустимый объем заявки или указана цена, которая выше или ниже максимально возможной цены или заявка выставляется в момент отсутствия торгов. 

Ошибки первого этапа возникают только во время первичных, тестовых торгов роботом. После того как скрипт отлажен, ошибок этого типа не бывает. Ошибки второго этапа могут появляться в независимости от кода скрипта, но опять же при хорошем коде, в котором исключены моменты, при которых ошибка теоретически возможна, появление ошибки второго этапа сведена к минимуму. На самом деле остается одна единственная ситуация, при которой появление ошибки второго этапа возможна – это обрыв связи с сервером в момент, когда транзакция принята терминалом QUIK, но не дошла до сервера брокера. К счастью, это исключительно редкая ситуация, я бы сказал больше — теоретическая. По транзакциям пока все, пойдем дальше. 

Наш робот действует не молча, он постоянно записывает свои действия и параметры в указанные файлы. На каждой свече робот записывает текущие параметры  — Дата, Время, Цена закрытия, Значение скользящей средней, Ускорение, Текущая позиция лонг – шорт. Эти данные помогают отследить возможные ошибки в направлении позиции или восстановить работу скрипта после, например, случайного закрытия терминала QUIK. 
Помимо текущих параметров, записываются и результаты транзакций. Всегда есть возможность посмотреть, когда робот совершал сделки и прочитать сообщение об ошибках, если таковые будут. 

Хорошо, что нам уже известно про алгоритм укорение скользящей средней? Мы подробно разобрали этот алгоритм, кода тестировали его на исторических данных. Скрипт торгового робота и скрипт тестирования очень похожи. Оба этих скрипта получают данные, тестовый  скрипт считывает данные из файлов, торговый скрипт считывает данные с графиков QUIK. Далее оба скрипта выполняют одинаковые действия, рассчитывают параметры индикаторов и отслеживают условия для входа в позицию или выхода из нее. Различия, в действиях начинаются после того, как выполняются условия входа или выхода. Тестовый скрипт просто записывает данные, а торговый скрипт отправляет транзакцию на сервер QUIKПо сути, это единственное их различие. Один совершает реальные сделки, другой просто записывает данные.  

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

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

—[[ Простой MA-робот (c)QuikLuaCShapr.ru !!! ДЛЯ ИСПОЛЬЗОВАНИЯ ТОЛЬКО В ОБРАЗОВАТЕЛЬНЫХ ЦЕЛЯХ НА ДЕМО-СЧЕТЕ !!! Робот торгует по 2-м простым(simple) скользящим средним (MA). 1.Если нет открытых роботом позиций выполняется следующий алгоритм: 1.Если быстрая скользящая пересекает медленную снизу вверх и на текущей свече еще не открывались позиции, совершается покупка. 2.Если быстрая скользящая пересекает медленную сверху вниз и на текущей свече еще не открывались позиции, совершается продажа. 2.Если открылась позиция, выставляется «Тейк-профит и Стоп-лимит» 3.Робот ждет, пока закроется позичия по Стоп-лоссу, либо Тейк-профиту, после чего переходит к пункту №1 Особенности: 1.Робот выводит в текстовых сообщениях информацию о ключевых моментах алгоритма, у всех его сообщений префикс «Простой MA-робот:». 2.Робот всегда находится в 1-м из 2-х состояний(ROBOT_STATE):’В ПРОЦЕССЕ СДЕЛКИ’, либо ‘В ПОИСКЕ ТОЧКИ ВХОДА’. 3.Если при выставлении заявки на продажу робот узнает, что операции шорт запрещены по данному инструменту, он больше не будет открывать шорт, только лонг. 4.Когда робот получает сигнал на открытие сделки, он совершает 10 попыток с прмежутками в 100 мс открыть позицию, если этого не удается, останавливает скрипт. 5.После открытия позиции робот совершает 10 попыток с прмежутками в 100 мс выставить Тэйк-профит и Стоп-лимит, а затем дождаться закрытия позиции, если этого не удается, останавливает скрипт. 6.Если стоп-заявка сработала, но позиция не закрылась в течении 10 секунд, пытается за 10 попыток принудительно закрыть позицию встречной сделкой. Если позицию удалось закрыть (даже принудительно), продолжает работать, иначе скрипт останавливается. ]] /*НАСТРАИВАЕМЫЕ ПАРАМЕТРЫ*/ ACCOUNT = ‘NL0011100043’; — Идентификатор счета ACCOUNT = SPBFUTaya; Идентификатор счета CLASS_CODE = ‘QJSIM’; — Код класса CLASS_CODE = SPBFUT; Код класса SEC_CODE = ‘SBER’; — Код бумаги SEC_CODE = BRH6; Код бумаги INTERVAL = INTERVAL_M1; Таймфрейм графика (для построения скользящих) SLOW_MA_PERIOD = 40; ПЕРИОД МЕДЛЕННОЙ скользящей SLOW_MA_SOURCE = C; ИСТОЧНИК МЕДЛЕННОЙ скользящей [O — open, C — close, H — hi, L — low] FAST_MA_PERIOD = 20; ПЕРИОД БЫСТРОЙ скользящей FAST_MA_SOURCE = C; ИСТОЧНИК БЫСТРОЙ скользящей [O — open, C — close, H — hi, L — low] STOP_LOSS = 11; Размер СТОП-ЛОССА (в шагах цены) TAKE_PROFIT = 20; Размер ТЕЙК-ПРОФИТА (в шагах цены) /*РАБОЧИЕ ПЕРЕМЕННЫЕ РОБОТА (менять не нужно)*/ SEC_PRICE_STEP = 0; ШАГ ЦЕНЫ ИНСТРУМЕНТА SEC_NO_SHORT = false; Флаг, что по данному инструменту запрещены операции шорт DS = nil; Источник данных графика (DataSource) ROBOT_STATE =В ПОИСКЕ ТОЧКИ ВХОДА; СОСТОЯНИЕ робота [‘В ПРОЦЕССЕ СДЕЛКИ’, либо ‘В ПОИСКЕ ТОЧКИ ВХОДА’] trans_id = os.time(); Задает начальный номер ID транзакций trans_Status = nil; Статус текущей транзакции из функции OnTransPeply trans_result_msg = ; Сообщение по текущей транзакции из функции OnTransPeply CurrentDirect = BUY; Текущее НАПРАВЛЕНИЕ [‘BUY’, или ‘SELL’] LastOpenBarIndex = 0; Индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще одну позицию) Run = true; Флаг поддержания работы бесконечного цикла в main Функция первичной инициализации скрипта (ВЫЗЫВАЕТСЯ ТЕРМИНАЛОМ QUIK в самом начале) function OnInit() Получает доступ к свечам графика local Error = ; DS,Error = CreateDataSource(CLASS_CODE, SEC_CODE, INTERVAL); Проверка if DS == nil then message(Простой MA-робот:ОШИБКА получения доступа к свечам! ..Error); Завершает выполнение скрипта Run = false; return; end; Получает ШАГ ЦЕНЫ ИНСТРУМЕНТА SEC_PRICE_STEP = getParamEx(CLASS_CODE, SEC_CODE, «SEC_PRICE_STEP«).param_value; end; function main() Выводит сообщение message(Простой MA-робот: ..ROBOT_STATE); «Бесконечный» цикл while Run do Если СОСТОЯНИЕ робота «В ПРОЦЕССЕ СДЕЛКИ» if ROBOT_STATE == В ПРОЦЕССЕ СДЕЛКИ then Выводит сообщение message(Простой MA-робот: В ПРОЦЕССЕ СДЕЛКИ); Делает 10 попыток открыть сделку local Price = false; Переменная для получения результата открытия позиции (цена, либо ошибка(false)) for i=1,10 do if not Run then return; end; Если скрипт останавливается, не затягивает процесс Если первый раз пытается открыть SELL, а операции шорт по данному инструменту запрещены if CurrentDirect == «SELL« and SEC_NO_SHORT then Прерывает цикл FOR break; end; Совершает СДЕЛКУ указанного типа [«BUY», или «SELL»] по рыночной(текущей) цене размером в 1 лот, — возвращает цену открытой сделки, либо FALSE, если невозможно открыть сделку Price = Trade(CurrentDirect); Если сделка открылась if Price ~= false then Прерывает цикл FOR break; end; sleep(100); Пауза в 100 мс между попытками открыть сделку end; if not Run then return; end; Если скрипт останавливается, не затягивает процесс Если сделка открыта if Price ~= false then Запоминает индекс свечи, на которой была открыта последняя позиция (нужен для того, чтобы после закрытия по стопу тут же не открыть еще одну позицию) LastOpenBarIndex = DS:Size(); Выводит сообщение message(Простой MA-робот: Открыта сделка ..CurrentDirect.. по цене ..Price); Делает 10 попыток выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, и дождаться закрытия сделки message(Простой MA-робот: Делает 10 попыток выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, и дождаться закрытия сделки); local Result = nil; Переменная для получения результата выставления и срабатывания СТОП-ЛОСС и ТЕЙК-ПРОФИТ for i=1,10 do if not Run then return; end; Если скрипт останавливается, не затягивает процесс Выставляет СТОП-ЛОСС и ТЕЙК-ПРОФИТ, ЖДЕТ пока он сработает, принимает ЦЕНУ и ТИП [«BUY», или «SELL»] открытой сделки, — возвращает FALSE, если не удалось выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, или TRUE, если сделка закрылась, — либо NIL, если при ошибке за 10 попыток не удалось принудительно закрыть позицию Result = SL_TP(Price, CurrentDirect); Если сделка закрылась if Result == true then Прерывает цикл FOR break; end; end; Если за 10 попыток не удалось закрыть позицию if Result == nil or Result == false then Выводит сообщение message(Простой MA-робот: После 10-и попыток не удалось закрыть сделку!!! Завершение скрипта!!!); Завершает выполнение скрипта Run = false; Прерывает основной цикл WHILE break; else УДАЛОСЬ ЗАКРЫТЬ СДЕЛКУ Выводит сообщение message(Простой MA-робот: Сделка закрыта по СТОП-ЛОССУ, либо ТЕЙК-ПРОФИТУ); Меняет СОСТОЯНИЕ робота на «В ПОИСКЕ ТОЧКИ ВХОДА» ROBOT_STATE = В ПОИСКЕ ТОЧКИ ВХОДА; Выводит сообщение message(Простой MA-робот: В ПОИСКЕ ТОЧКИ ВХОДА); end; else Сделку не удалось открыть Если первый раз пытался открыть SELL, а операции шорт по данному инструменту запрещены if CurrentDirect == «SELL« and SEC_NO_SHORT then Выводит сообщение message(Простой MA-робот: Была первая попытка совершить запрещенную операцию шорт! Больше этого не повторится:)); Меняет СОСТОЯНИЕ робота на «В ПОИСКЕ ТОЧКИ ВХОДА» ROBOT_STATE = В ПОИСКЕ ТОЧКИ ВХОДА; Выводит сообщение message(Простой MA-робот: В ПОИСКЕ ТОЧКИ ВХОДА); else Выводит сообщение message(Простой MA-робот: После 10-и попыток не удалось открыть сделку!!! Завершение скрипта!!!); Завершает выполнение скрипта Run = false; end; end; else СОСТОЯНИЕ робота ‘В ПОИСКЕ ТОЧКИ ВХОДА’ Если на этой свече еще не было открыто позиций if DS:Size() > LastOpenBarIndex then Если быстрая пересекла медленную СНИЗУ ВВЕРХ if FastMA(DS:Size()1) <= SlowMA(DS:Size()1) and FastMA() > SlowMA() then Задает направление НА ПОКУПКУ CurrentDirect = BUY; message(CurrentDirect = «BUY»); Меняет СОСТОЯНИЕ робота на «В ПРОЦЕССЕ СДЕЛКИ» ROBOT_STATE = В ПРОЦЕССЕ СДЕЛКИ; Если быстрая пересекла медленную СВЕРХУ ВНИЗ elseif FastMA(DS:Size()1) >= SlowMA(DS:Size()1) and FastMA() < SlowMA() then Если по данному инструменту не запрещены операции шорт if not SEC_NO_SHORT then Задает направление НА ПРОДАЖУ CurrentDirect = SELL; message(CurrentDirect = «SELL»); Меняет СОСТОЯНИЕ робота на «В ПРОЦЕССЕ СДЕЛКИ» ROBOT_STATE = В ПРОЦЕССЕ СДЕЛКИ; end; end; end; end; sleep(10);Пауза 10 мс, для того, чтобы не перегружать процессор компьютера end; end; Функция вызывается терминалом QUIK при получении ответа на транзакцию пользователя function OnTransReply(trans_reply) Если поступила информация по текущей транзакции if trans_reply.trans_id == trans_id then Передает статус в глобальную переменную trans_Status = trans_reply.status; Передает сообщение в глобальную переменную trans_result_msg = trans_reply.result_msg; end; end; Функция ВЫЗЫВАЕТСЯ ТЕРМИНАЛОМ QUIK при остановке скрипта function OnStop() Run = false; end; ————————— ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ — ————————— Совершает СДЕЛКУ указанного типа (Type) [«BUY», или «SELL»] по рыночной(текущей) цене размером в 1 лот, — возвращает цену открытой сделки, либо FALSE, если невозможно открыть сделку function Trade(Type) Получает ID транзакции trans_id = trans_id + 1; local Price = 0; local Operation = ; Устанавливает цену и операцию, в зависимости от типа сделки и от класса инструмента if Type == BUY then if CLASS_CODE ~= QJSIM and CLASS_CODE ~= TQBR then Price = getParamEx(CLASS_CODE, SEC_CODE, offer).param_value + 10*SEC_PRICE_STEP;end; по цене, завышенной на 10 мин. шагов цены Operation = B; else if CLASS_CODE ~= QJSIM and CLASS_CODE ~= TQBR then Price = getParamEx(CLASS_CODE, SEC_CODE, bid).param_value 10*SEC_PRICE_STEP;end; по цене, заниженной на 10 мин. шагов цены Operation = S; end; Заполняет структуру для отправки транзакции local Transaction={ [TRANS_ID] = tostring(trans_id), [ACTION] = NEW_ORDER, [CLASSCODE] = CLASS_CODE, [SECCODE] = SEC_CODE, [OPERATION] = Operation, операция («B» — buy, или «S» — sell) [TYPE] = M, по рынку (MARKET) [QUANTITY] = 1, количество [ACCOUNT] = ACCOUNT, [PRICE] = tostring(Price), [COMMENT] = Простой MA-робот } Отправляет транзакцию sendTransaction(Transaction); Ждет, пока получит статус текущей транзакции (переменные «trans_Status» и «trans_result_msg» заполняются в функции OnTransReply()) while Run and trans_Status == nil do sleep(1); end; Запоминает значение local Status = trans_Status; Очищает глобальную переменную trans_Status = nil; Если транзакция не выполнена по какой-то причине if Status ~= 3 then Если данный инструмент запрещен для операции шорт if Status == 6 then Выводит сообщение message(Простой MA-робот: Данный инструмент запрещен для операции шорт!); SEC_NO_SHORT = true; else Выводит сообщение с ошибкой message(Простой MA-робот: Транзакция не прошла!nОШИБКА: ..trans_result_msg); end; Возвращает FALSE return false; else Транзакция отправлена local OrderNum = nil; ЖДЕТ пока ЗАЯВКА на ОТКРЫТИЕ сделки будет ИСПОЛНЕНА полностью Запоминает время начала в секундах local BeginTime = os.time(); while Run and OrderNum == nil do Перебирает ТАБЛИЦУ ЗАЯВОК for i=0,getNumberOf(orders)1 do local order = getItem(orders, i); Если заявка по отправленной транзакции ИСПОЛНЕНА ПОЛНОСТЬЮ if order.trans_id == trans_id and order.balance == 0 then Запоминает номер заявки OrderNum = order.order_num; Прерывает цикл FOR break; end; end; Если прошло 10 секунд, а заявка не исполнена, значит произошла ошибка if os.time() BeginTime > 9 then Выводит сообщение с ошибкой message(Простой MA-робот: Прошло 10 секунд, а заявка не исполнена, значит произошла ошибка); Возвращает FALSE return false; end; sleep(10); Пауза 10 мс, чтобы не перегружать процессор компьютера end; ЖДЕТ пока СДЕЛКА ОТКРЫТИЯ позиции будет СОВЕРШЕНА Запоминает время начала в секундах BeginTime = os.time(); while Run do Перебирает ТАБЛИЦУ СДЕЛОК for i=0,getNumberOf(trades)1 do local trade = getItem(trades, i); Если сделка по текущей заявке if trade.order_num == OrderNum then Возвращает фАКТИЧЕСКУЮ ЦЕНУ открытой сделки return trade.price; end; end; Если прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка if os.time() BeginTime > 9 then Выводит сообщение с ошибкой message(Простой MA-робот: Прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка); Возвращает FALSE return false; end; sleep(10); Пауза 10 мс, чтобы не перегружать процессор компьютера end; end; end; ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ переданного типа (Type) [«BUY», или «SELL»] function KillPos(Type) Дается 10 попыток local Count = 0; Счетчик попыток if Type == BUY then Пока скрипт не остановлен и позиция не закрыта while Run and not Trade(SELL) do Открывает SELL, тем самым закрывая BUY, если Trade(‘SELL’) вернет TRUE, цикл прекратится Count = Count + 1; Увеличивает счетчик Если за 10 попыток не удалось закрыть позицию if Count == 10 then Возвращает NIL return nil; end; sleep(100); Пауза 100 мс, чтобы изменилась ситуация на сервере end; else Пока скрипт не остановлен и позиция не закрыта while Run and not Trade(BUY) do Открывает BUY, тем самым закрывая SELL, если Trade(‘BUY’) вернет TRUE, цикл прекратится Count = Count + 1; Увеличивает счетчик Если за 10 попыток не удалось закрыть позицию if Count == 10 then Возвращает NIL return nil; end; sleep(100); Пауза 100 мс, чтобы изменилась ситуация на сервере end; end; Возвращает TRUE, если удалось принудительно закрыть позицию return true; end; Выставляет СТОП-ЛОСС и ТЕЙК-ПРОФИТ, ЖДЕТ пока он сработает, принимает ЦЕНУ (Price) и ТИП (Type) [«BUY», или «SELL»] открытой сделки, — возвращает FALSE, если не удалось выставить СТОП-ЛОСС и ТЕЙК-ПРОФИТ, либо TRUE, если сделка закрылась, — либо NIL, если при ошибке за 10 попыток не удалось принудительно закрыть позицию function SL_TP(Price, Type) ID транзакции trans_id = trans_id + 1; Находит направление для заявки local operation = ««; local price = «0«; Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0) local stopprice = ««; Цена Тейк-Профита local stopprice2 = ««; Цена Стоп-Лосса local market = «YES«; После срабатывания Тейка, или Стопа, заявка сработает по рыночной цене Если открыт BUY, то направление стоп-лосса и тейк-профита SELL, иначе направление стоп-лосса и тейк-профита BUY if Type == BUY then operation = «S«; Тейк-профит и Стоп-лосс на продажу(чтобы закрыть BUY, нужно открыть SELL) Если не акции if CLASS_CODE ~= QJSIM and CLASS_CODE ~= TQBR then price = tostring(math.floor(getParamEx(CLASS_CODE, SEC_CODE, PRICEMIN).param_value)); Цена выставляемой заявки после страбатывания Стопа минимально возможная, чтобы не проскользнуло market = «NO«; После срабатывания Тейка, или Стопа, заявка сработает НЕ по рыночной цене end; stopprice = tostring(Price + TAKE_PROFIT*SEC_PRICE_STEP); Уровень цены, когда активируется Тейк-профит stopprice2 = tostring(Price STOP_LOSS*SEC_PRICE_STEP); Уровень цены, когда активируется Стоп-лосс else открыт SELL operation = «B«; Тейк-профит и Стоп-лосс на покупку(чтобы закрыть SELL, нужно открыть BUY) Если не акции if CLASS_CODE ~= QJSIM and CLASS_CODE ~= TQBR then price = tostring(math.floor(getParamEx(CLASS_CODE, SEC_CODE, PRICEMAX).param_value)); Цена выставляемой заявки после страбатывания Стопа максимально возможная, чтобы не проскользнуло market = «NO«; После срабатывания Тейка, или Стопа, заявка сработает НЕ по рыночной цене end; stopprice = tostring(Price TAKE_PROFIT*SEC_PRICE_STEP); Уровень цены, когда активируется Тейк-профит stopprice2 = tostring(Price + STOP_LOSS*SEC_PRICE_STEP); Уровень цены, когда активируется Стоп-лосс end; Заполняет структуру для отправки транзакции на Стоп-лосс и Тейк-профит local Transaction = { [«ACTION«] = «NEW_STOP_ORDER«, Тип заявки [«TRANS_ID«] = tostring(trans_id), [«CLASSCODE«] = CLASS_CODE, [«SECCODE«] = SEC_CODE, [«ACCOUNT«] = ACCOUNT, [«OPERATION«] = operation, Операция («B» — покупка(BUY), «S» — продажа(SELL)) [«QUANTITY«] = «1«, Количество в лотах [«PRICE«] = price, Цена, по которой выставится заявка при срабатывании Стоп-Лосса (для рыночной заявки по акциям должна быть 0) [«STOPPRICE«] = stopprice, Цена Тейк-Профита [«STOP_ORDER_KIND«] = «TAKE_PROFIT_AND_STOP_LIMIT_ORDER«, Тип стоп-заявки [«EXPIRY_DATE«] = «TODAY«, Срок действия стоп-заявки («GTC» – до отмены,»TODAY» — до окончания текущей торговой сессии, Дата в формате «ГГММДД») «OFFSET» — (ОТСТУП)Если цена достигла Тейк-профита и идет дальше в прибыль, то Тейк-профит сработает только когда цена вернется минимум на 2 шага цены назад, это может потенциально увеличить прибыль [«OFFSET«] = tostring(2*SEC_PRICE_STEP), [«OFFSET_UNITS«] = «PRICE_UNITS«, Единицы измерения отступа («PRICE_UNITS» — шаг цены, или «PERCENTS» — проценты) «SPREAD» — Когда сработает Тейк-профит, выставится заявка по цене хуже текущей на 100 шагов цены, которая АВТОМАТИЧЕСКИ УДОВЛЕТВОРИТСЯ ПО ТЕКУЩЕЙ ЛУЧШЕЙ ЦЕНЕ, но то, что цена значительно хуже, спасет от проскальзывания, иначе, сделка может просто не закрыться (заявка на закрытие будет выставлена, но цена к тому времени ее уже проскочит) [«SPREAD«] = tostring(100*SEC_PRICE_STEP), [«SPREAD_UNITS«] = «PRICE_UNITS«, Единицы измерения защитного спрэда («PRICE_UNITS» — шаг цены, или «PERCENTS» — проценты) «MARKET_TAKE_PROFIT» = («YES», или «NO») должна ли выставится заявка по рыночной цене при срабатывании Тейк-Профита. Для рынка FORTS рыночные заявки, как правило, запрещены, для лимитированной заявки на FORTS нужно указывать заведомо худшую цену, чтобы она сработала сразу же, как рыночная [«MARKET_TAKE_PROFIT«] = market, [«STOPPRICE2«] = stopprice2, Цена Стоп-Лосса [«IS_ACTIVE_IN_TIME«] = «NO«, «MARKET_TAKE_PROFIT» = («YES», или «NO») должна ли выставится заявка по рыночной цене при срабатывании Стоп-Лосса. Для рынка FORTS рыночные заявки, как правило, запрещены, для лимитированной заявки на FORTS нужно указывать заведомо худшую цену, чтобы она сработала сразу же, как рыночная [«MARKET_STOP_LIMIT«] = market, [«COMMENT«] = «Простой MA-робот ТЕЙК-ПРОФИТ и СТОП-ЛОСС« } Отправляет транзакцию на установку ТЕЙК-ПРОФИТ и СТОП-ЛОСС sendTransaction(Transaction); Ждет, пока не получит статус текущей транзакции (переменные «trans_Status» и «trans_result_msg» заполняются в функции OnTransReply()) while Run and trans_Status == nil do sleep(10); end; Запоминает значение local Status = trans_Status; Очищает глобальную переменную trans_Status = nil; Если транзакция не выполнена по какой-то причине if Status ~= 3 then Выводит сообщение с ошибкой message(Простой MA-робот: Установка ТЕЙК-ПРОФИТ и СТОП-ЛОСС не удалась!nОШИБКА: ..trans_result_msg); Возвращает FALSE return false; else Выводит сообщение message(Простой MA-робот: ВЫСТАВЛЕНА заявка ТЕЙК-ПРОФИТ и СТОП-ЛОСС: ..trans_id); local OrderNum_CLOSE = nil; ЖДЕТ пока СТОП-ЗАЯВКА на СТОП-ЛОСС и ТЕЙК-ПРОФИТ будет ИСПОЛНЕНА полностью while Run and OrderNum_CLOSE == nil do Перебирает ТАБЛИЦУ СТОП-ЗАЯВОК for i=0,getNumberOf(«stop_orders«)1 do local stop_order = getItem(«stop_orders«, i); Если заявка по текущей транзакции СТОП-ЛОСС и ТЕЙК-ПРОФИТ if stop_order.trans_id == trans_id then Если заявка по отправленной СТОП-ЛОСС и ТЕЙК-ПРОФИТ транзакции ИСПОЛНЕНА ПОЛНОСТЬЮ if stop_order.balance == 0 then Если по наступлению стоп-цены выставлена заявка if stop_order.linkedorder > 0 then Запоминает номер заявки, которая была создана при срабатывании СТОП-ЛОСС, или ТЕЙК-ПРОФИТ OrderNum_CLOSE = stop_order.linkedorder; Прерывает цикл FOR break; Стоп-заявка сработала, но была отвергнута торговой системой elseif CheckBit(stop_order.flags, 10) == 1 then ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ и выходит из функции return KillPos(Type); end; end; end; end; sleep(10); Пауза 10 мс, чтобы не перегружать процессор компьютера end; ЖДЕТ пока СДЕЛКА ЗАКРЫТИЯ позиции будет СОВЕРШЕНА Запоминает время начала в секундах BeginTime = os.time(); while Run do Перебирает ТАБЛИЦУ СДЕЛОК for i=0,getNumberOf(«trades«)1 do local trade = getItem(«trades«, i); Если сделка по текущей заявке на СТОП-ЛОСС и ТЕЙК-ПРОФИТ if trade.order_num == OrderNum_CLOSE then Возвращает TRUE return true; end; end; Если прошло 10 секунд, а сделка не совершена, значит на демо-счете произошла ошибка сервера «Обработка кросс-заявок блокирована» if os.time() BeginTime > 9 then ПРИНУДИТЕЛЬНО ЗАКРЫВАЕТ ОТКРЫТУЮ ПОЗИЦИЮ и выходит из функции return KillPos(Type); end; sleep(1); end; end; end; Возвращает ЗНАЧЕНИЕ МЕДЛЕННОЙ скользящей по индексу свечи (по умолчанию: последняя) function SlowMA(Index) Если индекс свечи не указан, то устанавливает индекс последней свечи if Index == nil then Index = DS:Size(); end; Сумма значений SLOW_MA_SOURCE на SLOW_MA_PERIOD свечах local Sum = 0; Перебирает последние SLOW_MA_PERIOD свечей for i=Index, Index (SLOW_MA_PERIOD 1), 1 do Считает сумму, исходя из выбранного источника для медленной скользящей if SLOW_MA_SOURCE == O then Sum = Sum + DS:O(i); elseif SLOW_MA_SOURCE == C then Sum = Sum + DS:C(i); elseif SLOW_MA_SOURCE == H then Sum = Sum + DS:H(i); elseif SLOW_MA_SOURCE == L then Sum = Sum + DS:L(i); else message(Простой MA-робот:ОШИБКА! Не верно указан источник для медленной скользящей!); Останавливает скрипт OnStop(); end; end; Возвращает значение return Sum/SLOW_MA_PERIOD; end; Возвращает ЗНАЧЕНИЕ БЫСТРОЙ скользящей по индексу свечи (по умолчанию: последняя) function FastMA(Index) Если индекс свечи не указан, то устанавливает индекс последней свечи if Index == nil then Index = DS:Size(); end; Сумма значений FAST_MA_SOURCE на FAST_MA_PERIOD свечах local Sum = 0; Перебирает последние FAST_MA_PERIOD свечей for i=Index, Index (FAST_MA_PERIOD 1), 1 do Считает сумму, исходя из выбранного источника для быстрой скользящей if FAST_MA_SOURCE == O then Sum = Sum + DS:O(i); elseif FAST_MA_SOURCE == C then Sum = Sum + DS:C(i); elseif FAST_MA_SOURCE == H then Sum = Sum + DS:H(i); elseif FAST_MA_SOURCE == L then Sum = Sum + DS:L(i); else message(Простой MA-робот:ОШИБКА! Не верно указан источник для быстрой скользящей!); Останавливает скрипт OnStop(); end; end; Возвращает значение return Sum/FAST_MA_PERIOD; end; Функция возвращает значение бита (число 0, или 1) под номером bit (начинаются с 0) в числе flags, если такого бита нет, возвращает nil function CheckBit(flags, bit) Проверяет, что переданные аргументы являются числами if type(flags) ~= «number« then error(«Предупреждение!!! Checkbit: 1-й аргумент не число!«); end; if type(bit) ~= «number« then error(«Предупреждение!!! Checkbit: 2-й аргумент не число!«); end; local RevBitsStr = ««; Перевернутое (задом наперед) строковое представление двоичного представления переданного десятичного числа (flags) local Fmod = 0; Остаток от деления local Go = true; Флаг работы цикла while Go do Fmod = math.fmod(flags, 2); Остаток от деления flags = math.floor(flags/2); Оставляет для следующей итерации цикла только целую часть от деления RevBitsStr = RevBitsStr ..tostring(Fmod); Добавляет справа остаток от деления if flags == 0 then Go = false; end; Если был последний бит, завершает цикл end; Возвращает значение бита local Result = RevBitsStr :sub(bit+1,bit+1); if Result == «0« then return 0; elseif Result == «1« then return 1; else return nil; end; end;

April 1 2014, 23:41

Category:

  • IT
  • Cancel

Язык луа предоставляет достаточно широкие возможности для создания инфраструктуры торговых алгоритмов. Большой плюс Луа его интегрированность непосредственно в торговый терминал квик, что снижает риск обрыва потока данных из квика в стороннюю оболочку, снижение времени на передачу данных, его обработку и передачу торговых сигналов обратно в квик. Кроме этого, луа позволяет создать собственный интерфейс внутри квика, отслеживать исполнение (полное и частичное) ордеров, возможность встраивать элементы ММ.
Документация по Луа, как им то невооборазимым образом разбросана по разным документам. Разумеется пользоваться скриптами будет небольшое кол-во пользователей, тем не менее, документацию по языку все же можно было собрать в одном месте.

Итак, синтаксис языка и его описание
Если с синтаксисом языка все понятно, двигаем дальше.

Проверим, готов ли наш квик к обработке скриптов на луа.
Работу QLua обеспечивает файл qlua.dll, который должен находиться в одной папке с файлами рабочего места QUIK, например, C:Program FilesQUIK. Если файла нет, пробуем обновить квик или скачиваем dll с сайта quik.ru

Программирование
Скрипт пишется непосредственно в текстовом файле и сохраняется с расширением .lua
Возможно 3 подхода к написанию скриптов:
1. Скрипт пишется в текстовом файле и при его запуске выполняется 1 раз. Удобен для выполнения каких то разовых операций.
2. Можно написать в Lua-скрипте функцию с предопределенным именем и всю логику работы робота (или вычислительного скрипта) поместить в эту функцию. Функция main выполняется в отдельном потоке, т.е. она не мешает работе основного функционала терминала QUIK, наличие функции позволяет выполнять периодически приостанавливать скрипт и возобновлять его работу. Если зациклить main() и вставить sleep(), то получаем полную эмуляцию подхода, использующегося при программировании на встроенном QPILE: периодический расчет чего-либо через заданный интервал времени.
3. В QLUA доступна событийная модель программирования.
При выборе такого подхода получаем весьма гибкую среду выполнения пользовательских скриптов внутри QUIK, позволяющую мгновенно получать интересующие события от терминала, производя нужную нам обработку этих событий. Для того, чтобы обработать то или иное событие, необходимо просто прописать в своем скрипте функцию с предопределенным названием. Все доступные функции обработки событий есть в документации по QLUA. Поддерживаются самые различные события совершение на бирже очередной сделки, выставление новой заявки пользователем (или скриптом), изменение стакана котировок и т.д. Схематически принцип выполнения скриптов внутри терминала можно изобразить следующим образом:

Скрипт LUA в QUIK может содержать несколько функций с предопределенными названиями, являющимися обработчиками событий (таких как новая сделка, изменение лимитов, изменение котировок и т.д.).
Выполнение скрипта происходит после пожатия на кнопку «Запустить» в диалоге «Таблицы -> Lua -> Доступные скрипты», и всегда начинается с обработки тела скрипта вне каких-либо функций (на схеме обозначено [BODY]) и вызова обработчика с именем Init() (если он есть). После того, как функция Init() завершится, происходит создание отдельного потока приложения QUIK, и в этом потоке начинает выполняться функция main(), которая обязательно должна быть. Скрипт считается работающим до тех пор, пока выполняется функция main. Как только она завершится — прекращается и выполнение скрипта, т.е. вызов из него обработчиков событий. Обратите внимание, что все функции обработки событий, в отличие от функции main(), выполняются в рамках основного потока терминала QUIK, а значит время их работы должно быть сравнительно небольшим, иначе будут заметны «подвисания» в работе терминала.

Начать программирование можно с такого кода:

is_run = true

function main()

while is_run do

sleep(1000)   — приостановка (1000 = на 1 секунду)

robot()

end

end

function robot() —тело робота
— здесь ваш код

end —конец тела робота

function OnStop(stop_flag)

is_run=false

stop_flag=1

end

Описание функций находятся здесь в файле «Интерпетатор языка луа» или в веб-версии здесь.
К сожалению, описание функций не полное и когда вы дойдете то программирования отправки транзакций (функция «sendtransaction()» ) то обнаружите что параметры нужно искать в другом месте, а именно в «Руководстве пользователя квик» в разделе 6 «Работа с другми приложениями», правда удобно?)) Я потратил несколько минут на поиски.

В остальном язык сильно проще и приятнее QPILE.
Примеры работающего робота, торгующего в спреде здесь. Его принцип работы и описание в предыдущем посте.

текст частично взят отсюда

запуск-первого-скрипта-в-КВИК

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

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

Как и где создаем каталоги и файлы

Открываем папку программы QUIK и прямо в ней создаем свою новую папку. Назвать ее можно как угодно, но желательно название задавать латинскими буквами, например «LuaScripts». В этой папке и будут храниться все созданные нами скрипты для КВИК на языке QLUA.

Заходим в нашу созданную папку «LuaScripts» и в этой папке создаем простой текстовый документ. ПКМ — правой кнопкой мыши в свободном пространстве и выбираем пункт «Текстовый документ».
создает-текстовый-файл

Затем переименовываем этот файл и пишем любое название латинскими буквами, например пусть будет «Script_N1», а также меняем расширение на нужное нам .lua, в итоге у нас получается файл «Script_N1.lua». Но хочу предупредить, что чаще всего расширения не видно у файлов, поэтому если Вы все сделаете как написал выше, то виндоус по умолчанию все равно добавит расширение .txt, поэтому можно открыть либо в проводнике, либо в «тотал командоре» либо включить отображение расширений файлов в свойствах папки, либо создать файл прямо в программе NotePad++.

Если хотим создать через НотПад. То вверху в меню данной программы нажимаем вкладку «Синтаксисы», выбираем пункт «L» и в открывшейся области выбираем «Lua».
синтаксис-в-Notepad

Далее в меню NotePad нажимаем кнопку «Файл»«Сохранить как».
сохранить-как-_скрипт-в-notepad

В открывшемся новом окне находите папку с Квиком и открываете созданную папку «Lua scripts». Внизу в этом же окне задаете имя файла «Script_N1» и еще чуть ниже выбираете тип файла LUA. Затем нажимаете «Сохранить».
создаем-скрипт-в-notepad

Все, файл нашего будущего скрипта создали, осталось дело за малым, написать в него код на языке LUA! Как мы уже знаем, что есть инструкция в КВИКе о том, как создается скрипт, называется этот файл с инструкцией QLUA.chm, располагается в папке с Квиком.

Напишем простейший код для примера

function main()
  message("Запущен мой скрипт");
end

И сохраним сделанные нами изменения в файл путем нажатия на кнопку в меню
сохраняем-скрипт-в-notepad

Переходим к запуску скрипта «Script_N1.lua»
Хоть и очень просто, но все же уже первый скрипт написан! Давайте запустим его в QUIK.
Для этого Открываем терминал КВИК, в меню выбираем пункт «Сервисы»«LUA скрипты…».

луа-скрипт

В появившемся окне «Доступные скрипты» справа нажимаем на кнопку «Добавить». Находим свой скрипт «Script_N1.lua» в папке «Lua scripts» и жмем «Открыть».

открыть-скрипт-квик

У нас открывается наш скрипт. Теперь, чтобы его запустить, выделяем строку с нашим скриптом «Script_N1.lua» и справа в окне нажимаем кнопку «Запустить».
запустить-наш-скрипт-в-квик

После этого у нас появляется окно сообщений в КВИК вот такое!

Окно-с-неправилным-сообщением-квик

В окне какие-то каракули 🙂 Но давайте это устраним. В NotePad в редакторе скрипта в меню находим раздел «Кодировки» и далее выбираем «Преобразовать в ANSI».
преобразовать-в-ANSI

Еще раз сохраняем наш скрипт. Возвращаемся в КВИК и в окне «Доступные скрипты» жмем кнопку «Запустить».

Окно-с-правилным-сообщением-квик

Теперь выходит окно с понятным сообщением!

Для того, чтобы постоянно в создаваемых в NotePad файлах не проводить преобразование в ANSI, можно сразу один раз в настройках указать кодировку нового документа.
Для этого в меню «Опции» — «Настройки». В появившемся окне выбираем из списка «Новый документ» и справа настраиваем следующее: «Кодировка — ANSI», «Формат Конца Строк — Windows (CR LF).

Кодировка-в-ANSI

На этом пока завершим наши действия на сегодня, не все сразу, но многое мы уже познали, много интересного еще нас ждет впереди!



Понравилась статья? Поделить с друзьями:
  • Как написать рифф на гитаре
  • Как написать ритм партию гитары
  • Как написать ритм к песне
  • Как написать родителям учеников об увольнении
  • Как написать ритм игру