Новая версия терминала 8.13 для отладки должна была появиться в этой папке: ftp://ftp.quik.ru/public/updates/8.13/ но там на момент написания этого сообщения пусто. Не повезло 13-й версии, к сожалению.
Это не утечка. Это Lua не делает уборку мусора, пока его не накопится "достаточно" для того, чтобы уборка имела смысл. Если постоянно дёргать сборщик мусора, то это будет замедлять исполнение программы. Возможно, что Lua имеет свои "представления" о том, когда надо запускать сборку мусора, и они не совпадают с Вашими. Можете, как уже советовали, запускать сборку мусора самостоятельно в подходящие на Ваш взгляд моменты.
Правильный вопрос. Можно попробовать вот такую конструкцию, где используется синхронизация через table.ssort (см. документацию для прояснения деталей; в потоке коллбэков эта конструкция не нужна, а в потоке main нужна):
Код
local paramTable = {}
table.ssort({ 0, 1 }, function(a, b)
-- Тут делаем нужные операции, которые, по идее, должны быть выполнены атомарно
paramTable["bid"] = GetParamEx(аргументы для получения bid).param_value
paramTable["offer"] = GetParamEx(аргументы для получения offer).param_value
-- Далее аналогично
return true
end)
Но вопрос к разработчикам терминала такой: этим мы достигнем требуемого (см. первое сообщение темы)?
Кто-нибудь из знающих товарищей может либо подсказать, как это делается, либо просто скомпилировать и выложить на форум socket core.dll для Quik 8.11 и Lua 5.4.1?
Просто напоминаю, что аварийные падения терминала продолжаются, а на отосланные дампы приходит ответ разработчиков:
Цитата
К сожалению, нам не удалось точно установить причину возникшей ошибки. Однако, в ближайшей версии мы изменим работу с коллбэками Lua. Ожидаем, что изменения исключат возникающую ошибку.
Приносим извинения за причинённые неудобства.
При этом новой версии уже давно нет. Как-то плохо это выглядит со стороны пользователей.
Есть список слушателей, которых надо уведомлять о коллбэках (каждого слушателя о своём подмножестве коллбэков) как можно быстрее. При этом в main() время от времени возникают вычислительно тяжёлые задачи (заранее нельзя контролировать время их выполнения и разбивать на мелкие подзадачи).
Если пробрасывать события из потока коллбэков в поток main(), то будут возникать задержки, чего не стоит допускать.
Есть потребность в отключении слушателей по таймауту (а не когда придёт очередной коллбэк) и подключении новых вне зависимости от приходящих коллбэков. При этом итерирование по таблице с добавлением/удалением сразу в двух потоках без должной синхронизации как-то плохо выглядит.
Выходит, что удобно иметь возможность инициировать какой-то коллбэк из потока main, не надо думать про синхронизацию. Пункты 1 и 2 были обсуждением таких возможностей.
Антон, спасибо за предложенные варианты по п.1. Опасения по поводу п.2 понял. Получается, что лучше просить разработчиков реализовать п.1.
swerg, я же не просто так написал, что
Цитата
Надо всё это для того, чтобы можно было выполнять в потоке коллбэков регулярные задачи, которые удобнее и архитектурно правильнее делать там, а не в main.
1) Добавить функцию обратного вызова OnTimer(), которая будет вызываться терминалом с некоторой периодичностью. Надо понять, как указывать период срабатывания. Конкретно для моих задач хватит срабатывания раз в секунду, но мало ли кому чаще надо. Предлагаю обсудить, кому интересно.
Пускай первый раз функция OnTimer() запускается сразу после OnInit() и возвращает число число миллисекунд, через которое будет сделан следующий вызов функции OnTimer(). Если возвращается не число, то пусть следующий вызов будет через 1 секунду.
Реализация на стороне терминала кажется несложной.
2) Дать пользователю возможность из main-потока подать команду на запуск своей функции в потоке коллбэков. Нормальное название не придумал, но надо что-то типа
Код
executeAsCallback(function() ... end)
В принципе, имея п.2, пользователь сможет самостоятельно реализовать п.2, если только в main-потоке не выполняются тяжёлые вычислительные задачи.
Надо всё это для того, чтобы можно было выполнять в потоке коллбэков регулярные задачи, которые удобнее и архитектурно правильнее делать там, а не в main.
Функция возвращает таблицу Lua с параметрами таблицы «Позиции по деньгам».
В случае ошибки функция возвращает «nil».
Что за ошибка у вас или не у вас возникает -- неясно, но раз в документации написано, что может быть nil, это надо предусматривать. Но осадочек остаётся, согласен.
Про особенности национальной DestroyTable и, вообще, о корректной работе с окнами в QLua написал бы кто-нибудь хороший мануал с примерами и объяснениями, почему именно так надо. И на форум выложить на память и для начинающих. Или уже есть где такое в интернете?
Ваше пожелание зарегистрировано, мы постараемся его рассмотреть. Впоследствии, по результатам анализа, будет приниматься решение о реализации пожелания в будущих версиях ПО.
Вот тоже самое хочется сказать. Похоже, очередной выпуск на пенсию состоялся. Варились в собственном соку 40 лет да так, что протухли уже.
Я только одним утешаюсь: если начать торговать по-крупному на свои деньги, то с таким психологическим бэкграундом разорение будет весьма быстрым, после чего посты на форуме закончатся.
Можно довольно простую программку сделать для приема дде-соединения ради только имен столбцов.
Я не умею, да и разбираться с устаревшими технологиями как-то не хочется.
Цитата
А давайте другого пожелаем, чтоб нам дали функцию в луа типа STRING getClassParameters(STRING classcode). А то как-то непорядочек, количество параметров в getClassInfo есть, а самих их негде взять.
Цитата
Я другое хотел предложить: возвращать таблицу вида: {["Имя параметра"] = "Формальный заголовок", } Но не знаю, насколько это юзабельно, поэтому не стал
Наверное, это неплохие варианты. Надобность в таком, по-видимому, будет редкая (один раз запустил и всё). Как сделать по уму и наиболее просто, пусть разработчики подумают.
при временных остановках никто о них не думает и не меняет в них 1 на что-то другое
Да, бирже пофиг на это. В крайнем случае, она вас за неэффективные транзакции оштрафует. Трейдер за всё в ответе, даже если не виноват. :)
А если серьёзно, то для ликвидных инструментов можно отслеживать количество сделок по классу или по классу+инструменту. И если в последние, скажем, 10 секунд нет ни одной сделки, полагаем, что торговля остановлена. Для неликвидных инструментов это будет плохо работать, понятное дело.
Похоже, что параметры "Мин." и "Макс." -- это что-то непонятное, а для акций TQBR и облигаций TQOB параметры "Мин. возм. цена" и "Макс. возм. цена" просто не транслируются.
Ещё вопрос, почему при запросе параметра last для TQBR и SBER за 10 минут до начала торгов мы видим нули:
Во прямо сейчас я вижу в ТТТ для класса RTSIDX и инструмента RTSI значения Мин. 1151,45 и Макс. 1165,58, но внутри QLua эти значения пока не смог получить.
А в потоке котировок для Акций вообще нет параметров Макс. и Мин. (есть Максимальная цена сделки и Минимальная цена сделки, но это же не то, кажется), при том, что сервер квик на аутсорсинге у Арки стоит и должен быть настроен как надо.
И как понять название параметра на английском, если в справке они перечислены в разделе про QPile, а он уже не обновляется? Как называются параметры "Мин." и "Макс." внутри Qlua?
Не пора ли перенести эти описания в справку QLua и актуализировать?
Продолжим тему. Получается, что в Lua-скрипте для получения верхней границы цены надо писать примерно так:
Код
local paramName = "priceMax"
if classCode == "TQBR" or classCode == "RTSIDX" then
paramName = "Max" -- тут точно такое название параметра???
end
local param = getParamEx(classCode, secCode, paramName)
Какие ещё коды классов помимо TQBR и RTSIDX надо указать, чтобы это всё работало корректно?
Я понимаю, что нельзя гарантировать, что в терминале есть вся последняя информация с биржи. Но трудно писать код, когда ты проверил в if условие, что всё должно быть хорошо, а после then всё сразу стало плохо (ситуация поменялась). Нет атомарности.
Вот и приходится без проверки всяких условий isConnected() делать какой-нибудь getXXX(), а потом проверять заполненность полей таблицы внутри результата, потому, что с очень малой вероятностью "что-то пошло не так".
Может, я что-то не до конца понимаю, и мне кто-нибудь прояснит ситуацию по приведённым ниже пунктам.
1) isConnected(), конечно, хорошая функция, но мне кажется, что её корректное использование таково: если она вернула 0, то разработчик может предусмотреть запрет на выполнение каких-то действий в скрипте, поскольку информация в терминале не соответствует действительности (устарела). А вот если isConnected() вернула 1, то это ничего не гарантирует: может, нужные шлюзы ещё не подключены, может информация не до конца загрузилась с сервера. Т.е. мы можем избежать гарантированных ошибок при isConnected() == 0, но не сделать ошибок при isConnected() == 1 эта функция не помогает.
2) При разрыве соединения и повторном подключении терминала к серверу в какой-то момент происходит пропадание информации в ТТТ (и других) и её повторное появление. При работе скриптов в момент "пропадания" информации справочные функции типа getSecurityInfo() могут вернуть пустые таблицы. Это приводит к тому, что скрипт, скажем, читает информацию о размере лота, а там его нет. Неприятно.
По второму пункту хотелось бы понять, как избежать постоянных проверок, что информация отсутствует. Возможно, что при перезагрузке данных в таблицах имеет смысл блокировать запросы к функциям типа getSecurityInfo(), пока они не могут вернуть данные. Пусть скрипты подождут на этой блокировке.
Может кто поделиться своим опытом по данному поводу? Из каких ещё функций, кроме getSecurityInfo(), будут получаться пустые таблицы в моменты перезагрузки данных? Могут ли разработчики прокомментировать это (и отразить в документации, что возвращается, если нет данных)?
В начало функции поставьте проверку на nil и выводите сообщение (через message, например, или в лог, если он у есть). При работе со вложенными массивами/таблицами часто бывает, что забыли внутренние структуры инициализировать.
Цитата
Я уже до ручки дошёл: перед вызовом функции обуваю аргумент в tostring, а внутри функции переворачиваю его в tonumber - пофиг: "attempt to concatenate a nil value (local 's')".
Тут же явно на nil намёк.
Можно ещё всё в xpcall завернуть и печатать стек, чтобы было понятно, откуда вы попали со значением nil. Пример, как это обычно делается в моём коде приведён ниже. При любой ошибке видно, где она произошла.
Код
logger:info("STARTED")
local status, errMessage = xpcall(function()
if isConnected() ~= 1 then
local msg = config.name .. " is not connected on start. Shutdown."
logger:error(msg)
else
initialize()
run()
end
end, function(err)
logger:error(tostring(err))
logger:error(debug.traceback())
end)
if not status and errMessage ~= nil then
logger:error(errMessage)
end