Блог им. morefinances
Продолжаем изучение qlua, cегодня:
Интегрируем таблицы в структуру скрипта qlua.
Удаляем таблицы через DestroyTable.
Останавливаем скрипт через IsWindowClosed.
Обработка события закрытия таблицы через коллбэк.
Работа с цветом SetColor, Highlight, SetSelectedRow.
Пишем простого советника.
В прошлый раз мы рассмотрели как с помощью qlua создать таблицу в торговом терминале и заполнить её информацией из таблицы текущих торгов. Но это была статичная табличка, чтобы её «оживить» нужно разместить операции с нашей таблицей внутри структуры скрипта в функции main.
Саму таблицу мы можем создать до цикла while и внести неизменяемые данные (в нашем случае тикер и наименование бумаги), а уже заполнить цифрами и обновлять внутри цикла. Пока будет работать скрипт таблица будет обновляться.
function OnInit() tikers = {"GAZP", "SBER", "VKCO"} progname = "mytable :" timeout = 5000 end function OnStop() do_it = false message(progname.." Финиш.") end function main() message(progname. <a name="cut"></a> ." Старт.") do_it = true if m_t==nil then -- если таблица не создана ранее, то m_t=AllocTable() -- создать таблицу AddColumn(m_t, 1, "Тикер", true, QTABLE_STRING_TYPE, 10) -- добавить 1 столбец шириной в 10 символов AddColumn(m_t, 2, "Бумага", true, QTABLE_STRING_TYPE, 20) -- добавить 2 столбец шириной в 20 символов AddColumn(m_t, 3, "Цена", true, QTABLE_STRING_TYPE, 10) -- 3й столбец с шириной в 10 AddColumn(m_t, 4, "Суммарное предложение", true, QTABLE_STRING_TYPE, 25) -- 4й и 5й столбцы с шириной в 25 AddColumn(m_t, 5, "Суммарный спрос", true, QTABLE_STRING_TYPE, 25) CreateWindow(m_t) -- создание окна таблицы SetWindowPos(m_t, 0, 430, 700, 110) -- позиционирвание (x,y от левого верхнего угла) -- и размеры (ширина, высота) SetWindowCaption(m_t, "Вывод данных через таблицу") -- показать таблицу, пишем заголовок for u = 1, #tikers do InsertRow(m_t,-1) -- добавить строку end end -- вносим в таблицу неизменяемые данные for i = 1, #tikers do local tName = getParamEx("TQBR", tikers[i], "SHORTNAME").param_image SetCell(m_t, i, 1, tikers[i]) SetCell(m_t, i, 2, tName) end while do_it do -- заполнение и дальнейшее обновление таблицы for i = 1, #tikers do local tLast = getParamEx("TQBR", tikers[i], "LAST").param_image local tOffer = getParamEx("TQBR", tikers[i], "OFFERDEPTHT").param_image local tBid = getParamEx("TQBR", tikers[i], "BIDDEPTHT").param_image <br /> SetCell(m_t, i, 3, tLast) SetCell(m_t, i, 4, tOffer) SetCell(m_t, i, 5, tBid) end sleep(timeout) <br />end end
Теперь наша таблица будет обновляться каждые 5 секунд, пока мы принудительно не остановим скрипт соответствующей кнопкой на панели управления доступных скриптов.
Функция DestroyTable.
Удалить таблицу можно с помощью DestroyTable.
Если добавить, например, в функцию OnStop строку DestroyTable(m_t), то при нажатии на кнопку остановки одновременно с завершением работы скрипта автоматически закроется и таблица.
function OnStop() do_it = false DestroyTable(m_t) message(progname.." Финиш.") end
Скрипт можно также остановить не только кнопкой и вызовом OnStop, но и через закрытие таблицы. Есть 2 варианта, как это можно выполнить. Первый: использовать функцию IsWindowClosed.
Функция IsWindowClosed
Вернёт true если окно таблицы закрыто пользователем вручную, false если окно таблицы открыто в терминале и nil если таблица c запрашиваемым id не существует (указан несуществующий/некорректный id или таблица удалена функцией DestroyTable).
Так если добавить в цикл while строку:
if IsWindowClosed(m_t) then OnStop() end
То после закрытия окна мы увидим, что остановится и скрипт в панели доступных скриптов (это будет не мгновенно, т.к. цикл while ожидает завершение паузы sleep(timeout), которая у нас в OnInit определена как 5 секунд (timeout=5000), цикл выжидает его завершения.
Обработка события закрытия таблицы через коллбэк.
Мы можем получать коллбэки на различные операции, которые пользователь проводит с таблицей. В дальнейшем это нам особенно пригодится для создания диалоговых окон и взаимодействия со скриптом (когда можно уже в процессе запуска алгоритма менять различные параметры и переменные, например размер стопа и тейка, размера позиции и пр.).
В текущей ситуации нам нужно только отслеживать коллбэк на закрытие таблицы. Для этого подпишемся после создания таблицы на коллбэки по ней:
SetTableNotificationCallback(m_t, table_callback)
А выше функции main создадим функцию table_callback, в которой укажем, что если таблицу закрыта пользователям, то вызвать OnStop:
function table_callback(m_t,msg,par1,par2) if msg==QTABLE_CLOSE then OnStop()end end
Если подходить с позиции ресурсов, хоть IsWindowClosed и запрашивается нами постоянно в цикле, а функция table_callback вызывается всего один раз от коллбэка, но сам коллбэк несет в себе существенно большую информацию по событиям таблицы (как мы будем рассматривать позднее) и дает эту информацию не разово, а на постоянной основе, поэтому будет потреблять и несколько больше ресурсов:
Поэтому пока оставим в нашем скрипте только функцию IsWindowClosed, хотя в дальнейшем еще задействуем в полном объеме и коллбэки.
Работа с цветом
Разработчики терминала предусмотрели возможность выделять цветом информацию в пользовательских таблицах. Для этого есть несколько функций:
SetColorпозволяет установить цвет ячейки и/или столбца таблицы в терминале:
SetColor(id таблицы, строка, столбец, цвет фона, цвет текста, цвет фона2, цвет текста2)
фон2 и текст2 – цвета при выделении ячейки (когда на соответствующую строку таблицы нажимаем мышкой).
Для определения цветов предусмотрена функция RGB, через которую можно задавать соответствующую палитру.
Так наш скрипт можно немного изменить, если сравнить покупки и продажи и подсветить большую из этих цифр. Для этого если ранее в таблице мы данные отражали как строки, то теперь нам нужно будет в цикле for в локальные переменные получить сразу всю таблицу данных от getParamEx по инструменту, в таблице разместим как строки, а в условиях сравнения уже цифры.
for i = 1, #tikers do local tLast = getParamEx("TQBR", tikers[i], "LAST") local tOffer = getParamEx("TQBR", tikers[i], "OFFERDEPTHT") local tBid = getParamEx("TQBR", tikers[i], "BIDDEPTHT") SetCell(m_t, i, 3, tLast.param_image) SetCell(m_t, i, 4, tOffer.param_image) SetCell(m_t, i, 5, tBid.param_image) -- продажи больше покупок подсвечиваем продажи, покупки без цвета if tOffer.param_value > tBid.param_value then SetColor(m_t, i, 4, RGB(255,204,250), RGB(0,0,0), RGB(255,204,250), RGB(0,0,0)) SetColor(m_t, i, 5, RGB(255,255,255), RGB(0,0,0), RGB(255,255,255), RGB(0,0,0)) -- покупки больше продаж подсвечивам покупки, продажи без цвета elseif tOffer.param_value < tBid.param_value then SetColor(m_t, i, 4, RGB(255,255,255), RGB(0,0,0), RGB(255,255,255), RGB(0,0,0)) SetColor(m_t, i, 5, RGB(199,254,236), RGB(0,0,0), RGB(199,254,236), RGB(0,0,0)) -- продажи равны покупкам оставляем без подсветки оба значения else SetColor(m_t, i, 4, RGB(255,255,255), RGB(0,0,0), RGB(255,255,255), RGB(0,0,0)) SetColor(m_t, i, 5, RGB(255,255,255), RGB(0,0,0), RGB(255,255,255), RGB(0,0,0)) end end
Если продажи будут равны покупкам (редкий случай), то цветом ничего не выделяется.
В функциях работы с цветом предусмотрено несколько констант:
QTABLE_DEFAULT_COLOR – цвет заданный по умолчанию в системе.
QTABLE_NO_INDEX – в параметре столбца будет распространять цвет сразу на весь столбец, а не на одну ячейку. В параметре строки аналогично сделает цвет на всю строку. Если поставить эту константу (равна -1) и там, и там, то окрасится вся таблица.
Есть еще несколько вариантов выделения цветом ячеек – это так называем плавное затухание:
Highlight этот эффект работает по умолчанию в таблице текущих торгов, если не убирать цветовые настройки:
Highlight(id таблицы, строка, столбец, цвет фона, цвет текста, время подсветки в мс). Пример мы рассмотрим позднее при создании советника.
И выделение строки (аналог нажатия мышкой на строку таблицы в терминале) через SetSelectedRow:
SetSelectedRow(id таблицы, номер строки).
Закрывая основные функции работы с таблицами можно еще указать функцию GetCell, которая позволяет получить данные из конкретной ячейки:
GetCell (id таблицы, строка, столбец).
Вернет таблицу с ключами: value – числовое значение, image – строковое представление в таблице.
Но это очень прикладная ситуация, когда нам нужно получить такие данные из таблицы, т.к. а) все ячейки таблицы, как правило, содержат переменные или константы, которые мы и так знаем и б) работа с переменными в разы быстрее, чем делать подобный запрос на получение значения ячейки. Даже если мы хотим посмотреть какое именно значение было ранее в таблице в конкретной ячейке до того как обновим в ней информацию проще ввести еще одну переменную, которая будет эти данные хранить и перед обновлением ячейки присваивать текущее значение.
Полный список всех функций работы с таблицами можно посмотреть в файле помощи по qlua от разработчиков (это файл QLUA.chm, который лежит в папке торгового терминала с info.exe). Открываем файл QLUA, в поиске забиваем «Функции для работы с таблицами», в числе первых находим:
Создание нескольких таблиц.
При необходимости можно одновременно создавать и несколько таблиц (id в этом случае должны отличаться). Например при написании советника мы можем сделать еще одну таблицу, где будем фиксировать все сигналы, чтобы не отлавливать их в таблице системных сообщений.
По ситуации также можно в процессе работы скрипта добавлять таблицы и в необходимый момент удалять их. Но всё хорошо в меру. Порой достаточно просто предусмотреть дополнительные поля в одной таблице, чтобы всё было органично.
Таблицы и вкладки в терминале.
Скриптом не получится разместить таблицу в конкретную вкладку терминала. Если необходимо перенести таблицу в другую вкладку — это делается уже вручную: после того как мы запустили скрипт и нужная таблица создана в самом терминале выделяем её (нажимаем на заголовок или на любую строку), далее в меню Окна > Переместить окно на вкладку.
Чтобы закрепить тему работы с таблицами предлагаю самостоятельно поупражняться в создании таблиц.
Например, сделав шахматную доску:
Или создав таблицу умножения с цветовой подсветкой. Выделив, например, те ячейки, по которым в результате перемножения получается такая двузначная величина цифра единиц которой целочисленно делится на цифру десятков:
К текущему моменту мы изучили qlua уже достаточно, чтобы попробовать написать своего советника.
Но прежде чем перейти к этому шагу считаю необходимым разместить важный дисклеймер, т.к. наше законодательство ужесточается и нужно предусмотреть всё, в т.ч. чтобы данные статьи не «выпилили» в будущем в результате очередных ограничений и различных законотворческих инициатив со стороны ЦБ по робоэдвайзингу или инвестсоветникам:
Дисклеймер: все рассматриваемые примеры здесь и далее являются исключительно упражнениями для закрепления материала по программированию на qlua и не несут каких-либо инвестиционных идей или конкретных сигналов. Автор не планирует делиться в каком-либо виде своими торговыми стратегиями, либо инвестиционными рекомендациями, либо в иной форме (в т.ч. в виде кода) давать сигналы по конкретным инструментам. Любая торговая стратегия или гипотеза (в т.ч. высказанная в комментариях к статьям) требует многократного тестирования на истории на разных таймфреймах и инструментах, правильного управления капиталом и риск-менеджментом. Вся ответственность за торговлю по данным и иным материалам автора полностью лежит на инвесторе. Доходность прошлых периодов любой торговой стратегии или финансового инструмента не гарантирует аналогичной доходности в будущем.
Одним словом, ни прошлые, ни текущие, ни будущие материалы не являются инвестрекомендацией. Мы здесь «про программирование», а уже какие именно стратегии закладывать в код решает каждый самостоятельно.
По этой же причине (риски работы скриптов на реальных счетах и ответственность за их работу) прежде чем мы начнем работать в терминале с реальными заявками, сделками и торговыми счетами нужно будет установить демо терминал и изначально потренироваться на «кошечках» в деморежиме, всё настроить, получить и исправить первые ошибки в алгоритмах и только после всех проб и доработок можно будет переходить на работу с реальным счетом. Не предлагаю установить демо квик сейчас, т.к. чаще всего демодоступ дают только на небольшой интервал (3-4 недели). И чтобы не запрашивать продление или проходить перерегистрацию позже под другой почтой проще дождаться этих темы и только потом установить, чтобы успеть максимум в эти временные рамки работы демо терминала.
Обычно советников пишутся либо для ручной торговли, либо для «полуатоматической», когда трейдер дополнительно хочет поучаствовать в решении и в каких-то случаях менять, например, размер позиции, либо вообще пропустить сигнал и не входить в сделку. Иногда советника запускают перед тем как полностью автоматизировать скрипт, чтобы проверить, что всё работает правильно, в соответствии с логикой стратегии.
Торговая логика советника.
С одной стороны, на текущем этапе нам нужен алгоритм с достаточно динамичной генерацией сигналов, т.к., например, 1 торговый сигнал в день или неделю может быть даже очень не плохо для оптимизированной торговой стратегии, но не очень показательно в учебных целях.
С другой стороны, всегда проще достраивать логику на примере, который мы уже рассматривали, чем прописывать алгоритм с нуля. По этой причине возьмем за основу предыдущий пример и просто немного дополним его. Будем отслеживать не 3 акции, а например, список акций, входящих в индекс Московской Биржи IMOEX как наиболее ликвидных инструментов.
На текущий момент в индекс входит 42 компании (состав индекса всегда можно посмотреть на сайте биржи: https://www.moex.com/ru/index/IMOEX/constituents/)
Нам достаточно только расширить массив с тикерами необходимыми бумагами, чтобы автоматически таблица перестроилась:
tikers = { "AFKS" , "AFLT" , "AGRO" , "ALRS" , "CBOM" , "CHMF" , "ENPG" , "FEES" , "FIVE" , "FIXP" , "GAZP" , "GLTR" , "GMKN" , "HYDR" , "IRAO" , "LKOH" , "MAGN" , "MGNT" , "MOEX" , "MTSS" , "NLMK" , "NVTK" , "OZON" , "PHOR" , "PIKK" , "PLZL" , "POLY" , "ROSN" , "RTKM" , "RUAL" , "SBER" , "SBERP" , "SGZH" , "SNGS" , "SNGSP" , "TATN" , "TATNP" , "TCSG" , "TRNFP" , "VKCO" , "VTBR" , "YNDX" }
Если на момент прочтения статьи индекс поменялся в составе (что периодически происходит) просто добавьте новый или удалите неактуальный тикер из этого массива.
Стратегия
В качестве торговой стратегии (предположим, что мы выдвинули несколько гипотез и смогли после тестирования на истории определиться на одной, наиболее перспективной по показателям) возьмем следующую логику:
Сигнал LONG: в случае, если количество лотов на покупку (суммарный спрос) в 2 раза больше аналогичного объема на продажу (суммарное предложение), при этом текущая цена выше цены закрытия вчерашнего дня (основной торговой сессии).
Сигнал SHORT: если суммарные продажи в 2 раза больше суммарного спроса и текущая цена ниже цены закрытия вчерашнего дня (основной сессии).
Если условия не соблюдаются, то остаемся вне позиций.
Есть несколько вариантов как можно получать биржевую информацию с таблицы текущих торгов через getParamEx. Один из них – пример с которого мы начали: мы в функции main с определенной периодичностью (указанную через sleep) запрашиваем данные и обрабатываем их.
Второй вариант: мы можем получать информацию через функцию обратного вызова OnParam, которая при любых изменениях таблицы будет давать нам коллбэк на изменения, которые мы будем далее обрабатывать. Второй вариант мы рассмотрим позднее, он пригодится для более быстродейственных скриптов. В нашем текущем т.к. рассматриваемые параметры не меняются глобально каждую секунду достаточно выставить паузу в 10 секунд.
Делаем небольшие правки в нашем предыдущем примере:
1) вынесем цветовые константы в OnInit;
2) уменьшим размеры и название столбцов;
3) добавим цену закрытия предыдущего дня по основной сессии (используем для этого параметр PREVLEGALCLOSEPR);
4) добавляем условия сигналов LONG/SHORT;
5) создаем массив signal, который будет хранить текущий статус по каждому инструменту (1 – лонг, -1 – шорт, 0 – вне позиции).
function OnInit() tikers = { "AFKS" , "AFLT" , "AGRO" , "ALRS" , "CBOM" , "CHMF" , "ENPG" , "FEES" , "FIVE" , "FIXP" , "GAZP" , "GLTR" , "GMKN" , "HYDR" , "IRAO" , "LKOH" , "MAGN" , "MGNT" , "MOEX" , "MTSS" , "NLMK" , "NVTK" , "OZON" , "PHOR" , "PIKK" , "PLZL" , "POLY" , "ROSN" , "RTKM" , "RUAL" , "SBER" , "SBERP" , "SGZH" , "SNGS" , "SNGSP" , "TATN" , "TATNP" , "TCSG" , "TRNFP" , "VKCO" , "VTBR" , "YNDX" } progname = "simple advisor v.1 :" timeout = 10000 startind = 0 -- индекс старта скрипта (первой итерации) -- цветовые константы mBlack = RGB(0,0,0) mWhite = RGB(255,255,255) mRed = RGB(255,204,250) mGreen = RGB(199,254,236) mGray = RGB(226,226,226) end function OnStop() DestroyTable(m_t) do_it = false message(progname.." Финиш.") end function main() message(progname.." Старт.") do_it = true if m_t==nil then -- если таблица не создана ранее, то m_t=AllocTable() -- создать таблицу AddColumn(m_t, 1, "Тикер", true, QTABLE_STRING_TYPE, 10) AddColumn(m_t, 2, "Бумага", true, QTABLE_STRING_TYPE, 20) AddColumn(m_t, 3, "Тек.Цена", true, QTABLE_STRING_TYPE, 10) AddColumn(m_t, 4, "Закрытие", true, QTABLE_STRING_TYPE, 10) AddColumn(m_t, 5, "Продажи", true, QTABLE_STRING_TYPE, 10) AddColumn(m_t, 6, "Покупки", true, QTABLE_STRING_TYPE, 10) AddColumn(m_t, 7, "Сигнал", true, QTABLE_STRING_TYPE, 10) CreateWindow(m_t) SetWindowPos(m_t,700,0,690,780) SetWindowCaption(m_t, progname.." создание советника") -- показать таблицу, пишем заголовок -- добавляем строки циклом for u = 1, #tikers do InsertRow(m_t,-1) end end closeprice = {} -- создаем массив цен закрытия signal = {} for x = 1, #tikers do closeprice[x] = getParamEx("TQBR", tikers[x], "PREVLEGALCLOSEPR") -- цена закрытия предыдущего дня signal[x] = 0 -- статус сигнала по инструменту end while do_it do -- заполнение таблицы for i = 1, #tikers do local tLast = getParamEx("TQBR", tikers[i], "LAST") local tOffer = getParamEx("TQBR", tikers[i], "OFFERDEPTHT") local tBid = getParamEx("TQBR", tikers[i], "BIDDEPTHT") if startind == 0 then -- вывод неизменямой части таблицы local tName = getParamEx("TQBR", tikers[i], "SHORTNAME") SetCell(m_t, i, 1, tikers[i]) SetCell(m_t, i, 2, tName.param_image) SetCell(m_t, i, 4, closeprice[i].param_image) end SetCell(m_t, i, 3, tLast.param_image) SetCell(m_t, i, 5, tOffer.param_image) SetCell(m_t, i, 6, tBid.param_image) if tonumber(tOffer.param_value) > 2 * tonumber(tBid.param_value) then SetColor(m_t, i, 5, mRed, mBlack, mRed, mBlack) SetColor(m_t, i, 6, mWhite, mBlack, mWhite, mBlack) if tonumber(tLast.param_value)<tonumber(closeprice[i].param_value) then SetCell(m_t, i, 7, "SHORT") if signal[i] == 0 then message(progname.." сигнал SHORT по "..tikers[i].." по цене "..tLast.param_image) signal[i] = -1 end end elseif 2 * tonumber(tOffer.param_value) < tonumber(tBid.param_value) then SetColor(m_t, i, 5, mWhite, mBlack, mWhite, mBlack) SetColor(m_t, i, 6, mGreen, mBlack, mGreen, mBlack) if tonumber(tLast.param_value)>tonumber(closeprice[i].param_value) then SetCell(m_t, i, 7, "LONG") if signal[i] == 0 then message(progname.." сигнал LONG по "..tikers[i].." по цене "..tLast.param_image) signal[i] = 1 end end else SetColor(m_t, i, 5, mWhite, mBlack, mWhite, mBlack) SetColor(m_t, i, 6, mWhite, mBlack, mWhite, mBlack) SetCell(m_t, i, 7, " ") if signal[i] ~= 0 then signal[i] = 0 end end Highlight(m_t, i, QTABLE_NO_INDEX, mGray, mBlack, 500) -- выделение цветом вносимых изменений if startind == 0 and i == #tikers then startind = 1 end -- убираем индекс первой итерации sleep(100) end if IsWindowClosed(m_t) then OnStop() end sleep(timeout) end end
И запускаем:
Видим, что на старте сегодня в районе полудня скрипт показал всего 1 сигнал в лонг (по VKCO). По остальным бумагам вне позиций, хотя есть тикеры, по которым суммарные продажи превышают более чем в 2 раза суммарные покупки.
За пару часов работы алгоритм бы добавил еще несколько сигналов:
В первичном варианте советник работает, но есть несколько но:
1: если мы запускаем скрипт не в начале торговой сессии, то при внутридневной торговле правильнее не использовать сигналы, которые получаем на старте, а «отлавливать» только новые. Это как пересечение скользящих – оно могло состоятся уже приличное время назад, а мы будем работать по сильно отстающему сигналу, который, возможно, в ближайшее время уже полностью «отработает» свой потенциал. Для кого-то позиция покажется спорной, но мы возьмем в работу для корректировки алгоритма.
2: учитываем время старта. Многие не торгуют первую минуту открытия рынка (разновидности: не торгуют первые 5М, 30М или даже 1H).
3: аналогично нужно учесть время приостановки получения сигналов на вход. Например, торгуя внутри дня в основную сессию, кто-то не принимает новые сигналы, если они приходят после 18:00 (либо за 10-15 минут до аукциона закрытия).
Наглядно посмотреть в жизни 1 и 3 пункты мы могли бы, если бы запустили наш скрипт в пятницу ближе к вечеру:
Мы видим, что первые 3 сигнала сформировались сразу на старте (и возможно уже потеряли свою актуальность), а более 20 сигналов генерируются в аукцион закрытия и позднее, что при торговле в основную сессию нас бы уже не интересовало.
4: правильнее было бы, чтобы советник не только сигналил о входе в LONG/SHORT, но и как только условия на соответствующий сигнал перестали бы выполняться подавал сигнал на закрытие позиции.
5: по результатам торгового дня скрипт также может выводить информация о бумажном финансовом результате по каждой позиции (понятно, что при торговле руками может быть сильная задержка по времени и расхождение).
6: сейчас все сигналы выводятся через message, а было бы полезно сделать отдельную таблицу, где бы фиксировались все сигналы, чтобы не отлавливать сообщения в общем потоке таблицы.
7: скрипт можно также дополнить отработкой прерывания связи с сервером. Т.к. при возобновлении связи может быть сгенерировано большое число новых сигналов, часть из которых уже не будут актуальны (аналогия с первым пунктом).
8: все сигналы и информацию о разрыве соединения можно записать в файл. Первые нужны для анализа качества сигналов, вторые для понимания насколько всё устойчиво работает и не пропустили из-за технических проблем какие-то входы/выходы.
9: скрипт должен уметь останавливаться по таймеру. Так если торгуем в основную сессию внутри дня сигналы на вход, например, мы можем получать до 18:30. При этом в 18:35 должны просигналить о закрытии всех текущих открытых позиций и, например, в 18:40 завершить работу скрипта с выводом всей необходимой информации в таблицы и файл (есть трейдеры, торгующие только в первую половину дня, у них скрипт в обед по таймеру будет завершать работу).
Какие-то из этих пунктов кто-то посчитает избыточными, а кто-то, уверен, захочет еще дополнить парой десятковважных для него позиций. В любом случае есть что улучшать и мы займемся этим уже в следующий раз.
Кто хочет попробовать свои силы самостоятельно может выбрать несколько пунктов из этого списка для апгрейда скрипта и попробовать их реализовать в качестве «домашнего задания».
Сегодняшние примеры, как и ранее, размещены на github:
https://github.com/morefinances/qlua
(example_10, 11 и simple adviser)
Теги: qlua для начинающих, кружок авиамоделизма.
В предыдущих сериях:
Qlua: введение
https://smart-lab.ru/blog/917696.php
Настраиваем торговый терминал и редактор кода
https://smart-lab.ru/blog/918869.php
Основы qlua, часть 1:
message, конкатенация
фильтрация по сообщениям в терминале
PrintDbgStr, комментарии
типы данных, type
операции с числами
операции со строками
операции с таблицами
условные операторы
https://smart-lab.ru/blog/920031.php
Основы qlua, часть 2:
Циклы for … do … end, while do … end, repeat … until.
sleep, break, goto.
как пройти весь массив циклом, как пройти таблицу по ключам
локальные и глобальные переменные, функции
получение даты и времени
получение данных через getInfoParam
https://smart-lab.ru/blog/921366.php
Qlua: структура скрипта.
Структура типового скрипта qlua с примерами.
Обработка скриптом «обрыва связи» с сервером и возобновления работы.
Работа с файлами: запись, перезапись и чтение файла.
getScriptPath, getWorkingFolder
https://smart-lab.ru/blog/922044.php
Qlua: получение данных из таблицы текущих торгов, создание таблиц в торговом терминале.
Получение биржевых данных через функцию getParamEx
Выгрузка списка параметров функции getParamEx через DDE из торгового терминала
Создание пользовательских таблиц в торговом терминале
https://smart-lab.ru/blog/923365.php
Если кто-то захочет реализовывать масштабные вещи, то вам дорога в Transaq Connector. Благо на Github куча проектов для изучения. Хоть на NodeJS всё делай.
Если хотите самостоятельно посмотреть примеры — погуглите робот Бегемот в сети (не мой, просто там как раз стратегия на стакане работает).
smart-lab.ru/blog/928152.php
github.com/finsight/QUIKSharp/issues
2. Возможность делать нормальный пользовательский интерфейс. В QLua из вменяемого был VLC, но его возможностей в какой-то момент стало не хватать.
3. Доступ к мощной среде разработки, и что еще важнее ОТЛАДКИ.
4. Доступ к внешним библиотекам, про которые я в QLua даже не слышал. В качестве примера: Попробуйте какую-нибудь нейронку к QLua прикрутить.
5. Возможность создать самостоятельное (не привязанное к терминалу) приложение для бектестирования стратегий, код которых потом можно легко превращать в «боевых» роботов, практически не переписывая его.
Что касается ограничений qlua, то чтобы ответить на вопрос надо понять, что конкретно Вы имеете в виду.
Ну и наконец, хочу чтобы Вы правильно меня поняли. Я не собираюсь никого убеждать в необходимости перехода на QUIKSharp. Если человека полностью устраивает QLua — пусть остается на нем. Мне, несколько лет назад, его возможностей стало не хватать, и тогда я начал поиски альтернативы. Для себя нашел. И бесплатной альтернативы этому решению, на сколько я знаю, до сих пор не появилось.