swerg написал: Как такое можно было сделать?! Интересно, в компании Arqa есть отдел Quality Assurance? включают ли сотрудники этого отдела иногда голову?
Средства разработки многопоточных скриптов в QUIK., OS_Quesha, свидетельство регистрации в Роспатенте № RU 2020612905. Бесплатная для некоммерческого использования.
К объёму дневной свечи предыдущего дня примазывается объём вечерней сессии, т.е. вечёрка считается два раза. Утром сразу после подключения к серверу начинают грузится тики. И объём вчерашней дневной свечи и close меняются с каждым тиком.
Надо делать так, как надо. А как не надо - делать не надо.
Искажает данные QUIK-клиент: на минутных и часовых таймфреймах задваивает объём последней свечи предыдущего дня. На дневном таймфрейме не понятно, как считает.
Надо делать так, как надо. А как не надо - делать не надо.
Похоже, к объёму предпоследней свечи приплюсовывается ещё какая-то левота.
Евгений, Проделайте следующее: закройте QUIK и удалите curr_data.log и info.log. После запустите QUIK. Не подключаясь к серверу, посчитайте объём. На всех таймфреймах сумма за 26.01 должна быть 4199489.
Надо делать так, как надо. А как не надо - делать не надо.
Евгений написал: На подсказке вот эта цифра 4846095
Такая же фигня. На разных таймфреймах за 26.01 такие суммы объёмов по SiH1 : Причём, если по тикам считать, то будет 4 199 489 за день. В dat-файле такое же значение на дневном графике. Скрин из QMinEditor:
Надо делать так, как надо. А как не надо - делать не надо.
Roman Azarov, о вашей заинтересованности говорит реакция на обращения (не только в этой теме): спустя три недели после сообщения о MOVE_ORDERS:
Цитата
Roman Azarov написал: Данная проблема нами не разбиралась.
Так ка на самом деле
Цитата
Незнайка написал: заполняется ли вообще поле сервером при перестановке заявок
или нет? О каких разных конфигурациях вы пишите, когда эта проблема общего характера. И рассматривать её надо не в частном порядке.
Цитата
Roman Azarov написал: устранение проблемы на демо, вовсе не обязательно приведет к скорейшему устранению проблемы на боевом сервере брокера
MOVE_ORDERS - это ведь проблема не демо, не так ли? Как только (и если) ошибка будет исправлена, и брокер накатит обновление, canceled_uid будет заполняться и на серверах брокера. Некоторые брокеры устанавливают обновления сразу после их выпуска, другие - прежде тестируют на своём тестовом контуре. Это решение брокера, что и когда обновлять.
Цитата
Roman Azarov написал: обращение будем вынуждены закрыть
Моё дело малое: сообщить об обнаруженной проблеме разработчику ПО. Если это никому не надо, так мне тем более.
Надо делать так, как надо. А как не надо - делать не надо.
BlaZed написал: определить что данные еще не загружены легко. Например для числовых значений getParamEx2 будет возвращать всегда 0
Это не показатель. Многие параметры легко могут быть равны нулю. И цена, наверное, тоже (?), тот же фьючерс на нефть. Но это не точно. Признаком, что данные в течение торговой сессии не были получены является пустая строка в param_image.
Надо делать так, как надо. А как не надо - делать не надо.
swerg, ну давайте пофантазируем, что в ближайшем десятилетии нам дали такую функцию:
Цитата
ParameterReceived(STRING class_code, STRING sec_code, STRING param_name) Возвращаемые значения: nil - при ошибке 0 - подписка не включена 1 - подписка включена, но параметр ещё не получен с момента последней подписки 2 - подписка включена, параметр получен
Либо можно возвращать то же значение дополнительным параметром received в таблице, возвращаемой getParamEx2 Значение 0 - можно и не делать, не вижу возможности его использовать. Возможно, и сам клиент, узнаёт об успешности подписки только по факту получения параметра с сервера. В любом случае, сервер что-то шлёт клиенту (хотя бы дефолтные нули и пустые строки), даже если
Цитата
swerg написал: данных объективно этих может не быть (неликвид)
Использование:
Скрытый текст
Код
function main()
ParamRequest(class_code, sec_code, param_name)
local r = ParameterReceived(class_code, sec_code, param_name)
if r == 2 then
-- Параметр уже был заказан и получен
local param = getParamEx2(class_code, sec_code, param_name)
-- Далее можно сразу работать с этим параметром или ждать колбека
...
elseif r == 1 then
-- Параметр ещё не получен, ждём колбек
elseif r == 0 or r == nil then
message("Что-то пошло не так", 3)
end
end
function OnParam(class_code, sec_code)
local r = ParameterReceived(class_code, sec_code, param_name)
if r == 2 then
-- Параметр получен
local param = getParamEx2(class_code, sec_code, param_name)
...
else
-- Ждём следующий колбек
return
end
end
Либо то же с использованием параметра в таблице, возвращаемой getParamEx2:
Код
function main()
ParamRequest(class_code, sec_code, param_name)
local param = getParamEx2(class_code, sec_code, param_name)
if param.received == 2 then
-- Параметр уже был заказан и получен
-- Далее можно сразу работать с этим значением или ждать колбека
...
elseif param.received == 1 then
-- Параметр ещё не получен, ждём колбек
elseif param.received == 0 or param.received == nil then
message("Что-то пошло не так", 3)
end
end
function OnParam(class_code, sec_code)
local param = getParamEx2(class_code, sec_code, param_name)
if param.received == 2 then
-- Параметр получен
...
else
-- Ждём следующий колбек
return
end
end
Если дальше фонтазировать, то в OnParam можно добавить третьим таблицу изменившихся параметров. Но есть мнение, что сервер шлёт клиенту сразу всю строку заказанных параметров, в не зависимости от того, какой из них изменился. И дальнейший разбор отдан на откуп скриптеру. Можно было бы на стороне клиента, разобрать этот список и в таблицу записать только те, что изменились, и вернуть в OnParam.
Цитата
BlaZed написал: При отмене подписки на параметр в кеш ТТТ должен заноситься nil
Так-то, да, было бы понятней, но боюсь, не все с этим согласятся.
Цитата
BlaZed написал: Как временное решение, перезапуск квика с параметром "-clear", тогда кеш будет чистый
В начале сессии кеш и так очищается, а ежели в течение сессии, да ну нафиг такое решение
Надо делать так, как надо. А как не надо - делать не надо.
Нужны данные ТОС (Таблица Обезличенных Сделок) по SBER с 11 по 15 января 2021., К сообществу_Не к техподу_Нужна помощь для восстановления пропущенных данных.
Старатель написал: Про время передачи данных по каналам связи ведь все в курсе?
Я к тому, что выражение "актуальные данные" в данном случае относительно. И, надеюсь, это понятно, и мы не будем в 100500 раз мусолить тему, о том, что "сам QUIK не знает, что вот прям щас происходит на бирже".
Надо делать так, как надо. А как не надо - делать не надо.
swerg написал: не позволяет вам узнать актуальные сейчас вы видите данные в терминале или нет.
Про время передачи данных по каналам связи ведь все в курсе? Надеюсь вы не это хотите обсудить, а вопрос:
Цитата
Старатель написал: Но пока сообщение о подписке дойдёт до сервера, тот будет слать раннее заказанные параметры (без Р). В результате, вызвав getParamEx2 в коллбэке OnParam(), мы получим старое значение параметра Р, сохранённое в кеше на момент времени Т0.
Если последнее, то давайте начнём хотя бы с сообщения #19
Надо делать так, как надо. А как не надо - делать не надо.
Старатель написал: В заявках, снятых в результате транзакций "MOVE_ORDERS" на FORTS и "Изменение заявки" на ФР, UID снявшего не заполняется в любом случае. Наблюдается как на демо, так и на боевом.
Ошибку в серверном ПО QUIK сами найдёте?
Надо делать так, как надо. А как не надо - делать не надо.
Владимир, как я понял, у вас всегда открыта ТТТ, и в неё добавлены все интересующие вас инструменты и параметры. И вы принципиально не пользуетесь ParamRequest, которая в таком случае и не требуется. Эта же тема для истинных ценителей автоматизации, у которых в терминале открыто только одно окно: Доступные скрипты, ну ещё могут быть окна, созданные скриптами.
Цитата
Владимир написал: за это время котировка ТОЖЕ может поменяться
Имелось ввиду, что пока трейдер примет решение, котировка обновится до актуальной, а не то, что вы подумали.
Надо делать так, как надо. А как не надо - делать не надо.
Sergey Gorokhov написал: Для понимания что заказанный параметр начал ехать нужно ждать коллбэк OnParam(), после чего вызывать getParamEx2
Даже в таком варианте вместо актуального значения можно получить не пойми что. Пример: в момент времени Т0 клиент был подписан на получение нескольких параметров по бумаге, затем подписка на параметр Р была закрыта. Через несколько часов в момент времени Т1 скрипт снова подписывается на параметр Р. Но пока сообщение о подписке дойдёт до сервера, тот будет слать раннее заказанные параметры (без Р). В результате, вызвав getParamEx2 в коллбэке OnParam(), мы получим старое значение параметра Р, сохранённое в кеше на момент времени Т0.
Надо делать так, как надо. А как не надо - делать не надо.
Nikolay написал: Если источник-приемник один, то достаточно в источнике открыть файл в режиме записи, а в приемнике в режиме чтения и просто проверять новые данные в этом файле через read("*l").
Можно и так. Тогда отпадает необходимость в открытии/зактытии/удалении файлов, что затратно.
При частой записи данных файл обмена может сильно разрастись. В этом случае есть сомнения в скорости сброса данных на диск. Маленькие файлы кешируются в памяти операционной системой, а большие? Не получится ли так, что каждый новый flush будет дольше предыдущего?
Цитата
Игорь Б написал: Я делаю так же, только с одним файлом. Передатчик создает файл и пишет туда инфу. Приемник смотрит наличие этого файла. Для него это флаг, что можно читать свежую инфу. Он (приемник) ее читает и удаляет файл. Для передатчика отсутствие файла означает, что файл прочитан и можно создавать файл для передачи новой инфы. Если это и хуже варианта с двумя файлами, то интересно чем?
Думаю, не хуже. Но в обоих случаях при одновременном открытии файла обмена несколькими источниками гипотетически часть данных может быть потеряна.
Цитата
Владимир написал: файл - один, но для каждого скрипта выделяется собственное пространство (фиксированного объёма). Далее они читают и пишут только в свои зоны, а общий диспетчер (который эту таблицу формирует) ставит флаги "прочитано, можно писать следующую порцию".
Вариант, заслуживающий внимания.
Ещё вариант: читать/писать под локом. Файл один, открывается один раз, при остановке скрипта закрывается. Данные записываются в режиме изменения в начало файла, сообщение добивается нулями до фиксированной длины.
Надо делать так, как надо. А как не надо - делать не надо.
Nikolay написал: Конечно, все это для обмена большими объемами и несколькими источниками-потребителями. Если данных мало и они редки, то все это излишне.
ТС задал вопрос по выводу данных в общую визуальную таблицу. Врядли там большие объёмы данных.
Цитата
Nikolay написал: Если пара одна, то это приведет к тому, что все будут ждать пока он освободится.
А в других перечисленных вами вариантах ждать не будут?
Цитата
Nikolay написал: Если источник-приемник один, то достаточно в источнике открыть файл в режиме записи, а в приемнике в режиме чтения и просто проверять новые данные в этом файле через read("*l").
Можно и так. Тогда отпадает необходимость в открытии/зактытии/удалении файлов, что затратно.
Надо делать так, как надо. А как не надо - делать не надо.
Nikolay написал: Это надо для каждый пары источник-приемник создавать пару файлов. Как только несколько писателей, с двумя файлами может быть блокировка.
Второй файл как раз служит для писателей индикатором, что первый файл занят, и запись не возможна. Как только приёмник прочитает данные, он удаляет 2-й файл, что сигнализирует о возможности записи. Т.е. организуются синхронные запись/чтение.
Кстати, ни разу не видел, блокировок файлов, одновременно открытых несколькими Lua-скриптами. Если файл открыт другим приложением, то, да, было.
Надо делать так, как надо. А как не надо - делать не надо.
Kolossi написал: Номер окна выдает каждый раз следующий.16-17-18-19... и так по порядку каждый раз при убиении и запуске скрипта.
Так и есть. При перезапуске скрипта идентификаторы новых окон увеличиваются.
Цитата
Nikolay написал: Вариант 3 - файлы. Но взаимные блокировки будет проблемой.
Вот тут пример организации обмена с двумя файлами: 1-й файл для передачи данных, 2-й - пустой файл, служит флагом для индикации готовности данных к считыванию.
Надо делать так, как надо. А как не надо - делать не надо.
s_mike@rambler.ru написал: Имея номер окна, можно писать в него из любых скриптов.
Не знаю, может у меня версия квика какая-то другая. Но нумерация окон не сквозная, у каждого скрипта своя, начинающаяся с 1. Т.е., идентификаторы окон разных скриптов могут совпадать. И писать в окно другого скрипта не получается.
Надо делать так, как надо. А как не надо - делать не надо.
Чтобы обход таблицы был более корректным, изменим тестовый скрипт, так, чтобы добавление и удаление полей таблицы осуществлялось в одном потоке:
Скрытый текст
Код
local run = true
function OnStop()
run = nil
end
local function f(t)
for i = 1, 100 do
t[""..i] = i
end
for k, v in pairs(t) do
t[k] = nil
end
t = {}
return t
end
p = {}
function OnParam(class_code, sec_code)
p = f(p)
end
a = {}
function OnAllTrade(alltrade)
a = f(a)
end
m = {}
function main()
while run do
m = f(m)
sleep(1)
end
end
Ошибка "invalid key to 'next'" никуда не делась. Для форсирования: перезаказать все обезличенные сделки.
Надо делать так, как надо. А как не надо - делать не надо.
Roman Azarov написал: С некоторой вероятностью может появиться следующая ошибка?
Верно
Цитата
Roman Azarov написал: У себя подобного за целый день не увидели
Можете форсировать события:
Скрытый текст
Код
local p = {}
function OnParam(class_code, sec_code)
for i = 1, 20 do
p[sec_code..i] = class_code
end
end
local t = {}
function OnAllTrade(alltrade)
for i = 1, 20 do
t[alltrade.sec_code..i] = alltrade
end
end
function main()
while run do
for k, v in pairs(p) do
p[k] = nil
end
for k, v in pairs(t) do
t[k] = nil
end
sleep(1)
end
end
и сделать перезаказ всех обезличенных сделок.
Надо делать так, как надо. А как не надо - делать не надо.
Существует ненулевая вероятность, что сборщик придёт как раз во время обхода таблицы в одном из потоков.
Скрытый текст
Код
local p = {}
function OnParam(class_code, sec_code)
p[sec_code] = class_code
end
local t = {}
function OnAllTrade(alltrade)
t[alltrade.sec_code] = alltrade.class_code
end
function main()
while run do
for k, v in pairs(p) do
p[k] = nil
end
for k, v in pairs(t) do
t[k] = nil
end
sleep(1)
end
end
Надо делать так, как надо. А как не надо - делать не надо.
local T = {}
function main()
while run do
...
for k, v in pairs(T) do -- тут ошибка
...
end
...
sleep(1)
end
end
stack traceback:
Цитата
in for iterator 'for iterator'
На момент возникновения ошибки таблица T была пуста: за время работы (3 часа) робота в таблицу элементы не добавлялись. Написать тестовый скрипт для воспроизведения или повторить ошибку не удалось.
Надо делать так, как надо. А как не надо - делать не надо.
swerg написал: Согласитесь, то, что речь про демо-сервер
Да, как оказалось, отсутствие canceled_uid в заявках для "KILL_ORDER" относится к только демо-серверу. В боевом квике все заявки без canceled_uid были сняты "MOVE_ORDERS".
Надо делать так, как надо. А как не надо - делать не надо.
Roman Azarov, В заявках, снятых транзакцией "KILL_ORDER", не заполняется UID снявшего, если транзакция снята вскоре после выставления. В 8.11 наблюдается в junior. В заявках, снятых в результате транзакций "MOVE_ORDERS" на FORTS и "Изменение заявки" на ФР, UID снявшего не заполняется в любом случае. Наблюдается как на демо, так и на боевом.
Будете ли вы это исправлять в своём ПО - это ваше решение.
Надо делать так, как надо. А как не надо - делать не надо.