Блог им. vtvladim

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

   Кратко расскажу принципы и некоторые нюансы работы с графиком в Qiuk в плане создания своего индикатора (здесь и далее – подразумевается использование языка программирования Lua). В конце текста изначально хотел прикрепить видео с демонстрацией и краткими пояснениями работы моих индикаторов, но решил сделать это во второй части статьи, чтобы совместить просто иллюстрацию с небольшим анализом фьючерсов и акций.
   На полноту изложения вопроса по работе с индикаторами на графике Quik не претендую. Информация будет полезна интересующимся данной темой, не рассчитана на профессионалов (которые и так все знают, умеют и реализовали – свято в это верю), но все же предполагает наличие определенного уровня знаний Lua.
   Зачем мучиться со своими индикаторами? Конечно, в этом нет смысла, если вас устраивают стандартные индикаторы или отсутствуют самостоятельные подходы (методы) торговли, либо визуализация вам в принципе не требуется (не интересна).
   В моем случае мне банально захотелось сделать визуализацию своего метода прогнозирования экстремумов цены следующего интервала. Алго этого метода уже давно было реализовано. Предполагал, что сварганю все красиво максимум за день, ведь работающий код уже есть, оставалась только связка с графиком в Quik и наведение красоты. Но Quik внес свои коррективы в этот план: чтобы окончательно все сделать, разобраться в незадокументированных нюансах работы с графиком в Quik потребовалось больше времени. Про некоторые «грабли» расскажу, а также изложу основные азы для тех, кто заинтересуется вопросом в прикладном плане.
   Основы создания индикаторов в Quik изложены в соответствующих руководствах разработчиков (полнота и доступность изложения в них — на совести разработчиков), переписывать все нет смысла, но основные моменты изложу для полноты содержания и восприятия.

   Структура кода любого индикатора должна содержать три составляющих: глобальную таблицу с именем Settings, функции Init() и OnCalculate(), а файл с кодом размещается в папке LuaIndicators (создается вами если отсутствует), которая в свою очередь размещается внутри папки, где установлен сам Quik. Если отсутствует одна из вышеуказанных частей, либо в коде допущена ошибка, то в списке добавляемых на график индикаторов (в режиме редактирования графика – редактировать – добавить индикатор) вы свой индикатор не увидите. Кратко про каждую составляющую:

1). Settings =

