Работа колбеков

Страницы: Пред. 1 2
RSS
Работа колбеков
 
Цитата
TGB написал:
Цитата
nikolz написал:
что не так?
   Наверное, из-за того, что в таблице:
Код
   local  sec_list       =  {
    {'SPBFUT',  'MMU6' },
    {'SPBFUT',  'SiU6' },
    {'SPBFUT',  'RIU6' },
    {'SPBFUT',  'USDRUBF' },
    {'SPBFUT',  'CNYRUBF' },
    {'TQBR',  'SBER' },
    {'TQBR',  'GAZP' }
}
  

  классы заданы для реального рынка.
возможно  Вы правы пока не проверял.
Но тогда это прикольно. По крайней мере для меня.
-------------------------------
Полагаю ,что правильный скрипт запускается при любой погоде.
-------------------------
А эти тупо молчат нет никаких сообщений об ошибках или о нелетной погоде .  
 
сделал свой тест на демо сервере
---------------
Так как скрипт рабочий и большой, то расскажу основные моменты, чтобы было понятно , что и как
--------------------
У меня все колбеки сделаны одинаков  таким образом:
Код
OnOr der=function(t) setQ(4,t);   end
Функция setQ  помещает таблицу параметров колбека t и ключ колбека в очередь.
В этой функции я добавил в параметры значение точного счетчика Windows   Этот счетчик считает с квантом 0.1 мкс
--------------------------------
В функции main  я в цикле обрабатываю элементы очереди
В данном тесте я из значения точного счетчика вычитаю  сохраненное его значения при записи в очередь
Результат пишу в лог файл
-------------------------------------------
Таким образом в лог файле я получаю время задержки от вызова любого колбека до начала обработки параметров этого колбека в main
Т е фактически это время задержки от момента записи в очередь до момента начала обработки элемента из очереди
Код
t(мкс)=12.7
t(мкс)=8.6
t(мкс)=8.6
t(мкс)=3.1
t(мкс)=6.2
...
t(мкс)=59.1
t(мкс)=60.7
t(мкс)=60.0
t(мкс)=59.7
...
t(мкс)=32.9
t(мкс)=25.0
t(мкс)=14.1
t(мкс)=12.7

Т е время задержки примерно 0.00006 сек
 
Тест был направлен не на измерение скорости работы скрипта, а на влияние работы скрипта на работу терминала, его интерфейса в первую очередь. Какой смысл показать, да ещё на демо, что main работает быстро.
У меня на демо тоже всё прекрасно. Переключаешься на реальный и во время  открытия рынка - терминал вкладки переключает по 30 секунд.  Была бы возможность инструментально измерить работу самого терминала - так и вопросов не было бы.

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

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

Я готов это списать на влияние работы скриптов. Но тогда хотелось бы понять, что именно и какие методы оказывают максимальное влияние. Иначе делаешь банальный скрипт, а эффект как будто модель на 100b считаешь.
 
Ок. Теперь исключим колбеки.

Берем скрипт и выкидываем их, будем просто читать раз в секунду цены и класть их в ту же очередь. Логи тоже уберем.
Код
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 id = 'script1'
local logFile

local function log_tostring(...)
  local args = table.pack(...)
  if args.n == 1 then
    return tostring(args[1])
  end
  for i = 1, args.n do
    args[i] = tostring(args[i])
  end
  return table.concat(args, " ", 1, args.n)
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 last_data = {}


local rev_lis = {}
for i = 1, #sec_list do
    rev_lis[sec_list[i][2]] = i
    last_data[sec_list[i][2]] = {}
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 IsWindowClosed    = _G.IsWindowClosed

local Queue = {}
Queue.__index = Queue

function Queue.new()
   return setmetatable({first = 1, last = 0 }, Queue)
end
---  Записать в очередь --
function Queue:push(v)
   if v == nil then error('!!! Ошибка. Параметр v функции QueuePush nil') end
   local last = self.last + 1
   self[last] = v
   self.last = last
   return last - self.first + 1    -- количество элементов в очереди ---
end
---  Читать очередь  --
function Queue:pop()
   local  first =  self.first
   if  first > self.last then return nil   end
   local v, v1  =  self[first], self[first + 1]
   self.first = first + 1
   self[first] =  nil
   v1 = type (v1) == 'string' and #v1 or 0
   return  v, v1   -- v1 -длина следующего элемента (если строка) ---
end

-- Текущий размер очереди ---
function Queue:size()
   return self.last - self.first + 1
end

local queue = Queue.new()

local function process_queue()
    local n = 0
    local res = queue:pop()
    while res do
        n = n + 1
        local last_price = res[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[res[2]], 1, tostring(last_price), last_price)
        -- log(id, ' --- set pop value',  n, rev_lis[res[2]], last_price)
        res = queue:pop()
    end
    return n
end

local function GetPrice(class_code,  sec_code)
    local last_price = tonumber((getParamEx(class_code,  sec_code, 'LAST') or {}).param_value) or nil
    if last_data[sec_code][1] == last_price then return end
    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
    last_data[sec_code][1] = last_price
    queue:push{class_code,  sec_code, last_price}
end

local function event_callback(_, msg, par1, par2)
    if (msg == _G.QTABLE_CLOSE) then
        isRun = false
    end
    if msg == _G.QTABLE_CHAR then
        SetCell(t_id, par1, 2, tostring(par2), par2)
    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, "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
    _G.SetTableNotificationCallback(t_id, event_callback)
end

function _G.main()

    CreateTable()

    -- logFile = io.open(path.."\\load_"..id.."txt", "w")

    local lt = 0
    local req_int = 1

    while isRun do

        local cwt = os.time()
        if cwt - lt > req_int then
            lt = cwt
            for i = 1, #sec_list do
                GetPrice(sec_list[i][1], sec_list[i][2])
            end
        end

        -- local s = os.clock()
        -- log(id, 'Enter main loop', os.date())
      IsWindowClosed(t_id)
        local n = process_queue()
        bl1.func1()
        bl2.func100()
        bl3.func200()
        bl4.func300()
        -- log(id, 'Exit main loop', os.clock()-s, 'in queue', n)
        -- 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


Запускаем скрипты и нет влияния. Хоть 5 скриптов запускай. Но как только подключаем OnParam и пишем в очередь, то уже на трех заметно влияение на работу интерфейса теримнала.
 
