Получение в QLua(Lua) данных из графиков и индикаторов

Автор записи: Дмитрий (Admin)
1 звезда2 звезды3 звезды4 звезды5 звезд (Голосов 10, среднее: 5,00 из 5)
Загрузка...

QUIK-Qlua-poluchenie-dannyhЧтобы получить доступ к графику(не индикатору) какого-либо инструмента нужно создать источник данных при помощи функции CreateDataSource().
ВАЖНО!!! Для получения новых данных, кроме тех, что уже есть в открытом графике на текущий момент (тем более, если он не открыт), требуется использовать одну из следующих функций: SetUpdateCallback(), или SetEmptyCallback(), о которых будет написано ниже.

Описание функции CreateDataSource()

Функция CreateDataSource возвращает таблицу QLua с параметрами, получать значения которых можно следующим образом:

local O = ds:O(i); -- Получить значение Open для указанной свечи (цена открытия свечи)
local H = ds:H(i); -- Получить значение High для указанной свечи (наибольшая цена свечи)
local L = ds:L(i); -- Получить значение Low для указанной свечи (наименьшая цена свечи)
local C = ds:C(i); -- Получить значение Close для указанной свечи (цена закрытия свечи)
local V = ds:V(i); -- Получить значение Volume для указанной свечи (объем сделок в свече)
local T = ds:T(i); -- Получить значение Time для указанной свечи (время открытия свечи (таблица datetime))
   -- Где i - индекс свечи (от 1 до ds:Size())
 
local Size = ds:Size(); -- Возвращает текущий размер (количество свечей в источнике данных) 
ds:Close(); -- Удаляет источник данных, отписывается от получения данных
 
ds:SetUpdateCallback(MyFuncName); -- Позволяет задать пользователю функцию обратного вызова для обработки изменившихся свечей, т.е. когда по выбранному в CreateDataSource параметру в терминал поступит новое значение (возможно такое же), автоматически будет вызвана данная функция, в которую будут передан индекс последней свечи графика, а так же добавятся новые данные в таблицу ds
-- Пример функции:
function MyFuncName(index)
   message('На '..index..'-й свече объем вырос до '..ds:V(index));
end;
 
-- Чтобы получать новые данные без использования функции обратного вызова, а просто получать новые данные в ds и брать их оттуда по необходимости существует функция:
ds:SetEmptyCallback(); -- Которая подписывается на получение новых данных

Списки возможных параметров, используемых в функции CreateDataSource():

Список для АКЦИЙ
Список для ВАЛЮТЫ
Список для ОПЦИОНОВ
Список для ФЬЮЧЕРСОВ

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

После этого, можно использовать следующие функции для доступа к данным:

-- Функция предназначена для получения КОЛИЧЕСТВА ЛИНИЙ в графике (индикаторе) по выбранному идентификатору
getLinesCount(tag); -- Возвращает число
   -- tag - (STRING) идентификатор графика (индикатора), о котором писалось выше
 
-- Функция предназначена для получения информации о КОЛИЧЕСТВЕ СВЕЧЕЙ по выбранному идентификатору
getNumCandles(tag); -- Возвращает число
   -- tag - (STRING) идентификатор графика (индикатора), о котором писалось выше
 
