Nikolay (Все сообщения пользователя)

Выбрать дату в календаре…Выбрать дату в календаре

Страницы: Пред. 1 2 3 4 5 6 7 8 9 10 11 ... 29 След.
Работа колбеков
 
Цитата
TGB написал:
Может  быть протестировать измененный вариант:
Вот убрал запрос getInfoParam, запустил. Всего 86 колбеков. Все они в 1 мкс. Но main выполнялся 1.37 сек. Как-то не верится, что банальный разбор такой небольшой очереди будет столько, хоть и через очень неэффективную table.sremove. Возможна и она проблема. Позже сделаю тест с другой двойной очередью без table.sremove.

[17220] Enter main loop Wed Jul 22 17:27:34 2026
[17220] Enter OnQuote SPBFUT MMU6
[17220] Exit OnQuote 0.0 SPBFUT MMU6
[17220] ---------------------------------------------------------------------------
[17220] Enter OnQuote SPBFUT SiU6
[17220] Exit OnQuote 0.0 SPBFUT SiU6
[17220] ---------------------------------------------------------------------------
[17220] Enter OnQuote SPBFUT RIU6
[17220] Exit OnQuote 0.0 SPBFUT RIU6
[17220] ---------------------------------------------------------------------------
[17220] Enter OnQuote TQBR SBER
[17220] Exit OnQuote 0.0 TQBR SBER
[17220] ---------------------------------------------------------------------------
[17220] Enter OnQuote TQBR GAZP
[17220] Exit OnQuote 0.0 TQBR GAZP
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT SiU6
[17220] Exit OnParam 0.0 SPBFUT SiU6 nil 79273.0
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT MMU6
[17220] Exit OnParam 0.0010000000038417 SPBFUT MMU6 nil 2180.4
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT SiU6
[17220] Exit OnParam 0.0010000000038417 SPBFUT SiU6 nil 79273.0
[17220] ---------------------------------------------------------------------------

...................................................................................................................................
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT USDRUBF
[17220] Exit OnParam 0.0 SPBFUT USDRUBF nil 78.34
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT MMU6
[17220] Exit OnParam 0.0 SPBFUT MMU6 nil 2180.4
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT SiU6
[17220] Exit OnParam 0.0 SPBFUT SiU6 nil 79275.0
[17220] ---------------------------------------------------------------------------
[17220] Exit main loop 1.3700000000026  
Работа колбеков
 
Цитата
Старатель написал:
В LASTRECORDTIME проставляется время сервера или клиента?
По моим наблюдениям - сервера.
Работа колбеков
 
Цитата
TGB написал:
функция  getInfoParam в высокочастотном  OnParam подозрительна
Ничего особенного по ней в описании нет. Впрочем, документация очень шаткая на данный момент.

Проблема OnParam в том, что он очень убог. Этот колбек должен быть реализован в виде:

OnParam(STRING class_code, STRING sec_code, TABLE chng_params)

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

В текущей реализации приходится DDOS-ить API чтобы получить данные. Сделки могут быть раз в час, цена посл. сделки может не изменятся, а будет постоянно менять лучший спрос, предложение (есть такие инструменты где роботы постоянно меняют стакан) - постоянный холостой вызов колбека.

Впрочем, этот тест просто для нагрузки, так что можно и запрашивать данные. Тот же LASTRECORDTIME важен, т.к. иногда приходят опоздавшие данные и их лучше пропускать.

Цитата
TGB написал:
А если отключить запись в пользовательскую таблицу?
Тогда это будет скрипт без таблицы, по факту. Хотелось бы решить вопрос при наличии таблицы. Я специально добавил постоянный опрос IsWindowClosed.
Работа колбеков
 
Цитата
TGB написал:
А если запустить только ваш тест без остальных ваших скритов?
Выключил все скрипты, только этот. Лог в PrintDbgStr. Вот участок с странным временем. Для начала, есть OnParam с 244 млс. Общее время 1.193 сек для 55 отфильтрованных колбеков.

[17220] ---------------------------------------------------------------------------
[17220] Enter main loop Wed Jul 22 11:05:47 2026
17220] Enter OnParam SPBFUT MMU6
[17220] Exit OnParam 0.2440000000006 SPBFUT MMU6 110547 2110.85
[17220] ---------------------------------------------------------------------------
[17220] Enter OnQuote SPBFUT MMU6
[17220] Exit OnQuote 0.0019999999985885 SPBFUT MMU6
[17220] ---------------------------------------------------------------------------
[17220] Enter OnQuote TQBR SBER
[17220] Exit OnQuote 0.0 TQBR SBER
................................................................................................................
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT MMU6
[17220] Exit OnParam 0.0 SPBFUT MMU6 110548 2110.65
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT SiU6
[17220] Exit OnParam 0.0 SPBFUT SiU6 110548 79593.0
[17220] ---------------------------------------------------------------------------
[17220] Enter OnParam SPBFUT MMU6
[17220] Exit OnParam 0.0 SPBFUT MMU6 110548 2110.65
[17220] ---------------------------------------------------------------------------
[17220] Exit main loop 1.1930000000011
[17220] ---------------------------------------------------------------------------

