Anton (Автор тем)

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

Страницы: 1
Вопросы, не требующие ответов
 
Дабы не загаживать прочие темы своими наблюдениями, создам себе отдельную. Цели получить ответ от сотрудников арки не ставлю, но если лишний раз глянут в свой код и убедятся, что заданный вопрос всего лишь ошибка наблюдений, или, паче чаяния, поправят что-нибудь, никому хуже не станет. Если в какой-то момент арка решит, что вопросы здесь излишне интимные, фил фри, как говорится, прибить всю ветку без размышлений, никто не обидится.


Для начала наблюдение номер один. Где-то в глубине qlua.dll готовится вызов OnAllTrade. Как положено, вызывается lua_checkstack. Но погодите, вызывается lua_checkstack(state, 1) и тут же на стек кладутся ДВА элемента, сама функция и ее аргумент. Кроме того, в процессе заполнения таблицы еще и временные элементы кладутся. Все ли верно, нигде ли стек не испортится?

Аналогичное наблюдение можно сделать при вызове (видимо) любого колбека с аргументом. OnConnected, OnDepoLimit, OnDepoLimitDelete, OnFirm, ...
Состояние Quik в момент вызова OnCleanUp
 
1. Верно ли следующее утверждение: в момент вызова колбека OnCleanUp и, как минимум, до момента его завершения очистка состояния еще не началась, то есть все таблицы содержат всю информацию, предшествующую получению от сервера сообщения о смене сессии? В частности, это подразумевает, что в теле OnCleanUp можно сделать "полный снимок состояния таблиц на момент непосредственно перед очисткой", при этом ничто не наткнется на nil или внезапно удаленные "из-под носа" данные (все файлы типа alltrade.dat все еще отмэплены в своем последнем размере, их количество записей еще не обнулено и т.д. и т.п.).

2. И, если верно, то верно ли другое утверждение: после возврата из колбека OnCleanUp любой вызов любого колбека, связанного с изменением состояния таблиц, будет относиться уже к новой сессии? То есть, грубо говоря, никакой очереди из вчерашних колбеков вслед за OnCleanUp быть в принципе не может, а если что-то волшебным образом затесалось, оно будет прибито как часть смены сессии.
*AllTrade*, новые функции для qlua
 
Как показывает практика, полноценно использовать CreateDataSource для подписки на таблицу обезличенных сделок не представляется возможным. Посему предлагаю добавить в qlua несколько новых функций.

1. Функция getAllTradeFilters
Код
STRING getAllTradeFilters(STRING class_code)
Возвращает строку в формате, аналогичном возвращаемому getClassSecurities(class_code), содержащую список бумаг указанного класса, для которых включен заказ всех сделок (попросту строку из INFO.ini для данного класса). Если для класса нет выбранных инструментов (эквивалентно отсутствию класса в INFO.ini или же там записано class_code=), возвращает пустую строку. Если для класса выбраны все инструменты (эквивалентно class_code=ALL в INFO.ini), возвращает то же самое, что и getClassSecurities(). В случае ошибки возвращает nil.

2. Функция setAllTradeFilters
Код
BOOLEAN setAllTradeFilters(STRING class_code, STRING security_code_list)
Устанавливает список бумаг, для которых включен заказ всех сделок, для указанного класса. Если security_code_list является пустой строкой, отключает заказ всех сделок для всего класса (эквивалентно class_code= в INFO.ini). Если security_code_list содержит ту же строку, что вернула бы getClassSecurities(class_code), включает заказ всех инструментов класса (эквивалентно class_code=ALL в INFO.ini). Возвращает true, если фильтры были изменены, false в случае ошибки (и гарантирует, что во втором случае имевшиеся до вызова фильтры остались неизменными). Определенная сложность видится при наличии открытых в терминале ТОС, что делать, если данный вызов пытается отменить заказ данных для используемых таблицами инструментов?

