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

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

Страницы: 1 2 3 4 5 6 7 8 9 10 11 ... 29 След.
Периодические паузы в получении данных
 
Цитата
nikolz написал:
Последнее время или потому, что версия КВИК 12 , но у Сбербанка вообще полный писец.
Когда ему вздумается останавливает передачу данных на минуты и связь не разрывает.
Теперь может принимать данные ,но не отображать в строке время сервера и принятые данные,
а прыгать через несколько или даже десятки секунд.
-------------------------------------
Я уже  перестал эти приколы записывать в лог файлы. Смысла нет.
----------------------------------
Советую тем ,
кто начинает -  не связывайтесь с этим брокером ,
чтобы потом не удивляться почему такое Г..но получается,
Удивительным образом, у меня Сбер один из лучших. ВТБ - полный кошмар. Альфа - лучше всех.
Финам - такое... Очень часто рвет связь.

Но самое печальное на последних версиях - это если обрыв связи был продолжительное время, минут 10, то после восстановления будет вызван колбек OnCleanUp. По крайней мере я это вижу на двух брокерах.

До 10-ой версии я этот колбек видел может раз в год.
Периодические паузы в получении данных
 
Да уже столько писал про это. В последних версиях явно что-то изменили в работе поток данных. В итоге они идут неспешно, при длительном отключении терминала от брокера, вызовется очистка данных терминала и вы получите все что было от начала дня снова. Также потоки данных игнорируют умный заказ данных...
Но все как задумано. Правда, судя по всему, разработчики забыли или даже не пробовали 7-ую версию, которая работала стабильно столько лет. А теперь мажорные версии каждый квартал, но до сих пор древняя кодировка в текстах, нет поддержки HDPI мониторов, нет изменения размеров базовых окно терминала...
Каким запросом на Lua получить уровень риска?, Квик стал транслировать уровень риска и ГО по типам риска. Как их получить в Lua?
 
Подниму вопрос: в каких случаях брокер не транслирует данный параметр в таблицу? ГО по разным уровням есть в ТТТ, а самого уровня нет в таблице "Ограничения по клиентским счетам"
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Да, Вы правы в части теории о чувствительности к ставке. У меня в голове была другая мысль, и написал что 238 менее чувствительна.

Теория говорит "большей модифицированной дюрацией обычно изменяется в цене сильнее". Да. Но моя мысль была иной:
в нашей реальности дальние облигации слабее реагируют на ставку и почти всегда торгуются хуже ближних.
Для этого идете на сайт ЦБ и смотрите на кривую бескупонной доходности и станет видно, что для длинных сроков есть "премия за срок", что можно перевести как "премия за риск".
Точнее сказать - они не обязаны реагировать на сиюминутные события, но могут, с этим не поспоришь.

У нас есть 15-и летний деньги. И теперь начнем инвестировать на срок 15 лет... Чтобы это было как в теории, надо чтобы хотя бы лет 10 было без "киндер сюрприза", не горя уже о кризисах.

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

Моя мысль была в этом. Думал об одном, написал другое.

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

Мы торгуем ожидания по ДКП, Ставке, климату в стране, киндер-сюрпризы. Покупая текущую доходность, вы ожидаете, что будут изменения в ту или иную сторону по ставке. Или будет на месте. В конечном итоге уже накопилось достаточно лет, чтобы понять куда и как все двигается.

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

Что касается ДДС, то никто не мешает подобрать выпуски, чтобы приходы были хоть каждый месяц. Также можно использовать "лестницу".
А если хотите защиты, то есть фьючерс RGBI. Никто не мешает захеджировать позицию.

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

Собственно облигации - это про экономику, а не про тех. анализ. Спекулировать облигациями можно, и нужно. Это даст доп. доход. Но опять же, покупаю облигацию, вы в первую очередь, анализируете эмитента, решаете что с ним будет, а не сравниваете с другим эмитентом. И уже точно не график, а строки баланса о задолженности, ДДС эмитента в будут важнее. Рейтинг - это перекладывать ответственность на других. Открой баланс и сам посмотри.
QUIK и жор памяти, Проблема с памятью скриптов Qlua
 
