Блог им. vtvladim

Создание на Lua своего индикатора в графике Quik: Часть 2.

Создание на Lua своего индикатора в графике Quik: Часть 2. Пример работы нестандартных индикаторов: спред между инструментами, прогноз Highи Lowследующего интервала; ценовых уровней по объемам

 

В первой части (https://smart-lab.ru/blog/930907.php) были изложены основы принципа создания своего индикатора и некоторые нюансы работы с кодом индикатора графика в Qiuk (подразумевается использование языка программирования Lua).
   В данной статье немного продолжу тему нюансов кодирования индикатора и для иллюстрации приведу простой код индикатора спреда. В конце текста прикреплю видео с демонстрацией работы индикатора спреда и моих собственных индикаторов.
   Небольшое лирическое отступление. Суть данных статей — показать, что делать подобные индикаторы вполне реально и не столь сложно, как может показаться на первый взгляд. Но, безусловно, требует определенных знаний в программировании. Создавать индикаторы из стандартного набора торговой системы Qiuk смысла нет – ведь они уже реализованы. Создавать свой индикатор целесообразно тогда, когда у вас имеется своя идея (метод, способ анализа и т.п.). Приведенный в 1 части код индикатора SMA, написанный разработчиками Quik, давал возможность увидеть текст и структуру кода индикатора, «своими руками» попробовать запустить индикатор и испытать его в работе. Оптимизация кода SMA мне абсолютно не интересна, он был приведен в варианте «как было в инструкции» – эта ремарка для особо нервных программистов. Реально полезную вам расчетную часть кода каждый должен написать самостоятельно в соответствии со своими целями.
   Теперь по теме.
   В предыдущем обсуждении, благодаря коллеге (https://smart-lab.ru/profile/Vkt/, за что ему огромное спасибо) открылся еще один нюанс при кодировании индикаторов. Связан он, как я понимаю, с некоторой недоработкой в самом Quik. А именно: в случае, когда в качестве основного источника данных используется более одного инструмента, верно работает только коллбек источника данных (ИД) первого инструмента. OnCalculate, как известно, по сути является коллбеком ИД в индикаторах. Следовательно, он по идее должен выполнять эту функцию по всем ИД, которые выведены на график. Однако, это происходит «половинчато» — цена у всех инструментов на графике меняется верно, но вот с обращением к значениям ИД в коде возникают проблемы. Проявляется это в следующем: при смене интервала данные второго и последующих инструментов недоступны (хотя графически их цена на графике отображается верно), аналогичная проблема — при смене инструмента и после запуска Quik. При первичном же добавлении индикатора на график все работает нормально. Т.е. коллбек (OnCalculate) начинает правильно работать при добавлении или обновлении индикатора (редактировании – причем в режиме редактирования можно ничего не менять, просто нажать «ОК» — см. видео). Никакими хитростями в коде ситуацию исправить не смог. Ну а какая платформа без греха? В принципе не смертельно – обновить график, но вот когда таких графиков несколько… Можно подписываться на инструменты самостоятельно через CreateDataSource — но в рамках задачи визуализации смысла так делать не вижу (если кто-то сможет «победить» этот нюанс – напишите, интересно будет узнать способ). Как вариант, эту проблему косвенно можно обойти следующим образом: создать несколько графиков вместо одного (для каждого инструмента, по которым считается спред) и присвоить идентификаторы инструментам на каждом графике. Тогда мы избегаем ситуации с несколькими ИД на одном графике и коллбек работает верно. Я этот способ не использую, потому что лишние графики в Quik – это лишние потоки данных со всеми вытекающими последствиями.
   Ниже привожу скрипт расчета спреда (за основу взят код, публично выложенный в обсуждении первой части статьи). Для использования у себя в терминале следует сделать следующее:
   1. Сохраняем текст индикатора в файле с расширением, например Spread.lua, в папке LuaIndicators (если отсутствует – создать), которая находится внутри папки Quik.
   2. Создаем новую вкладку (например, «Спреды», чтобы разделить информацию разного рода)
   3. На вкладке «Спреды» создаем таблицу текущих торгов (с нужными вам инструментами) и график (окно с объемом в графике спреда я считаю лишним и удаляю), «якорим» график с таблицей текущих торгов
   4. Строим на графике цену двух инструментов (например, GOLD-9.23 и GOLD-12.23). Поскольку график заякорен, в таблице выбираем GOLD-9.23, а GOLD-12.23 добавляем. В дальнейшем, при смене инструмента в таблице, на графике сменятся оба инструмента и второй надо будет заменить вручную.
   5. Задаем идентификаторы инструментов: для цены Gold-9 id1, для Gold-12 id2 (правой кнопкой мыши щелкаем на линии цены (или подписи внизу графика) — дополнительно — там есть поле «идентификатор» вписываем id1 (id2) и сохраняем).
   6. Добавляем индикатор (ищем название «2AV_Spread») в новую область. Смотрим на результат. В режиме редактирования пользовательских настроек – если необходимо – меняем начальную дату расчета, цвет и тип линий. Названия идентификаторов НЕ ТРОГАЕМ!
   Величина спреда определяется как (Цена1*K1 — Цена2*K2), а в случае отсутствия одного из значений цены равна предыдущему значению спреда.
   Коэффициенты К1 и К2 – балансировочные. Если инструменты различаются только датой контракта, они должны быть равны 1. В случае разных инструментов (например, Si и его спот), К2 должен быть равен 1000. В общем случае может потребоваться изменение значений обоих коэффициентов (сложная балансировка в пределах вашей фантазии), поэтому коэффициентов два.
   При вставке кода обратите внимание: начальные текстовые значения данных в Settings должны быть заключены в вот такие двойные кавычки "", а не в такие «». Иначе Quik выдаст ошибку и индикатор не запустится (в зависимости от начальных языковых параметров винды кавычки могут вставляться при копировании неверно). 

— индикатор спреда между двумя инструментами

— «id1» и «id2» это идентификаторы графика (не забудьте задать на графике) первого и второго инструмента, K1 и K2 балансировочные коэффициенты

— величина спреда определяется как (Цена1*K1 — Цена2*K2), ее расчет начинается с даты «From_Date» 

Settings =
                     {
                     Name = «2AV_Spread»,
                     Symbol1 = «id1»,
                     K1 = 1,
                     Symbol2 = «id2»,
                     K2 = 1,
                     From_Date = «01.08.2023»,
                     line=
                                 {
                                 {Name = «Spread»,
                                   Color = RGB(128, 0, 2),
                                   Type = 1,Width = 2}
                                 }
                     } 

Cur_Date    = {}
result_prev = 0
 

function Init()
return #Settings.line
end

 

function OnCalculate(index)
         local indx1, indx2, Start_Date, result
         Cur_Date.day   = tonumber(string.sub(Settings.From_Date, 1, 2 ))
         Cur_Date.month = tonumber(string.sub(Settings.From_Date, 4, 5 ))
         Cur_Date.year  = tonumber(string.sub(Settings.From_Date, 7, 10))
         Start_Date = os.time(Cur_Date) 

         if index >= 1 then
                     if os.time(T(index)) >= Start_Date then
                                 indx1, indx2 = Find_Spread(index)
                                 if indx2 == 0 then
                                             result = result_prev  --- нет данных: берем предыдущее значение
                                 else
                                             result = indx2 — indx1    — значение спреда на интервале
                                             result_prev = result   — запоминаем значение спреда
                                 end
                     end    
         return result
         end
end  

function Find_Spread(index)
         local indx1, indx2
         local Data1, Num_candl, Name_Line = getCandlesByIndex(Settings.Symbol1, 0, index-1, 1) 
                     if Num_candl ~= 0 and Num_candl ~= nil then
                                 indx1 = tonumber(Data1[0].close) * Settings.K1
                     else
                                 indx1 = 0
                     end
                     if indx1 ~= 0 then 
                                 Data1, Num_candl, Name_Line = getCandlesByIndex(Settings.Symbol2, 0, index-1, 1)
                                 if Num_candl ~= 0 and Num_candl ~= nil then
                                             indx2 = tonumber(Data1[0].close) * Settings.K2
                                 else
                                             indx2 = 0
                                 end
                     else   — ветка по идее невозможна, но лучше «затыкать» вариант потери данных
                                 indx2 = 0
                     end
         return indx1, indx2
end  

 

Индикаторы прогнозных значений Highи Lowследующего интервала, уровней цены с максимальными объемами

Индикатор High и Low следующего интервала сделан мною для визуализации результатов расчетов алго: моя ТС использует прогнозные экстремумы цены (High/Low) как точки входа /выхода. Желание визуализировать эти расчеты и подтолкнуло меня к освоению темы индикаторов в Quik. А это, в свою очередь, послужило причиной написания статей с целью немного облегчить путь интересующемуся этим вопросом народу.
   Вообще-то я не использую никаких стандартных индикаторов. Совсем. И данный индикатор называю индикатором только по причине того, что в Quik добавление «не родных» линий на график называется индикатором. Данный индикатор (назвал его High_Low) показывает значения прогнозных H и L следующего интервала. Расчет основан на данных OHLCV предыдущих интервалов. Сразу хочу успокоить нервно реагирующих на слова «прогноз» и «следующий интервал» — я в курсе, что такое предсказание невозможно, читал про это, осуждаю и проч. И, да — это не регрессия и не экстраполяция.
   Краткое пояснение по индикатору High_Low:
   Расчет полностью формализован: используется формула, в которой нет привязки к конкретному инструменту или интервалу, нет оптимизируемых параметров и т.д. Но есть несколько методов, скажем так, численного решения, в моей классификации это: линейный, нелинейный, матричный и средний. Зачем так много? Самому не нравится, но это вынужденная мера для достижения более корректной обработки «тяжелых хвостов». В общем случае, характер движения цены на интервале вверх (значение High) и вниз (значение Low) различен. Поэтому методы расчета High и Low на интервале раздельные (разные). Точность расчета существенно связана с ликвидностью инструмента – чем выше ликвидность, тем лучше точность. В коде (реализован на Lua, так исторически получилось, теперь переписывать нет желания) можно задавать конкретный метод расчета прогноза, либо задать авто выбор оптимального (по критерию минимального СКО между фактическими и прогнозными значениями для заданной глубины ретроспективы). Пользовательские настройки содержат: начальную дату расчета, метод расчета и глубину авто поиска оптимального метода. Как это работает продемонстрировано в видео.
   Индикатор уровней цены по объемам (уровни цены с наибольшими объемами) сделал внепланово в качестве бонуса и закрепления полученных знаний по теме индикаторов в Quik. Хотя, по объемам и уровням сам не торгую. Просто из любопытства сделал, интересно было глянуть, да и «запас карман не тянет». Очевидно, что чем меньше задан интервал, тем точнее результат расчета. Для периода от начальной даты по текущий момент определяются по два уровня (дополнительный и основной -с наибольшими объемами.). Пользовательские настройки содержат: начальную дату расчета, способ отбора уровней (по два уровня выше/ниже: либо средней цены за анализируемый период, либо ближайшие к текущей цене), точность определения уровней, флаг отображать (рассчитывать) уровни или нет.
   Оба индикатора реализовал в одном скрипте. В видео показана работа такого индикатора.
   Краткий путеводитель по видео (мин: сек):
00-03:32 => про индикатор спреда;
03:33-15:25 => индикатор High_Low (демонстрируются: расчет прогнозных High и Low, масштабируемость расчетов по интервалам, инструментам и классам активов, управление выводом индикатора. Немного про определение трендов.
15:26-18:15 => индикатор уровней объемов (демонстрируются: расчет уровней, изменение уровней в зависимости от периода расчета (начальной даты), масштабируемость по интервалам, инструментам и классам активов, управление выводом индикатора.

  • обсудить на форуме:
  • QUIK
★9
6 комментариев
За топик — спасибо. Не в качестве критики, а для предостережения: в видео на 9:07 взгляд зацепился за ситуацию, когда в прогнозе High меньше Low (видимо, не до конца разобран вариант, в котором у предыдущей свечи High равен Low). При таком прогнозе на этой свече незатейливый торговый алгоритм может начать бешено покупать-продавать (цена ниже лоу — покупаем, выше хай -продаем)



avatar
Спасибо за замечание. Сейчас трудно понять что там было — возможно клиринг частично попал в интервал или премаркет, по видео не понятно на какой по времени свечке. В последнее время в свечках появились итоги премаркета, раньше их не было, что там засовывают в свечки — хз, но стакан во время премаркета выглядит очень интересно. Не обращали внимания? Согласен, такого не должно быть. А алго конечно такую ситуацию не торгует, она в логике отсечется как минимум. 
Владимиров Владимир, рекомендую тщательно пересмотреть ту часть реализации алгоритма, которая явно или неявно использует в вычислениях разброс High-Low предыдущей свечи. Может, для избежания ситуации деления на ноль в том месте берется какое-то устаревшее значение. На видео на 13:07 в пяти местах видно, что такие свечи продуцируют прогнозные H=L для следующей свечи, так что при редком стечении обстоятельств что-то непредусмотренное после пустой свечи вероятно и вызывает подобное поведение






avatar
Дед Нечипор, Еще раз спасибо за обсуждение по делу, на сайте от этого быстро отвыкаешь ))) 
   Вопрос немного сложнее и шире. Вы правильно заметили, что подобная ситуация возникает после свечек с одинаковыми HL. Но это только часть ответа. На графике кажущееся в силу масштаба равенство экстремумов не всегда в цифрах действительно верно. В OnCalculate индекс — это порядковый номер (по времени) свечки, а в реальном ИД, на который вы подписываетесь — в нем пустых свечек нет. Я «засунул» в индикатор только расчетную часть кода от алго, где работа с реальными свечками (без пустот), а в ИД от графика как сказано выше — есть особенность. Пустые свечки в индикаторе я «затыкаю» предыдущими значениями (в алго это не требуется), а бороться с «практически пустыми» свечками… Не факт что вообще надо, разве что для красоты или на всякий случай. Почему? — В плане алго — эта ситуация не страшна, поскольку эти места бот 100 % пропускает (фильтр по слишком малой величине торгового диапазона, или резкому изменению торгового диапазона). 
   Я по ЕД нашел место с вашего первого сообщения. Это 22-20 вчерашнего дня, когда было две 5-минутки с одной ценой (O=H=L=C). А на видео использовался авто выбор метода расчета, видимо для этих трех интервалов выбрался нелинейный, чтобы сгладить резкое сужение ценового коридора. Вот он и сгладил )), а поскольку расчет по H и L раздельный, то по L сгладилось сильнее. Но это аномалии, которые, я уверен, надо не решать, а обходить. И самое простое и правильное — не ломать и не портить «затычками» расчет, а отсекать активную работу самого алго для этих моментов. Мое мнение — универсальность расчета это подтверждение его работоспособности. Из этих соображений я не использую оптимизационную подгонку под конкретный инструмент (интервал).. 
Владимир, роботов пишите на заказ на lua для Quik?
Напишите в личку, пожалуйста
avatar
Alex, Нет, не пишу на заказ ни роботов, ни индикаторов, ни чего-то иного прочего. Извиняюсь, если не оправдал ожидания ))). Мне это не нужно в плане заработка, для этого есть торговля на бирже. Если какие-то не архисложные вопросы — обращайтесь, попробую помочь СОВЕТОМ, если смогу. 

теги блога Владимиров Владимир

....все тэги



UPDONW
Новый дизайн