Цитата
Nikolay написал:
Но как только подключаем OnParam и пишем в очередь, то уже на трех заметно влияение на работу интерфейса теримнала.
   Можно запустить скрипт из одной строки (хоть в песочнице, хоть в продуктиве):
Код
 for i = 1, 5000000000 do   end

  и любоваться на то, как весь интерфейс QUIK зависнет пока будет выполняться эта строка в потоке обрабатывающем:
1) все коллбеки (всех скриптов и всех пользовательских таблиц);
2) все графики;
3) весь графический интерфейс QUIK;
4) возможно что-то еще (например, трафик данных?).
  Разработчикам QUIK не стыдно наблюдать такое который год подряд?
 
Цитата
Nikolay написал:
Вот прямо сейчас смотрю на один из терминалов - банально время сервера в строке состояния изменяется скачками, замирая на длительный период. И так каждый раз при старте, что, в теории, нормально, т.к. приходят пакеты. Но не 15 минут же. На терминалах других брокеров это же занимает минуту, 5-ять иногда. И в это время отчетливо видно как с трудом совершаются любые операции с интерфейсом.

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

Я готов это списать на влияние работы скриптов. Но тогда хотелось бы понять, что именно и какие методы оказывают максимальное влияние. Иначе делаешь банальный скрипт, а эффект как будто модель на 100b считаешь.
О скачках времени сервера читайте здесь:
https://forum.quik.ru/forum1/topic9514/
 
Сами скачки волне ожидаемы в клиент-серверном взаимодействии. Ну не пришли ещё пакеты, бывает, если оставить в сторону реализацию обмена.
Но вот почему в эти периоды терминал становится "тупым" - уже не столь очевидно и вызывает вопросы. Как будто сделали блокирующий цикл, ожидающий пакеты, а интерфейс - да кому он нужен.
 
Цитата
Nikolay написал:
Но как только подключаем OnParam и пишем в очередь
А если какой-нибудь другой колбэк заюзать? Любой достаточно частый.
 
Просто мысли вслух. Таблицы обезличенных сделок и левел2 это тиковые данные, в то время как  OnParam приходит пакетами примерно раз в секунду. Я кто тому что и очереди и буфер  и тайм фреймы у них ме могут быть одинаковы. OnParam требует индивидуального подхода и подписок на параметры. Может собака в сомой обработке зарыта?
 
OnParam и OnQuote самые быстрые. Остальные не так часто.

Поэтому добавим пустой OnParam. Почти нет влияния. Хотя есть уже ощущение, что не так, как без скриптов. Опять же - только визуальный контроль.

Добавим внутрь колбека одну строку

tonumber((getParamEx(class_code,  sec_code, 'LAST') or {}).param_value)

И уже явно что-то есть.

Также, чисто субъективно, кажется, что если дать ностояться, т.е. поработать скриптам, то уже как-то сильнее влияние.

Еще из странного - зависит от сервера брокера. Я тестирую на ВТБ, как одном из "тормозных", переключаешь на другой сервер - и уже не так заметно влияние.
 
Цитата
VPM написал:
Просто мысли вслух. Таблицы обезличенных сделок и левел2 это тиковые данные, в то время как  OnParam приходит пакетами примерно раз в секунду. Я кто тому что и очереди и буфер  и тайм фреймы у них ме могут быть одинаковы. OnParam требует индивидуального подхода и подписок на параметры. Может собака в сомой обработке зарыта?
Я довольно редко использую обезличенные сделки и getQuoteLevel2. В данном тесте getQuoteLevel2 просто для создания какой-то нагрузки на qlua API. Простые адекватные расчёты в main не особо влияют.
Тот же ВТБ и без скриптов стартует, да и работает, очень медленно. Я думал Кит - медленный (а там большие потоки данных). Но нет ВТБ выдает результаты в информационном окне типа такого:

средняя задержка данных = 1.877.
Максимальная задержка данных = 29.656.
 
Оставил скрипты тестовые с колбеками запущенными. В итоге:

задержка данных при обмене с сервером = 11.109
средняя задержка данных = 26.384.
Максимальная задержка данных = 175.063

Как только остановил скрипты - побежал счётчик полученных пакетов, отставших записей в информационном окне. И пока данные бегут, то терминал и без скриптов не особо спешит.
 
Цитата
Nikolay написал:
Оставил скрипты тестовые с колбеками запущенными. В итоге:

задержка данных при обмене с сервером = 11.109
средняя задержка данных = 26.384.
Максимальная задержка данных = 175.063

Как только остановил скрипты - побежал счётчик полученных пакетов, отставших записей в информационном окне. И пока данные бегут, то терминал и без скриптов не особо спешит.
Вывод -тормозят Ваши скрипты.
Измерьте время их исполнения.
------------------
Кроме того, тормозит сервер брокера . И это не зависит от скриптов.
Последнее время  у Сбера видны задержки в выставлении заявок
Т е уходит заявка из терминала а в стакане она появляется с заметной задержкой визуально.  
Так как задержка реакция нашего глаза  не менее 0.2 сек то можно говорить что заявка выставляется с задержкой сотни миллисекунд.
------------------------  
Объяснить это можно лишь задержкой на стороне сервера.
Два варианта
1) Умышленная задержка на стороне сервера.
2) Большие очереди заявок.
-------------------------------------  
Но так как эта задержка существует и при спокойном рынке, то скорее всего это 1 вариант
 
 
Цитата
Nikolay написал:
Тест был направлен не на измерение скорости работы скрипта, а на влияние работы скрипта на работу терминала, его интерфейса в первую очередь. Какой смысл показать, да ещё на демо, что main работает быстро.
У меня на демо тоже всё прекрасно. Переключаешься на реальный и во время  открытия рынка - терминал вкладки переключает по 30 секунд.  Была бы возможность инструментально измерить работу самого терминала - так и вопросов не было бы.
Вы не поняли мой тест.
Тест проверяет быстродействие выполнения колбеков, включая обработку очереди .
 
Цитата
nikolz написал:
Вывод -тормозят Ваши скрипты.
Измерьте время их исполнения.
------------------
Но так как эта задержка существует и при спокойном рынке, то скорее всего это 1 вариант
Тексты скриптов выложены выше. Что там конкретно приводит к "тормозам"? Просто интересно, раз такое категоричное утверждение.