{
                        Name = " Имя индикатора ",
                        line = {
                                   {Name = «Название линии»,
                                   Type = (Тип линии),
                                   Width = (Толщина линии),
                                   Color = (Цвет линии)
                                    }
            }

Таблица Settings задает:
— имя индикатора, под которым вы найдете его в списке всех индикаторов (избегайте уже испольованных названий). В этой же области (между элементами Name и line) вы можете определить дополнительно свои параметры (не просто используемые в расчете, а которые вы предполагаете изменять)
— тип линии можно задавать следующим:
Type = TYPE_LINE                      --- линии
Type = TYPE_HISTOGRAM           --- гистограммы
Type = TYPE_POINT                    --- точки
Type = TYPE_DASHDOT               --- точка-тире
Type = TYPE_DASH                     --- тире
Type = TYPE_TRIANGLE_UP         --- треугольники вверх
Type = TYPE_TRIANGLE_DOWN   --- треугольники вниз
— толщина линии (число)
— цвет линии – в формате модели RGB
   Толщину, тип и цвет линий удобно выбирать в режиме редактирования настроек индикатора (после его добавления) и потом внести выбранные значения в код.
   Поле line может содержать несколько элементов (по числу линий, используемых в индикаторе), структура которых аналогична приведенной выше.  

2). function Init()
       interval_1 = getDataSourceInfo().interval     — не обязательная строка (см. текст ниже)
      return #Settings.line
   end

   Эта функция инициализирует скрипт индикатора и срабатывает в момент его добавления. Возвращается число линий индикатора, перечисленных в Settings.Line и управление передается функции function OnCalculate(index).
   Здесь есть первый нюанс. Когда вы меняете на графике инструмент (график «заякорен») или интервал — вновь срабатывает Init() и график автоматически подписывается на новые данные. Но если ваша расчетная часть кода использует параметры, существенно зависимые от верности указания кода актива (к примеру, шаг и точность цены, стоимость шага цены) вам надо считать класс и код инструмента, интервал. Это позволяет сделать стандартная функция getDataSourceInfo(), возвращающая эти параметры: interval; class_code и sec_code. Но считать изменение этих параметров в скрипте индикатора сразу же невозможно, предположу – что из-за паузы подгрузки данных. По факту, getDataSourceInfo в Init() возвращает только интервал (причем не стабильно верно).
   Чем это чревато – когда расчетная часть кода использует параметры, зависимые от верности указания кода актива, можно много чего интересного насчитать с неверными interval, class_code и sec_code. Для проверки такого «нежданчика» советую ставить вторую строчку (с interval_1, и объявить ее глобальной), которая будет вторично проверяться на верность в OnCalculate(). Считывать в Init() кода и класса актива бессмысленно, поскольку они в Init() вообще не определяются, это надо делать в OnCalculate().
   3). function OnCalculate(index)
   В функции происходит основная магия рисования индикатора. В ней перебираются данные TOHLCV (начиная с наиболее поздних) и по умолчанию отображается график цены. Чтобы на графике отобразился еще и ваш индикатор, необходимо чтобы OnCalculate() вернула значения индикатора, причем в порядке их перечисления в Settings.Line (если выводимых на график значений несколько). Естественно, до этого значения индикатора должны быть рассчитаны.
   Городить существенные расчеты внутри функции разработчики не рекомендуют. Впрочем, все равно более-менее сложные расчеты реализовываются в разрезе функций (или методов).
   Из нюансов.
   Считывание из getDataSourceInfo информации о интервале (interval), коде класса (class_code) и коде инструмента (sec_code) в OnCalculate при index = 1 нестабильно, я считываю их при index = 2 и проверяю верность считанного в Init интервала. Зачем так заморачиваться? Потому что в расчетной части используются такие параметры инструмента, как точность и шаг цены, которые определяются только при известных классах кода и инструмента. Если вам не требуются специфические параметры инструмента, то нет смысла делать эту проверку.
   В руководстве сказано, что функции со сложными вычислениями следует размещать в отдельном файле, который размещен в ином каталоге, чем папка индикатора. Разработчикам виднее, в моем случае более 600 строк кода индикатора с более 30 функциями и итерационными расчетами прекрасно работают, будучи размещенными в одном файле с вышеперечисленными функциями.
   Работа с данными TOHLCV имеет особенности: вне OnCalculate у Т() нет даты, только время, хотя Т(index) непосредственно в OnCalculate() является нормальной полной таблицей. Еще один нюанс — вне OnCalculate() получение значений OHLCV нестабильно при смене инструмента или интервала. Причину нестабильности не понял, пришлось с данными работать через таблицу данных (getCandlesByIndex), а не напрямую с источником данных (OHLCV). Этот вариант немного более извращенный в плане кода, но основное его неудобство не в этом – надо помнить о необходимости присвоения графику идентификатора, который служит названием таблицы, содержащей данные TOHLCV (график – редактировать — дополнительно). Связанное с этим неудобство заключается еще в том, что разные графики не могут иметь один идентификатор, а значит для добавления на другой график индикатора, работающего с таблицей источника данных, необходимо менять название идентификатора как в коде, так в самом графике. Поясню на примере: в Quik созданы два графика, работающих со срочным и фондовым рынками (каждый график «заякорен» к одному классу активов). Чтобы добавить один и тот же индикатор на оба графика, приходится сделать два файла с кодом индикатора, отличающиеся только названиями индикатора и идентификатора, и соответственно добавлять на графики разные по названию индикаторы. Этого геморроя не было бы, если бы можно было работать стабильно напрямую с TOHLCV.
   Небольшие советы:
   1) задавать в Settings параметр, который будет разрешать/запрещать вывод вашего индикатора на график. Например, для этого у меня задается параметр Settings.Show_Ind, который управляет выводом значений индикатора (=1 – есть вывод, =0 индикатор не выводится). Управление выводом в этом случае простое: OnCalculate() при Settings.Show_Ind=1 возвращает значения индикатора, а при Settings.Show_Ind=0 – нет; Такое управление позволяет простым изменением параметра Settings.Show_Ind при редактировании его значения (график – редактирование) убрать индикатор с графика не удаляя сам индикатор, когда вывод его на график не требуется (см. пример кода ниже);
   2) задавать в Settings параметр, который будет задавать дату, с которой рассчитывать и (или) показывать на графике индикатор (под датой здесь понимается дата+время, т.е. таблица). Для этого у меня задается параметр Settings.From_Date (например, он равен «20.07.2023» (в Lua нет типа данных «дата»). А в коде вы переводите данное значение в формат POSIX (Unix-время, т.е. число секунд, прошедших с полуночи 1 января 1970 г.). Такое представление времени упрощает сравнение времени разных дат до банального сравнения двух чисел (к примеру, сравнение разных дат в формате, который я видел в советах на сайте – приведение даты 10.07.2023 к виду 10072023 гораздо сложнее, как минимум год здесь надо будет обрабатывать отдельно). Делается перевод даты и времени несложно, в приведенном выше примере это будет так:
Cur_Date = {}       — не забываем объявить массив
Cur_Date.day = tonumber(string.sub(Settings.From_Date, 1, 2 ))        вырезали день из From_Date
Cur_Date.month = tonumber(string.sub(Settings.From_Date, 4, 5 ))   вырезали месяц из From_Date
Cur_Date.year  = tonumber(string.sub(Settings.From_Date, 7, 10)) )) вырезали год из From_Date
Start_Date = os.time(Cur_Date)         число (количество секунд) = дата и время в формате POSIX

   (Если есть необходимость уточнять дату до часов и минут, то аналогично формируются элементы массива с часом и минутой).
   Небольшое уточнение: данные OHLCV с МБ – естественно по Московскому времени, а перевод в POSIX делается по локальному (местному времени, т.е. времени вашего компьютера). Чтобы все было совсем верно, следует сдвинуть значение Start_Date на разницу часовых поясов вашего региона с Москвой. У меня это +4 часа или 4*60*60 секунд.
   Управление выводом (и расчетами) в этом случае аналогичное: если os.time(T(index)) > Start_Date (т.е. дата индекса позже заданной) то рассчитываем значения индикатора и возвращаем их значения, иначе – не возвращаем (не считаем). В моем случае расчет всех свечек, имеющихся в источнике данных (как известно, это минимум 3000 свечек (на коротких интервалах больше) + сохраненные данные предыдущих торговых сессий, доходило до более 40 тыс. свечек) при итерационных расчетах приводил к заметной (несколько секунд) задержке при выводе индикатора на график (смена интервала, инструмента). Поэтому при сложных расчетах такое управление с начальной датой существенно снизит «залипание» графика при смене инструмента или интервала, и собственно — самого Quik.
   3) задавайте в Settings все те параметры, которые вы собираетесь «качать». Это позволит простым их редактированием в свойствах графика увидеть результат изменений в индикаторе. Поскольку изменение таких параметров в самом коде приведет к изменению на графике только после удаления индикатора и добавления его вновь (сам код считывается только один раз – при запуске Quik если индикатор уже добавлен на график, или при добавлении индикатора). 

   Для примера: Стандартный индикатор скользящей SMA с возможностью изменения: периода, источника данных (O, H, L, C, V и их сочетания) для средней и управлением вывода значений индикатора (авторство – разработчиков Quik, мною исправлена пара «очепяток» и добавлено управление выводом). Желающие могут скопировать код и вставить его в размещенный в соответствующей папке (см. выше) файл с расширением «lua», в списке индикаторов вы увидите этот индикатор под именем «Test_SMA».