И вот такие моменты проявляются. Естественно, чем больше скриптов запущено, тем, возможно, больше влияние.
Работа колбеков
 
Цитата
Старатель написал:
Вы так ничего не измерите. Обработка кода  внутри  колбэка идёт на ура. Оверхэд возникает из-за самого вызова колбэка, даже пустого. Смотрите мой пример  выше
Возможно, но моя задача понять что вызывает проблемы. Пока я натолкнулся на влияние на работу main при странном сочетание наличия таблицы и обработки её реакций, библиотек и колбеков. Сами колбеки, судя по всему, работают быстро.
Работа колбеков
 
Правда здесь уже, видимо, проявилась нагрузка на запись лога внутри колбеков, т.к. запись в файл внутри колбека -  это очень плохая идея.
Поэтому лучше заменить логирование на PrintDbgStr.
Код
local function log(...)
    PrintDbgStr(log_tostring(...))
end

В первой версии теста лога не было. Но, с другой стороны, это тоже такой тест, явно  показывающий как main просто перестает вызываться.

Цитата
TGB написал:
В моих скриптах эти колбэки не отрабатываются.
Тоже, особенно OnQuote. Но бывают задачи где это необходимо и я стал замечать проблемы. Пришлось смотреть, что происходит.
Работа колбеков
 
Еще раз тест, что запустил на 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

Работа колбеков
 
Ок. Запустил на VTB  в 10 утра. И это просто финиш. В основном была обработка main за 0-0.5 секунды.

А вот этот кусок за 593.92. Записал даже видео работы, где видно что происходит если запустить скрипт. Редактирование поля в таблице обновляется только после пинка. Цена не соответствует данным в ТТТ.
Как только скрипт остановлен, то ТТТ сразу оживает.

Видео https://disk.360.yandex.ru/i/VcfnlUv3JoFizQ


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
---------------------------------------------------------------------------

колбеки, колбеки ....


---------------------------------------------------------------------------
Enter OnParam SPBFUT CNYRUBF
Exit OnParam 0.0 SPBFUT CNYRUBF 100754 11.603
---------------------------------------------------------------------------
Enter OnParam SPBFUT MMU6
Exit OnParam 0.0 SPBFUT MMU6 100754 2087.15
---------------------------------------------------------------------------
Enter OnParam SPBFUT MMU6
Exit OnParam 0.0 SPBFUT MMU6 100754 2087.15
---------------------------------------------------------------------------
Enter OnParam SPBFUT MMU6
Exit OnParam 0.0 SPBFUT MMU6 100754 2087.15
---------------------------------------------------------------------------
Enter OnParam SPBFUT SiU6
Exit OnParam 0.0 SPBFUT SiU6 100754 79628.0
---------------------------------------------------------------------------
Enter OnParam SPBFUT MMU6
Exit OnParam 0.0 SPBFUT MMU6 100754 2087.15
---------------------------------------------------------------------------
Enter OnParam SPBFUT MMU6
Exit OnParam 0.0 SPBFUT MMU6 100754 2087.15
---------------------------------------------------------------------------
Exit main loop 593.92
---------------------------------------------------------------------------
Работа колбеков
 
Думаю, что всё зависит от времени, потока данных, да и брокера. На ВТБ позже проверю.
Я проверяю на одном небольшом брокере, у которого сервера не особо "широкие".

Вот в 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
Работа колбеков
 
Цитата
nikolz написал:
можете сделать вывод времени входа и выхода для колбеков и для  main  c помощью точного счетчик Windows?
Если без излишеств, просто через os.clock, то сами колбеки работаю микросекунды. main уже зависит о того, что делает, но там тоже не что-то существенное.

Я пока наблюдаю странное - подключаешь несколько библиотек на 1000 функций - и привет. Собственно это в тесте и сделано, чтобы проверить влияние этого.
Я, конечно, понимаю, что Lua - не тот язык в котором обычно пишутся модули на тысячи строк, но как-то я не встречал упоминаний о влиянии этого на производительность. Хотя если вспомнить World of Warcraft, то ему не мешает.