Цитата
nikolz написал:
Тест проверяет быстродействие выполнения колбеков, включая обработку очереди .
У меня и не было задачи проверять работу колбеков, а влияние их наличия на скорость работы интерфейса терминала. Буду рад если предложите другой инструментальный тест.  
 
Ок. Чтобы наглядно показать как влияют колбеки на работу терминала запустил такой тест.

Запускаем скрипты, в которых есть только колбек OnParam с одной строкой внутри getParamEx(class_code,  sec_code, 'LAST')

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

https://disk.360.yandex.ru/i/TOFhfjD7EHQtfA

Скрипт банальный. Раз в секунду запрашиваем getParamEx. Также для проверки влияния getInfoParam были добавлены запросы в main, но как показало - не влияет. Лог тоже закомментирован.
Если есть колбек OnParam - влияние видно глазами, через скорость изменения данных в информационном окне. Если его нет, то наличие работающего скрипта визуально не заметно.

Судя по всему, вызывается OnParam на каждую пропущенную запись для какого-то служебного поля, что и приводит к такому. Но это просто догадка. Если так, то это совсем не корректно.

Отдельно также можно сказать, что если не было соединения достаточно долго, то брокер кидает OnCleanUp (это видно по логу и по параметру "число записей" в информационном окне, он скидывается в 0), судя по всему, срабатывает условие "смена сессии". И в итоге загрузка данных происходит долго, очень долго. По крайней мере у ВТБ. Другие бырокеры побыстрее.

Наблюдая за инфомационнгым окном у ВТБ - достатоно часто видно как бежит число "отставшая запись" и в это время терминал занят этим и будет такое же влияние.
Код
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 id = 'script3'
local logFile

local function log_tostring(...)
  local args = table.pack(...)
  if args.n == 1 then
    return tostring(args[1])
  end
  for i = 1, args.n do
    args[i] = tostring(args[i])
  end
  return table.concat(args, " ", 1, args.n)
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 last_data = {}


local rev_lis = {}
for i = 1, #sec_list do
    rev_lis[sec_list[i][2]] = i
    last_data[sec_list[i][2]] = {}
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 IsWindowClosed    = _G.IsWindowClosed

local Queue = {}
Queue.__index = Queue

function Queue.new()
   return setmetatable({first = 1, last = 0 }, Queue)
end
---  Записать в очередь --
function Queue:push(v)
   if v == nil then error('!!! Ошибка. Параметр v функции QueuePush nil') end
   local last = self.last + 1
   self[last] = v
   self.last = last
   return last - self.first + 1    -- количество элементов в очереди ---
end
---  Читать очередь  --
function Queue:pop()
   local  first =  self.first
   if  first > self.last then return nil   end
   local v, v1  =  self[first], self[first + 1]
   self.first = first + 1
   self[first] =  nil
   v1 = type (v1) == 'string' and #v1 or 0
   return  v, v1   -- v1 -длина следующего элемента (если строка) ---
end

-- Текущий размер очереди ---
function Queue:size()
   return self.last - self.first + 1
end

local queue = Queue.new()

local function process_queue()
    local n = 0
    local res = queue:pop()
    while res do
        n = n + 1
        local last_price = res[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[res[2]], 1, tostring(last_price), last_price)
        -- log(id, ' --- set pop value',  n, rev_lis[res[2]], last_price)
        res = queue:pop()
    end
    return n
end

local function GetPrice(class_code,  sec_code)
    local last_price = tonumber((getParamEx(class_code,  sec_code, 'LAST') or {}).param_value) or nil
    if last_data[sec_code][1] == last_price then return end
    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
    last_data[sec_code][1] = last_price
    queue:push{class_code,  sec_code, last_price}
end

local function event_callback(_, msg, par1, par2)
    if (msg == _G.QTABLE_CLOSE) then
        isRun = false
    end
    if msg == _G.QTABLE_CHAR then
        SetCell(t_id, par1, 2, tostring(par2), par2)
    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, "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
    _G.SetTableNotificationCallback(t_id, event_callback)
end

function _G.OnParam(class_code,  sec_code)
    local last_price = tonumber((getParamEx(class_code,  sec_code, 'LAST') or {}).param_value) or nil

   -- if rev_lis[sec_code] then
      -- GetPrice(class_code,  sec_code)
   -- end
end

local last_rec, last_time, time_diff


function _G.main()

    CreateTable()

    logFile = io.open(path.."\\load_"..id..".txt", "w")

    local lt = 0
    local req_int = 1

    while isRun do

        local cwt = os.time()
        if cwt - lt > req_int then
            lt = cwt
            for i = 1, #sec_list do
                GetPrice(sec_list[i][1], sec_list[i][2])
            end
        end

   last_rec   = tonumber((tostring(getInfoParam('LASTRECORDTIME') or ''):gsub(':', ''))) or 0
   local_time = (tonumber((tostring(getInfoParam('LOCALTIME') or ''):gsub(':', ''))) or 0)
   time_diff  = math.abs(local_time - last_rec) + 10

        -- log(id, 'Enter main loop')
   IsWindowClosed(t_id)
        local n = process_queue()
        bl1.func1()
        bl2.func100()
        bl3.func200()
        bl4.func300()
        -- log(id, 'Exit main loop', 'in queue', n)
        -- 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
 
В моем примере выше, архитектура устроена так что, все колбэки ни чего не делают, они "ТУПЫЕ", их задача сводится просигнализировать "Терминал сообщает, что параметры инструмента изменились". Это другой уровень абстракции.
В этой архитектуре главное: QUIK callbacks нельзя превращать в бизнес-события напрямую

Код
function OnParam(class_code, sec_code)
    if not Robot or not c.is_run or not c.IS_WARMED_UP or Robot.Is_Inside_HotPath then return end
    if Robot.ProcessParamEvent and type(Robot.ProcessParamEvent) == "function" then 
        Robot:ProcessParamEvent(class_code, sec_code) 
    end
end


У Вас же идет вызов тяжеловеса, еще нужно понять где фильтруется:
Код
function _G.OnParam(class_code,  sec_code)
    local last_price = tonumber((getParamEx(class_code,  sec_code, 'LAST') or {}).param_value) or nil

   -- if rev_lis[sec_code] then
      -- GetPrice(class_code,  sec_code)
   -- end