Т.к. это в цикле, то получение данных таблицы - это каждый раз выделение памяти.
local tab=getItem('orders',iord);

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

А вот фьючерсы и календарные спреды сеткой - много и часто.

Что касается налогов, то не все так печально, т.к. есть сальдирование и перенос убытков на будущее. Т.о. можно зачесть убытки, рассчитанные из-за разницы методологий учета.
В целом, метод FIFO хорош для склада продуктов, т.к. есть сроки хранения. Для инвестирования - это очень спорное решение.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Я торгую уровни сетки. Последний открылся, первый закрылся. А что считает брокер меня не интересует, кроме его расчета налогов.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Цитата
VPM написал:
FIFO или средневзвешенная
Это зависит от алгоритма учета позиции. Я в основном торгую по LIFO, например. А брокер всегда считает по FIFO.
Средняя - это для алгоритмов с единой ценой позиции, например чтобы стоп поставить от этого уровня. Но опять же она будет разная для разных режимов расчета по частичному закрытию.
Система принятия решений и/или Нечеткая логика(FuzzyLogic), Нечеткая логика или Система принятия решений в трейдинге
 
Цитата
VPM написал:
QUIK утром отдал среднюю цену как 2994 вместо 2.994
Если у инструмента изменится лотность, то такое может быть. Либо просто ошибка.
Хотя, получать среднюю из терминала - это значит доверять расчетам по FIFO от брокера.
Лучше рассчитывать среднюю по своим сделкам. Хранить эту информацию, чтобы всегда иметь возможность пересчитывать, рассчитывать прибыль и т.д.

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

https://iss.moex.com/iss/engines/stock/markets/shares/boards/TQBR/securities/­SBER/candles.json?from=...
Вопросы про OnParam
 
И почему это совсем не праздный вопрос только для писателя скриптов. Если посмотреть на дисковую активность терминала, то видно, что он постоянно пишет в файл info.log
Ну пишет, в чем проблема. А проблема в том, что к концу дня этот файл становится 2+ гб. При это в терминала заказано всего сотня инструментов. И так каждый день. А диски SSD...

Т.е. можно предположить, что раз OnParam принимает всё, игнорируя фильтры, то и терминал сам по себе тащит все, даже что не просили. И при этом пишет всё это в лог. Зачем?
Такое ощущение, что банально забыли отладочные контуры отключить.
Вопросы про OnParam
 
Т.к. эти вопросы были проигнорированы в соответствующей теме https://forum.quik.ru/messages/forum10/message83015/topic9604/#message83015

Вынесем в отдельную тему:

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

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

...............
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 - это влияние на производительность.
...................

Также вопрос про OnCleanUp, который уже задавался:

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

Вчерашний лог брокера Финам:

[INFO  2026-09-08 10:02:45] :           OnDisconnected
[INFO  2026-09-08 10:03:29] :           OnCleanUp
[INFO  2026-09-08 10:03:30] :           OnConnected
[INFO  2026-09-08 10:04:57] :           OnDisconnected
[INFO  2026-09-08 10:05:07] :           OnCleanUp
[INFO  2026-09-08 10:05:08] :           OnConnected
[INFO  2026-09-08 10:20:07] :           OnDisconnected
[INFO  2026-09-08 10:20:17] :           OnCleanUp
[INFO  2026-09-08 10:20:19] :           OnConnected
[INFO  2026-09-08 10:31:12] :           OnDisconnected
[INFO  2026-09-08 10:31:24] :           OnCleanUp
[INFO  2026-09-08 10:31:28] :           OnConnected
[INFO  2026-09-08 11:15:36] :           OnDisconnected
[INFO  2026-09-08 11:15:41] :           OnConnected
[INFO  2026-09-08 11:20:20] :           OnDisconnected
[INFO  2026-09-08 11:20:36] :           OnCleanUp
[INFO  2026-09-08 11:20:38] :           OnConnected
[INFO  2026-09-08 12:24:08] :           OnDisconnected
[INFO  2026-09-08 12:24:23] :           OnCleanUp
[INFO  2026-09-08 12:24:29] :           OnConnected
[INFO  2026-09-08 12:54:37] :           OnDisconnected
[INFO  2026-09-08 12:54:51] :           OnCleanUp
[INFO  2026-09-08 12:54:53] :           OnConnected
[INFO  2026-09-08 12:56:35] :           OnDisconnected
[INFO  2026-09-08 12:56:39] :           OnConnected
[INFO  2026-09-08 13:43:17] :           OnDisconnected
[INFO  2026-09-08 13:43:22] :           OnConnected
[INFO  2026-09-08 13:47:37] :           OnDisconnected
[INFO  2026-09-08 13:47:49] :           OnCleanUp
[INFO  2026-09-08 13:47:51] :           OnConnected
Уведомление о необходимости обновления торговых терминалов в связи с изменениями на срочном рынке Московской биржи, Список проблем при работе устаревших версий QUIK после обновления торговой системы срочного рынка МБ
 