Пришлось разбираться, т.к. в последних версиях терминала стало всё очень медленно если запущены "большие" скрипты, банально вкладки переключаются со скрипом.
Пальцем в небо - что-то сделали с GC. У меня функции использую локальные переменные, а функций много. В итоге GC надо работать. Как он работает в Квике не знаю, но надеюсь, что независимо от основного потока.

Впрочем, приведённый нагрузочный тест не делает ничего такого, чтобы сильно потреблять память, а падение производительности есть.
Работа колбеков
 
Ок. Сделал нагрузочный тест https://disk.360.yandex.ru/d/P7xB_poL0QlKfAВерсия терминала 13.02.20. Реальный брокер.

Создаётся простая таблица. По списку (всего 7) инструментов выводятся данные.

Чтобы была какая-то нагрузка, то берем два колбека OnParam и OnQuote. Данные в таблицу выводятся только из main. В колбеке OnParam вызывается getParamEx несколько раз (нагрузка же).

OnQuote только для записи флага. Чтение DOM тоже в main.

Чтобы хоть как-то смоделировать реальный скрипт, созданы библиотеки заглушки с большим число функций. В main вызывается по одной функции из них.

В итоге скрипт занимает примерно 1 мб. Что не так и много.

Запускаю на реальном брокере  и терминал "умирает". Не совсем, но уже ощутимо видно как таблица текущих торгов, да и весь интерфейс, перестаёт работать "плавно".
В таблице данные выводятся с задержками, приходится "пинать окно" (вспоминаем тему https://forum.quik.ru/messages/forum10/message49120/topic3777/#message49120)

Также на этом примере можно увидеть (по крайней мере у меня), что если не подключать большие библиотеки, то уже всё заметно живее. Т.е. банальный размер скрипта влияет.

При этом в более старых версиях терминала не замечалось особого влияния размера скрипта на работу.
Работа колбеков
 
Просто наблюдение. Пока не могу подтвердить реальным тестом.

В последних версиях терминала, по ощущениям начиная с 12-ой, наличие колбека в скрипте стало ощутимо "тормозить" терминал.
Естественно и раннее колбек использовался только для передачи в очередь. Но теперь даже это приводит к периодическим замираниям терминала.
Особенно если у скрипта есть и окно со своим колбеком на обновление.

Наблюдаю у разных брокеров. Есть те, у кого это, можно сказать, постоянно. А есть те, у кого это только в периоды повышенной ликвидности.

Сильнее это проявляется если запустить несколько скриптов и даже в спокойное время. При этом на демо Quik Junior, где поток данных очевидно низкий, это малозаметно.

Хотелось бы понять - это у меня что-то вылезло и надо глубоко копать или такое есть и других.

Примечательно, что есть скрипт из одной строки запускающий dll реализующий jRPC сервер из Квика на плюсах. И если подписаться в нём на OnQuote, то происходит то же самое. Казалось бы - библиотека на ++, нет окон - а терминал не даёт переключить закладки, думает.
Задать время снятия заявки программно при выставлении sendtransaction, Задать время снятия заявки программно при выставлении sendtransaction
 
Вы не указали о каком виде заявке вопрос.

В описании формата параметров есть упоминание только для заявки типа Тейк-профит и стоп-лимит:

IS_ACTIVE_IN_TIMEПризнак действия заявки типа «Тейк-профит и стоп-лимит» в течение  определенного интервала времени. Значения «YES» или «NO»
ACTIVE_FROM_TIMEВремя начала действия заявки типа «Тейк-профит и стоп-лимит» в формате  «ЧЧММСС»
ACTIVE_TO_TIMEВремя окончания действия заявки типа «Тейк-профит и стоп-лимит» в формате  «ЧЧММСС»
При размещении заявки на продажу ошибка "Вам запрещена работа по данному торговому счету"
 
Цитата
Vitalii Savinov написал:
То есть если у меня бумаги нет, но я пытаюсь ее выставить на продажу, или пытаюсь выставить на продажу больше, чем у меня есть - то я могу получить такое сообщение?
Если текущий уровень риска у брокера запрещает вам операции в Шорт, то да. Хотя, сообщение должно выглядеть немного иначе. У Вас же сообщение о счёте. Впрочем, может у разных брокеров формат сообщений разный.
При размещении заявки на продажу ошибка "Вам запрещена работа по данному торговому счету"
 
Т.к. операция продажа на фондовой секции, то убедитесь, что Вам разрешена операция ШОРТ (если это операция без открытой позиции).
Если бы я был архитектором QUIK, Что стоило бы изменить в QUIK по-крупному
 
Это всё хорошо, если считать, что данная архитектура имеет смысл для сложных видоизменений. Кажется, что это уже не имеет смысла, т.к. число конечных пользователей физ. лиц терминала Квик не будет расти, а скорее падать. Вертикальный формат экрана окончательно победил, даже базовые вещи стали переводить в этот недоформат. Тем, кто открывает телефон с кнопками - страдать.
Также стоит учесть, что мелким брокерам имеет смысл использовать готовые решения, а крупным - нет, т.к. лучше загнать их в свою инфраструктуру. С учётом состояния российского рынка, кажется, что мелким не выжить.

Поэтому скрипты на Lua - это такая далёкая по важности вещь, что даже банальные просьбы о поддержке UTF - нереализуемы.
В этой ситуации я бы предпочел - банальный API интерфейс, чтобы терминал был только как шлюз к данным. Клиент же написать на чём угодно, на любой OS (наконец бы не запускать этот Windows), со своим интерфейсом, базой данных и т.д.
Не срабатывает лимитная заявка sendTransaction, хотя рыночная проходит, Знатоки, подскажите возможную причину
 
Цена указывается с той точностью, что указана в спецификации инструмента. Транслируется в параметре SEC_SCALE для метода getParamEx - Точность цены

Либо, если получать информацию по инструменту через метод getSecurityInfo, то там это уже будет параметр scale.

Также важно привести цену к шагу цены, например, если шаг цены 10 (контракт Ri), то цена 111007 - некорректна.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
 
Это проблема не нова, т.к. вырастить просто знающего - не сложно. А вот думающего - уже проблема.

Ричард Фейнман говорил: Воображение побеждает знание. Пока текущие модели так и будут попугаями - они и будут делать также как и большинство.
Но вот где требуются чистые знания - уже игра проиграна.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
 
Цитата
TGB написал:
Действительно, для AI-агента существенным является только наличие API взаимодействия с биржей и, возможно, через QUIK это не лучший вариант. Но надо понимать, что существуют затраты на содержание AI-агента, а также кто гарантирует, что AI-агент оправдает расходы на него? У меня есть подозрение (которые не могу доказательно подтвердить), что ожидания высокой доходности AI-агентов могут оказаться завышенными. Поэтому нельзя списывать в "утиль" возможности разработки роботов в ручную, существующие в фреймворке.
Так весь twitter в сообщениях как Hermes, а ранее OpenClaw баснословно торгует. Что, конечно, есть просто маркетинг.

Поэтому всё должно быть наоборот - первично свой алгоритм, вторично - некая модель для анализа.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
 
Ок. Как реализован интерфейс к терминалам, бирже MOEX? Если только к иным биржам, то действительно, можно взять уже готовые решения.
Фреймворк B_Q создания торговых роботов для QUIK (с возможностью использования AI-агентов биржевой торговли), в названии
 
А смысл? Просто вопрос, не более. Вы же не предоставите открытие коды, или выдадите? Если нет, то - это надо быть супер смелым, чтобы запускать чужой код, не зная как он работает, особенно если там так называемый ИИ.
UTF-8 или Windows-1251
 
Это уже регистрировалось, и, кажется, не один раз.
Демо Quik Junior, Нет данных
 
Цитата
Alexey Ivannikov написал:
Цитата
Nikolay написал:
Да, данные появились, спасибо.

Но всё же лотность некорректна, как-то неаккуратно. Ранее было правильно.

Добрый день.

У акций Сбербанка лотность =1 с 1 августа 2025 года.
Ок, значит пропустил. Спасибо.
Демо Quik Junior, Нет данных
 
Да, данные появились, спасибо.

Но всё же лотность некорректна, как-то неаккуратно. Ранее было правильно.
Демо Quik Junior, Нет данных
 
Второй день в демо-терминале Junior нет данных по акциям.
Также некорректно передаются параметры, например размер лота. У Сбер = 1, хотя должен быть 10.

Т.к. данные акций не транслируются биржей, то явно есть проблема, мешающая тестированию скриптов.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
В чём проблема обновить принадлежность ордера к стратегии? По trans_id нашли ордер и уже ордер принадлежит стратегии, что сейчас, что завтра, и далее до исполнения.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Как-то я и ранее не замечал очистки поля trans_id в вечерний клиринг.

Цитата
VPM написал:
либо при принудительном разрыве и реконнекте сессии брокером
Но этого достаточно, чтобы сказать, что данный подход не надёжен.

Я уже говорил ранее, что trans_id - это внутренний ключ и поэтому и использование его как сквозной опасна.

Вы отправляете транзакцию и генерируете trans_id. Это первый ключ для дальнейшей фильтрации.
Далее читаете ответ транзакции по trans_id и ждёте появления записи в таблице ордеров с этим trans_id.
Как только получили оную, то уже переходите на реальный, постоянный ключ - order_num, trans_id уже не используете.

Т.к. есть ключ order_num, то все последующие действия будете проводить уже с ним - проверка состояния ордера, прошедшие сделки. Т.о. вы запоминаете свои ордера, а не свои транзакции. Это существенное отличие.
Тем более что если ордер устанавливается с переносом через сутки. Или стоп-ордера на длительный период. Важен номер ордера, а не с каким внутренним ключём вчера отправили транзакцию.
Как точно определить, можно ли в данный момент торговать или нет?
 
Затем, что в каких-то режимах можно устанавливать заявки, а в каких-то нет. Также разные допустимые диапазоны цен.

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

Аукцион открытия и закрытия - это разные аукционы, тоже иногда необходимо понимать.
Дискретный аукцион - это вообще в режиме стоп торгов при достижении лимита волатильности.

Т.о. это всё не просто про можно-нельзя, а про текущий статус торговой сессии.
Как точно определить, можно ли в данный момент торговать или нет?
 
Раньше для срочной секции можно было получать параметр CLSTATE - Статус клиринга, чтобы понимать в каком клиринге. Теперь же, после 23.03.2026 многие брокеры перестали его транслировать корректно.
Как точно определить, можно ли в данный момент торговать или нет?
 
Запрашивать необходимо TRADINGSTATUS, т.к. по инструменту могут быть стоп торги. STATUS - это просто, что допущен к торговле.
А т.к. стоп может быть в любую секунду, хотя другие инструменты торгуются, то и запрашивать необходимо постоянно или хотя бы перед торговой операцией.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Действительно, некоторые брокеры, например АЛОР, не записывают brokerref в таблицу ордеров, а значит и в таблицу сделок. В таблице  обезличенных сделок их не может быть, т.к. они обезличены. Не для этого эти данные.

Поэтому остаётся использовать номер транзакции+номер ордера как ключ. При этом недопустимы операции по сдвигу ордера на графике, вводу ордера руками с тем же комментарием. Т.е. скрипту допустимо только оперировать своими ордерами, запомненными в состоянии.

Т.о. при перезапуске скрипта просто сканируете таблицы и находите свои ордера. Если их там нет, значит они были сняты в клиринг, т.е. это уже следующая торговая сессия (теперь так после 23.03.2026).
Т.е. если пропустили сделки, то уже их нет, если только, как говорил выше, нет своего независимого журнала сделок, в котором можно их найти.

Поэтому кроме ограничения на некоторые действия особой разницы нет как фильтровать таблицы. Просто по brokerref  это позволяет более гибко работать с ордерами. А без оного, уже только по ключу запомненного ордера.

Я советую Вам почитать про хэш функции, чтобы потом не оказалось, что получены дубли, особенно если работают параллельно несколько скриптов.
При этом намного проще помнить свои ордера и проверять сделки именно по ним. Зачем создавать хэш, при этом не криптографически уникальный - не ясно.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Цитата
VPM написал:
А что brokerref он в одном колбеке приходит в другом нет
Такого не замечал. Впрочем, т.к. я их не использую, то мог и не видеть такого.

Для меня универсальный механизм - это "опрос датчика в реальном времени". Аргумент против: это же постоянно делать запросы. Для меня не аргумент. Надежность важнее.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Для этого и есть идентификатор, задаваемый в комментарии к ордеру, сделке. поле brokerref. По номеру ордера, если эта информация запомнена, тоже можно фильтровать сделки.

В любом подходе необходимо хранить информацию о учтенных сделках, ордерах. Если позиция - это результат сделок скрипта, то значит они и не должна сходиться с балансом. Её необходимо рассчитывать по сделкам.
Сумма всех позиций, разных скриптов, стратегий - это уже ближе к балансу, но тоже не обязательно должна сходится, т.к. могут быть и ручные сделки вне стратегий.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Цитата
VPM написал:
либо не зафиксировали
Если это произошло во время простоя скрипта, то после запуска, и если сделки ещё есть в данных терминала, то легко просканировать сделки в таблицах. Если же очень вероятна ситуация, что перезапуск случится уже на следующую торговую сессию и сделок уже в терминале нет, то необходимо организовывать запись сделок в файлы отдельным независимым скриптов - журнал. И при старте проверять и там.

Цитата
VPM написал:
либо зафиксировали дважды (из-за повторной отправки после таймаута)
Даже при использовании колбека, точно обязательно, необходимо фиксировать какие сделки уже были учтены. И это необходимо хранить в хранимом состоянии. Чистить данные можно на следующую сессию, т.к в Квике они удаляются и уже не могут появится заново.

Т.о. подходы одинаковые.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Цитата
VPM написал:
То есть нужен какой то уникальный ключ для таблицы луа. Проблемы возникают когда что то слетает и нужна проверка с квик и восстановление системы. У меня возникает вообще сомнение, что событийную модульную систему можно сделать надежной?
Вопрос не очень понятен. Колбеки можно использовать только для обработки событий в процессе работы алгоритма. Если же событие случилось когда скрипт не работал, то только данные таблиц.
Поэтому, даже если используете колбеки, то необходимо запоминать состояние. Для скрипта - это некий идентификатор, который будет добавляться к транзакции (правда если брокер это поддерживает). Для установленных ордеров - это его номер, что позволит проверить состояние этого ордера после рестарта скрипта.

Что касается позиции, то необходимо сканировать прошедшие сделки при старте - и здесь тоже идентификатор, который тоже будет добавлен к сделке по ордеру. Можно также учитывать только сделки по запомненным ордерам, но это если алгоритм запрещает чужие операции.

Так что ничего сложного. Просто эти методы необходимы в любом случае. Колбеки же просто облегчают код в части проверок в процессе работы алгоритма скрипта. Я просто очень давно поймал ситуации когда колбека не было и поэтому не использую их, даже если сейчас говорят, что всё надежно. При работе с деньгами я предпочитаю простые, надежные методы.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Так у них подход такой же, только набор параметров другой. Работает простая статистика - если есть набор данных за года, который кластеризуется по параметрам. При этом данные - это текущие показатели датчиков: скорость сделок, показатели сделок, скорость установки снятия заявок в стакане, скорость движения цены - читай реакция и т.д. Также и внешние данные - новости, арбитраж...

Т.о. задача не сказать куда, а сказать как. Нет попытки предсказать цену, а просто анализ реакций на огромной базе статистических данных. Поэтому и необходимы мощности, т.к. как текущие данные большие, так и набор базы весов модели большой, а решение необходимо принимать за единицы млс.

Вы же пробуете использовать набор показателей, который "как бы" описывает состояние, но по факту показывает уже прошедшую реакцию на состояние. Т.к. рынок все же содержит некий лаг, то да, возможно поймать конец движения.
Но всё же физ. лицам гораздо проще работать с рисками используя достаточно простые модели, а не пробовать обогнать тех, кто будет заведомо быстрее в принятии решений.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Вы просто повторяете давно пройденный путь квантами. Проблема только в одном - окно прогнозирования мало и размер движения мал. Поэтому необходимы объёмы и индивидуальные договора с биржей по комиссиям.

Физ. лицам уже сложнее, т.к., как минимум, не у всех в подвале установлена ферма серверов.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Если всё было так просто, то физики уже давно бы захватили рынок. Но нет, как раз наоборот.
В отличие от механистических систем, у "рынка" нет инерции. Может показаться что есть, и на этом и построены большинство стратегий, но цена никому ничего не обязана. Она может мгновенно изменить вектор движения, скорость и ускорение. И уже через мгновение - опять. Физический, наблюдаемый мир так не устроен.

Аналитической формулы для силы, движущей цену, нет. Вы можете только построить некую стохастическую модель, которая будет работать в некой узкой области и она действительно будет учитывать прошлое, но это всё равно модель, например модель Геометрического броуновского движения (GBM) позволяет рассчитать конус вероятностей (Volatility Cone). Т.е. у Вас есть вероятности - не более.
Очередное зависание Lua-скрипта, в функции getDepoEx
 
Цитата
Oleg Kuzembaev написал:
Тест проводился на подключенном к серверу терминале, что является корректным условием для работы скрипта.
Зря Вы это написали, т.к. в таком случае документацию необходимо обновлять и точно указывать для каких методов не гарантируется корректная работа при отсутствии соединения. Более того, если в процессе работы происходит разрыв соединения и в этот момент скрипт выполняет метод, который может привести к "зависанию" терминала, то это уже совсем не корректно, а вполне себе ошибка. И чтобы её избежать необходимо понимать какие методы необходимо обрамлять предварительной проверкой на наличие соединения.
Не срабатывает лимитная заявка sendTransaction, хотя рыночная проходит, Знатоки, подскажите возможную причину
 
Проверяйте что возвращает метод sendTransaction. Если не пустая строка, то даст описание ошибки.

Формат вызова:

STRING result sendTransaction(TABLE transaction)

Параметры:

  • result – строка, содержащая текст ошибки, если она случилась при обработке транзакции;
  • transaction – таблица с параметрами транзакции.
Внешние Lua-модули
 
Если искателю require задан путь к модулю и модуль имеет открытый код или скомпилирован с той же версией что и qlua, то он будет подключен.

А будет ли он работать уже зависит от того как он написан, какие у него зависимости. В общем случае - будет.
Восстановление заявок после обрыва связи
 
Цитата
Максим Трейдер написал:
но это всё ненадёждные методы
Надёжных в API нет. В этом и проблема. Искать по форуму - обсуждения были и 10 лет назад.
Восстановление заявок после обрыва связи
 
Это так часто уже обсуждалось на форуме. Необходимо организовать процедуру ожидания загрузки данных с сервера брокера. К сожалению, разработчики считают, что это не проблема, поэтому никаких конкретных методов для этого нет.

Что можно сделать:
- Сравнивать время последнего полученного пакета и время сервера, чтобы расхождение укладывалось в допустимую дельту.
- Также можно сравнивать время сервера и локальное время, но оно должно быть точным.
- Если в процессе работы были установлены ордера, то необходимо запоминать число записей в таблицах и после восстановления связи ожидать пока там не появится необходимое число записей, или хотя бы отличное от нуля.
- Ожидать загрузки таблиц денежных лимитов и лимитов срочной секции (если есть)

т.е. процедуры проверки, что в таблицах данные есть, что пакеты данных догнали время сервера.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Кажется, что Вы опять увлекаетесь теорией. Для чего существуют фильтры - для обработки сигналов.

В физическом мире, для примера, есть сигнал, необходимо его обработать и расшифровать, принять сообщение. Мы не гадаем, а просто обрабатываем.  Или, например, спектральный анализ радиотелескопа - тоже, обработали, получили пики. Т.е. задача сделать сигнал удобным для обработки, разобрать сигнал, принять сообщение.

Потом физики подумали - мы же можем это применить для ценового ряда. А что - тоже сигнал. Ок. Разобрали, отфильтровали, обработали. А дальше то что? Сигнал стал красивым, есть его характеристики. Но это принятый сигнал.
В реальном мире, в большинстве случаев, это бы сказало что будет дальше. Но для рынка не скажет.

Потому стали выдумывать разные подходы, искать закономерности. Т.е. гадать, на самом деле. В итоге, можно было бы просто взять карандаш и обвести график некой линией, и выполнять такие же упражнения.
Всё сводится к предположениям: если цена выше, ниже отфильтрованной линии; пересекает линию; две-три отфильтрованные линии пересекаются и т.д. и т.д. И всё это носит вероятностный характер. Что кардинально отличается от того как применяются фильтры и для чего они были придуманы изначально.

Так называемые Кванты, когда ресурсы и скорости позволили это, сразу ушли от этого наскального творчества и перешли на анализ в реальном времени. Действительно, если задача найти закономерности в поведении участников, а именно это и есть основная задача, то необходимо анализировать именно это - данные стакана, ленту сделок, реакцию на эти изменения. Т.е. что происходит с заявками в этот момент времени, как участники реагируют, снимают, устанавливают заявки, совершают сделки, с какой скоростью, объемами и т.д. И здесь уже закономерности важны, т.к. психология толпы вполне себе закономерна.

Это не значит, что фильтры и анализ прошлого совсем бесполезны. У рынка, т.е. у участников, тоже есть инерция. Поэтому какое-то время можно строить предположения на базе прошлого. Но приходится постоянно изменять параметры, проверять, контролировать. В текущих реалиях это вполне можно отдать какому-то агенту, т.к. это очень муторная, скучная работа.

Т.о. нет идеального фильтра. Они примерно одинаковы, решают одну задачу - нарисовать карандашом линию. Свечи - это тоже, вполне себе, фильтр. А т.к. многие поколения участников проходят один и тот же путь, то 200 EMA становится чем-то магическим, от которой цена отскакивает. И действительно может. Почему, потому что работает обратная связь. Т.е. хвост виляет собакой.

Цитата
VPM написал:
Почему это логично:   1. Защита от "Ракет", не шортить инструмент, который растет вертикально (где темп роста 1\% в бар, а шум всего 0.2\%).   2. Фильтрация флета, заключается в понимании факта, что выход за Сигму в боковике — это дар, а в тренде — это ловушка.   3. Ну и конечно Нормировка. Сравнивая проценты со скоростью, мы получаем безразмерный коэффициент - Здоровья тренда.В своей реализации сделал инженерное предложение. Добавил в "подвал" к осциллятор Z-Prob, вторую линию — TSI (Trend Strength). Теперь:  * Когда Z-Prob в зоне экстремума, а TSI идет вниз — это Идеальный вход.  * Когда оба на максимуме — это Сильный импульс (не трогать!).Добавив расчет этого коэффициента TSI в фабрику, для финальной фильтрации сигналов получил фактически мозг будущего робота.
Мне сложно комментировать это, т.к. это просто эмоции. Хотя вполне себе можно напомнить о penny-stock, которые вполне себе шортят на выносах, "наплевав на всякую статистику", т.к. это банальный pump.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Цитата
VPM написал:
Nikolay,  Мне не понятно почему Вы делаете такой вывод? На чем основываетесь?
Потому что фильтр - это алгоритм для существующего сигнала. Фурье - это тоже фильтр. И проблемы у него такие же как у фильтра. Т.е. есть сигнал. Задача привести его к какому-то виду, выделить составляющие.
Применяем фильтр, радуемся.

Если же задача - сказать, а сколько будет завтра? То уже всё сложнее, т.к. фильтр может сказать фазу (частоту), амплитуду, но и все. Можете пробовать продолжать сигнал на шаг вперед, никто не мешает. Но дело-то в том, что сигнал может взять и развернуться, просто так, наплевав на вычисленную фазу. Неправильный сигнал... Если мы обрабатываем физические сигналы, то там фильтрация отлично работает. Если же у нас не сигнал, а хаотичные кнопко-нажатия участников рынка, то здесь уже поведенческие модели лучше будут работать.

Мои применения методики приводили к тому, что можно пробовать предсказывать на отрезке 5-10% от окна вперед, не больше. Делать далеко-идущие выводы - точно нет.

Банальная ансамблевая регрессия лучше работает, без всякого Фурье. Но и его можно оставить, просто чтобы оценить первую-вторую гармоники.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Цитата
Регрессионный канал. Статистическая (прошлые данные). Строится методом наименьших квадратов (LMS)
Это банальный подход. Регрессия чрез фильтр Калмана, да даже гармоническая регрессия уже более стабильны.

Что касается фильтра, то какой бы простой не использовали, это будет просто фильтрация, без определения значимых гармоник. И назначение - убрать шум. Никакого будущего не может быть предсказано. Т.е. тренд, направление фильтром не определяется.

Т.к. ценовые ряды стохастические, без явной автокорреляции, то и выход за канал, какой бы широкий он ни был (хоть 5 сигма) - не является чем-то сверхъестественным. А скорее вполне закономерным явлением.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Этим методикам уже столько лет. Детрендинг - это построение регрессионной модели ряда и получение остатка.
Циклы - это чаще всего преобразование Фурье, для получения периодических составляющих.

Если используете Питон, то можно это сделать достаточно просто.
Я для Lua писал библиотеку на С, проводящую такого рода операции.

Если же говорить о Квантах, то советую углубится в стохастические процессы и цепи Маркова.
Работа OnClose
 
Цитата
Oleg Kuzembaev написал:
Nikolay, благодарим вас за уточнение.

Мы можем зарегистрировать пожелание на доработку будущих версий. Подскажите, будем регистрировать?
Регистрируйте.
Работа OnClose
 
Цитата
Oleg Kuzembaev написал:
Добрый день.

Функция OnClose будет возвращена перед самым закрытием Рабочего места, но уже после того как закрыты все таблицы и окна. Таков дизайн этой функции.
Понять закрыто ли конкретное окно можно с помощью функции IsWindowClosed, что вам удалось продемонстрировать в изначальном сообщении.
Т.о. колбек OnClose приходит лишь при полном закрытии приложения терминала.
Если он будет вызван после, то этот колбек бесполезен.

В итоге понять, что терминал закрывается, и необходимо изменить алгоритм обработки окон, таблиц, графиков из main - нельзя. Поэтому и требуется флаг, сигнализирующий о начале процесса закрытия терминала.
Работа OnClose
 
Есть колбек 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 30.832 sleep
Tue May 12 15:07:59 2026 30.946 1 IsWindowClosed false label_params table: 00000205F30168A0 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.947 2 IsWindowClosed false label_params table: 00000205F3016BA0 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.947 3 IsWindowClosed false label_params table: 00000205F3017BA0 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.947 4 IsWindowClosed false label_params table: 00000205F3017160 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.947 5 IsWindowClosed false label_params table: 00000205F3018020 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.947 6 IsWindowClosed false label_params table: 00000205F30171A0 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.947 7 IsWindowClosed false label_params table: 00000205F30171E0 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.947 8 IsWindowClosed false label_params table: 00000205F3017F20 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.948 9 IsWindowClosed false label_params table: 00000205F3017A60 virt_test l_id 13.0 324.0
Tue May 12 15:07:59 2026 30.948 sleep

Окно ещё не уничтожено, но графика уже нет и данные метки не получены.
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 вызывался до уничтожения объектов.
Страницы: Пред. 1 2 3 4 5 6 7 8 9 10 11 ... 29 След.
Наверх