end
 
Цитата
VPM написал:
У Вас же идет вызов тяжеловеса, еще нужно понять где фильтруется
Это нагрузочный тест, фильтр закомментирован. В этом и смысл. Также, если getParamEx - это тяжелая функция, то тогда можно смело даже не думать о написании чего-то существенного на QLUA.
Эта функция, по идее, максимально быстро выдает данные из кеша терминала. Никуда не ходит, просто берет из хранилища.

Почему-то кажется, что получение типа, да ещё сравнение с строкой (её бы в константу загнать, чтобы Lua каждый раз не бегал в пул строк), а потом ещё и вызов функции, потяжелее будет.
Код
    if not Robot or not c.is_run or not c.IS_WARMED_UP or Robot.Is_Inside_HotPath then return end
    if Robot.ProcessParamEvent and type(Robot.ProcessParamEvent) == "function" then 
        Robot:ProcessParamEvent(class_code, sec_code) 
    end

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

Т.о., если даже такое - это тяжело для Квика, то, как сказал выше, можно расслабиться.

Но почему-то кажется, что проблема все-же в архитектуре. OnParam - по идее, должен быть событием реального времени. Если он будет вызываться на все приходящие записи после OnCleanUp, да ещё вечером, когда этих записей много, то терминал, вместо работы, будет занимается разбором пакетов. Я такое второй день наблюдаю на ВТБ, и даже без скриптов в эти моменты терминалу плохо. При этом теперь OnCleanUp - это рядовое событие, стоит разорвать связь на не такое и длительное время и всё - получите всё с начала торгов. Данные баров, сделки, ордера - да. А OnParam зачем?
 
И ещё из странного. Добавляем в скрипт такое:

local function ds_callback(ds, index)
   PrintDbgStr(log_tostring('ds_callback', index, 'last_price', ds:C(index)))
end


function _G.main()

   logFile = io.open(path.."\\"..id..".txt", "w")

   local ds = _G.CreateDataSource('TQBR', 'SBER', 1)
   if not ds then
       isRun = false
   end
   ds:SetUpdateCallback(function(...) return ds_callback(ds, ...) end)

   while isRun do
       sleep(10)
   end
end

Что пробуем понять - что будет если остановить скрипт, брокер выдаст OnCleanUp и начнет передавать все пакеты заново. Предположение, что функция в SetUpdateCallback выдаст все, начиная с первого индекса.

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

Т.о. массовая передача отставших пакетов или даже всех от старта сессии, приведет к вызову всех колбеков заново. И скрипт будет это разбирать, вместо работы. И нагрузка на терминал, соответственно, т.к. на каждый пакет будут вызваны все назначенные пакеты. Вот тебе и событийная модель. Казалось бы, событие давно прошло - но нет, получи заново.
 
Вы абсолютно правы в своих рассуждениях, и Ваш анализ обнажает главные "болевые точки" архитектуры QUIK и его связки с Lua. Разработчики терминала создали механизм, который при определённых условиях начинает работать против стабильности всей системы. Ваше наблюдение за поведением после OnCleanUp (или при обрыве связи у ВТБ) абсолютно точно описывает проблему архитектуры QUIK. Вы в этом лучше меня разбираетесь. Я лишь хочу сказать о срытой проблеме выделение памяти (Garbage Collection). Каждый вызов getParamEx возвращает Lua-таблицу с полями param_value, param_image, param_type и result.На миллионах вызовов в секунду (при подгрузке истории) создаются миллионы временных таблиц и строк. Встроенный в QUIK сборщик мусора Lua не успевает их утилизировать. Происходит микро - зависание потока, обрабатывающего скрипт. Я не давно вывел т. обезличенных сделок, волосы дыбом, что сделали из ленты! Выведите посмотрите ради интереса. Думаю тут таже история. Можно бесконечно возмущаться, а воз и ныне там. Я о другом как быть в этой ситуации?
Разберу мой код выше. Посмотрите в примере именно на этот момент:

Код
while c.is_run do
    local cur_size = _G.Robot.ds:Size()
    if cur_size > last_known_size then
        last_known_size = cur_size
        c.BAR_CACHE_IDX = cur_size
        _G.Robot:ExecuteQuantumClosure(
            _G.Robot.ds,
            cur_size
        )
    end
    if _G.Robot.ProcessLearningQueue then 
        _G.Robot:ProcessLearningQueue()
    end

    if _G.Robot.TriggerUIRender then 
        _G.Robot:TriggerUIRender()
    end

    local current_work_time = GetMilliseconds() - TT
    c.LAST_EXECUTION_LAG = current_work_time
    local calculated_sleep = 200 - current_work_time

    if calculated_sleep <= 0 then 
        calculated_sleep = 1 
    end

    sleep(calculated_sleep)
    TT = GetMilliseconds()
end


Это не просто цикл. Это планировщик (scheduler).Что здесь уже правильно:
1. Баровый контур отделён от событий QUIK. Это главное. сейчас:
QUIK callbacks
       |
       v
 накопление состояния
       |
       v
     main()
       |
       v
ExecuteQuantumClosure()

То есть:
* OnQuote()    не запускает принятие решений;
* OnAllTrade() не запускает MDP;
* OnParam()    не вызывает расчёты S/D.
* DS                не вызывает расчёт индикаторов. И это уже правильно, у всех разные периоды тик, сеунда, минута.

2. Изохронность. задан период: sleep(200) То есть: 5 Hz scheduler А внутри: баровая фиксация; очередь обучения; рендеринг пользовательского интерфейса; пульс.
Это паттерн. Не, тик -> торговое решение -> ордер
            а, накопление событий + обработка, управляемая тактовым сигналом.

3. Выполнение квантового замыкания. Вот это фактически граница транзакции в MEO. Сейчас:
new bar
v
ExecuteQuantumClosure()
v
DSP
v
State Encoder
v
MDP
v
Decision
v
Execution
Именно здесь должен жить: НАЧАТЬ ТРАНЗАКЦИЮ; вычислить; подтвердить; совершить; хэш, а не в QUIK CALLBACK.
Ну по крайней мере сейчас так представляется.
 