Settings = {
                   Name = «Test_SMA»,
                   mode = «C»,
                   period = 5,
                   Show_Ind = 1,
                   line = {
                                      {Name = «Test_SMA»,
                                      Color = RGB(255, 0, 0),
                                      Type = TYPE_LINE,
                                      Width = 2 }
                                      }
                   }

function Init()
                   #Settings.line
end

 

function OnCalculate(idx)
            local per = Settings.period
            local mode = Settings.mode
            local lValue = iValue
            if idx >= per then
                        local ma_value=0
                        for j = (idx-per)+1, idx do
                                    ma_value = ma_value+dValue(j, mode)
                        end
                        if Settings.Show_Ind == 1 then
                                    return ma_value/per
                        end
            else
                        return nil
            end
end

 

function dValue(i,param)
            local v = param or «C»
            if v == «O» then
                        return O(i)
            elseif v == «H» then
                        return H(i)
            elseif v == «L» then
                        return L(i)
            elseif v == «C» then
                        return C(i)
            elseif v == «V» then
                        return V(i)
            elseif v == «M» then
                        return (H(i) + L(i))/2
            elseif v == «T» then
                        return (H(i) + L(i)+C(i))/3
            elseif v == «W» then
                        return (H(i) + L(i)+2*C(i))/4
            else
                        return C(i)
            end