Было же подробное объяснение - биржа перешла на срочном рынке на числовые номера ордеров, которые уже не влезали в 32 бита. Судя по всему уже просто не хватало числа, чтобы вместить все номера ордеров.
Поэтому собрать-то под 32 бита не сложно. Торговать на срочной секции будет невозможно.
Да и странно жаловаться на 64 бита, если найти 32-битную ОС уже почти невозможно. Само качество работ по переходу на 64 бита вызывает вопросы, это да.
Не проходит заявка на сделку в QUIK на счёте *, Номер счёта на * через / ??
 
Так создайте заявку в кармане транзакций, указав все значения. Потом выгрузите из кармана в файл и посмотрите как это выглядит.

Я не знаю как там у ВТБ с счетом, но у Альфа тоже счет с слешем - никаких проблем не наблюдается. Вы привели пример REQUEST, но не ясно с какой стороны этот лог - со стороны клиента или со стороны сервера уже после разбора полученного json. По идее, сервер не должен заниматься преобразованием данных, а просто транслировать их в таблицу транзакции к отправке.
Не проходит заявка на сделку в QUIK на счёте *, Номер счёта на * через / ??
 
Это не система не принимает, а коннектор вполне может парсить так данные, что режет через /.
Проверьте что принимает коннектор и что отправляет в транзакцию. Наверно есть уровень лога, позволяющий увидеть это.
Что происходит при экспирации опциона с точки зрения скрипта?
 
Вопрос же про экспирацию, т.е. наступление право исполнить обязательства. Т.е. продавец опциона (контрагент по покупке опциона) обязан поставить фьючерсный контракт, а покупатель — принять его.
Что происходит при экспирации опциона с точки зрения скрипта?
 
OnFuturesClientHolding - срабатывает на любые изменения в таблице, как и любой другой колбек в контексте Квика. Уж так сделано...

Сделки не должно быть, второго контрагента же нет.  
Коннектор на Lua 5.1 переписать под Lua 5.4 &, Lua 5.1 -Lua 5.4
 
Цитата
nikolz написал:
Для декомпиляции не нужна отладочная информация в скрипте
Декомпилируется байт-код.
Да. Но прочитать такой код будет сложно. Если это одна строка, то проблем нет. А если тысячи, что разбираться с этим будет сложно.
Коннектор на Lua 5.1 переписать под Lua 5.4 &, Lua 5.1 -Lua 5.4
 
Необходимы исходные тексты коннектора. Luac - это скомпилированный файл.
Возможно он состоит из одной строки загружающей dll. А может это код на тысячи строк.

Если это 5.1, то возможна декомпиляция, но без стопроцентной вероятности точного восстановления. Скрипт вполне могли скомпилировать с отключенной отладочной информацией.
Работа колбеков
 
Цитата
nikolz написал:
Но колбек вызывается перед записью в таблицу обезличенных сделок.
Если читать из таблицы то будет вызов колбека, запись в таблицу, и потом чтение из нее.
Здесь есть сомнения, т.к. колбек на таблицу - это событие на изменение данных, которые приходят в пакетах. Что произойдет первым - это вопрос, т.к. я не раз ловил в логах ситуацию когда запись в таблице появлялась ранее чем колбек. Но даже если это такт, то не очень понятно, что это изменяет. Я бы сказал, наоборот, наличие такого частого колбека приведет к падению производительности. Организовать процедуру чтения порций с опросом числа записей в таблице - довольно банальная задача. Сотни тысяч записей записываются в файл за пару секунд. А далее, по мере поступления уже новых, просто их обрабатываем. Зачем для этого колбек в потоке терминала - не ясно. Сама идея событийной модели - это абстракция, за которой все равно будет код, проверяющий что событие наступило. Поэтому я предпочитаю сам это делать, с необходимой мне частотой.
Работа колбеков
 