3. Функция SubscribeAllTrades
Код
TABLE SubscribeAllTrades(STRING class_code, STRING security_code_list)
Выполняет подписку на ТОС по указанному классу и списку инструментов, как если бы пользователь открыл ТОС с аналогичными фильтрами в терминале, но, в отличие от таблицы, не изменяет глобальные фильтры заказа данных, даже если установлена автонастройка фильтров по открытым таблицам. Возвращает таблицу, одним из полей которой является функция close() для последующей отписки, и, возможно, в таблице имеются поля с кодом класса и списком инструментов, прошедших глобальные фильтры, т.е. по которым действительно произошел заказ данных. Возвращает nil в случае ошибки.

Квик должен гарантировать, что при любом (нормальном/принудительном) завершении скрипта, выполнившего подписки на ТОС, все эти подписки будут автоматически отменены, что легко достижимо установкой метаметода __gc для возвращенной из SubscribeAllTrades таблицы на ее же метод close().


-----------------------------------

*Мячты конечно мячты.
CreateDataSource, не делает квик сильнее
 
Введение
Нужно экспортировать всю ТОС луа-скриптом. Дело не то чтобы сложное, если обязать юзера держать ТОС открытой. Но хочется покрасивше, давайте на старте подпишемся на все доступные инструменты? А давайте...

Глава первая, шапкозакидательская
Код
local SubscribeAllTrades = function()
   local cls = split(getClassesList(), ',')
   for k, v in pairs(cls) do
      local sec = split(getClassSecurities(v), ',')
      for kk, vv in pairs(sec) do
         local ds = CreateDataSource(v, vv, INTERVAL_TICK)
         if nil ~= ds then
            ds:SetEmptyCallback()
         else
            message(v .. ':' .. vv)
         end
         ds = nil
      end
   end
end

OnI nit = function()
   SubscribeAllTrades()
end
запускаем - и квик намертво виснет, загрузив одно ядро на 100%. Через час-другой экперимент прекращен через прибитие квика. В научных целях упражнение повторялось неоднократно, результат один.

Глава вторая, исследовательская
Добавляем логи, смотрим, что происходит. Квик резво создает датасорцы, добирается до класса с опционами (а их там ой как много) и процесс начинает постепенно замедляться. Моего терпения хватило до момента, когда создание одного датасорца занимало 6 секунд, проверить апорию об ахиллесе и черепахе не вышло, квик был снова прибит.

Глава третья, кулхацкерская
Где-то между неудачными запусками в волшебном INFO.ini была ручками очищена секция [ALL_TRADES_DATA], так что содержала только
Код
[ALL_TRADES_DATA]
req-all-trades-from-curr-moment=0
Запускаем наш недоскрипт снова и что мы видим? Квик по мере создания датасорцев пишет в эту секцию коды инструментов. Ну, это логично и ожидаемо, пока дело не доходит до секции с опционами. Их и так много, но квику все мало и он начинает добавлять и добавлять опционы по второму, третьему и так далее кругу. Удалось получить INFO.ini размером в 200 с лишним мегабайт, дальше ждать смысла не было. Весь этот объем был набран исключительно классом SPBOPT, остальные классы были "нормальные", хотя я бы и ожидал от квика чуть большего интеллекта, чтобы самому заменить исчерпывающий список класса на ALL (при выборе всех инструментов в диалоге настройки получаемых данных он же как-то догадывается).

Глава четвертая, предположительная
Судя по всему, при подписке на опционы (лучше сказать на класс с большим количеством инструментов) квик где-то попадает в бесконечный цикл. Предположительно из-за жесткого лимита на размер буфера, т.е. после достижения определенного размера строки с инструментами квик дальше уже не смотрит, делает вывод "инструмент не найден" и добавляет его еще раз. История с INFO.ini, скорее всего, лишь внешнее проявление внутренней проблемы, поскольку поведение не меняется даже если там для всех классов вручную выставлено ALL.

Глава пятая, вопросительная
Опуская множество сопутствующих вопросов, я спрошу:
1) как из скрипта при закрытой ТОС подписаться на все доступные инструменты, чтобы это было выполнено в обозримое время?
2) если я из скрипта залезу в INFO.ini и перепишу секцию [ALL_TRADES_DATA], дабы освободить юзера от ковыряния в диалоге заказа данных, это будет грязный хак или не очень?