end

 

 

  • обсудить на форуме:
  • Quik Lua
★20
53 комментария
Ничего себе кратко
avatar
bascomo, Ну, старался как мог )))
А ты, я смотрю, что-то разразился целой пачкой статей. Интересные мысли — не совсем согласен по сути и в терминологии… Но здорово — вот таких постов тут дефицит. Можно сказать, что я прервал молчание как минимум частично из за твоего цикла — заразное это дело )))  
Владимиров Владимир, неожиданно. Мне надо закончить уже этот весёлый проект, я никак не могу себя заставить, и заниматься тут писаниной — это, на самом деле, такой мой способ избегания.


avatar
bascomo, Понимаю о чем ты. Только я это называю «переключением». Дело не в «заставить», а в том, что бы «дозрело в голове». Для этого полезно давать возможность мозгу переключиться на иное дело, а он в параллельном режиме дооформит мысли. Мне, во всяком случае, это помогает.  
Владимиров Владимир, да я думал, какой-то критический взгляд получу, сам переосмыслю, но нет. Ушат говна в лучшем случае. Есть там хотя бы люди, которые просто интересуются, любопытствуют, что да как. Но их меньшинство, да и это не та обратная связь, которой я ожидал, честно сказать. А большинство — оно везде большинство :)

з.ы. вот у меня и дозревает)
avatar
bascomo, СЛ — не то место, где можно получить критический взгляд или нормальную дискуссию. Я несколько раз пробовал, отказался от такой затеи давно. Единственное что можно взять полезного от таких статей — возможность ссылки (если такая необходимость существует). А на «ушат» нет смысла реагировать от слова совсем. Это не мнение специалистов, да и сайт по сути комментариев — не тематический . Хотя понятно, что психологически все равно есть доля дискомфорта от такой реакции.  
Владимиров Владимир, да, это мешает. А всех мудаков по рекомендации Тимофея не заблокируешь.
avatar
Никогда не встречался такой косяк Квика, когда строишь график спреда между двумя инструментами с помощью getCandlesByIndex. Ну т.е. индикатор нормальный рабочий, график построился, а при новом включении Квика графика в окне нет — пусто. И нужно вручную его обновить чтобы он прорисовался. Жутко бесит, когда таких графиков много и все обновлять.
Может есть какой способ чтобы это исправить?
avatar
Vkt, Исправить можно все, если найти ошибку )))
С такой ситуацией не встречался. Т.е. индикатор добавлен на график и работает без проблем, а после выхода из квика и нового его запуска — индикатор уже добавлен на графике но не выводится? Рассуждаем логически: новое включение квика отличается от уже запущенного квика только считыванием сохраненных настроек. По идее такого глюка не должно быть. А что понимается под словами «нужно обновить график»? Не понял это, график же обновляется автоматически при изменении цены… Или это про режим редактирования графика «график-обновить»?
    Возможны нюансы в плане кода. Надо делать печать  промежуточных результатов для локализации ошибки (я использую для этого dbgview).  Из вопроса не понятно — в коде через getCandlesByIndex берутся данные из двух таблиц по двум инструментам или берется таблица готового спреда? В первом случае много нюансов, смотря как все реализовано.
Владимиров Владимир, Или это про режим редактирования графика «график-обновить»?

Именно!

Я тоже dbgview пользую.
А что там можно локализовывать? Квик ошибок не выдает.
Торги еще не начались. Окно с графиком пустое. после начала торгов индикатор начинает отрисовывать спред за сегодняшний день. Спред за прошлые дни только после ручного обновления появляется.
Данные да из двух таблиц. Делал и из 3х, 4х. Все едино.
Мало того. Иногда при новом запуске Квика спред может и сам прорисоваться!