Есть нюанс. Т.к. скорость движения цены заведомо выше скорости получения данных терминалом, то OnParam использовался для компромиссного быстрого получения последней цены, времени цены, времени пакета и проверки, что это не опоздавший пакет. И аггрегации этих данных между опросами в основном цикле. Можно и просто получать опросами, но только если не проблема пропустить выбросы цены.

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

Но теперь OnParam - это слишком накладно для терминала. Альтернатива - чтение таблицы обезличенных сделок (тики) и это будет самое точное (читать не через колбеки точно), но проблема что её не все брокеры выдают, поэтому это решение нельзя использовать как базовое.
Работа колбеков
 
Ок. Пока разработчики молчат, то в тех скриптах, где требовался колбек OnParam пришлось полностью его отключить, т.к. обрабатывать этот "мусор", подаваемый ему на вход, совсем странное занятие. Это был второй колбек, который я использовал, теперь остался только один - OnTransReply.
Работа колбеков
 
Цитата
TGB написал:
Как фрагмент между  lua_lock(L);     lua_unlock(L);   может исполняться в двух потоках
А есть подтверждение, что в qlua эти макросы задействованы, а не оставлены пустыми как в чистом lua.
Работа колбеков
 
Также вопрос по памяти:

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

function OnParam()
end

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

Т.е. наличие OnParam вызывает постоянный вызов GC, судя по всему при вызове OnParam. Это, конечно, наверно хорошо не расходовать память, но и агрессивный GC - это влияние на производительность.
Работа колбеков
 
Ок, вопрос разработчикам:

Почему 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    
...................
Работа колбеков
 
Кто хочет поразвлекаться, вот тестовый скрипт. Запускам. Разрываем связь, ждем, запускаем. Когда в информационном окне закончится очередь отставших сообщений остановить. была возможность считать этот параметр оставили бы сами. Для наглядности можно включить 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
Работа колбеков
 
И ещё из странного. Добавляем в скрипт такое:

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, и получаем вызовы от первого индекса.

Т.о. массовая передача отставших пакетов или даже всех от старта сессии, приведет к вызову всех колбеков заново. И скрипт будет это разбирать, вместо работы. И нагрузка на терминал, соответственно, т.к. на каждый пакет будут вызваны все назначенные пакеты. Вот тебе и событийная модель. Казалось бы, событие давно прошло - но нет, получи заново.
Работа колбеков
 
Цитата
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 зачем?
Работа колбеков
 
Ок. Чтобы наглядно показать как влияют колбеки на работу терминала запустил такой тест.

Запускаем скрипты, в которых есть только колбек 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
Очередь сетевых сообщений
 
Регистрируйте как пожелание, т.к. это единственный способ понять, что идет передача пропущенных данных.
Очередь сетевых сообщений
 
Есть такой параметр в информационном окне. И он, на самом деле, полезен, т.к. после разрыва связи или запуска терминала позволяет косвенно увидеть, что идёт загрузка отставших сообщений.

Но его нет в описании к методу getInfoParam. Как его получить?
Работа колбеков
 
Цитата
nikolz написал:
Вывод -тормозят Ваши скрипты.
Измерьте время их исполнения.
------------------
Но так как эта задержка существует и при спокойном рынке, то скорее всего это 1 вариант
Тексты скриптов выложены выше. Что там конкретно приводит к "тормозам"? Просто интересно, раз такое категоричное утверждение.

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

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

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

средняя задержка данных = 1.877.
Максимальная задержка данных = 29.656.
Работа колбеков
 
OnParam и OnQuote самые быстрые. Остальные не так часто.

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

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

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

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

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

Еще из странного - зависит от сервера брокера. Я тестирую на ВТБ, как одном из "тормозных", переключаешь на другой сервер - и уже не так заметно влияние.
Работа колбеков
 