-- Функция предназначена для получения информации о свечах по идентификатору (заказ данных для построения графика функция не осуществляет, поэтому для успешного доступа нужный график должен быть открыт)
t, n, l = getCandlesByIndex (tag, line, first_candle, count);
   -- Параметры:
      -- tag          – (STRING) строковый идентификатор графика или индикатора 
      -- line         – (NUMBER) номер линии графика или индикатора. Первая линия имеет номер 0 
      -- first_candle – (NUMBER) индекс первой свечи. !!! ПЕРВАЯ (САМАЯ ЛЕВАЯ) СВЕЧКА ИМЕЕТ ИНДЕКС 0 !!!
      -- count        – (NUMBER) количество запрашиваемых свечей
   -- Возвращаемые значения:
      -- t – таблица, содержащая запрашиваемые свечи, пример работы: 
         local O = t[i].open; -- Получить значение Open для указанной свечи (цена открытия свечи)
         local H = t[i].high; -- Получить значение High для указанной свечи (наибольшая цена свечи)
         local L = t[i].low; -- Получить значение Low для указанной свечи (наименьшая цена свечи)
         local C = t[i].close; -- Получить значение Close для указанной свечи (цена закрытия свечи)
         local V = t[i].volume; -- Получить значение Volume для указанной свечи (объем сделок в свече)
         local T = t[i].datetime; -- Получить значение datetime для указанной свечи
            -- Где i - индекс свечи от 0 до n-1
      -- n – количество свечей в таблице t
      -- l – легенда (подпись) графика

Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!

Добавить комментарий