avatar
Vkt, Такое у меня было связано с ошибкой деления на ноль в коде. Когда квик запускается в первый раз, данных ещё нет, и возникает такая ошибка. Терминал о ней не сообщает, а просто не отрисовывает индикатор. После догрузки данных всё начинает работать. Ну или не ноль, а null. То же самое будет
avatar
bascomo, вот это похоже на правду. Как-то можно решить эту проблему?
avatar
Vkt, Проверять делитель на нуль или пустое значение. Если в коде индикатора возникла ошибка, он выключается и не выполняется до принудительного обновления. Если ошибки не допускать, то он обновится на следующей новой свече. Можно писать значения в лог при первых 10 вызовах тела функции индикатора, чтобы проверить, что в них такое
avatar
bascomo, а если нет деления?
avatar
bascomo, Если расчетная ошибка в процессе выполнения кода, то квик выдает окно с сообщением про нее. Тут как я понял нет сообщений.
Владимиров Владимир, если null, то не выдаёт. Но без кода можно долго фантазировать. 
avatar
bascomo, Насчет долго фантазировать — согласен. Но похоже ошибка плавающая, раз то не рисует, то после обновления рисует. Поэтому я и написал, что нужно искать место, где информация портится. В расчетной части ошибка. Как вариант — на какой-то свечке данные по одному инструменту отсутствуют, а в коде не предусмотрена корректная обработка такой пары данных в спред. Код смотреть надо = 100% 
Vkt, Действие обновить график вызывает поиск пропущенных данных и вновь инициализирует Init(), т.е. прорисовку всего графика по новой. Если уже добавленный индикатор не выводится на график, видимо дело в коде. Тот факт, что квик не видит ошибки и разрешает добавление индикатора — не означает гарантированное отсутствие ошибок в коде. Квик видит только лексические ошибки. А если ошибка в логике расчета, ее не заметит. 
   Насчет локализации ошибок. Если расчетная часть большая, то нужно для начала отследить по мере выполнения кода верность используемых данных и возвращаемых значений индикатора. Если вылезет место, где они «ломаются» — тогда проще будет думать о причине. На словах трудно что-то советовать, код надо смотреть-анализировать…
Владимиров Владимир, да какой там большая. Получили данные и вычли одно из другого.

Settings =
{
   Name = «Spread»,
       Symbol1 = «s1»,
    K1 = 3,
       Symbol2 = «s2»,
    K2 = 4,
   line=
   {
      {Name = «Spread», Color = RGB(0, 0, 255), Type = 1,Width = 1}
   }
}

function Init()
   return 1
end

function OnCalculate(index)
    local indx1 = (getCandlesByIndex(Settings.Symbol1, 0, index-1, 1)[0].close or 0) * Settings.K1
    local indx2 = (getCandlesByIndex(Settings.Symbol2, 0, index-1, 1)[0].close or 0) * Settings.K2

    return indx1-indx2

end
avatar
Vkt, так у вас index при первом вызове 0, вы -1 элемент получаете) я тоже так делал))
добавьте вначале if index == 0 then return nil
avatar
bascomo, точно, в более поздних версиях этого индикатора есть if index == 0 then return nil
Но проблемы это не решает. Тут какие-то более хитрые проверки нужны, раз он то появляется, то нет.
avatar
Vkt, Я же спрашивал про способ работы с данными инструментов… Тут вызываются две таблицы с разными идентификаторами. В данном случае идентификатор задает имя таблицы с источником данных графика. А у графика не может быть два разных идентификатора, только один. Ведь данный код — это индикатор для графика? Более правильный вопрос в данном случае — почему это вообще иногда работает )))
Владимиров Владимир, это индикатор спреда между двумя разными графиками разных инструментов с разными идентификаторами. Почему бы ему не работать?
avatar
Vkt, Невнимательно прочитал, про два графика не заметил видимо. Тогда вопроса с ИД нет. 
Запись
getCandlesByIndex(Settings.Symbol1, 0, index-1, 1)[0].close
что-то недопонял )). Close = getCandlesByIndex(Settings.Symbol1, 0, index-1, 1).close — это значение С, а [0] — что такое? Ноль или О?
Владимиров Владимир, 0 это ноль: first_candle – индекс первой свечки. Первая (самая левая) свечка имеет индекс 0,
avatar
Vkt, Понятно. Тогда получается, что в indx1(2) засовывается значение самой дальней свечки? Не разумнее разве выводить туда текущую index?! 
   И потом — обновление графика задает OnCalculate при изменении цены. В данном случае — цены какого инструмента? Если подписка на ИД сделана на двух дополнительных графиках, то на графике со спредом то изменения цены нет! Вот он и не активируется. Один из инструментов следует подписать на графике спреда, оставить дополнительный график для подписки на второй инструмент, а вывод значений первого инструмента и спреда разнести по разным осям (правая/левая). Думаю, тогда не надо будет обновлять график ))) 
