При смене инструмента графика в Lua индикаторе OnDestroy() не вызывается
Пользователь
Сообщений: Регистрация: 30.11.2021
13.08.2024 10:19:10
Добрый день Вопрос еще в 2017г и был принят поддержкой :)
При смене инструмента графика OnDestroy() по прежнему не вызывается Вопрос - скажем скрипт-индикатор создает метку на графике Меняете инструмент и метка остается, причем скрипт полностью ее теряет Как ее удалить в коде скрипта перед тем как инструмент будет заменен или сохранить доступ к ней? Глобальные переменные обнулились, идентификатор метки тоже утерян
Пользователь
Сообщений: Регистрация: 20.03.2023
13.08.2024 23:07:03
На каждом I == 1 проверяете инструмент, если он не совпадает с предыдущим, удаляете метку. Т.е. удаляете после смены инструмента, не до.
Пользователь
Сообщений: Регистрация: 30.11.2021
13.08.2024 23:07:09
Никто из сообщества не откликнулся. Обращаюсь к разработчикам. Господа. Это не пожелание а вопрос некорректной работы терминала Терминал теряет метки. Создав метку на одном инструменте и переключившись на другой инструмент метка полностью теряется. Ее уже невозможно удалить ни стандартными функциями: - DelLabel(), потому что после смены инструмента id метки после переинициализации уже не существует - DelAllLabels() даже при том что chart_tag прежний эту метку уже не находит ни перебором в коде типа...
Код
local n = 0;
for _label_id = 1, 1000, 1 do
local L = GetLabelParams(chart_tag, _label_id )--table or nil
if L~= nil then
n = n + 1;
if L.hint:find("PnL")
then DelLabel(chart_tag, _label_id)
end;
end
end
В результате переключившись пару раз на другие инструменты и вернувшись на изначальный получите вот такую картинку :)
Какое решение найдут разработчики - не знаю Но ситуация требует исправления.
Возможно подключать OnDestroy() при смене инструмента, либо увязать это с параметром диаграммы - "Оставлять трендовые линии, фигуры и метки при смене инструмента"
funduk написал: На каждом I == 1 проверяете инструмент, если он не совпадает с предыдущим, удаляете метку. Т.е. удаляете после смены инструмента, не до.
Спасибо за ответ Но после смены инструмента ситуацию я только что описал - метку в коде уже не найти и соответственно не удалить Кроме того часто бывают ситуации когда пкм на графике не дает уже "Удалить - Все метки в диаграмме" Да и как после смены инструмента получить тикер предыдущего? После инита все переменные уже "обнулены" в том числе и id метка. Ну разве что в файле хранить, и постоянно сравнивать запись в файле с текущим
Пользователь
Сообщений: Регистрация: 30.11.2021
14.08.2024 08:18:03
Проблема с метками существует с древних времен - и и и с моим участием По второй ссылке - ответ типовой "К сожалению, проблему, о которой ведётся речь, воспроизвести не удалось"н Ну киньте что приводит пользователь по данной ссылке, переключите несколько раз между двумя инструментами график и увидите см файл - screen_1 Ну что еще нужно чтобы "воспроизвести" ошибку в работе терминала По обсуждения с моим участием еще в 2021 еще на версии терминала 9.3 ответ того же "Описанная в данном обращении проблема была устранена в версии 9.4.0 терминала QUIK." а проблема как была так и переходит уже несколько лет
Такой вопрос - где и как происходит прерывание расчетов в текущем цикле OnCulculate() при смене инструмента на графике? Цикл доходит до конца или прерывается случайным образом
Цель выполнить кусок кода по удалению текущей метки которая после смены инструмента уже "потеряется"
Пользователь
Сообщений: Регистрация: 30.11.2021
20.08.2024 15:42:09
При наложении индикатора или при периинициализации при смене инструмента графика терминал даже ни видит переменных объявленных в Settings
А увидит он идентификатор только на втором проходе Как тут метки не потерять :)
Тяжелая у вас работа парни (разработчики) За 20 лет как познакомился с этим монстром ничего не изменилось Кракозябы накапливаются и наслаиваются одни на другие
Settings=
{
Name = "Example1",
chart_ID = '' -- идентификатор графика
}
function Init()
if Settings.chart_ID == '' then message('Init() Chart ID не указан!!!') end;
return 1
end
function OnCalculate(index)
if index == Size() then
if Settings.chart_ID == '' then message('OnCalculate Chart ID не указан!!!'); end;
end
return nil
end
QUIK clients support
Сообщений: Регистрация: 27.02.2023
23.08.2024 16:29:50
Сергей, добрый день. Получили от Вас информацию на почте. По итогам разбора продублируем наш ответ в данной теме форума.
Пользователь
Сообщений: Регистрация: 03.03.2016
27.08.2024 19:39:45
За то за это время разработчики сделали для пользователей очень удобную весч - якорек на графике для переключения инструменов. Классно, можно переключать десяток инструментов имея один открытый график. А то, что метки всех инструментов торчат на одном и том же графике по всем углам, это не проблема шерифа.
Похоже не все понимают о чем речь... или не посмотрели ссылки на предыдущие обращения с аналогичной тематикой :) Причина моего обращения в техподдержку и этой темы в следующем: 1. При добавлении скрипта индикатора на график на первом проходе OnCalculate() скрипт не видит параметры настроек. Вызов OnChange() происходит между 1 и 2ым проходами Проблема известная но тем не менее... 2. Метки созданные на графике при смене инструмента не удаляются в связи с отсутствием вызова OnDestroy() перед заменой актива Это приводит к образовании новых меток по количеству переходов с актива на актив
Для детального понимания процессов я прикладываю скрипт индикатора для наблюдения за порядком расчетов и лог файл Скрипт создает единственную метку с динамически меняющимися параметрами на вновь образующемся баре Метка создается на первом тике бара с удалением предыдущей В течении бара метку можно сдвинуть в удобное место, на новом баре она займет "свое" место на HighestHigh(10) по оси Y и смещением на 3 бара влево по оси времени. В текстовый параметр запишем Close() посл свечи а в Hint номер посл бара Сам тест - добавляем скрипт индикатора и сразу же вводим идентификатор графика - смотрим DebugView
В отладчике смотрим порядок вызова функций при добавлении индикатора: Init() > OnCalculate() > OnChangeSettings() > OnCalculate() 2 > OnCalculate() 3 Первый вывод - первый проход до вызова OnChange входящие параметры скрипта недоступны, т.е. никакие расчеты этого цикла некорректны Поэтому делаем обход этого прохода Понаблюдав за меткой, отображением цены закрытия и номера бара, переместив ее в удобное место - переключаем таймфрейм - предыдущая метка удаляется и создается новая так же как на поступлении нового бара При смене таймфрейма сразу вызывается OnCalculate() без инита и без опроса параметров скрипта. Все работает корректно
Теперь переключаемся на другой инструмент (пусть из табл тек торгов) В логе видим инициализацию и цикл расчетов на новом активе Init() > OnChangeSettings() > OnCalculate() 1 Делаем выводы по порядку следования функций. Метка создается новая, к метке от предыдущего инструмента доступа нет, OnDestroy() не вызвается, метка "теряется" и доступ к ней скрипт не имеет Повторяем процедуру несколько раз и наблюдаем наслоение меток на графике и на одном и на втором инструментах см скрин test-2_переключ-инструментов.jpg Удаляем индикатор с графика Одна метка при этом остается на графике и не видна из меню пкм-удалить... Удаляется после выделения клавишей DEL Смотрим какие функции используются в коде для удаления меток Детально анализируем лог файл в DebugView Делаем выводы
Код скрипта индикатора
Код
Settings=
{
Name = "test_Example-2",
chart_ID = '' -- идентификатор графика
}
n_init = 0 -- переменная для подсчета заходов в функцию Init
n_OnCalcCount = 0 -- переменная для подсчета OnCalculate
n_OnChange = 0 -- счетчик OnChangeSettings
bars_prev = 0 -- номер посл бара
TF_prev = -4; -- таймфрейм
delay = 1.0; -- задержка в цикле рассчетов параметров для метки (параметров счета)
LastSecond = 0; -- время последнеих рассчетов
chart_tag = ''; -- идентификатор графика
label_params = {}; -- параметры метки
Label_ID = nil; -- идентификатор графика
Labels = {}; -- Массив номеров установленных меток - для контроля
function Init()
n_init = n_init + 1
PrintDbgStr("Init() = "..n_init)
n_OnCalcCount = 0
n_OnChange = 0
bar_prev = 0
TF_prev = -4 -- контроль смены таймфрейма
sec_code_prev = '' -- контроль перекл на др актив
return 1
end
function OnChangeSettings()
-- n_OnCalcCount = 0
n_OnChange = n_OnChange + 1
chart_tag = Settings.chart_ID
PrintDbgStr("OnChangeSettings() = "..n_OnChange..', n_OnCalcCount = '..n_OnCalcCount)
end
function OnDestroy()
PrintDbgStr("OnDestroy() #Labels Array "..#Labels);
if #Labels > 0 then -- Удаляет ранее установленные метки
for i=1,#Labels,1 do
local del = DelLabel(chart_tag, Labels[i]);
if del then PrintDbgStr("OnDestroy() Del id "..tostring(Labels[i])); end;
end;
end;
-- стараемся найти и удалить потерянные метки
PrintDbgStr("OnDestroy() LbDelete_2() "..LbDelete_2() .. ' labels were removed');
end
function OnCalculate(index)
-- Пропускаем первый проход между Init() и OnChangeSettings() при первичном добавлении индикатора
-- для исключения ошибки чтения параметра при первом запуске индикатора
-- Init() > OnCalculate() > OnChangeSettings() > OnCalculate() 2 > OnCalculate() 3
if n_OnChange == 0 then
while index <= Size() do
if index == Size() then PrintDbgStr('Пропускаем цикл до вызова OnChangeSettings() после Init(), index = ' .. index); end;
return
end;
end
-- предпоследний бар
if index == Size() - 1 then
PrintDbgStr('---- Begin Cycle Number '..(n_OnCalcCount+1))
DSInfo = getDataSourceInfo();
Sec_Code = DSInfo.sec_code;
if chart_tag == '' then message('OnCalculate Chart ID не указан!!!'); end;
PrintDbgStr("Seccode " .. Sec_Code ..", chart_ID = " .. chart_tag)
else
-- последняя свеча
if index == Size() then
-- раз в секунду (или больше)
if os.time() > LastSecond then -- if os.time() > LastSecond + 2*delay-1 then
LastSecond = os.time();
-- потиковое обновление (с задержкой)
label_params['TEXT'] = tostring(C(index))
label_params['HINT'] = tostring(index)
-- корректируем параметры метки
if Label_ID ~= nil and TF_prev == DSInfo.interval then
-- сохраняем прежнее положение метки если переместили в теч текущ бара)
-- при смене таймфрейма метка будет удалена и создана заново
local ly = GetLabelParams(chart_tag, Label_ID) --table or nil
if ly ~= nil then -- and ly.yvalue ~= nil
label_params['YVALUE'] = ly.yvalue
label_params['DATE'] = ly.date
label_params['TIME'] = ly.time
end;
--PrintDbgStr('index == Size() = ' .. Size() .. ', Label_ID ' .. Label_ID .. ', C(index) ' .. tostring(C(index)) )
SetLabelParams(chart_tag, Label_ID, label_params);
-- PrintDbgStr('SetLabelParams Label_ID = '..Label_ID)
end
-- первый тик нового бара или первый проход и метка еще не создавалась или смена таймфрейма
-- удаляем предыдущую и создаем новую метку с новыми координатами (метку можно смещать в удобное место...)
if bars_prev < Size() or Label_ID == nil or TF_prev ~= DSInfo.interval then
bars_prev = Size()
-- удаляем метку если она уже была и создаем новую
if Label_ID ~= nil then LbDelete() end;
Label_ID = labeldraw(chart_tag, index)
TF_prev = DSInfo.interval
end;
end -- every second
-- завершение цикла
n_OnCalcCount = n_OnCalcCount+1
--PrintDbgStr('END Cycle index == Size() ' .. Size()..', n_OnCalcCount = '..n_OnCalcCount)
end -- index == Size()
end -- if index > 1
return nil
end
function labeldraw(tag, index)
local highest = 0.0
for j = 0, 14 do
if H(index-j) > highest then highest = H(index-j) end;
end
local _t = T(index-3)
--PrintDbgStr(_t.year.._t.month.._t.day..' '.._t.hour..':'.._t.min..':'.._t.sec)
local Y = tostring(_t.year)
local m = tostring(_t.month) if #m == 1 then m = "0"..m end;
local d = tostring(_t.day) if #d == 1 then d = "0"..d end;
local hh = tostring(_t.hour) if #hh == 1 then hh = "0"..hh end;
local mm = tostring(_t.min) if #mm == 1 then mm = "0"..mm end;
local ss = '00'; -- tostring(_t.sec) if #ss == 1 then ss = "0"..ss end;
label_params['DATE'] = tonumber(Y..m..d);
label_params['TIME'] = tonumber(hh..mm..ss);
label_params['YVALUE'] = highest;
label_params['R'] = 0; -- DOUBLE Красная компонента цвета в формате RGB. Число в интервале [0;255]
label_params['G'] = 0; -- DOUBLE Зеленая компонента цвета в формате RGB. Число в интервале [0;255]
label_params['B'] = 255; -- DOUBLE Синяя компонента цвета в формате RGB. Число в интервале [0;255]
label_params['TRANSPARENCY'] = 0;
label_params['TRANSPARENT_BACKGROUND'] = 1;
label_params['FONT_FACE_NAME'] = 'Verdana';
label_params['FONT_HEIGHT'] = 12;
local label_id = AddLabel(tag, label_params) -- number
if label_id ~= nil then
Labels[#Labels+1] = label_id
PrintDbgStr('Create Label_id: '..tostring(label_id))
else
message('Don\'t create label')
end
return label_id -- number
end
---- удаление меток
-- ищем и удаляем все метки независимо как и кем установленные :)
-- функцию используем полной очистки от меток и контроля
function LbDelete_2()
-- local _labels = {}
local n = 0;
for _label_id = 1, 10000, 1 do
local L = GetLabelParams(chart_tag, _label_id )--table or nil Табл параметров метки - все значения имеют тип – STRING
if L ~= nil then
local del = DelLabel(chart_tag, _label_id)
if del == true then
n = n + 1;
PrintDbgStr('LbDelete_2() Label number '..tostring(_label_id).. ' deleted ')
end;
end
end
Label_ID = nil;
Labels = {}
return n;
end
-- Удаляет ранее установленные метки сохраненые в массиве
function LbDelete()
local n = 0
if #Labels > 0 then
for j=1,#Labels,1 do
if DelLabel(chart_tag, tonumber(Labels[j])) then
n = n + 1
PrintDbgStr("LbDelete() id "..tostring(Labels[j]) .. ' deleted')
end
end;
Labels = {};
Label_ID = nil;
end;
return n;
end;
К сожалению, сориентировать по срокам не можем. Как только версия Рабочего места QUIK с исправлением будет выпущена - Вас сразу же проинформируют об этом (уведомление поступит на электронный адрес, указанный в письме, по которому был заведен тикет). Приносим свои извинения за доставленные неудобства и задержку.
Пользователь
Сообщений: Регистрация: 15.12.2025
15.12.2025 17:47:11
Уважаемая поддержка QUIK, проблема, указанная в этом топике (отсутствие срабатывания OnDestroy() в индикаторах при смене инструмента) действительно не всегда решается контролем индекса свечи. Некоторые операция нужно выполнять именно по OnDestroy(). Например - закрывать файлы, которые открывает скрипт индикатора в процессе работы (так как объекты/таблицы/указатели/хэндлы доступны только в том экземпляре скрипта, который их открыл, не говоря уже о том, что закрывать из другого экземпляра - это мега-костыль само по себе). Иногда файлы перестают открываться вообще, если много раз менять инструменты. Помогает только перезапуск QUIK. Пожалуйста, реализуйте вызов OnDestroy(), чтобы можно было хотя бы очистить память и закрыть файлы, чтобы не копить в приложении выделенную память и хэнлы открытых файлов. Это не просто хотелка, это ошибка приложения QUIK.
QUIK clients support
Сообщений: Регистрация: 11.08.2025
16.12.2025 09:26:51
Добрый день.
Не могли бы вы, пожалуйста, прислать на нашу почту часть или весь скрипт, в котором представлена описываемая процедура, а также описание и/или скриншоты с ошибкой возникающей в процессе?
Пользователь
Сообщений: Регистрация: 27.01.2017
16.12.2025 10:12:11
Вы уже определитесь ошибка ли это или нет. А то здесь уже обещали исправить
Пользователь
Сообщений: Регистрация: 30.01.2015
16.12.2025 12:43:52
Информация к размышлению Взял скрипт который приведен здесь тест показал, что проблемы нет:
т е при смене инструмента старые матки удаляются. Что не так?
Не могли бы вы, пожалуйста, прислать на нашу почту часть или весь скрипт, в котором представлена описываемая процедура, а также описание и/или скриншоты с ошибкой возникающей в процессе?
На почту присылать так себе идея, скрипт многомодульный, индикаторы увязаны с торговыми скриптами и имеют общие библиотеки/компоненты. Там суть проблемы просто потеряется.
Подготовил простейший пример, когда требуется сохранить лог и от предыдущего исполняемого экземпляра скрипта индикатора и новому экземпляру тоже дать возможность вести лог. Для этого достаточно (казалось бы) переименовать старый лог-файл при открытии нового. НО!!! для этого, как минимум, нужно старый лог закрыть. И вот это как раз недоступно. Закрывать же каждый раз при записи очередной строки в лог (и соответственно открывать тоже) - совсем не вариант, так как логи бывают огромные и время выполнения логирования часто критично.
Код
Settings = {
Name = 'Test',
line = {
{
Name = 'Test',
Color = RGB(255, 255, 0),
Type = TYPE_LINE,
Width = 1
}
}
}
oldLogFilePath = 'test_.log'
logFilePath = 'test.log'
logFile = nil
function Init()
return #(Settings.line)
end
function OnDestroy()
io.close(logFile)
end
function OnCalculate(ci)
if logFile == nil then
if FileExists(logFilePath) then
if FileExists(oldLogFilePath) then
os.remove(oldLogFilePath)
end
local renRes, renErr = os.rename(logFilePath, oldLogFilePath)
if not renRes then
message(renErr)
end
end
logFile = io.open(logFilePath, 'w')
end
Log(tostring(ci))
return ci
end
function Log(txt)
if logFile ~= nil then
logFile:write(txt, '\n')
end
end
function FileExists(path)
local f = io.open(path, "r")
if f ~= nil then
io.close(f)
return true
else
return false
end
end
В данном примере, если бы OnDestroy() срабатывал, пусть даже не сразу (не проблема сделать пачку логов, выдавая имена по кругу) то все файлы в итоге были бы закрыты, а так, по факту, OnDestroy() похоже срабатывает только при закрытии приложения, возможно при закрытии окна графика (давно разбирался, уже не помню, когда срабатывает, зато ясно что при смене инструмента НЕ срабатывает). Проблема резко усугубляется, если индикатор не один, инструментов десятки, и графиков с индикаторами тоже далеко не один. И логирование - только одна из задач, когда нужно держать файл открытым пока выполняется скрипт.
Пользователь
Сообщений: Регистрация: 30.01.2015
16.12.2025 15:26:21
Судя по документации функция OnDestroy() не предназначена для решения указанной задачи.
Код
Функция вызывается при удалении индикатора с графика, либо при закрытии окна
диаграммы и не является обязательной для индикатора.
Формат вызова:
OnDestroy ()
В данном случае индикатор не удаляется и окно не закрывается. А при смене инструмента в окне это функция и не должна работать. --------------------- Задача смены лог файла при смене инструмента решается другим способом.
Ваше обращение получено, проблема изучается. Постараемся в ближайшее время дать ответ.
Пользователь
Сообщений: Регистрация: 15.12.2025
17.12.2025 14:54:44
Цитата
nikolz написал: Задача смены лог файла при смене инструмента решается другим способом.
Конечно решается. И решена (вариант решения есть и в тексте предыдущего сообщения). Это просто пример/иллюстрация невозможности освободить ресурсы, когда индикатор фактически больше не работает, "ожидая" закрытия окна.
Пользователь
Сообщений: Регистрация: 30.11.2021
19.12.2025 07:53:06
Цитата
nikolz написал: т е при смене инструмента старые матки удаляются.Что не так?
Приветствую коллега 1. При наложении при первом проходе индикатора терминал ни видит идентификатора объявленного в Settings, и увидит его только на втором проходе и соответственно если есть в коде проверка на идентификатор то выдаст аларм на отсутствие его 2. При переключении инструмента в окне с индикатором метка предыдущего инструмента теряется и создается новая для текущего инструмента Старую метку в коде скрипта уже не найти и не удалить из кода скрипта, но это же не решение проблемы В результате сколько переключений по смене инструмента столько и меток оказывается в окне графика. В и код тестового скрипта и скрин есть Ну при удалении скрипта с графика да метки будут удалены Вопрос- как при смене инструмента удалить все следы от предыдущего?
Теперь к стандартной фразе техподдержки "проблема изучается" Ну парни у вас вобще чувство собственного достоинства есть? ответственность за продукт вашей работы, за авторитет компании? Мне лично дважды обещал и здесь на форуме и в переписке по email.
Цитата
Вот от 12.09.2024 "Да, мы приносим Вам благодарность за развернутое описание проблемы. Однако, что является для Вас неожиданным в нашем ответе? Проблемы, которые Вы обозначили признаны таковыми и будут исправлены в следующих версиях рабочего места QUIK"
Как мы отмечали в наших прошлых сообщениях, данная проблема зафиксирована и действительно изучается разработчиками в данный момент. К сожалению, более точной информации мы предоставить пока не можем.
Пользователь
Сообщений: Регистрация: 15.12.2025
20.12.2025 14:01:00
Цитата
Oleg Kuzembaev написал: Старую метку в коде скрипта уже не найти и не удалить из кода скрипта, но это же не решение проблемы
По моему скромному опыту DelAllLabels(ID) отрабатывает корректно и удаляет все метки (включая добавленные для предыдущего инструмента), если идентификатор графика задан в настройках графика (редактирование графика -> вкладка "дополнительно") и при вызове DelAllLabels указывается такой же идентификатор. Тогда снимаются и сложности с получением идентификатора графика, но появляется необходимость для каждого графика с таким индикатором клонировать индикатор (но не обязательно же весь клонировать, только некую индивидуальную часть и подгружать потом общую), чтобы каждый работал со своим графиком и идентификатором.
Пользователь
Сообщений: Регистрация: 30.11.2021
21.12.2025 08:49:34
Цитата
Бес Паники написал: По моему скромному опыту DelAllLabels(ID) отрабатывает корректно и удаляет все метки (включая добавленные для предыдущего инструмента)
Нет не удаляет Например включение в OnCalculate(index) строки if index == 1 then DelAllLabels(chart_tag) end не удаляет Метки удаляются только при удалении самого индикатора с графика
Как мы отмечали в наших прошлых сообщениях, данная проблема зафиксирована и действительно изучается разработчиками в данный момент. К сожалению, более точной информации мы предоставить пока не можем.
В самом темы я привел ссылки на предыдущие вопросы пользователей еще с 2017 года ! а Вы говорите изучается Я понимаю что одна из ваших главных задач это обеспечение безукоризненного функционала финансовых и учетных задач, но это не исключает работу по другому функционалу
Пользователь
Сообщений: Регистрация: 15.12.2025
21.12.2025 09:21:02
Цитата
Сергей написал: Например включение в OnCalculate(index) строки if index == 1 then DelAllLabels(chart_tag) end не удаляет
chart_tag точно не меняется? Вот пример как реализовано у меня и метки уверенно удаляются, долго морочился, пока не зафиксировал "chart_tag" в виде константы в коде.
Код
function Init()
if FirstInit then
FirstInit = false
Logger:Init(true)
GraphLabels:Init(GRAPHID_TRADERTARGETS)
else
Log('Повторная инициализация')
end
return #(Settings.line)
end
function OnCalculate(ci)
-- Настраиваемся
if ci == 1 then
local dsi = getDataSourceInfo()
Board = dsi.class_code
Ticker = dsi.sec_code
GraphLabels:Clear()
LabelSB = GraphLabels:Add()
LabelSS = GraphLabels:Add()
LabelBB = GraphLabels:Add()
LabelBS = GraphLabels:Add()
LinesHist = {}
InitialCalcCandlesCnt = getNumCandles(GRAPHID_TRADERTARGETS)
WhenUpdated = 0
end
-- Дальше собственно генерация данных...
end
-- Удаление всех меток
function GraphLabels:Clear()
self.Labels = {}
DelAllLabels(self.GraphId)
end
В методе GraphLabels:Clear() поле self.GraphId объекта GraphLabels содержит значение, переданное в Init() вызовом GraphLabels:Init(GRAPHID_TRADERTARGETS). Старые метки удаляются и создаются новые как раз по ci == 1
И на всякий случай - использую сборку Quik от брокера ВТБ, мало-ли, теоретически и в зависимости от сборок могут быть расхождения в работе терминалов.
Пользователь
Сообщений: Регистрация: 30.11.2021
22.12.2025 20:43:10
Цитата
Бес Паники написал: В методе GraphLabels:Clear() поле self.GraphId объекта GraphLabels содержит значение, переданное в Init() вызовом GraphLabels:Init(GRAPHID_TRADERTARGETS). Старые метки удаляются и создаются новые как раз по ci == 1
Спасибо, красивый вариант Но честно говоря не совсем уловил :) GRAPHID_TRADERTARGETS - это метка из Settings ? LabelSB, ..., LabelBS - это что?
Пользователь
Сообщений: Регистрация: 30.11.2021
22.12.2025 22:10:44
Цитата
Бес Паники написал: -- Удаление всех меток function GraphLabels:Clear() self.Labels = {} DelAllLabels(self.GraphId) end
Не разобрался :) Выдает ошибку
Код
test_Example2.lua:190: attempt to index a nil value (global 'GraphLabels')
Пользователь
Сообщений: Регистрация: 30.11.2021
22.12.2025 22:46:23
Цитата
Бес Паники написал: Вот пример как реализовано у меня и метки уверенно удаляются, долго морочился, пока не зафиксировал "chart_tag" в виде константы в коде
Не совсем удобно но я попрошу - допишите необходимые фрагменты кода прямо в Тогда разберусь неприменно Здорово выручите !
Пользователь
Сообщений: Регистрация: 30.01.2015
23.12.2025 05:45:29
, Я взял Ваш скрипт вот этот:
Код
Settings=
{
Name = "*test_Example-2",
chart_ID ="Metka" -- идентификатор графика
}
n_init = 0 -- переменная для подсчета заходов в функцию Init
n_OnCalcCount = 0 -- переменная для подсчета OnCalculate
n_OnChange = 0 -- счетчик OnChangeSettings
bars_prev = 0 -- номер посл бара
TF_prev = -4; -- таймфрейм
delay = 1.0; -- задержка в цикле рассчетов параметров для метки (параметров счета)
LastSecond = 0; -- время последнеих рассчетов
chart_tag = ''; -- идентификатор графика
label_params = {}; -- параметры метки
Label_ID = nil; -- идентификатор графика
Labels = {}; -- Массив номеров установленных меток - для контроля
function Init()
n_init = n_init + 1
PrintDbgStr("Init() = "..n_init)
n_OnCalcCount = 0
n_OnChange = 0
bar_prev = 0
TF_prev = -4 -- контроль смены таймфрейма
sec_code_prev = '' -- контроль перекл на др актив
return 1
end
function OnChangeSettings()
-- n_OnCalcCount = 0
n_OnChange = n_OnChange + 1
chart_tag = Settings.chart_ID
PrintDbgStr("OnChangeSettings() = "..n_OnChange..', n_OnCalcCount = '..n_OnCalcCount)
end
function OnDestroy()
PrintDbgStr("OnDestroy() #Labels Array "..#Labels);
if #Labels > 0 then -- Удаляет ранее установленные метки
for i=1,#Labels,1 do
local del = DelLabel(chart_tag, Labels[i]);
if del then PrintDbgStr("OnDestroy() Del id "..tostring(Labels[i])); end;
end;
end;
-- стараемся найти и удалить потерянные метки
PrintDbgStr("OnDestroy() LbDelete_2() "..LbDelete_2() .. ' labels were removed');
end
function OnCalculate(index)
-- Пропускаем первый проход между Init() и OnChangeSettings() при первичном добавлении индикатора
-- для исключения ошибки чтения параметра при первом запуске индикатора
-- Init() > OnCalculate() > OnChangeSettings() > OnCalculate() 2 > OnCalculate() 3
if n_OnChange == 0 then
while index <= Size() do
if index == Size() then PrintDbgStr('Пропускаем цикл до вызова OnChangeSettings() после Init(), index = ' .. index); end;
return
end;
end
-- предпоследний бар
if index == Size() - 1 then
PrintDbgStr('---- Begin Cycle Number '..(n_OnCalcCount+1))
DSInfo = getDataSourceInfo();
Sec_Code = DSInfo.sec_code;
if chart_tag == '' then message('OnCalculate Chart ID не указан!!!'); end;
PrintDbgStr("Seccode " .. Sec_Code ..", chart_ID = " .. chart_tag)
else
-- последняя свеча
if index == Size() then
-- раз в секунду (или больше)
if os.time() > LastSecond then -- if os.time() > LastSecond + 2*delay-1 then
LastSecond = os.time();
-- потиковое обновление (с задержкой)
label_params['TEXT'] = tostring(C(index))
label_params['HINT'] = tostring(index)
-- корректируем параметры метки
if Label_ID ~= nil and TF_prev == DSInfo.interval then
-- сохраняем прежнее положение метки если переместили в теч текущ бара)
-- при смене таймфрейма метка будет удалена и создана заново
local ly = GetLabelParams(chart_tag, Label_ID) --table or nil
if ly ~= nil then -- and ly.yvalue ~= nil
label_params['YVALUE'] = ly.yvalue
label_params['DATE'] = ly.date
label_params['TIME'] = ly.time
end;
--PrintDbgStr('index == Size() = ' .. Size() .. ', Label_ID ' .. Label_ID .. ', C(index) ' .. tostring(C(index)) )
SetLabelParams(chart_tag, Label_ID, label_params);
-- PrintDbgStr('SetLabelParams Label_ID = '..Label_ID)
end
-- первый тик нового бара или первый проход и метка еще не создавалась или смена таймфрейма
-- удаляем предыдущую и создаем новую метку с новыми координатами (метку можно смещать в удобное место...)
if bars_prev < Size() or Label_ID == nil or TF_prev ~= DSInfo.interval then
bars_prev = Size()
-- удаляем метку если она уже была и создаем новую
if Label_ID ~= nil then LbDelete() end;
Label_ID = labeldraw(chart_tag, index)
TF_prev = DSInfo.interval
end;
end -- every second
-- завершение цикла
n_OnCalcCount = n_OnCalcCount+1
--PrintDbgStr('END Cycle index == Size() ' .. Size()..', n_OnCalcCount = '..n_OnCalcCount)
end -- index == Size()
end -- if index > 1
return nil
end
function labeldraw(tag, index)
local highest = 0.0
for j = 0, 14 do
local m=index-j;
if m>0 and H(m) > highest then highest = H(m) end;
end
local _t = T(index-3)
--PrintDbgStr(_t.year.._t.month.._t.day..' '.._t.hour..':'.._t.min..':'.._t.sec)
local Y = tostring(_t.year)
local m = tostring(_t.month) if #m == 1 then m = "0"..m end;
local d = tostring(_t.day) if #d == 1 then d = "0"..d end;
local hh = tostring(_t.hour) if #hh == 1 then hh = "0"..hh end;
local mm = tostring(_t.min) if #mm == 1 then mm = "0"..mm end;
local ss = '00'; -- tostring(_t.sec) if #ss == 1 then ss = "0"..ss end;
label_params['DATE'] = tonumber(Y..m..d);
label_params['TIME'] = tonumber(hh..mm..ss);
label_params['YVALUE'] = highest;
label_params['R'] = 0; -- DOUBLE Красная компонента цвета в формате RGB. Число в интервале [0;255]
label_params['G'] = 0; -- DOUBLE Зеленая компонента цвета в формате RGB. Число в интервале [0;255]
label_params['B'] = 255; -- DOUBLE Синяя компонента цвета в формате RGB. Число в интервале [0;255]
label_params['TRANSPARENCY'] = 0;
label_params['TRANSPARENT_BACKGROUND'] = 1;
label_params['FONT_FACE_NAME'] = 'Verdana';
label_params['FONT_HEIGHT'] = 12;
local label_id = AddLabel(tag, label_params) -- number
if label_id ~= nil then
Labels[#Labels+1] = label_id
PrintDbgStr('Create Label_id: '..tostring(label_id))
else
message('Don\'t create label')
end
return label_id -- number
end
---- удаление меток
-- ищем и удаляем все метки независимо как и кем установленные :)
-- функцию используем полной очистки от меток и контроля
function LbDelete_2()
-- local _labels = {}
local n = 0;
for _label_id = 1, 10000, 1 do
local L = GetLabelParams(chart_tag, _label_id )--table or nil Табл параметров метки - все значения имеют тип – STRING
if L ~= nil then
local del = DelLabel(chart_tag, _label_id)
if del == true then
n = n + 1;
PrintDbgStr('LbDelete_2() Label number '..tostring(_label_id).. ' deleted ')
end;
end
end
Label_ID = nil;
Labels = {}
return n;
end
-- Удаляет ранее установленные метки сохраненые в массиве
function LbDelete()
local n = 0
if #Labels > 0 then
for j=1,#Labels,1 do
if DelLabel(chart_tag, tonumber(Labels[j])) then
n = n + 1
PrintDbgStr("LbDelete() id "..tostring(Labels[j]) .. ' deleted')
end
end;
Labels = {};
Label_ID = nil;
end;
return n;
end;
и запустил его на демо сервере. Мне не удалось получить наложение меток. Можете объяснить, что надо сделать, чтобы получить наложение? вот картинки при переходе на различные инструменты. Метка всегда одна и соответствует инструменту
Пользователь
Сообщений: Регистрация: 30.01.2015
23.12.2025 06:52:47
Пользователь
Сообщений: Регистрация: 30.01.2015
23.12.2025 08:25:45
, Написал для вас ПРАВИЛЬНЫЙ индикатор подобно Вашему, если правильно понял алгоритм. ------------------------------ Он работает нормально и при смене инструментов и при смене интервалов и ВООБЩЕ ВСЕГДА . ------------------- Мой индикатор делает следующее: -------------------------- Через секунду он удаляет ранее выставленную метку и выводит новую. ---------------------------- Если это не то, то скажите что не так, я исправлю.
Код
Settings={ Name = "*nk_test", tag ="Metka" }
params={R = 255, G = 255, B = 255, TRANSPARENCY = 0,FONT_HEIGHT = 10,TRANSPARENT_BACKGROUND = 1, FONT_FACE_NAME = 'Verdana' }
function OnCalculate(i)
if i==1 then
OnChangeSettings()
elseif i== Size() then -- последняя свеча
if os.time() > LastSecond then -- раз в секунду (или больше)
LastSecond = os.time(); -- потиковое обновление (с задержкой)
if Label then DelLabel(Settings.tag,Label) end
params.TEXT =tostring(C(i))
params.HINT = tostring(i)
local h=0 local m=i;
while 14>i-m do if H(m) > h then h=H(m) end;m=m-1; end
local Ti=T(i-3)
params.DATE=100*(100*Ti.year+Ti.month)+Ti.day;
params.TIME=100*(100*Ti.hour+Ti.min)+Ti.sec;
params.YVALUE =h;
Label= AddLabel(Settings.tag, params)
end -- every second
end
end
function OnChangeSettings()
sec = getDataSourceInfo().sec_code; DelAllLabels(Settings.tag);
LastSecond=0 end
function OnDestroy() OnChangeSettings() end
function Init() return 1 end
написал: В методе GraphLabels:Clear() поле self.GraphId объекта GraphLabels содержит значение, переданное в Init() вызовом GraphLabels:Init(GRAPHID_TRADERTARGETS). Старые метки удаляются и создаются новые как раз по ci == 1
Спасибо, красивый вариант Но честно говоря не совсем уловил :) GRAPHID_TRADERTARGETS - это метка из Settings ? LabelSB, ..., LabelBS - это что?
Это не полный скрипт. Только часть. Он выводит метками цели купли и продажи задач торгового робота. Весь скрипт состоит из различных файлов, в одном из файлов задано и значение GRAPHID_TRADERTARGETS. Просто в виде константы (констант настоящих в Lua нет, но таковой в общем то можно считать любое неизменяемое по коду значение). Т.е. в файле констант скрипта прописано GRAPHID_TRADERTARGETS = 'AnyStringYouWantToBeGraphIdentifier' (ну почти, значение, которое тут присваивается может быть любым). Главное, чтобы точно такое же значение было указано в настройках (НЕ в Settings!!! Задавать в Settings - это альтернатива, опять же почти, варианту задавать константой). Настройки имеются ввиду другие, а именно диалоговое окно редактирования графика Quik, набора индикаторов на графике, там у каждого графика есть вкладка "Дополнительно" и вот там есть поле "Идентификатор". Именно его нужно прописать.
[img]data:image/png;base64, *[/img]
Причем прописать в настройках именно у того индикатора, который соответствует скрипту (настройки, включая идентификаторы у каждого индикатора свои).
Цитата
Сергей написал: test_Example2.lua:190: attempt to index a nil value (global 'GraphLabels')
Туту GraphLabels - это таблица/объект. Его тоже нужно создавать/объявлять/декларировать, чтобы он стал доступен. Так удобно для громоздких скриптов упорядочивать функционал и данные и потом использовать это в виде библиотек в различных скриптах. Это просто вариант реализации.
Пользователь
Сообщений: Регистрация: 30.11.2021
23.12.2025 10:10:49
Спасибо большое и Попробую все на выходных и отпишусь
Пользователь
Сообщений: Регистрация: 30.11.2021
23.12.2025 10:34:11
Цитата
nikolz написал: Мне не удалось получить наложение меток.
Попробовал Ваш скрипт в том виде как есть без каких либо изменений К сожалению метки дублируются Чтобы увидеть это переключайтесь между двумя активами туда и обратно Как только первый раз вернетесь на изначальный сместите метку и увидите что их две, потом еще раз на другой и обратно на исходный и будет уже три и тд