Глава пятая, пожелательная
Уже было такое пожелание здесь, но и я добавлю. Пусть CreateDataSource со вторым параметром nil и третьим INTERVAL_TICK подписывается сразу на весь класс (и при необходимости выставляет в INFO.ini для этого класса ALL).
Хозяйке на заметку: OnStop(), или "Зачем вы это делаете, мистер Андерсон?"
 
Код
stopflag = false

OnI nit = function()
   OnS top = function()
      message("Hello, I'm OnStop")
      stopflag = true
      return 10000
   end
end

main = function()
   while not stopflag do
      sleep(1000)
   end
end

Три дня в отладчике, три раза свой код переписал, "где ж я дедлок-то устроил". Ан нет, не я. Все колбеки как колбеки, OnStop почему-то кэшируется где-то до начала OnInit, так что в OnInit ее переопределять уже бесполезно. Зачем, зачем вы это делаете, мистер Андерсон?

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

П.П.С. OnI nit, OnS top это не я так написал, это движок форума упорно поправляет.
OnMainInit(), Новый луа-колбек
 
В нынешнем виде пара OnInit() / main() не позволяет надежно инициализировать скрипт, поскольку в момент выполнения OnInit() поток main еще не существует (или существует, но недоступен из скрипта), а в момент начала выполнения main() после выхода из OnInit() уже произоошли (или могли произойти) какие-то события, которые без принятия специальных мер будут пропущены. Очень удобно было бы иметь дополнительный колбек, назовем его условно OnMainInit(), выполняемый после создания потока main в контексте этого потока, но при условии, что основной поток квика ждет завершения этого колбека, то есть ни один другой колбек между OnInit и OnMainInit гарантированно не был дернут.

Очень грубо, только чтобы идею донести, как это могло бы выглядеть в коде квика

Код
void QuikInitializeLuaScript(...)
{
   // ...
   HANDLE hevent = CreateEvent(...);
   HANDLE hmainthread = _beginthreadex( ... , scriptmainproc, hevent, CREATE_SUSPENDED);
   script->OnInit();
   ResumeThread(hmainthread);
   WaitForSingleObject(hevent);
   // (по-хорошему тут надо на двух объектах ждать, на событии и самом потоке, кто первый выстрелит)
   // и только теперь продолжаем основной поток квика
}

unsigned __stdcall scriptmainproc(void * pvoid)
{
   HANDLE hevent = reinterpret_cast<HANDLE>(pvoid);
   myscript->OnMainInit();
   SetEvent(hevent);
   myscript->main();
   return 0;
}

Собственно, так и приходится делать, просто жаль создавать еще один поток, а main использовать только для ожидания завершения этого лишнего потока, когда то же самое могло бы быть из коробки.

lua_error() в OnInit, Неправильное поведение
 
Контекст: луа-скрипт загружает длл, все остальное делается в длл через луа-си интерфейс. В том числе создаются луа-объекты с метатаблицами и обработчиками сборки мусора (финализаторами). Наблюдаю два случая.

1. Где-то в main() вызывается lua_error(). Все вообще здорово, main() завершается, отрабатывают финализаторы для созданных луа-объектов, длл выгружается, в окне квика с луа-скриптами выводится описание ошибки.

2. Вызываем lua_error() в OnInit(). Скрипт также мгновенно останавливается, в окне квика с луа-скриптами появляется описание ошибки, дополнительно выводится окно сообщений с тем же описанием. Но вот внутри длл происходит адъ и израиль. Финализаторы луа-объектов не вызываются, длл остается загруженной в квик. Дальше возможны варианты:

2.1. Если закрыть квик, длл выгружается средствами ос, естественно, ни о каких финализаторах из луа речь уже не идет.

2.2. Если повторно запустить этот же скрипт, "зависшие" объекты из предыдущего экземпляра финализируются, отрабатывает вся последовательность очистки, как в случае с ошибкой в main(). Но, естественно, новый экземпляр скрипта остается "недобитым".