Владимиров Владимир, Один из инструментов следует подписать на графике спреда, оставить дополнительный график для подписки на второй инструмент, а вывод значений первого инструмента и спреда разнести по разным осям (правая/левая)
Пользуюсь Квиком более  15 лет, но из этой фразы понял только про разнесение по разным осям )
Я обычно оба инструмента строю в одном окне друг под другом, и ниже в том же окне график спреда.

avatar
Vkt, Наш диалог — типичный пример, показывающий что надо полностью выражать свою мысль и придерживаться принятой терминологии. Это без негатива к вам, и относится к нам обоим )))
   Для продолжения обсуждения — напишите конкретно как у вас реализован график. Из последнего вашего сообщения я понял так: окно графика имеет три окна по вертикали, в двух выводятся данные по инструментам, а в третьем окне рисуется спред. Все верно?
   Теперь — зачем такие подробности: Когда вы добавляете индикатор, то срабатывает Init, которая в свою очередь инициализирует OnCalculate. Такая же последовательность происходит при изменениях интервала или инструмента, а также при обновлении графика. Эта логика верна для каждого окна графика отдельно. А в процессе работы квика инициализация OnCalculate происходит при изменении цены основного источника данных текущего окна графика. А у вас, насколько я понял, в этом окне только расчетный спред. Т.е. «с точки зрения» квика в этом окне цена не меняется, соответственно нет причин для инициализации OnCalculate. Вот суть моего предположения. Это «отлавливается» промежуточной печатью — проверкой потока данных. Из этих соображений и я предложил линию спреда разместить в окне с одним из инструментов. Тогда основной ИД будет меняться и автоматически запускать перерисовку графика.
  Про код комментировать не буду, как я понял вы привели не окончательный вариант, либо  его часть.
Владимиров Владимир, окно графика имеет три окна по вертикали, в двух выводятся данные по инструментам, а в третьем окне рисуется спред. Все верно?

Да. Изредка бывает, что в одном окне 2 графика инструментов, а ниже спред.
А у вас, насколько я понял, в этом окне только расчетный спред. Т.е. «с точки зрения» квика в этом окне цена не меняется, соответственно нет причин для инициализации OnCalculate.
Это не так. Индикатор всегда привязан к какому либо графику инструмента. И в свойствах графика Квик это явно выводит. К тому же эта версия опровергается тем, что я писал выше. Бывает, что при загрузке Квика график спреда иногда появляется. Но даже если не появился, то после начала торгов он начинает отображаться, но только за текущий день. За предыдущие дни только после принудительного обновления. Значит OnCalculate таки срабатывает.
Промежуточная печать это хорошо, но что и в каком месте тут печатать не понятно.
avatar
Vkt, Докладываю: на собственной шкуре, так сказать, столкнулся с этой проблемой )))
   Сделал сегодня подобный индикатор спреда и на практике понял о чем был вопрос, собственно. Со слов я проблему недооценил и неверно понял. У меня эта проблема выражается в том, что при смене интервала спред не прорисовывается верно. Но после обновления графика или переустановки индикатора- все отображается правильно. Причину выявил — не обновляется ИД второго инструмента. Перемещение индикатора и инструментов по окнам  ничего не дает. В рамках getCandlesByIndex вроде испробовал все хитрости, какие пришли в голову, не помогло. Как принудительно заставить провести обновление ИД второго инструмента — способа не нашел. Погуглил — не мы одни с этой проблемой столкнулись. Похоже на еще один «косяк» квика. Все же видимо надо в таком случае подписываться на ИД второго инструмента самостоятельно. 
   На заметку: в общем случае (когда у какого-то инструмента есть свечки без сделок) — требуется более сложный код для отсечения таких моментов и согласования по времени значений, формирующих спред. Если будет желание — могу сбросить. 
Владимиров Владимир, спасибо за столь активное участие в решении данной проблемы! Да, это явно косяк Квика. Потому и спросил в самом начале, вдруг кто решил данную проблему. Но видать и вправду нерешаемая.
По поводу кода согласования, было бы интересно ознакомится.
Но снова вопрос терминологии. Что есть свечки без сделок? Неужели у Квика есть такие? Я всегда думал, что нет сделок — нет свечки. Если сделки за период свечки все по одной цене, то она вырождается в точку.
Или имелось ввиду просто пропуски свечек у одного из инструментов?
Тогда нужно понимание и соответствующий алгоритм того, что мы будем рисовать на месте этих пропусков.

