Alexey Ivannikov написал: Есть возможность прислать нам архив Вашего терминала с воспроизведением проблемы?
Нечего прислать.
Цитата
Alexey Ivannikov написал: Проблема продолжает воспроизводиться, или же нет? Если фильтр отключен - то сделки в таблице начинают показываться, или же нет?
Сделки прогрузились после удаления файлов trades.dat и orders.dat:
Цитата
Старатель написал: Пришлось закрывать QUIK и удалять файл trades.dat (заодно удалил и orders.dat). И только после таких манипуляций все сделки прогрузились.
Чтобы не было спекуляций на тему, что были выставлены какие-то фильтры и потому сделки не показывались, отмечу, что проблема была замечена в результате неправильной работы Lua-скрипта, которому на установленные пользователем фильтры до лампочки. В таблице futures_client_holding сумма todaybuy и todaysell не билась с суммой из таблицы trades, в результате чего и была обнаружена пропажа части сделок. Переподключение к серверу не решило проблему. Помогло только физическое удаление файлов.
Своим сообщением я хотел сказать, что проблема, описываемая ТС, могла быть локальной в самом QUIK, а не по вине брокера или биржи.
Надо делать так, как надо. А как не надо - делать не надо.
TGB написал: Прошел год. ----------------- Напомню описание ситуации и вариант ее устранения. --- Бывают ситуации зависания в QLua, когда основной поток обслуживания колбеков всех скриптов пользователя, а также таблиц QUIK (это не таблицы Lua), блокируется выполнением длинного цикла пользовательской программы на «чистом» Lua, в котором нет ни вызова seep ни вызова дру-гих C-функций.
TGB, как я понял, проблемы-то и нет вовсе. Или что вам продемонстрировала реальная жизнь?
Надо делать так, как надо. А как не надо - делать не надо.
QUIK 9.5.0.42 По какой-то причине в таблице сделок отображались не все совершённые сделки: по фильтру аккаунта и кода бумаги в таблице сделок было 83 строки, хотя в реальности было совершено 92 сделки. Причина этого глюка, скорее всего в том, что терминал постоянно тупил, и сервер принудительно его отключал по таймауту. (Сделки совершались с другого терминала.) После переподключения к серверу всё повторялось по кругу. Когда я закрыл два двойных графика (оба графика были созданы давно, и раньше таких проблем не вызывали), терминал ожил. Но вот прогрузились не все сделки. Пришлось закрывать QUIK и удалять файл trades.dat (заодно удалил и orders.dat). И только после таких манипуляций все сделки прогрузились.
В общем, в QUIK не хватает в этом месте проверки на корректность загрузки и отображения заявок и сделок. Заодно в менеджер окон неплохо бы добавить CPU Usage каждым окном, чтобы было проще искать проблемное окно.
Надо делать так, как надо. А как не надо - делать не надо.
nikolz написал: Например, зачем в очередь записывать множество изменений цены инструмента , полученные onParam? Если main не успело обработать 999 изменений из 1000, то 999 устарели и важно лишь последнее.
Сам QUIK по такому же принципу работает: если в какой-то момент интерфейс подвиснет на некоторое время, то после оживления покажет вам все изменения цены за то время, пока QUIK был в ауте.
Цитата
nikolz написал: При этом в функции ProcessingCallbakc есть sleep(3000) т е после обработки каждой записи спим 3 секунды Выше я привел данные по скорости работы колшбеков примерно 3 мкс за время сна 3 секунды в таблицу которую орабатывает main успеет записаться 1000 строк из колбеков Поэтому и зависает
Мимо. Функция ProcessingCallbakc даже не вызывается, потому что в очередь ничего не записывается.
Если в основном цикле while is_run do end нет слипа, то он выполняется бесконечно и не прерывается на вызов колбеков. Поэтому счётчик #MAIN_QUEUE не увеличивается, и код даже не заходит в ветку if #MAIN_QUEUE > 0 then Чтобы колбеки вызывались, нужно добавить в цикл sleep или любую другую C-функцию.
Надо делать так, как надо. А как не надо - делать не надо.
Постоянно сканировать таблицу orders - не лучшая затея: при большом числе заявок гарантированы тормоза. Самый простой вариант - работать с колбеками OnOrder и OnCleanup. В OnOrder проверяете статус заявки, если активна добавляете в таблицу. Если заявка исполнена или снята (в т.ч. в клиринг), вы получите об этом OnOrder и удалите из таблицы активных. После смены сессии при получении OnCleanup таблицы активных полностью очищаете. Все возможные варианты (разрыв соединения, исполнение заявки пока терминал находился офлайн, восстановление соединения через день, месяц, год, задержка ответа по заявке на 10 мин и пр.) будут полность покрыты этими двумя колбеками.
Надо делать так, как надо. А как не надо - делать не надо.
Во всех случаях ошибка возникала в месте явного (через tonumber #105, #108, #114) или неявного (путём математической операции сложения #28) преобразования строки в число.
Надо делать так, как надо. А как не надо - делать не надо.
nikolz написал: потоко не безопасные функции в ней? Например, использовал функцию getParamEx.
И давно getParamEx стала потоконебезопасной?
Цитата
nikolz написал: Представим , что в момент вывода функцией main параметров некоторой таблицы QUIK происходит вызов терминалом колбека QLUA и изменение этой таблицы., ------------ Вопросы: 1) что будет выводить функция main в этом случае, то что было до вызова колбека или то, что осталось после вызова.
main абсолютно по барабану когда и сколько было вызовов колбеков. Если состояние таблицы сохранено в переменную,
Код
local Param = getParamEx2(class, sec, 'TIME')
то даже через час, день, год... её состояние не изменится. main выведет именно то, что сохранили в переменной.
Цитата
nikolz написал: если будут в параметрах nil, то это ошибка QLUA, или ошибка писателя main
Если такое случится, то, однозначно, - это зона ответственности разработчика QLUA.
Надо делать так, как надо. А как не надо - делать не надо.
TGB написал: при чтении таблиц bid/offer и их проверки на существование они есть, но при обращении к ним они удалены в потоке обслуживания стакана.
Чего?!! От этого ушли лет десять назад ещё при QPILE. Щас все (или большинство функций) дают вам статическую копию состояния таблицы на момент обращения.
Надо делать так, как надо. А как не надо - делать не надо.
nikolz написал: почему формула и КВИК дает разные результаты
Специально для тупой головы картинки сделал и обвёл красненьким нужные настройки. nikolz, потыкайте галочки в настройках и посмотрите внимательно, что даёт функция и что даёт квик в каждом случае.
Цитата
nikolz написал: Торгую с маржинальными сделками давно
Хвалиться нечем, если честно
Цитата
nikolz написал: Удивляюсь, как Вы вообще торгуете, если ничего не понимаете
Надо делать так, как надо. А как не надо - делать не надо.
local function Quotes(queue)
if type(queue) ~= 'table' then return nil end
local queue_ = {}
for i = 1, #queue do
queue_[tonumber(queue[i].price)] = tonumber(queue[i].quantity) -- table index is nil
end
return queue_
end
local Stakan = getQuoteLevel2(class_code, sec_code)
if Stakan then
local bid = Quotes(Stakan['bid'])
local offer = Quotes(Stakan['offer'])
end
В какой-то момент возникает ошибка "table index is nil". Что-то не так с tonumber?
Надо делать так, как надо. А как не надо - делать не надо.
Павел написал: Интерфейс программы начинает зависать при добавлении Стакана котировок - ответ интерфейса и отклик на выставление заявки из Стакана несколько секунд (доходил до 120 в периоды высокой волатильности).
Павел, в стакане включена верхняя панель? Если отключить верхнюю панель, тормоза уменьшатся?
Надо делать так, как надо. А как не надо - делать не надо.
nikolz написал: На учебном сервере плечо 2. Т е максимум 600 тысяч, но не 1 млн.759тыс982. Даже, если предположить, что плечо максимальное (для квалифицированных инвесторов, которым я являюсь, плечо 5 ) , то все равно не выходит каменная чаша.
Квалифицированный инвестор, смотрите "Тип клиента" - признак используемого типа ведения позиций. У вас на учебном сервере, скорее всего, используется схема ведения позиции по дисконтам.
Надо делать так, как надо. А как не надо - делать не надо.
nikolz написал: вычисления без этой функции можно ускорить, если запомнить размер лота и не извлекать его из хранилища а также не извлекать размер свободных денежных средств а рассчитывать их при совершении сделки и иногда сверять с портфелем.
Чтобы поделить количество свободных денежных средств на цену действительно не нужна никакая функция. Но существует такая вещь, как маржинальная торговля. Есть бумаги разного типа: маржинальные/не маржинальные, принимаемые и не принимаемые в в обеспечение маржинального кредита и пр. Почитайте, узнаете много интересного.
Надо делать так, как надо. А как не надо - делать не надо.
Не часто. Примерно с такой же частотой, как выходят релизы самого квика.
Цитата
TGB написал: 2) Где это происходит (в потоке main? В колбеках?)?
В данном случае ошибка возникла в main. Были ли подобные ошибки в колбеках сложно сказать, поскольку скрипт не останавливается при ошибках в колбеках, и их можно запросто не заметить.
Цитата
TGB написал: Если это возникает в main, то, как реализован цикл, в котором ошибка проявляется.
while
Цитата
TGB написал: 3) Есть ли возможность создать тест, в котором обнаруженная вами ошибка QLua 5.4 проявляется часто?
Не представляю как это сделать.
Цитата
s_mike@rambler.ru написал: Если возникают проблемы, дело не в os.date, а в чем то другом.
Что-то мне подсказывает, что nil вернул tonumber, см. значения локальных переменных тут и 2-ю ошибку тут.
Надо делать так, как надо. А как не надо - делать не надо.
function f()
local Hour = tonumber((os.date('%H')))
return Hour >= 10 and Hour < 22 -- тут возникла ошибка "attempt to compare number with nil"
end
debug.getlocal() выдала следующие значения локальных переменных на уровне стека:
Цитата
Hour: nil (temporary): "21" -- это видимо, значение os.date('%H'), ошибка действительно возникла в 21 ч (temporary): nil (temporary): "attempt to compare number with nil"
Для информации: параллельно работали ещё два скрипта. В одном из них в это время за 15 мс (минимальный квант) прошли следующие события: OnParam, OnQuote и 4 х OnOrder
Надо делать так, как надо. А как не надо - делать не надо.
Обратите внимание на поля moment и moment_ns потока таблицы orders_aggr:
Код
Поле Тип Описание
moment t Время последнего обновления записи
moment_ns u8 Время последнего обновления записи (UNIX - время в наносекундах по стандарту UTC)
Цитата
Daniil Pozdnyakov написал: ТТТ формируется не из одного потока, а из нескольких. В основном это FORTS_FUTINFO_REPL, FORTS_FUTCOMMON_REPL (для опционов соответственно FORTS_OPTINFO_REPL, FORTS_OPTCOMMON_REPL)
Начиная с версии 6.5 потоки FORTS_FUTCOMMON_REPL и FORTS_OPTCOMMON_REPL объявляются устаревшими, вместо них следует использовать FORTS_COMMON_REPL.
Обратите внимание на поля mod_time и mod_time_ns таблицы common:
Код
Поле Тип Описание
mod_time t Дата и время изменения записи
mod_time_ns u8 Дата и время изменения записи (UNIX - время в наносекундах по стандарту UTC)
Присоединяюсь к пожеланию.
Надо делать так, как надо. А как не надо - делать не надо.
Брокер по своему усмотрению может ограничить список инструментов, по которым можно открывать новые позиции. В какой таблице QUIK можно ознакомиться с актуальным списком инструментов с установленными на них ограничениями?
Надо делать так, как надо. А как не надо - делать не надо.
Так тут вопросов больше не брокеру, а к разработчику. Обработкой заявок занимается торговая система биржи. Какой смысл обрабатывать заявку, если пользователь хочет её снять? Это лишняя работа.
Daniil Pozdnyakov, какую роль играет библиотека расчёта лимитов на срочной секции?
Надо делать так, как надо. А как не надо - делать не надо.
Владимир, Я в курсе, что вы уже десятки лет гадите на разных интернет-ресурсах, на многих из которых вас уже погнали ссаной тряпкой, и поделом. Теперь вы обгадились в половине тем в ветке "Программирование на языке Lua" на этом форуме. Дискутировать с неадекватами, вроде вас, - ситуация игры в шахматы с голубем:
Цитата
Спорить с дураком - всё равно, что играть в шахматы с голубем. Он раскидает фигуры, нагадит на доску и улетит всем рассказывать, как он тебя уделал.
Надо делать так, как надо. А как не надо - делать не надо.
Daniil Pozdnyakov, по сообщению #179 есть вопросы? Нужно пояснить, что делает скрипт?
После заказа обезличенных сделок скрипт замеряет время получения первых 200000 сделок. После этого скрипт выдаёт замеренное время и отключается. Тест проводится 4 раза: 1) с созданием локальной переменной, содержащей массив нулевого размера 2) с созданием локальной переменной, содержащей двумерный массив 100x1000 3) с подключением библиотеки socket 4) с подключением библиотеки iuplua
Предполагается, что во всех 4-х тестах время работы скрипта должно быть одинаковым, поскольку колбек OnAllTrade во всех тестах выполняет одну и ту же работу: OnAllTrade содержит только три простых оператора, они не должны существенно влиять на время работы функции. В действительности же время работы скрипта много больше в тестах 2, 3, 4 по сравнению с тестом 1. Выводы, вытекающие из теста я неоднократно приводил в этой теме.
Надо делать так, как надо. А как не надо - делать не надо.
nikolz написал: запустил Ваш тест на боевом квике ------------------ вот результаты:
Там колбеки-то были? Сравнивать имеет смысл при наличии колбеков.
Цитата
Старатель написал: Нагрузка на CPU пропорциональна количеству любых объявленных переменных [...] и количеству колбеков, получаемых скриптом.
Скрипт:
Скрытый текст
Код
--local n = 0
--local n = 1000000
--local n = 3000000
local list = {TQBR = {"SBER", "GAZP", "VTBR", "RUAL"}, CETS = {"USD000UTSTOM"}, SPBFUT = {"BRH2", "RIH2", "SiH2", "SRH2"}}
local param = {"BID", "OFFER", "LAST", "NUMTRADES", "NUMBIDS", "NUMOFFERS"}
local run
function OnStop()
run = nil
end
local c = 0
function OnQuote(class_code, sec_code)
if not run then return end
c = c + 1
end
function OnParam(class_code, sec_code)
if not run then return end
c = c + 1
end
function OnAllTrade(alltrade)
if not run then return end
c = c + 1
end
function main()
local a = {}
for i = 1, n do a[i] = i end
for class, sec in pairs(list) do
for i = 1, #sec do
Subscribe_Level_II_Quotes(class, sec[i])
for i = 1, #param do
ParamRequest(class, sec, param[i])
end
end
end
local t = os.clock()
run = true
while run and t + 90 > os.clock() do
sleep(500)
end
t = os.clock() - t
for class, sec in pairs(list) do
for i = 1, #sec do
Unsubscribe_Level_II_Quotes(class, sec[i])
for i = 1, #param do
CancelParamRequest(class, sec[i], param[i])
end
end
end
message(string.format("%u: %u; %.0f/сек", n, c, c/t))
end
Результаты в боевом QUIK 9.3.3.3
В утреннюю сессию (~ 9 ч. МСК)
1. n = 0: 18233; 202/сек
Скрытый текст
2. n = 1000000: 19444; 215/сек
Скрытый текст
3. n = 3000000: 18127; 200/сек
Скрытый текст
В дневную сессию (~ 10 ч. МСК)
4. n = 0: 54455; 605/сек
Скрытый текст
5. n = 1000000: 45956; 510/сек
Скрытый текст
Надо делать так, как надо. А как не надо - делать не надо.
--local n = 0
local n = 5000000 -- special for Junior. Тест проводится поздно вечером на тухлом рынке, потому такое большое число. Думаю, днём можно уменьшить
local list = {QJSIM = {"SBER", "GAZP", "VTBR", "RUAL"}, CETS = {"USD000UTSTOM"}, SPBFUT = {"BRH2", "RIH2", "SiH2", "SRH2"}}
local param = {"BID", "OFFER", "LAST", "NUMTRADES", "NUMBIDS", "NUMOFFERS"}
local run = true
function OnStop()
run = nil
end
function main()
local a = {}
for i = 1, n do a[i] = i end
for class, sec in pairs(list) do
for i = 1, #sec do
Subscribe_Level_II_Quotes(class, sec[i])
for i = 1, #param do
ParamRequest(class, sec, param[i])
end
end
end
while run do sleep(500) end
for class, sec in pairs(list) do
for i = 1, #sec do
Unsubscribe_Level_II_Quotes(class, sec[i])
for i = 1, #param do
CancelParamRequest(class, sec[i], param[i])
end
end
end
end
function OnQuote(class_code, sec_code) end
function OnParam(class_code, sec_code) end
function OnAllTrade(alltrade) end
Заказ обезличенных сделок:
Скрытый текст
Получение данных: снята галка "Запрашивать данные раз в ... сек."
1. Загрузка при запущенном скрипте с n = 0 и открытой ТТТ. В ТТТ добавлены 4 класса: те же, что и в ТОС, со всеми параметрами.
Скрытый текст
Практически нулевая - скрипт не оказывает никакого влияния.
2. Загрузка при запущенном скрипте с n = 5000000 и открытой ТТТ.
Скрытый текст
Загрузка стала больше. А что изменилось? Только добавился массив, больше ничего.
3. Загрузка при запущенном скрипте с n = 5000000 и закрытой ТТТ.
Скрытый текст
Daniil Pozdnyakov, если у вас не получается проделать то же самое, готов продемонстрировать на компьютере вашего тестировщика по RDP.
Надо делать так, как надо. А как не надо - делать не надо.
--local v = 0
--local v = 100
--local v = "socket"
--local v = "iuplua"
local function f(n)
if type(n) == "number" then
local a = {}
for i = 1, n do
a[i] = {}
for j = 1, 1000 do a[i][j] = j end
end
return a
elseif n == "socket" then
local socket = require("socket")
require("socket.smtp")
return socket
else
return require(n)
end
end
local run = true
function OnStop()
run = nil
end
local m
function OnInit()
m = f(v)
end
local t
function OnAllTrade()
if getNumberOf("all_trades") == 1 then
local num = 1
function OnAllTrade(alltrade)
if not run then return end
num = num + 1
if num == 200000 then
t = os.clock() - t
run = false
end
end
t = os.clock()
end
end
function main()
while run do sleep(500) end
if t then message(v .. ": " .. t) end
end
Поочерёдно раскомментирую одну из 4-х первых строк и запускаю скрипт. Далее делаю перезаказ обезличенных сделок: Система / Настройки / Основные настройки... -> «Программа» / «Получение данных» / «Обезличенные сделки». Выбираю класс "Акции 1-го уровня (эмулятор)" -> "Перезаказать данные"
Оговорюсь, что сейчас помимо тестового у меня запущено ещё 3 квика и несколько других приложений. Поэтому результаты могут быть искажены. Но позволяют сделать следующие выводы:
Цитата
Старатель написал: Сам по себе скрипт не выполняет какой-либо полезной работы, он только вызывает пустой OnAllTrade(), больше ничего. Увеличение времени работы скрипта при подключении различных библиотек как раз свидетельствует о наличии зависимости нагрузки, создаваемой колбеком, и количеством данных в скрипте.
Надо делать так, как надо. А как не надо - делать не надо.
Daniil Pozdnyakov, я правильно понимаю, что тесты проводились на Junior, где количество всех инструментов (если не считать информационный класс) чуть более тысячи? Из которых более половины редко обновляются или не торгуются вообще. Для справки: у меня в боевом квике, в котором открыты только стаканы и графики по 8-ми фьючерсам, "умный" заказ сформировал список из более 30 тыс. инструментов, которыми я вообще не торгую и не открывал даже. Плюс ко всему инструменты на бою обновляются на порядок чаще, чем у вас на демке. И все изменения параметров по всем этим инструментам вызывают OnParam во всех скриптах, где есть этот колбек. В таком случае и количество скриптов и/или количество данных надо увеличивать пропорционально. Это, конечно, если цель - разобраться в причинах повышенной загрузки, а не просто отчитаться "запустили/посмотрели".
Цитата
Daniil Pozdnyakov написал: "Какие конкретно функции используются в данных скриптах ?"
Исходя из вопросов, ясно, что никто ни черта не делал и даже не читал тему. Вижу, что "тестирование" (если это можно так назвать) проведено на от#сь, совершенно без включения мозгов. Как я отмечал, оказывают влияние не только функции, но также данные в локальных/глобальных таблицах.
Позже дам комментарий по сообщению #19. Думал, для людей с технической специализацией оно будет понятно. Видимо, придётся расписывать для тех, кто с компьютером на "Вы" и то шёпотом. Но на это потребуется время.
Надо делать так, как надо. А как не надо - делать не надо.
В целях минимизации количества транзакций и снижения нагрузки на сервера предлагаю повысить приоритет для данного пожелания. А то люди иногда попадают на повышенную комиссию при групповом снятии заявок: https://smart-lab.ru/blog/765010.php#comment13661438 Там, правда, сторонний привод используется. Но у квиковского функционала группового снятия заявок принцип схожий.
Надо делать так, как надо. А как не надо - делать не надо.
Anton написал: Дело в том, что нештатно останавливается. Если OnStop возвращает 0, сразу за ней выполняется WaitForSingleObject(main, 0), возвращаясь немедленно из-за нулевого таймаута, и сразу же TerminateThread. Там потом еще уведомление главному окну посылается. Видимо, какие-то сопли после этого всего остаются.
Получается с той или иной долей вероятности можно налететь на такое при любом значении в return и даже без него?
Надо делать так, как надо. А как не надо - делать не надо.
Так зависает не во время работы скрипта. Сначала скрипт штатно останавливается, при этом никаких ошибок не выдаёт. Затем скрипт уже перестаёт запускаться. И при попытке вызове какого-нибудь меню квик вешается.
Надо делать так, как надо. А как не надо - делать не надо.
Денис написал: после нажатия кнопки "Остановить" скрипт, вроде бы, штатно останавливается - но затем через пять-семь минут при нажатии на любой пункт меню QUIK намертво зависает.
Проверил - да, так и есть. Хоть с квиком работаю уже много лет, но на чём квик сейчас застопорился не пойму.
Надо делать так, как надо. А как не надо - делать не надо.