2.3. Если запустить какой-нибудь другой скрипт, на "недобитый" скрипт это никак не влияет.

Выводы:

1. На текущем этапе допускать lua_error() в OnInit() и, возможно, в каких-то еще колбеках (не проверял еще) не представляется возможным без сочинения костылей для принудительной очистки в случае ошибки.

2. Разработчикам квика просьба посмотреть, что можно "дернуть" после ошибки в OnInit(), чтобы скрипт был "добит" как положено. Думается, достаточно будет гарбидж коллектор попросить собрать мусор в контексте убитого скрипта.
ТВС: количество в штуках, Альтернатива полю quantity
 
Хотелось бы увидеть в ТВС столбец с количеством в штуках, а не в лотах, либо возможность переключить quantity на штуки. Зачем? Затем, что размеры лотов периодически меняются, превращая накопленную историю с количествами в лотах в шлак, требующий пересчета, биржа сама отдает количество в штуках и при синхронизации данных приходится все пересчитывать, а размеры лотов опять изменились и скачать старые негде, и т.д. и т.п, в общем сильно нужно.
Время в таблице всех сделок - баг или фича?, Точность наше все
 
Столкнулся со странностью во времени сделок, приходящих в таблицу всех сделок. Смотрим картинки:

а) ТВС в квике


б)  данные с сервера биржи


Как видим, в квике тик ненавязчиво переехал в  предыдущую секунду с миллисекундами 999. Таких мест можно найти  достаточно, если поискать на сервере биржи тики с миллисекундами, точно  равными 000. Соответственно вопросы: А) что это за чудеса и как от них  избавиться? Конечно, можно сказать, что это же всего лишь миллисекунда, у  тебя пинг в сто раз больше. И я бы согласился, если бы время было  16:53:12.001 и даже 16:53:12.501. Но не 16:53:11.999; Б) это так на всех  инструментах или только на фьючерсах и прочих, где биржа дает "честные"  миллисекунды? В) а может это на бирже округляют? )
Как узнать таймзону времени в таблицах
 
Здравствуйте.

Насколько понимаю, время в таблицах Quik может быть либо в часовом поясе биржи, либо в локальном часовом поясе, если включена соответствующая настройка. Вопрос: как узнать из скрипта, включена ли эта настройка?

Юзкейс такой. Запущен квик, подключения к серверу еще нет, скрипт читает все, что есть в интересующих его таблицах, конвертирует в "свой формат" и сохраняет. Радость в том, что "свой формат" хранит время в UTC и непонятно, какую таймзону использовать для конвертации.
Когда закончится память Quik?
 
Когда-то миллион сделок в ТВС бывало не каждый день, сегодня у меня их три с половиной миллиона, и тенденция, видимо, сохранится. В связи с этим, учитывая 32-битность квика, возникли такие вопросы:

1) проводились ли исследования, при каком количестве сделок в ТВС квик упадет с out of memory?
2) проводились ли исследования, при каком количестве сделок в ТВС квик не сможет начать DDE-экспорт этой таблицы из-за нехватки памяти?
3) и немного с другой стороны - компилируется ли квик с флагом 3GB, что на 64-битной системе дает лишний гигабайт виртуальной памяти?
Однопоточности в QLua захотелось
 
Где-то здесь (или на старом форуме, теперь не найти) прочел, что Quik синхронизирует доступ к данным из разных потоков. Поскольку в моем случае поток единственный (тот самый, из которого колбеки вызываются), а main проста до неприличия и выглядит примерно так

Код
int qMain(lua_State * pState)
{
   if(bInitialized)
   {
      WaitForSingleObject(hStopEvent, INFINITE);
      uninitialize();
   }
   return 0;
}
, то пришла большая програмистская жаба и начала душить по поводу "зачем нам эта синхронизация и как бы ее отключить бы, а то вся эта затея с экспортом через луа по скорости в разы проигрывает старому доброму DDE". Собственно, о том и вопрос, нет ли безобразно-хакерского способа сделать так, чтобы квик НЕ синхронизировал доступ к данным в луа?
Страницы: 1
Наверх