avatar
Vkt, Да получается что спасибо говорить не за что ))) Ну и мне самому интересно было сделать подобный индикатор спреда.
   В ИД действительно нет свечек без сделок. Но когда используется индикатор, то индекс в OnCalculate — это просто порядковый номер интервала. К примеру, спред между фьючерсами Сбера по 9 и 12 контрактам — там часто по 12-му не было сделок, а по 9-му были. На графике индекс изменяется по основному ИД — это 9-ый контракт. И получается, что у таких свечкек есть на одном индексе только данные одного контракта. Если брать данные просто по индексу, получается несоответствие. В этих местах разумно оставлять предыдущее значение спреда. А соответствие данных обеспечивается поиском свечки второго инструмента с одинаковым временем, соответствующим времени свечки первого инструмента. Это в общем случае. Но код лучше сразу писать с учетом таких неожиданностей. Ну и поскольку это квик — полезно проверять значения на nil, чтобы потом голову не чесать — откуда белиберда получается. 
Владимиров Владимир, сегодня включил основной комп, где лежат рабочие скрипты индикаторов. Там  после получения от getCandlesByIndex значений я как раз всегда проверяю их на nil. Если хоть одно значение nil, то и return nil. Результат меня устраивает. Дополнительных проверок на соответствие даты и времени не делал.
Видать не думали разрабы Квика, что мы еще мы спреды рисовать будем.
Вот и косяки такие с прорисовкой.
Кстати, в Метатрейдере еще веселее. Если в МТ4 спреды строятся хоть и не быстро, но  надежно, то в МТ5 построенный спред может в течении торгов просто пропасть, хотя утром был. Или улететь далеко вверх или вниз, нарисовать горизонтальную линию. Короче полный бардак. Хотя я в MQL5 еще хуже понимаю, чем в QLua. Впрочем они сделали возможность создания спреда в виде нового инструмента. Но работать с этим очень не удобно. Таки и мучаемся )

avatar
Vkt, Есть половинчатый вариант решения. Создается два графика, отдельно по каждому инструменту, им присваиваются идентификаторы. На любом из этих графиков добавляется индикатор спреда. При изменении инструментов или интервалов сначала изменяется график без индикатора, потом — с индикатором. Тогда все работает. Ну понятно — ИД у обоих инструментов получаются основными и обновляются автоматически. Не многим лучше, чем просто обновлять график )))
Владимиров Владимир, спасибо, попробую
avatar
Владимиров Владимир, По у сторонние силы)) я последний раз писал код на луа в 2020 году
avatar
bascomo, «у сторонние силы» они такие… запаришься ошибку искать )))

А что у вас в индикаторе за значение iValue? Мне кажется, оно не определено.
avatar
Михаил К., Хороший вопрос. По коду эта переменная не используется. Уже не помню, возможно осталась от моих художеств, которые я убрал из кода. На всякий случай проверил работу индикатора — все нормально пашет, как с этой переменной, так и без нее. Эту строчку с iValue можно просто удалить. 
Код для индикатора SMA в статье плохой, так писать плохо, при более сложных расчетах будет тормозить.
В примерах для Квика такой стиль был нуууу очень давно.
Новую версию индикаторов на Qlua скачай у них с форума, там все нормально и более быстрый вариант расчета.
А идентификатор графику зачем присваиваешь, ты его потом в роботе что ли используешь? — Если так то это плохой — тупой подход хоть и популярный у многих.
avatar
Beach Bunny, В тексте черным по белому написано: авторство кода принадлежит разработчикам, мною лишь исправлены опечатки и добавлено управление выводом. 
   Спасибо за совет про «скачать новую версию индикаторов». Я стандартными индикаторами не пользуюсь. Совсем. 
   Про идентификатор в тексте тоже написано — зачем и почему. 
Владимиров Владимир, Еще один нюанс — вне OnCalculate() получение значений OHLCV нестабильно при смене инструмента или интервала. Причину нестабильности не понял, пришлось с данными работать через таблицу данных (getCandlesByIndex), а не напрямую с источником данных (OHLCV).