Кто хочет поразвлекаться, вот тестовый скрипт. Запускам. Разрываем связь, ждем, запускаем. Когда в информационном окне закончится очередь отставших сообщений остановить. была возможность считать этот параметр оставили бы сами. Для наглядности можно включить PrintDbgStr в OnParam и посмотреть что приходит и с какой скоростью, после восстановления связи или в моменты когда терминал подгружает отставшие пакеты.

И почему все эти вызовы OnParam будут мусором, потому что если получить и вывести цену в колбеке, то она будет одинаковая, т.к. последняя. Т.е. можно предположить, что изменяется что-то служебное.

Также, включая PrintDbgStr в OnParam, увидел, что приходят данные даже не включенные в настройках. На том же ВТБ - включены только облигации, т.к. открыта ТТТ по ним. Но после разрыва связи получаю такое
Цитата
00009130    17:07:08.617    [15592] OnParam 9130 SPBOPT MX270000BU6 last_price 0.0    
00009131    17:07:08.618    [15592] OnParam 9131 SPBOPT MX275000BU6 last_price 0.0    
00009132    17:07:08.618    [15592] OnParam 9132 SPBOPT MX280000BU6 last_price 0.0    
00009133    17:07:08.618    [15592] OnParam 9133 SPBOPT MX285000BU6 last_price 0.0    
00009134    17:07:08.618    [15592] OnParam 9134 SPBOPT MX290000BU6 last_price 0.0    
00009135    17:07:08.618    [15592] OnParam 9135 SPBOPT MX295000BU6 last_price 0.0    
00009136    17:07:08.618    [15592] OnParam 9136 SPBOPT MX300000BU6 last_price 0.0    
00009137    17:07:08.618    [15592] OnParam 9137 SPBOPT MX305000BU6 last_price 0.0    
00009138    17:07:08.618    [15592] OnParam 9138 SPBOPT MX310000BU6 last_price 0.0    
00009139    17:07:08.618    [15592] OnParam 9139 SPBOPT MX315000BU6 last_price 0.0    
00009140    17:07:08.619    [15592] OnParam 9140 SPBOPT MX320000BU6 last_price 0.0    
00009141    17:07:08.619    [15592] OnParam 9141 SPBOPT MX325000BU6 last_price 0.0    
00009142    17:07:08.619    [15592] OnParam 9142 SPBOPT MX340000BU6 last_price 0.0    
00009143    17:07:08.619    [15592] OnParam 9143 SPBOPT SF595BI6 last_price 0.0    
00009144    17:07:08.619    [15592] OnParam 9144 SPBOPT SF600BI6 last_price 0.0    
00009145    17:07:08.619    [15592] OnParam 9145 SPBOPT SF605BI6 last_price 0.0    
00009146    17:07:08.619    [15592] OnParam 9146 SPBOPT SF610BI6 last_price 0.0    
00009147    17:07:08.619    [15592] OnParam 9147 SPBOPT SF615BI6 last_price 0.0    
00009148    17:07:08.619    [15592] OnParam 9148 SPBOPT SF620BI6 last_price 0.0    
00009149    17:07:08.620    [15592] OnParam 9149 SPBOPT SF625BI6 last_price 0.0    
00009150    17:07:08.620    [15592] OnParam 9150 SPBOPT SF630BI6 last_price 0.0    
00009151    17:07:08.620    [15592] OnParam 9151 SPBOPT SF635BI6 last_price 0.0    
00009152    17:07:08.620    [15592] OnParam 9152 SPBOPT SF640BI6 last_price 0.0    
00009153    17:07:08.620    [15592] OnParam 9153 SPBOPT SF645BI6 last_price 0.0    
00009154    17:07:08.620    [15592] OnParam 9154 SPBOPT SF650BI6 last_price 0.0    
00009155    17:07:08.620    [15592] OnParam 9155 SPBOPT SF655BI6 last_price 0.0    
00009156    17:07:08.620    [15592] OnParam 9156 SPBOPT SF660BI6 last_price 0.0    
00009157    17:07:08.621    [15592] OnParam 9157 SPBOPT SF665BI6 last_price 0.0    
00009158    17:07:08.622    [15592] OnParam 9158 SPBOPT SF670BI6 last_price 0.0    
00009159    17:07:08.622    [15592] OnParam 9159 SPBOPT SF675BI6 last_price 0.0    
00009160    17:07:08.622    [15592] OnParam 9160 SPBOPT SF680BI6 last_price 0.0    
00009161    17:07:08.622    [15592] OnParam 9161 SPBOPT SF685BI6 last_price 0.0    
00009162    17:07:08.622    [15592] OnParam 9162 SPBOPT SF690BI6 last_price 0.0    
00009163    17:07:08.623    [15592] OnParam 9163 SPBOPT SF695BI6 last_price 0.0

И много другого мусора по всем классам и инструментам даже не включенных в поток данных. Т.е. это банальная DDOS атака колбеков в скриптах, т.к. приходят десятки тысяч вызовов, по всем классам инструментов.
Зачем тогда эта настройка "умным заказом данных", если получаем всё...
Код
local path = _G.getScriptPath()
package.path = path.."/?.lua;"

local id = 'script1'
local logFile

function _G.OnInit(script_path)
   id = ((script_path:match("[^/\\]+$")):gsub('.lua', ''))
end

local function log_tostring(...)
  local args = table.pack(...)
  if args.n == 1 then
    return tostring(args[1])
  end
  for i = 1, args.n do
    args[i] = tostring(args[i])
  end
  return table.concat(args, " ", 1, args.n)
end

local function log(...)
    if logFile==nil then return end
    logFile:write(log_tostring(...).."\n");
    logFile:flush();
end

local sleep         = _G.sleep
local message       = _G.message
local isRun         = true

local last_rec, last_time, time_diff
local start_rec = tonumber(getInfoParam('LASTRECORD') or '') or 0

local q = 0
local t = os.time()
local start

function _G.OnParam(class_code,  sec_code)
   if q == 0 then t = os.time() end
   q = q + 1
   local last_price = tonumber((getParamEx(class_code,  sec_code, 'LAST') or {}).param_value)
   -- PrintDbgStr(log_tostring('OnParam', q, class_code,  sec_code, 'last_price', last_price))
end