Сами скачки волне ожидаемы в клиент-серверном взаимодействии. Ну не пришли ещё пакеты, бывает, если оставить в сторону реализацию обмена.
Но вот почему в эти периоды терминал становится "тупым" - уже не столь очевидно и вызывает вопросы. Как будто сделали блокирующий цикл, ожидающий пакеты, а интерфейс - да кому он нужен.
Работа колбеков
 
Ок. Теперь исключим колбеки.

Берем скрипт и выкидываем их, будем просто читать раз в секунду цены и класть их в ту же очередь. Логи тоже уберем.
Код
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 и пишем в очередь, то уже на трех заметно влияение на работу интерфейса теримнала.
Работа колбеков
 
Тест был направлен не на измерение скорости работы скрипта, а на влияние работы скрипта на работу терминала, его интерфейса в первую очередь. Какой смысл показать, да ещё на демо, что main работает быстро.
У меня на демо тоже всё прекрасно. Переключаешься на реальный и во время  открытия рынка - терминал вкладки переключает по 30 секунд.  Была бы возможность инструментально измерить работу самого терминала - так и вопросов не было бы.

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

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

Я готов это списать на влияние работы скриптов. Но тогда хотелось бы понять, что именно и какие методы оказывают максимальное влияние. Иначе делаешь банальный скрипт, а эффект как будто модель на 100b считаешь.
Работа колбеков
 
Текст скрипта выше, проблем с запуском на 4-ех терминалов нет. Библиотеки в архиве тоже есть.
Работа колбеков
 
Пока была задача воспроизвести "зависание" терминала. В реальности данные в таблицу не выводятся с такой частотой, да и в последнем  тесте убрал дублирующие установки, что сильно снизило частоту вызовов. Колбеки используются только служебные OnConnect, OnClenUp, OnTransReply, но идут частые запросы к данным через getParamEx, getItem. Здесь просто модельный скрипт с набором действий, которые часто можно встретить в скриптах.
Цитата
funduk написал:
вместо сторонних гуи либ
Я бы и рад их использовать, если писал только себе. Себе мне проще выдать данные по любому обмену, а всё написать вне Квика.

А чтобы найти надо всё отсекать и постепенно подключать, смотреть на результат. Проблема только в том, что работу скрипта ещё можно как-то измерить, а вот работу терминала только визуально. Ты вроде как SetCell вызвал, а данные в таблице появляются только если "пнуть" таблицу.
Работа колбеков
 
У меня 5 брокеров - 4-е терминала постоянно открыто. Сбер не самый "плохой" из них. Я бы даже сказал вполне нормально. Самый тормозной - Кит. Самый отзывчивый Альфа. Прямая корреляция с числом клиентов и мощностью серверов. Есть стойкое ощущение, что обмен данными сервер-клиенты - занимает всё время терминала.
Работа колбеков
 
Цитата
VPM написал:
Если правильно понял то проблемы уже заметны на 7 инструментах
Иногда они заметны и на меньшем числе. Здесь основной вопрос - это деградация производительности всего терминала при достаточно простых, но частых действиях.
Таблицы уже давно стали влиять на терминал, кажется с 8-ой версии. Но сейчас это уже выходит за рамки нормального. Вполне ожидаемо было бы квотирование процессорного времени скриптам, но сам терминал должен работать без влияния. Здесь же банальная задача - и терминал лежит.

Так что хотелось бы увидеть реакцию разработчиков. Правда сразу можно сказать - на демо Quik Junior такое не наблюдается, точнее, видимо, необходимо ещё больше приложить нагрузку. А вот на реальных брокерских терминалах, особенно если ещё есть поток данных по опционам - то уже даже один скрипт может "положить" терминал.

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

У нас есть колбеки - настоятельно рекомендуемые к использованию. Есть окно скрипта - единственный способ вывести данные.
Банальная задача - вывести данные, полученные через getParamEx. Также, для какой-то нагрузки - читаем 7 стаканов, при этом даже не разбирая их, а банально беря верхние значения, т.е. просто запрос данных.

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

Проверяем как это терминал переваривает, если таких скриптов несколько. Такое на 7-ой версии терминала даже не вызывало вопросов.
Работа колбеков
 