Выше вы советуете наоборот  с OHLC не работать. Противоречие мне видится в этом.
avatar
Vkt, Противоречие есть, да, но у меня более замороченная работа с ИД, чем здесь. Разработчики вообще пишут что напрямую работать с OHLCV нельзя. Работать у меня с ними получалось, а проблема заключалась в пропусках данных (ряд свечек были без H или L, или вообще nil). Заткнуть это для простой операции вычитания двух значений просто, а вот когда вам надо ретроспективу в n интервалов, то отсечение требуется заумное — периода, включающего в себя хотя бы одну «неправильную свечку», не говоря уже о верности расчета.   
Владимиров Владимир, и где они такое пишут ?
Разработчики вообще пишут что напрямую работать с OHLCV нельзя.
avatar
Beach Bunny, Имеется ввиду — вне OnCalculate. Написано было либо в инструкции, либо на форумах Quik. 
   У меня встречный вопрос — вас интересует «устроить срач» или по теме? Если первое — то ошиблись адресом. Если по теме — то напишите, почему код SMA «тупой, плохой, древний, считается медленно» и предложите вариант лучше. Уж больно ваши комментарии похожи на классическое предварительное «эй, закурить есть?»
Владимиров Владимир, код плохой потому что  одно и тоже пересчитывается каждый раз — это ж очевидно. Сочувствую вам если вы это не видите и не понимаете. Бывает.
Надо использовать «stream» реализацию.
Как сделать лучше -> я это уже писал, Это есть в новой версии от разработчиков Квика на форуме.
зы
Закурить значится не будет…
avatar
Beach Bunny, Суть статьи абсолютно не в данном коде. Он приведен именно для примера — чтобы желающие попробовали и поняли, что делать подобные индикаторы не страшно и не сложно. Но если их делать, то понятно — не из стандартного набора, смысла повторять их нет. Расчетную часть каждый должен создать сам по своим хотелкам.
  Взаимно сочувствую вам, что принимая участие в диалоге, не научились слушать оппонента и понимать о чем он говорит. 
Для примера: Стандартный индикатор скользящей SMA....

function OnCalculate(idx)
            local per = Settings.period
            local mode = Settings.mode
            local lValue = iValue
            if idx >= per then
                        local ma_value=0
                        for j = (idx-per)+1, idx do
                                    ma_value = ma_value+dValue(j, mode)
                        end
                        if Settings.Show_Ind == 1 then
                                    return ma_value/per
                        end
            else
                        return nil
            end
end

Мог бы быть и поаккуратней написан, раз это «пример». Если idx >= per и Settings.Show_Ind не равно 1, то функция ничего явно не вернет и, видимо, вернет nil по умолчанию в lua? Не ясно почему вторая ветка возвращает явно nil, а первая нет. И похоже, что нет смысла высчитывать ma_value если Show_Ind не 1. Лишняя работа.

Можно так переписать, вдобавок уменьшив количество вложений:

function OnCalculate(idx)
  local per = Settings.period
  local mode = Settings.mode
  local lValue = iValue

  if idx < per or Settings.Show_Ind ~= 1 then
    return nil
  end

  local ma_value = 0
  for j = (idx — per) + 1, idx do
    ma_value = ma_value + dValue(j, mode)
  end

  return ma_value / per
end



avatar
Фейтомия, Критика это хорошо, отвечу тем же )))
Мог(ла) бы и поаккуратней прочитать: Авторство кода в примере — разработчиков квика, об этом написано, я лишь поправил опечатки в нем и добавил для примера управление выводом. Суть статьи абсолютно не в данном коде. Он приведен именно для примера — чтобы желающие попробовали и поняли, что делать подобные индикаторы не страшно и не сложно. Но если их делать, то понятно — не из стандартного набора, смысла повторять их нет. Расчетную часть каждый должен создать сам по своим хотелкам.
Владимиров Владимир, 
 Суть статьи абсолютно не в данном коде. 

Эх, а в заголовке было написано:

Создание на Lua своего индикатора в графике Quik: основы, нюансы, пример.
avatar
Фейтомия, С проблемами в логике — в соседние ветки пожалуйста. Там, кстати, вы найдете и собеседников для словесного срача на отвлеченные темы. Потрудитесь мне не писать больше — тратить время на пустую болтовню не имею желания. Заранее благодарен. 
Теперь в QUIK индикаторы не в отдельной папке, а зашиты в каком файле? 
И как теперь добавить скажем lua-индикатор в QUIK?
avatar

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

....все тэги



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