Увеличение производительности терминала QUIK, Внести небольшие изменения в терминал QUIK, чтобы уменьшить нагрузку на слабые встроенные графические карты в серверах.
Добрый день. Недавно возникла необходимость перенести терминал QUIK на небольшой сервер(HP microserver gen8 12G E3-1220v2 2008R2). К сожалению у него достаточно слабая интегрированная видео карта. После установки и первого запуска терминала, получил 90-100% нагрузку на ядро. После чего я решил провести несколько тестов на интегрированной видео карте + на дополнительно установленной низкопрофильной карте geforce210. В итоге получился интересный результат: - основную нагрузку на видео карту и процессор давала (открытая "текущая таблица параметров") - при использовании дополнительной графической карты, нагрузка уменьшилась до 50-65% на ядро(открыта "текущая таблица параметров") - если свернуть таблицу "текущая таблица параметров", нагрузка падает в обоих случаях и составляет всего 1-3% в обоих случаях. Нагрузка примерно одинаковая(её практически нет!!!)
Далее решил посмотреть что же дает нагрузку в таблице "текущих параметров" и нашел. Установлен флаг в настройках таблицы -> редактирование таблицы текущих параметров -> цветовые настройки -> выделять строки цветом -> если произошло изменение цены последней сделки к предыдущей(выделяет цветом фон(красный желтый зелёный)). Выключив этот пункт нагрузка резко уменьшилась: - при использовании встроенной графической карты нагрузка упала с 90-100% на ядро до 22-25% - при использовании дополнительной графической карты нагрузка упала с 50-65% на ядро до 4-6%
В итоге, отключив данную опцию я могу использовать терминал QUIK даже с самой простой встроенной видео, но с пустым белым фоном. Но функция выделения цветом очень удобна и я не хотел бы от неё отказываться. Думаю ей пользуются многие. Однако тратить огромное количество ресурсов процессора(практически целое ядро xeon-a) в пустую на раскрашивание фона таблицы - это как то не нормально. Если можно было бы обновлять цвет фона скажем раз в полсекунды или с привязкой к волатильности у ценной бумаги. Все я думаю сказали бы спасибо, за улучшение работы Вашего терминала. Заранее благодарен если оптимизируете алгоритм обновления фона или же добавите ещё один вариант(checkbox), например обновлять не реже чем "n" сек или мили секунд. Спасибо.
Тоже столкнулся с этой ошибкой, когда потребовалось конвертировать дату, для сохранения в базу, посыпались ошибки. До времени 23:59:59 всё в порядке, а далее .... вместо 00:00:00 показывает 24:00:00. Далее дождался ещё час и хотел увидеть 01:00:00, а увидел 25:00:00. Дальше ждать не стал... Версия терминала последняя, соединение установлено. Это будние дни, что происходит в выходные ещё не смотрел. Это однозначно ошибка, все пользователи не должны сами отслеживать и ставить заплатки...разберитесь.
Alexandr Shumilin пишет: Сервер Quik получает стаканы с биржи и отображает их в терминалеровно в том виде, в котором они транслируются, без "очередей" и "рождения стакана на серверной стороне брокера". В разных клиентских терминалах, например Quik и прямой терминал биржи - стаканы должны и будут совпадать.
Николай Камынин пишет: Предположу, что не совсем так. Просто сервер биржи не транслирует стакана, пока не урегулирует очередь.
Предположу, что сервер биржи вообще ничего не знает о стакане. Стакана ещё нет!!! Биржа передаёт поток транзакций(новые заявки, отменённые заявки, переставленные заявки, частично исполненные) не более. А этот поток уже принимает серверная часть брокера, там то и рождается стакан!!! Причём, мой брокер на одном сервере создаёт стакан с глубиной 10, а на другом сервере с глубиной 20, из одного и того же потока два разных стакана.
Николай Камынин пишет: Если надо работать с очередями с 5000 элементов, то надо просто написать спец модуль такой очереди для луа на СИ. ------------------------------------------------- Я делаю именно так для обработки больших массивов, очередей, списков, пулов, ну и т д --------------------------------- Полагаю, что если чел смело берется за потоки с очередями в 5000 элементов, то создать спец модуль очереди для него - раз плюнуть.
Тоже пришел к такому выводу. Минимум на луа, остальное всё во внешней dll.
Michael Bulychev пишет: Еще раз: дело не в нашей реализации, а в том как устроены оригинальные функции. При удалении первого элемента из массива, остальные элементы сдвигаются на индекс -1. Это значит что чем больше у вас исходная таблица, тем больше нужно времени на удаление первого элемента. Вот простой тест:
Хороший пример. Думаю будет полезно для всех, кто будет разбираться в этой теме в будущем. Пожалуйста добавьте в документацию, что при использовании Ваших потокобезопастых функции есть ограничения на размер очереди(к примеру не более 5000 элементов), а также добавьте, что для реализации потокобезопасного накопительного буфера FIFO, целесообразно использовать другую конструкцию. Пример в документации, тоже желательно иметь, а то через год форум снова переедет и важные примеры потеряются. Спасибо.
Я Вам привёл пример теста на обрыв соединения, когда на определённое время пропадает связь с сервером брокера. После восстановления связи, в терминал начинают поступать все пропущенные сделки и поступают они все сразу. Создаётся большой поток событий. И их все нужно принять и обработать. Информации о том как устроены и какие ограничения Ваших потокобезопасных функции практически нет. По запросу в гугле: " sinsert site:forum.quik.ru " Всего 4 ссылки, но и они не проясняют ситуацию. В документации не слова об ограничениях. примеров правильного использования нет. Другие потокобезопасные функции решил даже не смотреть и не использовать, терять время не хочется...
Michael Bulychev пишет: Я имел ввиду подробности про "сотни раз медленнее", если не сложно.
Попробуйте, в конце для удалить alltrade.dat и пере зайти в терминал. Запустите скрипт сохранения всех сделок с использованием конструкции sinsert и sremove. В терминал будут поступать по несколько десятков тысяч новых сделок в сек. И терминал встанет на 100% загрузку цп + появятся тормоза в основном потоке терминала и процесс этот затянется на долго. А если использовать lock-free никаких проблем, загрузка не долее 10%, за несколько минут всё(1 000 000 сделок, к примеру) сохранится успешно. У меня в данном случае в базу.
DronGO пишет: Кстати table. s insert table. s remove, работают очень медленно, в сотни раз медленнее, чем lock-free системы. И в своей работе их использовать не стал.
Sergey Gorokhov пишет: Мы провели разбор полученной информации и обнаружили причины описанной Вами проблемы. Временное "увеличение строк в стакане котировок" объясняется итеративностью в обновлении стакана котировок, когда обновляются ценовые уровни, один за другим. В связи с этим, при частом обновлении большого количества ценовых уровней, возникает возможность временно наблюдать окно котировок в состоянии, когда обновлена только часть ценовых уровней, а вторая часть ещё не обновлена и соответствует предыдущему состоянию. В этот момент в стакане могут появляться "лишние строки".
Вы здесь всё достаточно подробно описали, чтобы понимать суть происходящего процесса. Плюс схожие(аналогичные) проблемы синхронизации были выявлены и рассмотрены в конструкции (table.insert table.remove). В результате проблему решили создав дополнительные методы (table.sinsert table.sremove).
Старатель пишет: Что-то мне подсказывает, что разбор ситуации проведён не тщательно, и действительные причины проблемы не найдены (свои мысли по этому поводу и возможным причинам я направил по e-mail). Одна из причин - изменения строк не в хронологическом порядке, т.е., не в том порядке, в каком они изменялись в действительности. И, как следствие, мы видим большее (меньшее) количество строк в "стакане" и пересечения цен спроса и предложения ( #1 , #2 ).
Отсюда у меня есть опасения, что будет устранена не действительная причина проблемы, а произведены, например, "косметические" правки, типа убраны "лишние" строки. Соответственно, есть опасения и за качество данных после таких исправлений.
Склонен согласится с Вами. По моему мнению, ошибки с таким неконтролируемым увеличением строк в стакане, возникают при ошибках синхронизации многопоточных приложений работающих с одной критической секцией. Т.е. один поток(reader) начинает считывать значения(в критической секции), а в это время другой поток(writer) начинает запись обновлений(в эту же секцию).Поток (reader) только сейчас заканчивает считывание и как результат, считал старые значения+несколько обновлений. Если в этот момент в стакане было быстрое движение цен, то получаем проблемы. нет движения цены в стакане, никто ничего не заметит. Так как клиентский терминал работает в одном основном потоке, то проблемы синхронизации тут не может быть вообще(за отсутствием второго потока) -> значит ошибки синхронизации на серверной стороне. Таким образом если получим обновление клиентского терминала, значит получим заплатку, с простым фильтром(''косметические правки"). Если же получим обновления серверной части, тогда скорее всего вопрос будет закрыт раз и навсегда. Спасибо.
Sergey Gorokhov пишет: Добрый день, Мы провели разбор полученной информации и обнаружили причины описанной Вами проблемы. Временное "увеличение строк в стакане котировок" объясняется итеративностью в обновлении стакана котировок, когда обновляются ценовые уровни, один за другим. В связи с этим, при частом обновлении большого количества ценовых уровней, возникает возможность временно наблюдать окно котировок в состоянии, когда обновлена только часть ценовых уровней, а вторая часть ещё не обновлена и соответствует предыдущему состоянию. В этот момент в стакане могут появляться "лишние строки".
Мы постараемся исправить данную особенность обновления стакана котировок в одной из ближайших версий ПО.
Приносим Вам свои извинения за доставленные неудобства.
Спасибо за проделанную работу. Но хотелось бы прояснить несколько моментов: 1. Как передаётся стакан от сервера брокера к клиенту? a. формируется на стороне брокера и пересылается целиком в виде нескольких массивов. б. формируется на стороне брокера и пересылается только изменения в стакане, а на клиенте идёт обновление предыдущих значений стакана. в. формируется на клиенте, от брокера пересылаются только изменения заявок. 2. Ждать изменений какого ПО? Серверного или клиентского?
Решил посмотреть, что на валютной секции. Добавил 4 стакана(USDRUB_TOM, USDRUB_TOD, EURRUB_TOM, EURRUB_TOD) ситуация аналогичная. Ниже обновил скрипт, под спойлером. P.S. Для того, чтобы определить, что проблема не у меня в коде, а в поступающих стаканах пришлось потратить неделю рабочего времени. И я думаю я не один такой...
Скрытый текст
Код
local io_open=io.open
-- Таблица кодов инструментов для Акции и фьючерсов forts
local stocks_codes = {SBER="TQBR",LKOH="TQBR",VTBR="TQBR",URKA="TQBR",SBERP="TQBR",TATN="TQBR",MTSS="TQBR",HYDR="TQBR",SiU5="SPBFUT",RIU5="SPBFUT",USD000UTSTOM="CETS",USD000000TOD="CETS",EUR_RUB__TOD="CETS",EUR_RUB__TOM="CETS"}
is_run = true
function main()
file=io_open(getScriptPath().."\\".."log_errors_in_quotes.txt",'a')
init_subscription_level2()
while is_run do
sleep(50)
end
stop_subscription_level2()
file:close()
end
function init_subscription_level2()
for key, value in next,stocks_codes do
Subscribe_Level_II_Quotes(value, key)
end
end
function stop_subscription_level2()
for key, value in next,stocks_codes do
Unsubscribe_Level_II_Quotes(value, key)
end
end
-- Два параметра, нужно изменить на те значения, которые соответствуют Вашему брокеру
local depth_stocks = 20
local depth_forts = 50
local depth_etc = 20
function OnQuote(class, sec)
if not is_run then return end
quote_level2 = getQuoteLevel2(class, sec)
if quote_level2==nil then return end
local bid_count = tonumber(quote_level2.bid_count)
local offer_count = tonumber(quote_level2.offer_count)
if bid_count == nil or offer_count == nil then return end
if class == "TQBR" and ( bid_count > depth_stocks or offer_count > depth_stocks) then
local tmpMsg =" Error in quotes '"..tostring(sec).."' bid_count = '"..tostring(bid_count).."' offer_count = '"..tostring(offer_count).."'"
PrintDbgStr(tmpMsg)
local get_time = GetInfoParam("SERVERTIME")
file:write(get_time.." "..tmpMsg.."\n")
end
if class == "SPBFUT" and ( bid_count > depth_forts or offer_count > depth_forts) then
local tmpMsg =" Error in quotes '"..tostring(sec).."' bid_count = '"..tostring(bid_count).."' offer_count = '"..tostring(offer_count).."'"
PrintDbgStr(tmpMsg)
local get_time = GetInfoParam("SERVERTIME")
file:write(get_time.." "..tmpMsg.."\n")
end
if class == "CETS" and ( bid_count > depth_etc or offer_count > depth_etc) then
local tmpMsg =" Error in quotes '"..tostring(sec).."' bid_count = '"..tostring(bid_count).."' offer_count = '"..tostring(offer_count).."'"
PrintDbgStr(tmpMsg)
local get_time = GetInfoParam("SERVERTIME")
file:write(get_time.." "..tmpMsg.."\n")
end
end
function OnStop()
is_run = false
end
Sergey Gorokhov пишет: Здравствуйте, Просьба привести пример скрипта считывающего bid_count и offer_count Также сообщите версию терминала и уточните это боевой доступ или тестовый?
Добрый день Сергей. Версия терминала 6.17.1.17 Сервер реальных торгов. Скрипт небольшой написал, в нем нужно только два параметра установить, в соответствии с Вашей глубиной стакана.Скрипт выводит информацию на консоль программы DebugView для визуального контроля. А так же сохраняет в файл log_errors_in_quotes.txt
Скрытый текст
local io_open=io.open
-- Таблица кодов инструментов для Акции и фьючерсов forts local stocks_codes = {SBER="TQBR",LKOH="TQBR",VTBR="TQBR",URKA="TQBR",SBERP="TQBR",TATN="TQBR",MTSS="TQBR",HYDR="TQBR",SiU5="SPBFUT",RIU5="SPBFUT"}
while is_run do sleep(50) end stop_subscription_level2() file:close() end
function init_subscription_level2() for key, value in next,stocks_codes do Subscribe_Level_II_Quotes(value, key) end end
function stop_subscription_level2() for key, value in next,stocks_codes do Unsubscribe_Level_II_Quotes(value, key) end end -- Два параметра, нужно изменить на те значения, которые соответствуют Вашему брокеру local depth_stocks = 20 local depth_forts = 50
function OnQuote(class, sec) if not is_run then return end
quote_level2 = getQuoteLevel2(class, sec)
if quote_level2==nil then return end
local bid_count = tonumber(quote_level2.bid_count) local offer_count = tonumber(quote_level2.offer_count)
if bid_count == nil or offer_count == nil then return end
if class == "TQBR" and ( bid_count > depth_stocks or offer_count > depth_stocks) then local tmpMsg =" Error in quotes '"..tostring(sec).."' bid_count = '"..tostring(bid_count).."' offer_count = '"..tostring(offer_count).."'" PrintDbgStr(tmpMsg) local get_time = GetInfoParam("SERVERTIME") file:write(get_time.." "..tmpMsg.."\n")
end if class == "SPBFUT" and ( bid_count > depth_forts or offer_count > depth_forts) then local tmpMsg =" Error in quotes '"..tostring(sec).."' bid_count = '"..tostring(bid_count).."' offer_count = '"..tostring(offer_count).."'" PrintDbgStr(tmpMsg) local get_time = GetInfoParam("SERVERTIME") file:write(get_time.." "..tmpMsg.."\n") end end
За сегодняшний день собрал небольшую статистику. Выбрал для теста несколько наиболее ликвидных акции и фьючерсов(SBER, LKOH, VTBR, URKA, SBERP, TATN, MTSS, HYDR, SiU5, RIU5). Привожу часть лога всего за 1 час!!! Т.к. весь лог за неполную сессию более 600 строк. В среднем получилось сегодня 1 коллизия в 2 минуты. Интересно было бы посмотреть, как у других складывается ситуация.
00000001 13:59:07 [4716] Стакан не полный. 'SiU5' Значения bid_count = '53' offer_count = '50' 00000002 13:59:11 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000003 14:11:38 [4716] Стакан не полный. 'TATN' Значения bid_count = '21' offer_count = '20' 00000004 14:16:43 [4716] Стакан не полный. 'RIU5' Значения bid_count = '51' offer_count = '50' 00000005 14:18:29 [4716] Стакан не полный. 'SiU5' Значения bid_count = '55' offer_count = '50' 00000006 14:24:23 [4716] Стакан не полный. 'RIU5' Значения bid_count = '51' offer_count = '50' 00000007 14:24:33 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000008 14:25:32 [4716] Стакан не полный. 'LKOH' Значения bid_count = '23' offer_count = '20' 00000009 14:26:33 [4716] Стакан не полный. 'SiU5' Значения bid_count = '52' offer_count = '50' 00000010 14:26:33 [4716] Стакан не полный. 'SiU5' Значения bid_count = '50' offer_count = '53' 00000011 14:26:38 [4716] Стакан не полный. 'RIU5' Значения bid_count = '50' offer_count = '51' 00000012 14:26:50 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000013 14:30:23 [4716] Стакан не полный. 'RIU5' Значения bid_count = '50' offer_count = '52' 00000014 14:30:27 [4716] Стакан не полный. 'RIU5' Значения bid_count = '50' offer_count = '51' 00000015 14:30:28 [4716] Стакан не полный. 'SBER' Значения bid_count = '20' offer_count = '21' 00000016 14:30:36 [4716] Стакан не полный. 'SBERP' Значения bid_count = '21' offer_count = '20' 00000017 14:31:17 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000018 14:33:38 [4716] Стакан не полный. 'TATN' Значения bid_count = '24' offer_count = '20' 00000019 14:37:09 [4716] Стакан не полный. 'SiU5' Значения bid_count = '52' offer_count = '50' 00000020 14:38:00 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000021 14:40:03 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000022 14:40:06 [4716] Стакан не полный. 'TATN' Значения bid_count = '20' offer_count = '21' 00000023 14:41:37 [4716] Стакан не полный. 'SiU5' Значения bid_count = '50' offer_count = '52' 00000024 14:42:19 [4716] Стакан не полный. 'TATN' Значения bid_count = '22' offer_count = '20' 00000025 14:43:32 [4716] Стакан не полный. 'TATN' Значения bid_count = '21' offer_count = '20' 00000026 14:43:51 [4716] Стакан не полный. 'TATN' Значения bid_count = '22' offer_count = '20' 00000027 14:45:13 [4716] Стакан не полный. 'SiU5' Значения bid_count = '55' offer_count = '50' 00000028 14:45:33 [4716] Стакан не полный. 'TATN' Значения bid_count = '21' offer_count = '20' 00000029 14:49:18 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000030 14:49:51 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000031 14:50:16 [4716] Стакан не полный. 'SiU5' Значения bid_count = '51' offer_count = '50' 00000032 14:50:38 [4716] Стакан не полный. 'RIU5' Значения bid_count = '51' offer_count = '50' 00000033 14:51:35 [4716] Стакан не полный. 'SiU5' Значения bid_count = '50' offer_count = '51' 00000034 14:51:45 [4716] Стакан не полный. 'SiU5' Значения bid_count = '50' offer_count = '54' 00000035 14:53:22 [4716] Стакан не полный. 'SiU5' Значения bid_count = '50' offer_count = '54' 00000036 14:53:38 [4716] Стакан не полный. 'TATN' Значения bid_count = '20' offer_count = '21' 00000037 14:56:12 [4716] Стакан не полный. 'SiU5' Значения bid_count = '50' offer_count = '51' 00000038 14:56:34 [4716] Стакан не полный. 'TATN' Значения bid_count = '21' offer_count = '20' 00000039 14:56:34 [4716] Стакан не полный. 'SiU5' Значения bid_count = '50' offer_count = '52' 00000040 14:57:13 [4716] Стакан не полный. 'RIU5' Значения bid_count = '51' offer_count = '50' 00000041 14:59:39 [4716] Стакан не полный. 'SiU5' Значения bid_count = '53' offer_count = '50' 00000042 14:59:56 [4716] Стакан не полный. 'SiU5' Значения bid_count = '52' offer_count = '50' 00000043 15:00:57 [4716] Стакан не полный. 'LKOH' Значения bid_count = '20' offer_count = '21' 00000044 15:02:08 [4716] Стакан не полный. 'SiU5' Значения bid_count = '52' offer_count = '50' 00000045 15:02:17 [4716] Стакан не полный. 'TATN' Значения bid_count = '22' offer_count = '20'
Ну вот! Можете же вести диалог, когда захотите, без эмоции и абстракций :) Я Вас понял, что Вы считаете - это невозможно технически и бессмысленно практически. Но почему тогда предложение было принято и рассмотрено как целесообразное к исполнению. А не отвергнуто.К мнению официальных лиц компании я отношусь серьезнее. И да, локальное время тут совсем не причем, т.е. могу предположить, что стакан формируется не на локальной машине, а на сервере брокера(об этом лучше спросить у разработчиков или у Вас если Вы уже в курсе).Так вот в момент когда формируется стакан на стороне брокера, как только стакан сформирован, зафиксировать время сервера и передать его дополнительным параметром. Вот и всё. Если же такой функционал, нужно ждать как Вы говорите 5 лет, тогда печаль....
Николай Камынин пишет: А можно пример из жизни, а то Ваш пример из страшного сна.
Николай, в прошлом году один человек попросил, что-бы появилась возможность получать информацию по стаканам без открытия стакана(ов). И эту возможность очень быстро сделали. За что, искренняя моя благодарность разработчикам. Я же попросил добавить (временную метку), что бы точно понимать когда был сделан срез стакана. Чтобы можно было точно синхронизировать поступающие сделки(у них есть точное время и миллисекунды) и поступающие стаканы(у них нет привязки ко времени). А вообще, Николай, я был о Вас лучшего мнения...
Николай Камынин пишет: Процесс увлекательный, но бессмысленный.
Смысл всегда есть. К примеру не было активности, а потом сразу 10 стаканов пришло. Как определить изменения в каком стакане повлияли на другие?(Т.е. какой стакан первым обновился?) В лучшем случае, можно получить время сервера "SERVERTIME" и предположить, что именно это время когда, был сделан снимок стакана, а оно с точностью до секунды. А если стакан изменится несколько раз за сек?Ставить разным стаканам одинаковую временную метку? Или какие то копии стакана проигнорировать?Какие? Первую копию или последнюю оставить? Как синхронизировать ленту всех сделок с поступающими новыми стаканами(может последний стакан отстаёт на 1-2 сек)? А определить должно быть просто. Время последнего внесённого изменения и есть время этого стакана.
К примеру: Стакан котировок по акциям брокер может давать глубиной 20. (т.е. параметры bid_count и offer_count должны быть = 20). Я ещё понимаю, когда в стакане мало заявок и реально получается, что bid_count или offer_count может быть меньше.(т.е.19 или 18 или ещё меньше ...). Но когда я получаю глубину стакана 22!!!! (Больше максимального значения), и котировки эти реальные с ценой и объемами.... У меня возникают большие сомнения, что данная функция работает без ошибок. С forts тоже самое. К примеру брокер предоставляет глубину стакана 50. А я получаю стакан со значением 62!!!! Это как нормально.... Это же стакан, основа основ, а не индикатор или график какой второстепенный... P.S. к разработчикам: доведите до ума, исправьте ошибки. И сделайте наконец, зарегистрированное пожелание о дополнительном параметре в этой функции, возвращающее точное время сформированного стакана (с точностью хотя бы до МИЛЛИСЕКУНД!!!)
Добавить в функцию getQuoteLevel2() дополнительный возвращаемый параметр datetime, Вызывая функцию getQuoteLevel2() хотелось бы точно знать время когда произошли изменения в стакане, не привязываясь к локальному времени на клиенте.
DronGO пишет: Добрый день уважаемые разработчики!!!
Используя функцию getQuoteLevel2() не хватает дополнительного возвращаемого параметра datetime. К примеру в функции OnAllTrade() он есть. Благодаря чему точно понятно время совершенной сделки. А получая срез стакана через getQuoteLevel2() установить точное время сформированного стакана невозможно. Использовать локальное время на клиенте в момент получения, считаю не совсем корректно.
Добрый день,
Мы рассмотрели Ваше пожелание. По итогам его анализа сообщаем Вам, что мы также считаем целесообразным его реализацию и постараемся включить в план доработок при выпуске одной из следующих версий нашего ПО.
Спасибо за понимание. Будем ждать в новых релизах. Надеюсь вместе мы сможем сделать Quik ещё лучше.
Добавить в функцию getQuoteLevel2() дополнительный возвращаемый параметр datetime, Вызывая функцию getQuoteLevel2() хотелось бы точно знать время когда произошли изменения в стакане, не привязываясь к локальному времени на клиенте.
Используя функцию getQuoteLevel2() не хватает дополнительного возвращаемого параметра datetime. К примеру в функции OnAllTrade() он есть. Благодаря чему точно понятно время совершенной сделки. А получая срез стакана через getQuoteLevel2() установить точное время сформированного стакана невозможно. Использовать локальное время на клиенте в момент получения, считаю не совсем корректно.