local function log_stat()

   local end_rec   = tonumber(getInfoParam('LASTRECORD') or '') or 0
   local loaded   = end_rec - start_rec
   t = os.time() - t
   local str = log_tostring(id, ': done', ', processed callbacks: ', q, ', in: ', t, 'sec', ', avg: ', q == 0 and '---' or t/q)
   message(str)
   local str1 = log_tostring(id, ': start_rec: ', start_rec, ', end_rec: ', end_rec, ', loaded records: ', loaded, ', avg: ', loaded == 0 and '---' or t/loaded)
   message(str1)
   if io.type(logFile) == 'file' then
       log(str)
       log(str1)
      logFile:close()
   end
end


function _G.main()

    logFile = io.open(path.."\\"..id..".txt", "w")

    while isRun do
        sleep(10)
    end
end

function _G.OnDisconnected()
   message('OnDisconnected, current_rec: '..tostring(tonumber(getInfoParam('LASTRECORD') or '') or 0))
   q = 0
   t = os.time()
end

function _G.OnConnected(flag)
   if flag then
      start_rec   = tonumber(getInfoParam('LASTRECORD') or '') or 0
      message('OnConnected, start_rec: '..tostring(start_rec))
      q = 0
      t = os.time()
      start = true
   end
end

function _G.OnStop()
    isRun = false
   log_stat()
end
 
Ок, вопрос разработчикам:

Почему callback OnParam игнорирует настройки терминала в разделе "Получение данных"?

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

В тестовом скрипте есть только OnParam и вывод данных через PrintDbgStr, нет заказов каких-то данных.

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

Также вопрос о самой настройке "умным заказом данных": если она допускает такой поток данных (а он явно есть, раз есть вызов OnParam), то о каком снижении потока передаваемых данных может быть речь?

Для примера:
...............
00002129    11:44:06.628    [1332] OnParam 2128 SPBFUT RAU6 last_price 1246.0    
00002130    11:44:06.629    [1332] OnParam 2129 SPBFUT FLU6 last_price 7975.0    
00002131    11:44:06.629    [1332] OnParam 2130 SPBFUT VBU6 last_price 5871.0    
00002132    11:44:06.629    [1332] OnParam 2131 SPBFUT CRZ6 last_price 12.167    
00002133    11:44:06.629    [1332] OnParam 2132 SPBFUT SiU6 last_price 80734.0    
00002134    11:44:06.629    [1332] OnParam 2133 SPBFUT ALU6 last_price 2220.0    
00002135    11:44:06.629    [1332] OnParam 2134 SPBFUT VKU6 last_price 1544.0    
00002136    11:44:06.629    [1332] OnParam 2135 SPBFUT EDU6 last_price 1.1432    
00002137    11:44:06.630    [1332] OnParam 2136 CROSSRATE CNY last_price 0.0    
00002138    11:44:06.690    [1332] OnParam 2137 CROSSRATE GLD last_price 0.0    
00002139    11:44:06.690    [1332] OnParam 2138 CROSSRATE CNY last_price 0.0    
00002140    11:44:06.750    [1332] OnParam 2139 CROSSRATE CNY last_price 0.0    
00002141    11:44:06.813    [1332] OnParam 2140 CROSSRATE CNY last_price 0.0    
00002142    11:44:06.935    [1332] OnParam 2141 SPBFUT SZU6 last_price 573.0    
00002143    11:44:06.935    [1332] OnParam 2142 SPBFUT RAU6 last_price 1246.0    
00002144    11:44:06.936    [1332] OnParam 2143 SPBFUT PHU6 last_price 5835.0    
00002145    11:44:06.936    [1332] OnParam 2144 SPBFUT VKU6 last_price 1544.0    
00002146    11:44:06.936    [1332] OnParam 2145 SPBFUT MMU6 last_price 2306.3    
00002147    11:44:06.936    [1332] OnParam 2146 SPBFUT GZU6 last_price 9578.0    
00002148    11:44:06.937    [1332] OnParam 2147 SPBFUT MXU6 last_price 230650.0    
00002149    11:44:06.937    [1332] OnParam 2148 SPBFUT RIU6 last_price 90110.0    
00002150    11:44:06.937    [1332] OnParam 2149 SPBFUT BRU6 last_price 88.65    
00002151    11:44:06.937    [1332] OnParam 2150 SPBFUT CKU6 last_price 6502.0    
00002152    11:44:06.937    [1332] OnParam 2151 SPBFUT LKU6 last_price 48859.0    
00002153    11:44:06.937    [1332] OnParam 2152 CROSSRATE GLD last_price 0.0    
00002154    11:44:06.937    [1332] OnParam 2153 CROSSRATE CNY last_price 0.0    
00002155    11:44:06.950    [1332] OnParam 2154 SPBFUT SNU6 last_price 15515.0    
00002156    11:44:06.950    [1332] OnParam 2155 SPBFUT MGU6 last_price 21122.0    
00002157    11:44:06.950    [1332] OnParam 2156 SPBFUT NMU6 last_price 7371.0    
00002158    11:44:06.950    [1332] OnParam 2157 SPBFUT GKU6 last_price 1191.0    
00002159    11:44:06.950    [1332] OnParam 2158 SPBFUT ALU6 last_price 2221.0    
00002160    11:44:06.950    [1332] OnParam 2159 SPBFUT IMOEXF last_price 2269.0    
00002161    11:44:06.950    [1332] OnParam 2160 SPBFUT X5U6 last_price 2115.0    
00002162    11:44:06.951    [1332] OnParam 2161 SPBFUT VBU6 last_price 5871.0    
00002163    11:44:06.951    [1332] OnParam 2162 SPBFUT SPU6 last_price 28329.0    
00002164    11:44:06.951    [1332] OnParam 2163 SPBFUT YDU6 last_price 4053.0    
00002165    11:44:06.951    [1332] OnParam 2164 SPBFUT NVU6 last_price 10553.0    
00002166    11:44:06.951    [1332] OnParam 2165 SPBFUT ONU6 last_price 3219.0    
00002167    11:44:06.951    [1332] OnParam 2166 SPBFUT GAZPF last_price 94.09    
00002168    11:44:06.951    [1332] OnParam 2167 CROSSRATE CNY last_price 0.0    
00002169    11:44:06.951    [1332] OnParam 2168 CROSSRATE CNY last_price 0.0    
00002170    11:44:06.996    [1332] OnParam 2169 CROSSRATE CNY last_price 0.0    
00002171    11:44:06.997    [1332] OnParam 2170 TQOY RU000A10DQB6 last_price 99.567    
00002172    11:44:06.997    [1332] OnParam 2171 TQOB SU26245RMFS9 last_price 85.493    
00002173    11:44:06.998    [1332] OnParam 2172 CROSSRATE CNY last_price 0.0    
00002174    11:44:07.132    [1332] OnParam 2173 TQOB SU26221RMFS0 last_price 70.893    
00002175    11:44:07.144    [1332] OnParam 2174 CROSSRATE CNY last_price 0.0    
00002176    11:44:07.147    [1332] OnParam 2175 CROSSRATE CNY last_price 0.0    
00002177    11:44:07.150    [1332] OnParam 2176 CROSSRATE CNY last_price 0.0    
00002178    11:44:07.153    [1332] OnParam 2177 CROSSRATE CNY last_price 0.0    
00002179    11:44:07.156    [1332] OnParam 2178 CROSSRATE CNY last_price 0.0    
00002180    11:44:07.159    [1332] OnParam 2179 CROSSRATE GLD last_price 0.0    
00002181    11:44:07.162    [1332] OnParam 2180 CROSSRATE CNY last_price 0.0    
00002182    11:44:07.165    [1332] OnParam 2181 CROSSRATE CNY last_price 0.0    
00002183    11:44:07.167    [1332] OnParam 2182 CROSSRATE CNY last_price 0.0    
00002184    11:44:07.170    [1332] OnParam 2183 CROSSRATE CNY last_price 0.0    
00002185    11:44:07.173    [1332] OnParam 2184 CROSSRATE CNY last_price 0.0    
00002186    11:44:07.211    [1332] OnParam 2185 CROSSRATE CNY last_price 0.0    
00002187    11:44:07.211    [1332] OnParam 2186 CROSSRATE CNY last_price 0.0    
00002188    11:44:07.211    [1332] OnParam 2187 CROSSRATE GLD last_price 0.0    
00002189    11:44:07.243    [1332] OnParam 2188 CROSSRATE CNY last_price 0.0    
00002190    11:44:07.244    [1332] OnParam 2189 CROSSRATE CNY last_price 0.0    
00002191    11:44:07.244    [1332] OnParam 2190 SPBFUT GZU6 last_price 9578.0    
00002192    11:44:07.244    [1332] OnParam 2191 SPBFUT X5Z6 last_price 2185.0    
...................
 