Получение в QLua(Lua) данных из графиков и индикаторов: 351 комментарий

  1. День добрый!

    Нужна информация, содержащаяся в заголовке открытого окна диаграммы (графика) цен по инструменту: код инструмента и тайм-фрейм.
    Как можно из скрипта обратиться к данному заголовку, чтобы потом использовать их в скрипте?

    Спасибо.

    1. День добрый!
      Написать индикатор, который будет транслировать изменения таймфрейма, ну и сам тикер.
      А тикер можно получить из легенды
      local t, n, l = getCandlesByIndex(tag, 0, getNumCandles(tag)-1, 1)
      ticker = string.match(l, "(%w+)")

      1. Спасибо, этот вариант (с использованием get...ByIndex()) я пробовал.
        Про string.match() как-то не подумал. Видимо, потому что плохо усвоил форматирующие символы и, как следствие, слаб в операциях со строками.
        Но задача несколько иная.
        Мне нужен именно заголовок (Capture). Это, вероятно, выглядит слишком оригинально, но мне важно именно такое решение: обращение к окну диаграммы в терминале, получение всех его свойств, но на первых порах мне достаточно только заголовка.

        1. К заголовку окна можно получить доступ используя w32.dll, например.
          Но есть сложность, графиков может быть много и по одному инструменту в том числе, т.е. танцы с бубном.
          Самый простой и проверенный способ получить интервал графика, это индикатор:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          19
          20
          21
          22
          23
          24
          25
          26
          27
          28
          29
          30
          31
          32
          33
          34
          35
          36
          37
          38
          39
          40
          41
          42
          43
          44
          45
          46
          47
          
          --Interval.lua
          local version = tonumber(string.match(getInfoParam("VERSION"),"(%d*)"))
          if version < 8 then
          	package.cpath=package.cpath..";"..getWorkingFolder().."/Include/lib/x32/?.dll"
          else
          	package.cpath=package.cpath..";"..getWorkingFolder().."/Include/lib/x64/?.dll"
          end
           
          require'StaticVar'
           
          Settings ={}
          Settings.Name = "*Interval" --с таким названием он появится в списке индикаторов
          local strInt = {
          	[0]		= "tick",  
          	[1]		= "M1", 
          	[2]		= "M2", 
          	[3]		= "M3", 
          	[4]		= "M4",
          	[5]		= "M5",
          	[6]		= "M6",
          	[10]	= "M10",
          	[15]	= "M15",
          	[20]	= "M20",
          	[30]	= "M30",
          	[60]	= "M60",
          	[120]	= "H2",
          	[240]	= "H4",
          	[-1]	= "D",
          	[-2]	= "W",
          	[-3]	= "MN",
          }
           
          function Init()
          	return 1
          end
           
          function OnCalculate(index)
          	if index == 1 then
          		local info = getDataSourceInfo()
          		--сохранил
          		stv.UseNameSpace(info.sec_code)
          		stv.SetVar("interval", strInt[info.interval])
          		--получил интервал в любом роботе, в рамках одного терминала
          		message(tostring(info.sec_code).." interval= "..tostring(stv.GetVar("interval")))
          	end
          	return nil
          end

          StaticVar.dll нужной версии можно взять тут: https://quik2dde.ru/viewtopic.php?id=61

                    1. А не пришел, и в свежих комментариях только этот коммент.

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

        2. Если нужен заголовок окна, то без dll не обойтись, например можно использовать w32.dll
          Не забывайте что Вы можете создать несколько окон с одним и тем же инструментом, тогда Вам придется каждое окно назвать вручную как то уникально, что бы различать эти окна, QUIK позволяет давать заголовки окнам.
          В этих окнах располагаются графики и их тоже несколько, цена, объем, индикатор...
          - "получение всех его свойств"
          - О каких свойствах идет речь? Ну размер окна, его положение еще можно получить, но что еще можно от этого окна получить, непонятно. Или Вы хотите заглянуть в окно? Получили окно, а внутри график цены, заглянули в график цены, а там бары, линии, метки... - Так не получится. Хотя может это и возможно, но как такое реализовать, не представляю.

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

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

              1. Паша, привет!
                Программирование это такая штука специфическая.
                Можно ушатать много лет на изучение не нужных аспектов, которые никогда не пригодятся в жизни.
                Мы и так потратили много времени в саду, школе, институте на Фигню с большой буквы.
                У некоторых это вообще вошло в привычку - изучение фигни, а дети тем временем голодные, потому что знание фигни или большого кол-ва фигней, очень редко позволяет заработать на хлеб насущный.
                Но, конечно это лишь мое субъективное мнение, а прислушиваться или нет - дело каждого внимающего.

                  1. Соглашусь, что получить инструмент и интервал не так сложно и не стоит заморачиваться с поиском окон и прочим.
                    Но далее было сказано:
                    "Но задача несколько иная.
                    Мне нужен именно заголовок (Capture) ..."
                    Из этого уже следует, что все же придется искать окно, для этого и нужна w32.dll можно конечно и свою dll написать, но суть остается таже. Что дальше автор будет делать с найденным окном уже не известно, да и задача далее не описана. Я не знаю что можно достать из окна, кроме заголовка, положения и размера, может автор сможет достать из него абсолютно все. Так почему же ему говорить сразу, не занимайся фигней, мы ему подсказали что нужно, дальше пусть работает.

                1. Ну если сад, школа, институт - это тоже фигня, тогда я вообще молчу на эту тему.

                  Я сам очень много потратил времени на написание разных программ, которыми вообще не пользуюсь, 2-3 раза запустил и все, забросил. Но я не считаю что потратил время зря, получил много опыта. Если бы я не писал ничего, то и сейчас бы так ничего бы и не написал.

                  1. Если оценивать потраченное время, то получается, что и сад и школа и институт - это зря потраченное время.
                    И не просто время, а 30% отведенного времени на жизнь.
                    Система обучения далека от совершенства. Иностранный язык изучают практически на протяжении всего обучения.
                    При окончании института, реально знают этот язык 2-3% выпускников.
                    А если приспичит, то этот язык будет выучен за пару месяцев в любом возрасте.
                    Значит время, потраченное на изучение этого языка прошло зря, как впрочем, и остального.
                    А путать практику с потребностью, не стоит.
                    Можно всю жизнь заниматься написанием бесполезных программ и писать их в совершенстве, только толку от них нет и не будет.
                    А можно один раз заплатить за курс по теме сотку-другую тыс. руб. и потратить пару месяцев на обучение, эффект в разы лучше, чем от самостоятельного изучения литературы, да по разным форумам в течении нескольких лет.
                    С нулевым входом самостоятельное изучение займет 3-4 года, при условии малой занятости другими делами.
                    Держите лайфхак:
                    зарплата дворника около 15тыс.р., считаем:
                    15 х 12 х 3 = 540 тыс, вуаля.
                    Вот так, опираясь на знание величины зарплаты дворника, можно посчитать много чего..., например, стоит ли воровать мешок картошки или миллион.

  2. Здравствуйте.

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

    Пробовал узнать их используя программу ArtMoney, однако, адреса ячеек памяти, в которые записаны координаты, меняются при перезапуске quik и на них нельзя полагаться. С winApi также не получается, потому что дальше окна графика Chart Window он не может смотреть.

    Есть какие-то идеи по поводу возможного решения, хотя бы в каком направлении двигаться?

      1. А вы не могли бы вспомнить, что она ещё делает, по какому принципу работает? У меня, на самом деле, идей нету, так как не понятен механизм получения данных из закрытого процесса quik.

    1. У меня так же нет вариантов влезть внутрь графика Квика, а хотелось бы, но если речь идет о "линии тренда", то есть немого другая идея.
      Получить доступ к линиям построенным вручную нет возможности, но линия тренда не такая сложная, ее можно создать на LUA, а управлять из терминала.
      Суть в следующем:
      Создать индикатор (именно индикатор, не скрипт).
      Для графика цены задать Идентификатор.
      Индикатор создает 2 метки в виде картинки, к примеру квадратик 3x3 пикселя, на графике цены используя Идентификатор. Эти метки пользователь может двигать мышью.
      Перемещая эти метки пользователь задает 2 точки, а индикатор, основываясь на координатах этих меток, рисует линию. Нужно лишь только опрашивать эти метки, что бы узнать, не изменились ли координаты.
      Опрашивать метки можно двумя способами:
      При поступлении новой сделки. Когда рынок активный и сделки поступают часто, линия будет реагировать на действия пользователя практически мгновенно, но если сделки поступают раз в несколько секунд, тогда после перемещения метки, линия изменится лишь только когда придет новая сделка.
      Другой вариант. Использовать DLL библиотеку, где создать функцию с бесконечным таймером с интервалом например 100мс. Запускаем индикатор, запускается таймер и вызывает функцию с интервалом 100 мс, а внутри этой функции происходит опрос координат меток. В этом случае линия будет реагировать на действия пользователя практически мгновенно независимо от сделок поступающих в терминал.
      Это все теоретически, на практике скорее всего нужно будет решить много задач, но думаю идея рабочая.
      Что касается остальных фигур, например прямоугольника, то тут уж ни как, линиями индикатора прямоугольник не построить.

  3. Дмитрий,здравствуйте. Функция SetUpdateCallback(MyFuncName) вызывается на каждый тик. Можно ли сделать так (через эту функцию или другую, но не запоминаем параметров свечи) чтобы вызов был именно после формирования ЦЕЛОЙ свечи выбранного таймфрейма?

    1. 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      local market = "SPBFUT"
      local ticker = "SiZ9"
      local tag = market.."_"..ticker.."_"..INTERVAL_M1
      local tag15 = market.."_"..ticker.."_"..INTERVAL_M15
      local DS = {}
      DS[tag] = CreateDataSource(market, ticker, INTERVAL_M1)
      local old_index = DS[tag]:Size()
      DS[tag15] = CreateDataSource(market, ticker, INTERVAL_M15)
      local old_index15 = DS[tag15]:Size()
       
      local function sourcecallback(interval, market, ticker, tag, index)
        if index > old_index then
          if interval == INTERVAL_M1 then
            message("new bar M1:\n C= "..DS[tag]:C(index),2)
          elseif interval == INTERVAL_M15 then
            message("new bar M15:\n C= "..DS[tag]:C(index),2)
          end
        end
      end
      DS[tag]:SetUpdateCallback(function(...) sourcecallback(INTERVAL_M1, market, ticker, tag, ...) end)
      DS[tag15]:SetUpdateCallback(function(...) sourcecallback(INTERVAL_M15, market, ticker, tag15, ...) end)
      1. Дмитрий, категорически приветствую!
        Есть куча не WordPress, у которых отлично построен и РАБОТАЕТ! форум, с редакцией и прочими плюшками и не каверкает символы.
        Всякое желание тут чего-то писать пропадает.

      2. после message нужно old_index и(не обозначил) old_index15 приравнять к текущему index. иначе квик захлебнется сообщениями (аркаводы - дебилы не могут отключить процесс, который начинает спамить message)

  4. Привет, Дмитрий.
    Подключаю график так -

    1
    
    DS = CreateDataSource("SPBFUT", Fut, TF)

    -- Получает доступ к свечам графика
    Отключаюсь так -

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    function OnStop() -- Функция вызывается терминалом QUIK при остановке скрипта из диалога управления
       is_run = false
    	DS:Close() -- Прекращает доступ к свечам графика
       KillAll() -- Снять все заявки
       DestroyTable(t_id_Menu) -- Закрыть таблицу меню
       ToLog(3) -- Пишет лог-файл
       Log:close() -- Закрывает лог-файл
       SaveCurrentState()
    end

    При остановке робота - терминал подвисает. Что не так я сделал?

              1. 1
                2
                3
                4
                5
                6
                7
                8
                9
                
                function KillAll() -- Функция снимает все заявки
                   if Status == 1 then -- Торговля разрешена
                      for n = 1, 8, 1 do -- Перебираем инструменты
                         if OpCl[n] > 0 and Num[n] > 0 then Kill(n) end -- Заявка есть, снять заявку
                         if OpCl[n] == -1 then OpCl[n] = 0 end -- Не хватило денег открыть/закрыть - разрешим открыть/закрыть
                      end
                      SaveCurrentState()
                   end
                end

                Здесь виснуть нечему

  5. Добрый день, Дмитрий!
    Каким образом можно получить с открытого графика данные фьючерса SiM9 (ГГГГMMДД, ЧЧММСС, OPEN, LOW, HIGH, CLOSE) для дальнейшего сохранения в файл (это уже умею)?
    И еще, как можно проверить отсутствие части котировок в файле, чтобы затем получить недостающие свечи? Все на форуме прочитано, вижу, истина где-то рядом, но как всегда знаний не хватает 🙂
    Заранее благодарен!

  6. Доброго времени суток! Могли бы мне помочь. У меня такая ситуация. Я не могу понять как обратиться к каждой линии в индикаторе price Channel когда создаю сигнал на покупку или продажу
    if ind~=nil and ind~="" then
    local N=getNumCandles(ind);
    if N 10 then
    -- upper
    t,n,i=getCandlesByIndex(ind, 0, N-2, 2);
    prevCloseUpper = math.floor(tonumber(t[0].close) * precision) / precision; -- предыдущее закрытие верхней линии индикатора PriceChannel
    closeUpper = math.floor(tonumber(t[1].close) * precision) / precision; -- закрытие верхней линии индикатора PriceChannel

    -- lower
    t,n,i=getCandlesByIndex(ind, 2, N-2, 2);

    prevCloseLower = math.floor(tonumber(t[0].close) * precision) / precision; -- предыдущее закрытие нижней линии индикатора PriceChannel
    closeLower = math.floor(tonumber(t[1].close) * precision) / precision; -- закрытие нижней линии индикатора PriceChannel
    ДАЛЕЕ ХОЧУ СРАВНИТЬ
    if PRICE<0 then
    SIGNAL="BAY"
    if PRICE==2 then
    SIGNAL="SELL"

    1. Здравствуйте, на сколько можно понять из этого кода (в следующий раз оборачивайте его в теги луа, над полем ввода комментария написано) подход, вроде бы, верный. Но если что-то работает не так, как ожидается, то для этого можно при помощи message выводить значения переменных и тогда станет ясно где ошибка.

  7. Здравствуйте, я вот не понял, а как получить значение индикаторов то на свече? Вызываю функцию getCandlesByIndex с идентификатором индикатора получаю таблицу, где все параметры (open ... volume), кроме datetime нулевые...