Ок. Чтобы показать влияние на работу интерфейса терминала еще сделал такой тест.

Терминал свежий, установлен не более двух недель назад. Брокер ВТБ. Остановил все скрипты, даже автологин.

В тестируемом скрипте добавил запоминание последних значений, чтобы выводились в окно только измененные данные.
Лог через PrintDbgStr. В колбеках вывода в лог нет, только в main.

Запускаем три экземпляра скрипта и смотрим на реакцию терминала.

Один скрипт - почти нет влияния.
Два - уже ТТТ "подвисла" и данные выводятся не так быстро.
Три - всё встало, данные появляются только если "пнуть" таблицу

В архиве скрипты, лог с таймингом работы main, запись видео работы терминала.

https://disk.360.yandex.ru/d/P7xB_poL0QlKfAКод тестового скрипта - по факту, он ничего не делает, кроме обработки колбеков, запроса данных через методы QLUA, и вывода данных в таблицу. Никаких расчётов, даже файловые операции убраны.
Код
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 = 'script 1:'
local logFile

local function log_tostring(...)
    local n = select('#', ...)
    if n == 1 then
    return tostring(select(1, ...))
    end
    local t = {}
    t[#t + 1] = id
    for i = 1, n do
    t[#t + 1] = tostring((select(i, ...)))
    end
    return table.concat(t, " ")
end

local function log(...)

    -- Пишем в отладочные сообщения, вместо лога
    PrintDbgStr(log_tostring(...))
    -- Пишем в отладочные сообщения, вместо лога

    -- 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 getQuoteLevel2    = _G.getQuoteLevel2
local IsWindowClosed    = _G.IsWindowClosed
local DestroyTable      = _G.DestroyTable
local message           = _G.message
-- local table_sremove     = _G.table.sremove

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 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
      -- Записываем в очередь только если цена изменилась
        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}
        -- 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 event_callback(_, msg, par1, par2)
    -- log('Enter TableNotificationCallback', msg, par1, par2)
    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', msg, par1, par2)
    -- log('---------------------------------------------------------------------------')
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
    _G.SetTableNotificationCallback(t_id, event_callback)
end

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(' --- set pop value',  n, rev_lis[res[2]], last_price)
        res = queue:pop()
    end
    return n
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"])

    if last_data[sec_code][2] ~= best_bid then
        SetCell(t_id, rev_lis[sec_code], 2, tostring(best_bid), best_bid)
        last_data[sec_code][2] = best_bid
    end
    if last_data[sec_code][3] ~= best_ask then
        SetCell(t_id, rev_lis[sec_code], 3, tostring(best_ask), best_ask)
        last_data[sec_code][3] = best_ask
    end
    get_dom[sec_code] = nil

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

    CreateTable()

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

    local lt = os.time()
    local wnd_kill_interval = 120

    while isRun do

        local cwt = os.time()
        if cwt - lt > wnd_kill_interval then
            lt = cwt
            DestroyTable(t_id)
            CreateTable()
        end

        -- тест скорости локальной очереди
            -- local list = Queue.new()
            -- for i = 1, 10000 do
            --     list:push(i)
            -- end
            -- local st = os.clock()
            -- while list:pop() do end
            -- log('process 10000 for time', os.clock() - st)
        -- тест скорости локальной очереди

        local s = os.clock()
        log('Enter main loop', os.date())
      IsWindowClosed(t_id)
        local ct = os.clock()
        local n = process_queue()
        local qt = os.clock() - ct
        ct = os.clock()
        local qn = 0
        for i = 1, #sec_list do
            if get_dom[sec_list[i][2]] then
                qn = qn + 1
                GetQuotes(sec_list[i][1], sec_list[i][2])
            end
        end
        local dt = os.clock() - ct
        bl1.func1()
        bl2.func100()
        bl3.func200()
        bl4.func300()
        log('Exit main loop', os.clock()-s, 'in queue', n, 'queue time', qt, 'dom time', dt, 'for', qn)
        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
Работа колбеков
 
Цитата
nikolz написал:
Наблюдал такой прикол  ставишь индикатора регрессии на большой истории и привет ждешь старта квик очень долго.  На демо все быстро потому что там история маленькая.
Это объяснимо. При старте, график инициализирует расчёт на всё истории дважды минимум (зачем? ответа так и нет). Если индикатор сделан так, что считает каждый бар с начала, что для регрессии очень редко нужно, и баров "до дури", ясно что будет так.
У регрессии сложность же O квадрат от периода, а если не линейная, то ещё хуже. И так каждый бар...
Работа колбеков
 
Иногда в логах работы скриптов видно, что банальная установка данных в таблицу может занимать 3 секунды.

Что-то типа такого:

 for k,column in ipairs(columns) do
   if values[column]~=nil then
     t:SetValue(line, k, values[column] and tostring(values[column]) or '', tonumber(values[column]) or 0)
   end
 end



Банальный итератор на 20 элементов (в таблице скрипта редко больше), а время столько

[DEBUG 2026-07-23 14:32:59] AddLine MMU6_SPBFUT_1500nt5_1500nt5_1784803613 line 4
[DEBUG 2026-07-23 14:33:02] AddLine sec_line
line:4|MXI-9.26|MMU6|SPBFUT


Три секунды...
Работа колбеков
 
Я уже давно перестал дергать таблицу, т.к. после 7-ой версии терминала она стала такой хрупкой. В реальных скриптах вывод данных только по необходимости, в лучшем случае раз в минуту.
Работа колбеков
 
Ок. Переделал на очередь (по факту - не особо лучше чем с банальной table.sremove)

Итог. Худшие результаты:

Exit main loop 4.3549999999996 in queue 164 queue time 3.875 dom time 0.19399999999951

Exit main loop 2.5430000000015 in queue 116 queue time 2.4950000000008 dom time 0.04700000000048

Exit main loop 2.3759999999984 in queue 97 queue time 2.357 dom time 0.018999999998414

Exit main loop 1.018 in queue 57 queue time 0.97600000000057 dom time 0.040999999999258

Exit main loop 0.9429999999993 in queue 43 queue time 0.94100000000071 dom time 0.0019999999985885

Exit main loop 0.88700000000063 in queue 43 queue time 0.48700000000099 dom time 0.39899999999943

Exit main loop 0.85599999999977 in queue 78 queue time 0.76599999999962 dom time 0.090000000000146


Если убрать из колбеков PrintDbgStr, то немного быстрее, точнее меньше выбросов, и максимальный результат такой:

Exit main loop 1.0730000000003 in queue 54 queue time 1.0709999999999 dom time 0.0020000000004075


Но есть и такие выбросы по чтению стакана

Exit main loop 0.46200000000135 in queue 3 queue time 0.055000000000291 dom time 0.40600000000086

Exit main loop 0.47899999999936 in queue 10 queue time 0.16300000000047 dom time 0.31499999999869

Exit main loop 0.34699999999975 in queue 7 queue time 0.041999999999462 dom time 0.30400000000009

При этом число инструментов по которым читался стакан (а он читается один раз за цикл main, если были вызовы OnQuote), не особо влияет. Могут все 7 прочитаться за 50 млс, а могут всего 2 уже за 300.


Пока по ощущениям - это GC. Чем больше мусора, тем хуже main.
Также прослеживается линейная зависимость от размера очереди - 100 элементов = 1 секунде работы main. Что, конечно, совершенно не сравнимо с чистым Lua где та же очередь из 10 тыс. элементов обрабатывается за 2 мкс.

При этом, если эту же очередь заполнить и разобрать внутри бесконечного цикла в main, то она и разберется за эти 2 мкс:
Код
        local list = Queue.new()
        for i = 1, 10000 do
            list:push(i)
        end
        local st = os.clock()
        while list:pop() do end
        log('process 10000 for time', os.clock() - st)



Но та же очередь, заполняемая на колбеках, а разбираемая в main - уже на порядок медленней.

Также однозначно - в колбеках никаких операций ввода-вывода.

Но я так и не могу выявить что-то одно, что может так влиять на интерфейс терминала, кроме банально низкой скорости вообще.
Страницы: 1 2 3 4 5 6 7 8 9 10 11 ... 29 След.
Наверх