Также вопрос по памяти:

Если в скрипте зарегистрировать OnParam, даже пустой:

function OnParam()
end

то память, отображаемая в окне Доступные скрипты, почти не изменяется. А если же OnParam нет вовсе, то она бегает от начального значения до удвоения, и потом уже срабатывает GC, сбрасывая её. Особенно это заметно на "объёмных" скриптах с большим числом локальных переменных.

Т.е. наличие OnParam вызывает постоянный вызов GC, судя по всему при вызове OnParam. Это, конечно, наверно хорошо не расходовать память, но и агрессивный GC - это влияние на производительность.
 
Цитата
Nikolay написал:
Т.е. наличие OnParam вызывает постоянный вызов GC, судя по всему при вызове OnParam. Это, конечно, наверно хорошо не расходовать память, но и агрессивный GC - это влияние на производительность.
 Насколько я понимаю, в текущем QUIK реализована следующая схема реализации QLua.
 1. В исходники Lua добавлены функции QLua и выбран вариант использования кода Lua в нескольких потоках, запускаемых на отдельных стеках (основном и стеках сопрограмм).
 2. Основной стек используется служебным основным потоком QUIK, обрабатывающим много чего (смотрите мой комментарий в этой ветке и в других тоже).
 3. Поток main запускается на стеке сопрограммы.
 4. Коды скрипта разделяются между потоками, использующими его. В выбранном варианте трансляции исходников Lua активирована синхронизация, обеспечивающая использование кода LUa только одним потоком, но на разных стеках. Сишные функции (не использующие управление память Lua) при их вызовах выполняются в потоке их вызова, и при этом разрешается выполнение потока на другом стеке. После звершения сишной функции, его поток блокируется до тех пор пока в потоке, исполняемом код Lua не будет вызвана сишная функция.
 5. При обработке коллбеков в QUIK:
    1) перед его вызовом:
       сохраняется состояние работы GC;
    2) выполняется: collectgarbage("stop") с тем, чтобы запретить работу Gc, который обрабатывает все стеки Lua (зашита от возможных ошибок многопоточности);
    3) запускается функция коллбека;
    4) после завершения коллбека восстанавливается состояние работы GC.
       Если восстановление collectgarbage("restart"), то это короткая операция (10000 вызовов ~ 1,5 млс.), но если разработчик сделаи collectgarbage("collect"), то это тяжелая операция принудительной сборки мусора.
----
  Я много писал, о том, что разработчиком QUIK устроен этот гемморой, в котором они захлебыватся сами и окунают в это пользователей, хотя есть давно известные решения, избавляющие от этого всех.
  Обработка в одном потоке почти всего это тоже ноу-хау разработчика QUIK  :smile: .
 
Цитата
TGB написал:
4. Коды скрипта разделяются между потоками, использующими его. В выбранном варианте трансляции исходников Lua активирована синхронизация, обеспечивающая использование кода LUa только одним потоком, но на разных стеках. Сишные функции (не использующие управление память Lua) при их вызовах выполняются в потоке их вызова, и при этом разрешается выполнение потока на другом стеке. После звершения сишной функции, его поток блокируется до тех пор пока в потоке, исполняемом код Lua не будет вызвана сишная функция.
Тут у Вас ошибка в понимании. Коды не надо разделять или синхронизировать  Они могут исполнятся потоками одновременно .
Разделяется обращение к памяти данных    Это еще называется синхронизация потоков. Т е синхронизация потоков это не про коды а про данные.
 
Цитата
nikolz написал:
Тут у Вас ошибка в понимании. Коды не надо разделять или синхронизировать  Они могут исполнятся потоками одновременно .
   Это, например, из исходников Lua 5.4.1:
Код
LUA_API void lua_pushboolean (lua_State *L, int b) {
  lua_lock(L);
  if (b)
    setbtvalue(s2v(L->top));
  else
    setbfvalue(s2v(L->top));
  api_incr_top(L);
  lua_unlock(L);
}

     Протрите глаза. Как фрагмент между  lua_lock(L);     lua_unlock(L);   может исполняться в двух потоках?
 
Цитата
TGB написал:
Как фрагмент между  lua_lock(L);     lua_unlock(L);   может исполняться в двух потоках
А есть подтверждение, что в qlua эти макросы задействованы, а не оставлены пустыми как в чистом lua.
 
Цитата
Nikolay написал:
А есть подтверждение, что в qlua эти макросы задействованы, а не оставлены пустыми как в чистом lua.

Цитата
TGB написал:
Коды скрипта разделяются между потоками, использующими его. В выбранном варианте трансляции исходников Lua активирована синхронизация, обеспечивающая использование кода LUa только одним потоком, но на разных стеках.
 
Цитата
Nikolay написал:
А есть подтверждение, что в qlua эти макросы задействованы, а не оставлены пустыми как в чистом lua.
     Для экспериментального подтверждения запустите код:
Код
for i = 1, 5000000000 do   end

в main и пока он будет выполняться ни один коллбек не будет выполнен, а интерфейс QUIK зависнет.
 
Ок. Пока разработчики молчат, то в тех скриптах, где требовался колбек OnParam пришлось полностью его отключить, т.к. обрабатывать этот "мусор", подаваемый ему на вход, совсем странное занятие. Это был второй колбек, который я использовал, теперь остался только один - OnTransReply.
 
Цитата
Nikolay написал:
в тех скриптах, где требовался колбек OnParam пришлось полностью его отключить
  В тех случаях, когда мне надо получать значения текущих торгов, я это делаю методом опроса, в цикле с фильтрацией повторений по параметру 'VOLTODAY'. Существует также параметр 'TIME' - время последней сделки, которое можно для чего то пригодится.
 
Ну прямо какие то "Страсти Мордастей" написали. Обматываю не большую таблицу, первоначально задав ее:   -- Реестр параметров
MEO_Config.REQ_PARAMS_REGISTRY = {
 
"STATUS", "TRADINGSTATUS", "TRADINGPHASE",

"BUYDEPO", "SELLDEPO",

   "BIDDEPTH", "OFFERDEPTH", "NUMBIDS", "NUMOFFERS", "NUMCONTRACTS",

   "WAPRICE", "PRICEMAX", "PRICEMIN", "VOLTODAY", "VALTODAY", "QTY",
   "LAST", "BID", "OFFER"

, "STEPPRICE"
}

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

А зачем он нужен, я специально разделил таблицу на группы параметров. Просто посмотрите. Ну ведь понятно что для высоколиквидных инструментов метрики сделок из ТТТ не годятся, для этого есть  тиковые колбэки. А как отследить если изменились параметры "PRICEMAX", "PRICEMIN" в течении сессии?  А где взять  "BIDDEPTH", "OFFERDEPTH", "NUMBIDS", "NUMOFFERS"? Опять опрос?     А посчитайте лаги?
 
Есть нюанс. Т.к. скорость движения цены заведомо выше скорости получения данных терминалом, то OnParam использовался для компромиссного быстрого получения последней цены, времени цены, времени пакета и проверки, что это не опоздавший пакет. И аггрегации этих данных между опросами в основном цикле. Можно и просто получать опросами, но только если не проблема пропустить выбросы цены.

Остальное параметры ТТТ не требует такой скорости и можно получать по месту требования. Тем более, что много параметров ТТТ вообще не изменяются в течении сессии и требуют однократного запроса.

Но теперь OnParam - это слишком накладно для терминала. Альтернатива - чтение таблицы обезличенных сделок (тики) и это будет самое точное (читать не через колбеки точно), но проблема что её не все брокеры выдают, поэтому это решение нельзя использовать как базовое.
 
Nikolay,  Так ведь и я про это. Есть круг задач решение которых, ну если не оптимальны то хотя бы  лучший способ использовать OnParam. Просто потому что лучше ни чего нет.
Вы подняли на само деле очень важную проблему. Возраст которой можно уже исчислять десятками лет. Я с Вами целиком согласен. Полное безобразие!

Но торгуем сегодня, и решения нужны сегодня они есть? Пусть не оптимальное.  Обсуждение очень важное, у себя даже усложняю структуру у меня вообще срез по рынку (фаза), задумался дописал модули, посчитал лаги на стратегию на принятие решения на исполнение суммарный. Усложнил изохронность, разделил на 3 контура.  Встал вопрос а какой ценой лимитный ордер заполнять? Ну точно не из ТТТ.
 
Цитата
Nikolay написал:
Альтернатива - чтение таблицы обезличенных сделок (тики) и это будет самое точное (читать не через колбеки точно), но проблема что её не все брокеры выдают, поэтому это решение нельзя использовать как базовое.
Но колбек вызывается перед записью в таблицу обезличенных сделок.
Если читать из таблицы то будет вызов колбека, запись в таблицу, и потом чтение из нее.
 
Цитата
nikolz написал:
Но колбек вызывается перед записью в таблицу обезличенных сделок.
Если читать из таблицы то будет вызов колбека, запись в таблицу, и потом чтение из нее.
Здесь есть сомнения, т.к. колбек на таблицу - это событие на изменение данных, которые приходят в пакетах. Что произойдет первым - это вопрос, т.к. я не раз ловил в логах ситуацию когда запись в таблице появлялась ранее чем колбек. Но даже если это такт, то не очень понятно, что это изменяет. Я бы сказал, наоборот, наличие такого частого колбека приведет к падению производительности. Организовать процедуру чтения порций с опросом числа записей в таблице - довольно банальная задача. Сотни тысяч записей записываются в файл за пару секунд. А далее, по мере поступления уже новых, просто их обрабатываем. Зачем для этого колбек в потоке терминала - не ясно. Сама идея событийной модели - это абстракция, за которой все равно будет код, проверяющий что событие наступило. Поэтому я предпочитаю сам это делать, с необходимой мне частотой.
Страницы: Пред. 1 2
Читают тему
Наверх