TGB написал: Может быть протестировать измененный вариант:
Вот убрал запрос getInfoParam, запустил. Всего 86 колбеков. Все они в 1 мкс. Но main выполнялся 1.37 сек. Как-то не верится, что банальный разбор такой небольшой очереди будет столько, хоть и через очень неэффективную table.sremove. Возможна и она проблема. Позже сделаю тест с другой двойной очередью без table.sremove.
где chng_params - это таблица с изменившимися ключами и их значениями (и еще лучше добавить время пакета в котором это приехало). А подписка на такой колбек должна быть адресной - указать инструмент и набор полей для отслеживания.
В текущей реализации приходится DDOS-ить API чтобы получить данные. Сделки могут быть раз в час, цена посл. сделки может не изменятся, а будет постоянно менять лучший спрос, предложение (есть такие инструменты где роботы постоянно меняют стакан) - постоянный холостой вызов колбека.
Впрочем, этот тест просто для нагрузки, так что можно и запрашивать данные. Тот же LASTRECORDTIME важен, т.к. иногда приходят опоздавшие данные и их лучше пропускать.
Цитата
TGB написал: А если отключить запись в пользовательскую таблицу?
Тогда это будет скрипт без таблицы, по факту. Хотелось бы решить вопрос при наличии таблицы. Я специально добавил постоянный опрос IsWindowClosed.
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 11:16:43
Цитата
TGB написал: А если запустить только ваш тест без остальных ваших скритов?
Выключил все скрипты, только этот. Лог в PrintDbgStr. Вот участок с странным временем. Для начала, есть OnParam с 244 млс. Общее время 1.193 сек для 55 отфильтрованных колбеков.
И вот такие моменты проявляются. Естественно, чем больше скриптов запущено, тем, возможно, больше влияние.
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 11:00:36
Цитата
Старатель написал: Вы так ничего не измерите. Обработка кода внутри колбэка идёт на ура. Оверхэд возникает из-за самого вызова колбэка, даже пустого. Смотрите мой пример
Возможно, но моя задача понять что вызывает проблемы. Пока я натолкнулся на влияние на работу main при странном сочетание наличия таблицы и обработки её реакций, библиотек и колбеков. Сами колбеки, судя по всему, работают быстро.
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 10:46:15
Правда здесь уже, видимо, проявилась нагрузка на запись лога внутри колбеков, т.к. запись в файл внутри колбека - это очень плохая идея. Поэтому лучше заменить логирование на PrintDbgStr.
Код
local function log(...)
PrintDbgStr(log_tostring(...))
end
В первой версии теста лога не было. Но, с другой стороны, это тоже такой тест, явно показывающий как main просто перестает вызываться.
Цитата
TGB написал: В моих скриптах эти колбэки не отрабатываются.
Тоже, особенно OnQuote. Но бывают задачи где это необходимо и я стал замечать проблемы. Пришлось смотреть, что происходит.
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 10:24:31
Еще раз тест, что запустил на VTB
Код
local path = _G.getScriptPath()
package.path = path.."/?.lua;"
local bl1 = require('big_lib')
local bl2 = require('big_lib2')
local bl3 = require('big_lib3')
local bl4 = require('big_lib4')
local logFile
local function log_tostring(...)
local n = select('#', ...)
if n == 1 then
return tostring(select(1, ...))
end
local t = {}
for i = 1, n do
t[#t + 1] = tostring((select(i, ...)))
end
return table.concat(t, " ")
end
local function log(...)
if logFile==nil then return end
logFile:write(log_tostring(...).."\n");
logFile:flush();
end
local sec_list = {
{'SPBFUT', 'MMU6'},
{'SPBFUT', 'SiU6'},
{'SPBFUT', 'RIU6'},
{'SPBFUT', 'USDRUBF'},
{'SPBFUT', 'CNYRUBF'},
{'TQBR', 'SBER'},
{'TQBR', 'GAZP'}
}
local rev_lis = {}
for i = 1, #sec_list do
rev_lis[sec_list[i][2]] = i
end
local sleep = _G.sleep
local isRun = true
local t_id = nil
-- local SeaGreen = 12713921 -- RGB(193, 255, 193) нежно-зеленый
-- local RosyBrown = 12698111 -- RGB(255, 193, 193) нежно-розовый
-- local GetCell = _G.GetCell
-- local Highlight = _G.Highlight
local SetCell = _G.SetCell
local getParamEx = _G.getParamEx
local getInfoParam = _G.getInfoParam
local getQuoteLevel2 = _G.getQuoteLevel2
local IsWindowClosed = _G.IsWindowClosed
local message = _G.message
local table_sremove = _G.table.sremove
local queue = {}
local get_dom = {}
function _G.OnParam(class_code, sec_code)
local s = os.clock()
if t_id and rev_lis[sec_code] then
log('Enter OnParam', class_code, sec_code)
local last_price = tonumber((getParamEx(class_code, sec_code, 'LAST') or {}).param_value) or nil
local res = {}
res.last_time = tonumber((getParamEx(class_code, sec_code, 'TIME') or {}).param_value) or 0
if res.last_time == 0 then return end
res.last_price = last_price
if not res.last_price then return end
local bid_depth = tonumber((getParamEx(class_code, sec_code, 'BIDDEPTHT') or {}).param_value) or 0
res.best_bid = bid_depth > 0 and tonumber((getParamEx(class_code, sec_code, 'BID') or {}).param_value) or nil
local ask_depth = tonumber((getParamEx(class_code, sec_code, 'OFFERDEPTHT') or {}).param_value) or 0
res.best_ask = ask_depth > 0 and tonumber((getParamEx(class_code, sec_code, 'OFFER') or {}).param_value) or nil
res.last_rec = tonumber((tostring(getInfoParam('LASTRECORDTIME') or ''):gsub(':', ''))) or 0
res.local_time = (tonumber((tostring(getInfoParam('LOCALTIME') or ''):gsub(':', ''))) or 0)
res.time_diff = math.abs(res.local_time - res.last_rec) + 10
queue[#queue+1] = {class_code, sec_code, last_price}
log('Exit OnParam', os.clock() - s, class_code, sec_code, res.last_rec, last_price)
log('---------------------------------------------------------------------------')
end
end
function _G.OnQuote(class_code, sec_code)
local s = os.clock()
if t_id and rev_lis[sec_code] and not get_dom[sec_code] then
log('Enter OnQuote', class_code, sec_code)
get_dom[sec_code] = {class_code, sec_code}
log('Exit OnQuote', os.clock() - s, class_code, sec_code)
log('---------------------------------------------------------------------------')
end
end
local function CreateTable()
t_id = _G.AllocTable()
_G.AddColumn(t_id, 0, "sec", true, _G.QTABLE_STRING_TYPE, 15)
_G.AddColumn(t_id, 1, "price", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.AddColumn(t_id, 2, "bid", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.AddColumn(t_id, 3, "ask", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.AddColumn(t_id, 4, "edit", true, _G.QTABLE_DOUBLE_TYPE, 15)
_G.CreateWindow(t_id)
_G.SetWindowPos(t_id, 90, 120, 470, 300)
for i = 1, #sec_list do
_G.InsertRow(t_id, i)
SetCell(t_id, i, 0, sec_list[i][2])
end
end
local function event_callback(_, msg, par1, par2)
log('Enter TableNotificationCallback')
if (msg == _G.QTABLE_CLOSE) then
isRun = false
end
if msg == _G.QTABLE_CHAR then
SetCell(t_id, par1, 4, tostring(par2), par2)
end
log('Exit TableNotificationCallback')
log('---------------------------------------------------------------------------')
end
local function process_queue()
if #queue == 0 then return end
while #queue > 0 do
local last_price = queue[1][3]
-- local lp = GetCell(t_id, 1, 0).value or last_price
-- if lp < last_price then
-- Highlight(t_id, 1, 0, SeaGreen, 0, 500)
-- elseif lp > last_price then
-- Highlight(t_id, 1, 0, RosyBrown, 0, 500)
-- end
SetCell(t_id, rev_lis[queue[1][2]], 1, tostring(last_price), last_price)
table_sremove(queue, 1)
end
end
local function GetQuotes(class_code, sec_code)
local q = getQuoteLevel2(class_code, sec_code)
if not q then return end
local qbid = q["bid"]
local qask = q["offer"]
if not qbid and not qask then
return
end
local best_bid = tonumber(qbid[#qbid]["price"])
local best_ask = tonumber(qask[1]["price"])
SetCell(t_id, rev_lis[sec_code], 2, tostring(best_bid), best_bid)
SetCell(t_id, rev_lis[sec_code], 3, tostring(best_ask), best_ask)
get_dom[sec_code] = nil
end
function _G.OnInit()
CreateTable()
_G.SetTableNotificationCallback(t_id, event_callback)
end
function _G.main()
for i = 1, #sec_list do
if not _G.Subscribe_Level_II_Quotes(sec_list[i][1], sec_list[i][2]) then
return message('Не удалось подписаться на получение данных стакана по инструменту '..tostring(sec_list[i][1])..'|'..tostring(sec_list[i][2]))
end
end
logFile = io.open(path.."\\load_test.txt", "w")
local lt = os.time()
while isRun do
local s = os.clock()
log('Enter main loop', os.date())
IsWindowClosed(t_id)
local ct = os.time()
local u
-- if ct-lt >= 2 then
-- for i = 1, #sec_list do
-- queue[#queue+1] = {sec_list[i][1], sec_list[i][2], tonumber(getParamEx(sec_list[i][1], sec_list[i][2], 'LAST').param_value)}
-- get_dom[sec_list[i][2]] = {sec_list[i][1], sec_list[i][2]}
-- u = true
-- end
-- lt = ct
-- end
process_queue()
for i = 1, #sec_list do
if get_dom[sec_list[i][2]] then
GetQuotes(sec_list[i][1], sec_list[i][2])
end
end
bl1.func1()
bl2.func100()
bl3.func200()
bl4.func300()
log('Exit main loop', os.clock()-s, u and 'update' or '')
log('---------------------------------------------------------------------------')
sleep(60)
end
end
function _G.OnStop()
isRun = false
if t_id and not IsWindowClosed(t_id) then
_G.DestroyTable(t_id)
end
end
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
22.07.2026 10:21:11
Ок. Запустил на VTB в 10 утра. И это просто финиш. В основном была обработка main за 0-0.5 секунды.
А вот этот кусок за 593.92. Записал даже видео работы, где видно что происходит если запустить скрипт. Редактирование поля в таблице обновляется только после пинка. Цена не соответствует данным в ТТТ. Как только скрипт остановлен, то ТТТ сразу оживает.
Видео
Enter main loop Wed Jul 22 10:01:25 2026 Enter OnParam SPBFUT USDRUBF Exit OnParam 0.0 SPBFUT USDRUBF 100102 78.52 --------------------------------------------------------------------------- Enter OnParam SPBFUT CNYRUBF Exit OnParam 0.0 SPBFUT CNYRUBF 100102 11.587 --------------------------------------------------------------------------- Enter OnParam SPBFUT SiU6 Exit OnParam 0.0 SPBFUT SiU6 100102 79532.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 100102 2095.95 ---------------------------------------------------------------------------
Думаю, что всё зависит от времени, потока данных, да и брокера. На ВТБ позже проверю. Я проверяю на одном небольшом брокере, у которого сервера не особо "широкие".
Вот в 9 утра на всплеске ликвидности запустил. Плюс, что важно, запущены и другие скрипты, но можно и этот дублировать и запускать, в реальности у нас же не один скрипт работает.
Вот участок , всего 125 млс, 9 колбеков
Enter main loop Wed Jul 22 09:03:18 2026 Enter OnQuote SPBFUT RIU6 Exit OnQuote 0.0 SPBFUT RIU6 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90318 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0 SPBFUT RIU6 90318 82730.0 --------------------------------------------------------------------------- Enter OnQuote SPBFUT MMU6 Exit OnQuote 0.0 SPBFUT MMU6 --------------------------------------------------------------------------- Enter OnParam TQBR SBER Exit OnParam 0.0 TQBR SBER 90318 260.58 --------------------------------------------------------------------------- Enter OnParam TQBR GAZP Exit OnParam 0.0 TQBR GAZP 90318 85.22 --------------------------------------------------------------------------- Enter OnQuote TQBR GAZP Exit OnQuote 0.0 TQBR GAZP --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90318 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0010000000002037 SPBFUT RIU6 90318 82740.0 --------------------------------------------------------------------------- Exit main loop 0.125
А вот участок уже 1.36 секунды, но уже 222 колбека.
Enter main loop Wed Jul 22 09:03:19 2026
Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90319 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT USDRUBF Exit OnParam 0.0 SPBFUT USDRUBF 90319 78.6 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90319 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT SiU6 Exit OnParam 0.00099999999974898 SPBFUT SiU6 90319 79637.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0 SPBFUT RIU6 90319 82730.0 --------------------------------------------------------------------------- Enter OnQuote SPBFUT SiU6 Exit OnQuote 0.0 SPBFUT SiU6 --------------------------------------------------------------------------- Enter OnQuote SPBFUT USDRUBF Exit OnQuote 0.0 SPBFUT USDRUBF --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90319 2089.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT RIU6 Exit OnParam 0.0 SPBFUT RIU6 90319 82730.0 --------------------------------------------------------------------------- ............................................................................................................... чтобы не делать сообщение очень длинным убрал часть ............................................................................................................... --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90320 2088.85 --------------------------------------------------------------------------- Enter OnParam SPBFUT SiU6 Exit OnParam 0.0 SPBFUT SiU6 90320 79634.0 --------------------------------------------------------------------------- Enter OnParam SPBFUT USDRUBF Exit OnParam 0.0 SPBFUT USDRUBF 90320 78.6 --------------------------------------------------------------------------- Enter OnParam SPBFUT MMU6 Exit OnParam 0.0 SPBFUT MMU6 90320 2088.85 --------------------------------------------------------------------------- Exit main loop 1.3679999999999
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
21.07.2026 16:24:42
Цитата
nikolz написал: можете сделать вывод времени входа и выхода для колбеков и для main c помощью точного счетчик Windows?
Если без излишеств, просто через os.clock, то сами колбеки работаю микросекунды. main уже зависит о того, что делает, но там тоже не что-то существенное.
Я пока наблюдаю странное - подключаешь несколько библиотек на 1000 функций - и привет. Собственно это в тесте и сделано, чтобы проверить влияние этого. Я, конечно, понимаю, что Lua - не тот язык в котором обычно пишутся модули на тысячи строк, но как-то я не встречал упоминаний о влиянии этого на производительность. Хотя если вспомнить World of Warcraft, то ему не мешает.
Пришлось разбираться, т.к. в последних версиях терминала стало всё очень медленно если запущены "большие" скрипты, банально вкладки переключаются со скрипом. Пальцем в небо - что-то сделали с GC. У меня функции использую локальные переменные, а функций много. В итоге GC надо работать. Как он работает в Квике не знаю, но надеюсь, что независимо от основного потока.
Впрочем, приведённый нагрузочный тест не делает ничего такого, чтобы сильно потреблять память, а падение производительности есть.
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
20.07.2026 16:50:17
Ок. Сделал нагрузочный тест Версия терминала 13.02.20. Реальный брокер.
Создаётся простая таблица. По списку (всего 7) инструментов выводятся данные.
Чтобы была какая-то нагрузка, то берем два колбека OnParam и OnQuote. Данные в таблицу выводятся только из main. В колбеке OnParam вызывается getParamEx несколько раз (нагрузка же).
OnQuote только для записи флага. Чтение DOM тоже в main.
Чтобы хоть как-то смоделировать реальный скрипт, созданы библиотеки заглушки с большим число функций. В main вызывается по одной функции из них.
В итоге скрипт занимает примерно 1 мб. Что не так и много.
Запускаю на реальном брокере и терминал "умирает". Не совсем, но уже ощутимо видно как таблица текущих торгов, да и весь интерфейс, перестаёт работать "плавно". В таблице данные выводятся с задержками, приходится "пинать окно" (вспоминаем тему )
Также на этом примере можно увидеть (по крайней мере у меня), что если не подключать большие библиотеки, то уже всё заметно живее. Т.е. банальный размер скрипта влияет.
При этом в более старых версиях терминала не замечалось особого влияния размера скрипта на работу.
Работа колбеков
Пользователь
Сообщений: Регистрация: 27.01.2017
20.07.2026 14:14:04
Просто наблюдение. Пока не могу подтвердить реальным тестом.
В последних версиях терминала, по ощущениям начиная с 12-ой, наличие колбека в скрипте стало ощутимо "тормозить" терминал. Естественно и раннее колбек использовался только для передачи в очередь. Но теперь даже это приводит к периодическим замираниям терминала. Особенно если у скрипта есть и окно со своим колбеком на обновление.
Наблюдаю у разных брокеров. Есть те, у кого это, можно сказать, постоянно. А есть те, у кого это только в периоды повышенной ликвидности.
Сильнее это проявляется если запустить несколько скриптов и даже в спокойное время. При этом на демо Quik Junior, где поток данных очевидно низкий, это малозаметно.
Хотелось бы понять - это у меня что-то вылезло и надо глубоко копать или такое есть и других.
Примечательно, что есть скрипт из одной строки запускающий dll реализующий jRPC сервер из Квика на плюсах. И если подписаться в нём на OnQuote, то происходит то же самое. Казалось бы - библиотека на ++, нет окон - а терминал не даёт переключить закладки, думает.
Задать время снятия заявки программно при выставлении sendtransaction, Задать время снятия заявки программно при выставлении sendtransaction
Пользователь
Сообщений: Регистрация: 27.01.2017
20.07.2026 12:09:43
Вы не указали о каком виде заявке вопрос.
В описании формата параметров есть упоминание только для заявки типа Тейк-профит и стоп-лимит:
IS_ACTIVE_IN_TIME
Признак действия заявки типа «Тейк-профит и стоп-лимит» в течение определенного интервала времени. Значения «YES» или «NO»
ACTIVE_FROM_TIME
Время начала действия заявки типа «Тейк-профит и стоп-лимит» в формате «ЧЧММСС»
ACTIVE_TO_TIME
Время окончания действия заявки типа «Тейк-профит и стоп-лимит» в формате «ЧЧММСС»
При размещении заявки на продажу ошибка "Вам запрещена работа по данному торговому счету"
Пользователь
Сообщений: Регистрация: 27.01.2017
18.07.2026 16:07:44
Цитата
Vitalii Savinov написал: То есть если у меня бумаги нет, но я пытаюсь ее выставить на продажу, или пытаюсь выставить на продажу больше, чем у меня есть - то я могу получить такое сообщение?
Если текущий уровень риска у брокера запрещает вам операции в Шорт, то да. Хотя, сообщение должно выглядеть немного иначе. У Вас же сообщение о счёте. Впрочем, может у разных брокеров формат сообщений разный.
При размещении заявки на продажу ошибка "Вам запрещена работа по данному торговому счету"
Пользователь
Сообщений: Регистрация: 27.01.2017
18.07.2026 08:51:18
Т.к. операция продажа на фондовой секции, то убедитесь, что Вам разрешена операция ШОРТ (если это операция без открытой позиции).
Если бы я был архитектором QUIK, Что стоило бы изменить в QUIK по-крупному
Пользователь
Сообщений: Регистрация: 27.01.2017
18.07.2026 08:49:50
Это всё хорошо, если считать, что данная архитектура имеет смысл для сложных видоизменений. Кажется, что это уже не имеет смысла, т.к. число конечных пользователей физ. лиц терминала Квик не будет расти, а скорее падать. Вертикальный формат экрана окончательно победил, даже базовые вещи стали переводить в этот недоформат. Тем, кто открывает телефон с кнопками - страдать. Также стоит учесть, что мелким брокерам имеет смысл использовать готовые решения, а крупным - нет, т.к. лучше загнать их в свою инфраструктуру. С учётом состояния российского рынка, кажется, что мелким не выжить.
Поэтому скрипты на Lua - это такая далёкая по важности вещь, что даже банальные просьбы о поддержке UTF - нереализуемы. В этой ситуации я бы предпочел - банальный API интерфейс, чтобы терминал был только как шлюз к данным. Клиент же написать на чём угодно, на любой OS (наконец бы не запускать этот Windows), со своим интерфейсом, базой данных и т.д.
Не срабатывает лимитная заявка sendTransaction, хотя рыночная проходит, Знатоки, подскажите возможную причину
Пользователь
Сообщений: Регистрация: 27.01.2017
17.07.2026 15:31:07
Цена указывается с той точностью, что указана в спецификации инструмента. Транслируется в параметре SEC_SCALE для метода getParamEx - Точность цены
Либо, если получать информацию по инструменту через метод getSecurityInfo, то там это уже будет параметр scale.
Также важно привести цену к шагу цены, например, если шаг цены 10 (контракт Ri), то цена 111007 - некорректна.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
Пользователь
Сообщений: Регистрация: 27.01.2017
08.07.2026 09:12:48
Это проблема не нова, т.к. вырастить просто знающего - не сложно. А вот думающего - уже проблема.
Ричард Фейнман говорил: Воображение побеждает знание. Пока текущие модели так и будут попугаями - они и будут делать также как и большинство. Но вот где требуются чистые знания - уже игра проиграна.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
Пользователь
Сообщений: Регистрация: 27.01.2017
07.07.2026 14:37:58
Цитата
TGB написал: Действительно, для AI-агента существенным является только наличие API взаимодействия с биржей и, возможно, через QUIK это не лучший вариант. Но надо понимать, что существуют затраты на содержание AI-агента, а также кто гарантирует, что AI-агент оправдает расходы на него? У меня есть подозрение (которые не могу доказательно подтвердить), что ожидания высокой доходности AI-агентов могут оказаться завышенными. Поэтому нельзя списывать в "утиль" возможности разработки роботов в ручную, существующие в фреймворке.
Так весь twitter в сообщениях как Hermes, а ранее OpenClaw баснословно торгует. Что, конечно, есть просто маркетинг.
Поэтому всё должно быть наоборот - первично свой алгоритм, вторично - некая модель для анализа.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
Пользователь
Сообщений: Регистрация: 27.01.2017
07.07.2026 13:30:44
Ок. Как реализован интерфейс к терминалам, бирже MOEX? Если только к иным биржам, то действительно, можно взять уже готовые решения.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
Пользователь
Сообщений: Регистрация: 27.01.2017
07.07.2026 11:54:17
А смысл? Просто вопрос, не более. Вы же не предоставите открытие коды, или выдадите? Если нет, то - это надо быть супер смелым, чтобы запускать чужой код, не зная как он работает, особенно если там так называемый ИИ.
UTF-8 или Windows-1251
Пользователь
Сообщений: Регистрация: 27.01.2017
06.07.2026 16:42:35
Это уже регистрировалось, и, кажется, не один раз.
Но всё же лотность некорректна, как-то неаккуратно. Ранее было правильно.
Добрый день.
У акций Сбербанка лотность =1 с 1 августа 2025 года.
Ок, значит пропустил. Спасибо.
Демо Quik Junior, Нет данных
Пользователь
Сообщений: Регистрация: 27.01.2017
02.07.2026 11:06:15
Да, данные появились, спасибо.
Но всё же лотность некорректна, как-то неаккуратно. Ранее было правильно.
Демо Quik Junior, Нет данных
Пользователь
Сообщений: Регистрация: 27.01.2017
01.07.2026 11:23:36
Второй день в демо-терминале Junior нет данных по акциям. Также некорректно передаются параметры, например размер лота. У Сбер = 1, хотя должен быть 10.
Т.к. данные акций не транслируются биржей, то явно есть проблема, мешающая тестированию скриптов.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
28.06.2026 17:49:56
В чём проблема обновить принадлежность ордера к стратегии? По trans_id нашли ордер и уже ордер принадлежит стратегии, что сейчас, что завтра, и далее до исполнения.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
28.06.2026 15:56:50
Как-то я и ранее не замечал очистки поля trans_id в вечерний клиринг.
Цитата
VPM написал: либо при принудительном разрыве и реконнекте сессии брокером
Но этого достаточно, чтобы сказать, что данный подход не надёжен.
Я уже говорил ранее, что trans_id - это внутренний ключ и поэтому и использование его как сквозной опасна.
Вы отправляете транзакцию и генерируете trans_id. Это первый ключ для дальнейшей фильтрации. Далее читаете ответ транзакции по trans_id и ждёте появления записи в таблице ордеров с этим trans_id. Как только получили оную, то уже переходите на реальный, постоянный ключ - order_num, trans_id уже не используете.
Т.к. есть ключ order_num, то все последующие действия будете проводить уже с ним - проверка состояния ордера, прошедшие сделки. Т.о. вы запоминаете свои ордера, а не свои транзакции. Это существенное отличие. Тем более что если ордер устанавливается с переносом через сутки. Или стоп-ордера на длительный период. Важен номер ордера, а не с каким внутренним ключём вчера отправили транзакцию.
Как точно определить, можно ли в данный момент торговать или нет?
Пользователь
Сообщений: Регистрация: 27.01.2017
23.06.2026 15:09:50
Затем, что в каких-то режимах можно устанавливать заявки, а в каких-то нет. Также разные допустимые диапазоны цен.
Например, в аукцион можно подать заявку, но диапазон цен намного уже обычной сессии.
Аукцион открытия и закрытия - это разные аукционы, тоже иногда необходимо понимать. Дискретный аукцион - это вообще в режиме стоп торгов при достижении лимита волатильности.
Т.о. это всё не просто про можно-нельзя, а про текущий статус торговой сессии.
Как точно определить, можно ли в данный момент торговать или нет?
Пользователь
Сообщений: Регистрация: 27.01.2017
23.06.2026 10:34:04
Раньше для срочной секции можно было получать параметр CLSTATE - Статус клиринга, чтобы понимать в каком клиринге. Теперь же, после 23.03.2026 многие брокеры перестали его транслировать корректно.
Как точно определить, можно ли в данный момент торговать или нет?
Пользователь
Сообщений: Регистрация: 27.01.2017
22.06.2026 17:31:27
Запрашивать необходимо TRADINGSTATUS, т.к. по инструменту могут быть стоп торги. STATUS - это просто, что допущен к торговле. А т.к. стоп может быть в любую секунду, хотя другие инструменты торгуются, то и запрашивать необходимо постоянно или хотя бы перед торговой операцией.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
21.06.2026 14:28:32
Действительно, некоторые брокеры, например АЛОР, не записывают brokerref в таблицу ордеров, а значит и в таблицу сделок. В таблице обезличенных сделок их не может быть, т.к. они обезличены. Не для этого эти данные.
Поэтому остаётся использовать номер транзакции+номер ордера как ключ. При этом недопустимы операции по сдвигу ордера на графике, вводу ордера руками с тем же комментарием. Т.е. скрипту допустимо только оперировать своими ордерами, запомненными в состоянии.
Т.о. при перезапуске скрипта просто сканируете таблицы и находите свои ордера. Если их там нет, значит они были сняты в клиринг, т.е. это уже следующая торговая сессия (теперь так после 23.03.2026). Т.е. если пропустили сделки, то уже их нет, если только, как говорил выше, нет своего независимого журнала сделок, в котором можно их найти.
Поэтому кроме ограничения на некоторые действия особой разницы нет как фильтровать таблицы. Просто по brokerref это позволяет более гибко работать с ордерами. А без оного, уже только по ключу запомненного ордера.
Я советую Вам почитать про хэш функции, чтобы потом не оказалось, что получены дубли, особенно если работают параллельно несколько скриптов. При этом намного проще помнить свои ордера и проверять сделки именно по ним. Зачем создавать хэш, при этом не криптографически уникальный - не ясно.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
19.06.2026 16:15:28
Цитата
VPM написал: А что brokerref он в одном колбеке приходит в другом нет
Такого не замечал. Впрочем, т.к. я их не использую, то мог и не видеть такого.
Для меня универсальный механизм - это "опрос датчика в реальном времени". Аргумент против: это же постоянно делать запросы. Для меня не аргумент. Надежность важнее.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
19.06.2026 15:20:46
Для этого и есть идентификатор, задаваемый в комментарии к ордеру, сделке. поле brokerref. По номеру ордера, если эта информация запомнена, тоже можно фильтровать сделки.
В любом подходе необходимо хранить информацию о учтенных сделках, ордерах. Если позиция - это результат сделок скрипта, то значит они и не должна сходиться с балансом. Её необходимо рассчитывать по сделкам. Сумма всех позиций, разных скриптов, стратегий - это уже ближе к балансу, но тоже не обязательно должна сходится, т.к. могут быть и ручные сделки вне стратегий.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Если это произошло во время простоя скрипта, то после запуска, и если сделки ещё есть в данных терминала, то легко просканировать сделки в таблицах. Если же очень вероятна ситуация, что перезапуск случится уже на следующую торговую сессию и сделок уже в терминале нет, то необходимо организовывать запись сделок в файлы отдельным независимым скриптов - журнал. И при старте проверять и там.
Цитата
VPM написал: либо зафиксировали дважды (из-за повторной отправки после таймаута)
Даже при использовании колбека, точно обязательно, необходимо фиксировать какие сделки уже были учтены. И это необходимо хранить в хранимом состоянии. Чистить данные можно на следующую сессию, т.к в Квике они удаляются и уже не могут появится заново.
Т.о. подходы одинаковые.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
19.06.2026 10:31:02
Цитата
VPM написал: То есть нужен какой то уникальный ключ для таблицы луа. Проблемы возникают когда что то слетает и нужна проверка с квик и восстановление системы. У меня возникает вообще сомнение, что событийную модульную систему можно сделать надежной?
Вопрос не очень понятен. Колбеки можно использовать только для обработки событий в процессе работы алгоритма. Если же событие случилось когда скрипт не работал, то только данные таблиц. Поэтому, даже если используете колбеки, то необходимо запоминать состояние. Для скрипта - это некий идентификатор, который будет добавляться к транзакции (правда если брокер это поддерживает). Для установленных ордеров - это его номер, что позволит проверить состояние этого ордера после рестарта скрипта.
Что касается позиции, то необходимо сканировать прошедшие сделки при старте - и здесь тоже идентификатор, который тоже будет добавлен к сделке по ордеру. Можно также учитывать только сделки по запомненным ордерам, но это если алгоритм запрещает чужие операции.
Так что ничего сложного. Просто эти методы необходимы в любом случае. Колбеки же просто облегчают код в части проверок в процессе работы алгоритма скрипта. Я просто очень давно поймал ситуации когда колбека не было и поэтому не использую их, даже если сейчас говорят, что всё надежно. При работе с деньгами я предпочитаю простые, надежные методы.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
17.06.2026 12:45:07
Так у них подход такой же, только набор параметров другой. Работает простая статистика - если есть набор данных за года, который кластеризуется по параметрам. При этом данные - это текущие показатели датчиков: скорость сделок, показатели сделок, скорость установки снятия заявок в стакане, скорость движения цены - читай реакция и т.д. Также и внешние данные - новости, арбитраж...
Т.о. задача не сказать куда, а сказать как. Нет попытки предсказать цену, а просто анализ реакций на огромной базе статистических данных. Поэтому и необходимы мощности, т.к. как текущие данные большие, так и набор базы весов модели большой, а решение необходимо принимать за единицы млс.
Вы же пробуете использовать набор показателей, который "как бы" описывает состояние, но по факту показывает уже прошедшую реакцию на состояние. Т.к. рынок все же содержит некий лаг, то да, возможно поймать конец движения. Но всё же физ. лицам гораздо проще работать с рисками используя достаточно простые модели, а не пробовать обогнать тех, кто будет заведомо быстрее в принятии решений.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
17.06.2026 10:56:05
Вы просто повторяете давно пройденный путь квантами. Проблема только в одном - окно прогнозирования мало и размер движения мал. Поэтому необходимы объёмы и индивидуальные договора с биржей по комиссиям.
Физ. лицам уже сложнее, т.к., как минимум, не у всех в подвале установлена ферма серверов.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
16.06.2026 14:32:53
Если всё было так просто, то физики уже давно бы захватили рынок. Но нет, как раз наоборот. В отличие от механистических систем, у "рынка" нет инерции. Может показаться что есть, и на этом и построены большинство стратегий, но цена никому ничего не обязана. Она может мгновенно изменить вектор движения, скорость и ускорение. И уже через мгновение - опять. Физический, наблюдаемый мир так не устроен.
Аналитической формулы для силы, движущей цену, нет. Вы можете только построить некую стохастическую модель, которая будет работать в некой узкой области и она действительно будет учитывать прошлое, но это всё равно модель, например модель Геометрического броуновского движения (GBM) позволяет рассчитать конус вероятностей (Volatility Cone). Т.е. у Вас есть вероятности - не более.
Очередное зависание Lua-скрипта, в функции getDepoEx
Пользователь
Сообщений: Регистрация: 27.01.2017
09.06.2026 19:34:14
Цитата
Oleg Kuzembaev написал: Тест проводился на подключенном к серверу терминале, что является корректным условием для работы скрипта.
Зря Вы это написали, т.к. в таком случае документацию необходимо обновлять и точно указывать для каких методов не гарантируется корректная работа при отсутствии соединения. Более того, если в процессе работы происходит разрыв соединения и в этот момент скрипт выполняет метод, который может привести к "зависанию" терминала, то это уже совсем не корректно, а вполне себе ошибка. И чтобы её избежать необходимо понимать какие методы необходимо обрамлять предварительной проверкой на наличие соединения.
Не срабатывает лимитная заявка sendTransaction, хотя рыночная проходит, Знатоки, подскажите возможную причину
Пользователь
Сообщений: Регистрация: 27.01.2017
08.06.2026 11:04:52
Проверяйте что возвращает метод sendTransaction. Если не пустая строка, то даст описание ошибки.
Формат вызова:
STRING result sendTransaction(TABLE transaction)
Параметры:
result – строка, содержащая текст ошибки, если она случилась при обработке транзакции;
transaction – таблица с параметрами транзакции.
Внешние Lua-модули
Пользователь
Сообщений: Регистрация: 27.01.2017
06.06.2026 16:35:25
Если искателю require задан путь к модулю и модуль имеет открытый код или скомпилирован с той же версией что и qlua, то он будет подключен.
А будет ли он работать уже зависит от того как он написан, какие у него зависимости. В общем случае - будет.
Надёжных в API нет. В этом и проблема. Искать по форуму - обсуждения были и 10 лет назад.
Восстановление заявок после обрыва связи
Пользователь
Сообщений: Регистрация: 27.01.2017
27.05.2026 19:23:53
Это так часто уже обсуждалось на форуме. Необходимо организовать процедуру ожидания загрузки данных с сервера брокера. К сожалению, разработчики считают, что это не проблема, поэтому никаких конкретных методов для этого нет.
Что можно сделать: - Сравнивать время последнего полученного пакета и время сервера, чтобы расхождение укладывалось в допустимую дельту. - Также можно сравнивать время сервера и локальное время, но оно должно быть точным. - Если в процессе работы были установлены ордера, то необходимо запоминать число записей в таблицах и после восстановления связи ожидать пока там не появится необходимое число записей, или хотя бы отличное от нуля. - Ожидать загрузки таблиц денежных лимитов и лимитов срочной секции (если есть)
т.е. процедуры проверки, что в таблицах данные есть, что пакеты данных догнали время сервера.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
16.05.2026 11:45:28
Кажется, что Вы опять увлекаетесь теорией. Для чего существуют фильтры - для обработки сигналов.
В физическом мире, для примера, есть сигнал, необходимо его обработать и расшифровать, принять сообщение. Мы не гадаем, а просто обрабатываем. Или, например, спектральный анализ радиотелескопа - тоже, обработали, получили пики. Т.е. задача сделать сигнал удобным для обработки, разобрать сигнал, принять сообщение.
Потом физики подумали - мы же можем это применить для ценового ряда. А что - тоже сигнал. Ок. Разобрали, отфильтровали, обработали. А дальше то что? Сигнал стал красивым, есть его характеристики. Но это принятый сигнал. В реальном мире, в большинстве случаев, это бы сказало что будет дальше. Но для рынка не скажет.
Потому стали выдумывать разные подходы, искать закономерности. Т.е. гадать, на самом деле. В итоге, можно было бы просто взять карандаш и обвести график некой линией, и выполнять такие же упражнения. Всё сводится к предположениям: если цена выше, ниже отфильтрованной линии; пересекает линию; две-три отфильтрованные линии пересекаются и т.д. и т.д. И всё это носит вероятностный характер. Что кардинально отличается от того как применяются фильтры и для чего они были придуманы изначально.
Так называемые Кванты, когда ресурсы и скорости позволили это, сразу ушли от этого наскального творчества и перешли на анализ в реальном времени. Действительно, если задача найти закономерности в поведении участников, а именно это и есть основная задача, то необходимо анализировать именно это - данные стакана, ленту сделок, реакцию на эти изменения. Т.е. что происходит с заявками в этот момент времени, как участники реагируют, снимают, устанавливают заявки, совершают сделки, с какой скоростью, объемами и т.д. И здесь уже закономерности важны, т.к. психология толпы вполне себе закономерна.
Это не значит, что фильтры и анализ прошлого совсем бесполезны. У рынка, т.е. у участников, тоже есть инерция. Поэтому какое-то время можно строить предположения на базе прошлого. Но приходится постоянно изменять параметры, проверять, контролировать. В текущих реалиях это вполне можно отдать какому-то агенту, т.к. это очень муторная, скучная работа.
Т.о. нет идеального фильтра. Они примерно одинаковы, решают одну задачу - нарисовать карандашом линию. Свечи - это тоже, вполне себе, фильтр. А т.к. многие поколения участников проходят один и тот же путь, то 200 EMA становится чем-то магическим, от которой цена отскакивает. И действительно может. Почему, потому что работает обратная связь. Т.е. хвост виляет собакой.
Цитата
VPM написал: Почему это логично: 1. Защита от "Ракет", не шортить инструмент, который растет вертикально (где темп роста 1\% в бар, а шум всего 0.2\%). 2. Фильтрация флета, заключается в понимании факта, что выход за Сигму в боковике — это дар, а в тренде — это ловушка. 3. Ну и конечно Нормировка. Сравнивая проценты со скоростью, мы получаем безразмерный коэффициент - Здоровья тренда.В своей реализации сделал инженерное предложение. Добавил в "подвал" к осциллятор Z-Prob, вторую линию — TSI (Trend Strength). Теперь: * Когда Z-Prob в зоне экстремума, а TSI идет вниз — это Идеальный вход. * Когда оба на максимуме — это Сильный импульс (не трогать!).Добавив расчет этого коэффициента TSI в фабрику, для финальной фильтрации сигналов получил фактически мозг будущего робота.
Мне сложно комментировать это, т.к. это просто эмоции. Хотя вполне себе можно напомнить о penny-stock, которые вполне себе шортят на выносах, "наплевав на всякую статистику", т.к. это банальный pump.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
15.05.2026 17:43:18
Цитата
VPM написал: , Мне не понятно почему Вы делаете такой вывод? На чем основываетесь?
Потому что фильтр - это алгоритм для существующего сигнала. Фурье - это тоже фильтр. И проблемы у него такие же как у фильтра. Т.е. есть сигнал. Задача привести его к какому-то виду, выделить составляющие. Применяем фильтр, радуемся.
Если же задача - сказать, а сколько будет завтра? То уже всё сложнее, т.к. фильтр может сказать фазу (частоту), амплитуду, но и все. Можете пробовать продолжать сигнал на шаг вперед, никто не мешает. Но дело-то в том, что сигнал может взять и развернуться, просто так, наплевав на вычисленную фазу. Неправильный сигнал... Если мы обрабатываем физические сигналы, то там фильтрация отлично работает. Если же у нас не сигнал, а хаотичные кнопко-нажатия участников рынка, то здесь уже поведенческие модели лучше будут работать.
Мои применения методики приводили к тому, что можно пробовать предсказывать на отрезке 5-10% от окна вперед, не больше. Делать далеко-идущие выводы - точно нет.
Банальная ансамблевая регрессия лучше работает, без всякого Фурье. Но и его можно оставить, просто чтобы оценить первую-вторую гармоники.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
15.05.2026 15:35:29
Цитата
Регрессионный канал. Статистическая (прошлые данные). Строится методом наименьших квадратов (LMS)
Это банальный подход. Регрессия чрез фильтр Калмана, да даже гармоническая регрессия уже более стабильны.
Что касается фильтра, то какой бы простой не использовали, это будет просто фильтрация, без определения значимых гармоник. И назначение - убрать шум. Никакого будущего не может быть предсказано. Т.е. тренд, направление фильтром не определяется.
Т.к. ценовые ряды стохастические, без явной автокорреляции, то и выход за канал, какой бы широкий он ни был (хоть 5 сигма) - не является чем-то сверхъестественным. А скорее вполне закономерным явлением.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
Пользователь
Сообщений: Регистрация: 27.01.2017
15.05.2026 14:27:43
Этим методикам уже столько лет. Детрендинг - это построение регрессионной модели ряда и получение остатка. Циклы - это чаще всего преобразование Фурье, для получения периодических составляющих.
Если используете Питон, то можно это сделать достаточно просто. Я для Lua писал библиотеку на С, проводящую такого рода операции.
Если же говорить о Квантах, то советую углубится в стохастические процессы и цепи Маркова.
Функция OnClose будет возвращена перед самым закрытием Рабочего места, но уже после того как закрыты все таблицы и окна. Таков дизайн этой функции. Понять закрыто ли конкретное окно можно с помощью функции IsWindowClosed, что вам удалось продемонстрировать в изначальном сообщении. Т.о. колбек OnClose приходит лишь при полном закрытии приложения терминала.
Если он будет вызван после, то этот колбек бесполезен.
В итоге понять, что терминал закрывается, и необходимо изменить алгоритм обработки окон, таблиц, графиков из main - нельзя. Поэтому и требуется флаг, сигнализирующий о начале процесса закрытия терминала.
Работа OnClose
Пользователь
Сообщений: Регистрация: 27.01.2017
12.05.2026 15:17:50
Есть колбек OnClose. Казалось бы, он должен помочь понять, что терминал остановлен. Но явно что-то пошло не так.
Берем простой скрипт. Закрываем терминал через "крестик".
Ожидание - 1. OnClose, сбрасываем флаг. 2. выходим из main.
Код
local sleep = _G.sleep
local isRun = true
local AddLabel = _G.AddLabel
local GetLabelParams = _G.GetLabelParams
local logFile
local function log_tostring(...)
local n = select('#', ...)
if n == 1 then
return tostring(select(1, ...))
end
local t = {}
for i = 1, n do
t[#t + 1] = tostring((select(i, ...)))
end
return table.concat(t, " ")
end
local function log(...)
if logFile==nil then return end
logFile:write(tostring(os.date("%c",os.time())).." "..log_tostring(...).."\n");
logFile:flush();
end
function _G.OnStop()
isRun = false
log("Script Stoped")
if logFile then logFile:close() end
end
function _G.OnClose()
isRun = false
log("Script OnClose")
if logFile then logFile:close() end
end
function _G.main()
logFile = io.open(_G.getScriptPath().."\\labels_test.txt", "w")
local t_id = _G.AllocTable()
_G.AddColumn(t_id, 1, 'test', true, _G.QTABLE_STRING_TYPE, 20)
_G.CreateWindow(t_id)
local tag = 'virt_test'
local label_params = {}
label_params.YVALUE = 324
label_params.TEXT = 'TEST |||||||||||||||||||||||||||||||||||||||||||||'
label_params.HINT = 'Еще текст'
label_params.DATE = 20260512
label_params.TIME = 135000
label_params.FONT_FACE_NAME = 'Arial'
label_params.ALIGNMENT = 'RIGHT'
label_params.FONT_HEIGHT = 10
label_params.TRANSPARENT_BACKGROUND = 1
local data = {price = 324}
local l_id = AddLabel(tag, label_params)
sleep(1000)
while isRun do
local i = 1
while isRun and i < 10 do
label_params = GetLabelParams(tag, l_id)
if label_params then
data.price = tonumber(label_params.yvalue) or 0
end
log(os.clock(), i, 'IsWindowClosed', tostring(_G.IsWindowClosed(t_id)), 'label_params', tostring(label_params), tag, 'l_id', l_id, tostring(data.price))
i = i+1
end
log(os.clock(), 'sleep')
sleep(100)
end
end
Выводит метку на график, создаём окно. Далее выводим данные метки с графика и закрыто ли окно. Смотрим лог:
Окно ещё не уничтожено, но графика уже нет и данные метки не получены. Tue May 12 15:07:59 2026 31.061 1 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 2 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 3 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 4 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 5 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 6 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 7 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 8 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 9 IsWindowClosed false label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:07:59 2026 31.061 sleep
А здесь уже и окна нет Tue May 12 15:08:00 2026 31.173 1 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 2 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 3 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 4 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 5 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 6 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 7 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 8 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 9 IsWindowClosed true label_params nil virt_test l_id 13.0 324.0 Tue May 12 15:08:00 2026 31.173 sleep
Только сейчас OnClose Tue May 12 15:08:00 2026 Script OnClose
Сама по себе схема, вроде, корректна, если бы OnClose мог выполнится параллельно, не обращая внимания на работу main. Но это не так. В итоге OnClose вызывается когда уже уничтожено окно скрипта и нет графика. Т.е. некоторое время (а точнее пока не вызовется C функция) у нас есть подвешенное состояние - мы не знаем окно, график закрыты пользователем или нет. В итоге, если в это время считаем метку, а её нет, можем принять не верное решение. Аналогично и по окну скрипта.
Что хотелось бы: вместо колбека иметь глобальный флаг, состояние или иное, сигнализирующее, что терминал закрывается. При этом он должен быть установлен быстро, перед уничтожением объектов терминала. Аналогия из электротехники - логическое 1 или 0 на контакте. Если 0, то уже всё, логика работы совершенно иная.
Текущая же реализация может работать, если бы OnClose вызывался до уничтожения объектов.