Игорь написал: Вы по сути предлагаете условный лимитник по другой бумаге
Он называется "по другой", но "по той же самой" прекрасно работает тоже. В отличие от прочих видов стопов позволяет поставить с любым условием и в любую сторону. Но трейлить не будет, да. По моему скромному мнению трейлинг не работает не потому, что он в квике плохо сделан (хотя и плохо тоже), а потому, что эта идея в принципе нерабочая.
Оно все фигня, даже если кажется, что не фигня. На конструктивную фигню арка, глядишь, и патчик сделает, а на вопли "тут все неправильно, все в топку" - нет.
Игорь написал: Но ведь и локальные максимумы при срабатывании «стоп-цены по другой бумаге» будут также отсчитываться от цены СЛЕДУЮЩЕЙ сделки ПОСЛЕ срабатывания этой стоп-цены.
Этот вид стопа ничего вообще не отсчитывает, он выставляет лимитник с фиксированной ценой и все. Тут у многих проблема, как я понимаю, в осознании факта, что цену вам не квик предлагает, это не дилерский рынок. Если прошел тик по вашей стоп-цене, он уже прошел, в стакане уже нет этого уровня, его уже слопали, ваш лимитник попадает в стакан на то, что там осталось. В частности, иногда там вообще ничего может не быть, тупо пустой стакан.
TGB написал: А если local gcrunner = {} (локальная переменная) то __gc срабатывает при при любом запуске collectgarbage (в том числе, и до завершения скрипта).
Есть такое. Значит, коллектор не считает объявление переменной ссылкой на нее. Значит, надо глобально ее объявлять или хотя бы иметь где-то ссылку на нее.
Насчет функций. Если gcrunner глобальный, то выскочит сообщение, мол используете прибитый файл, а это значит, что io еще не выгружена, это ее сообщение. Получается, файл прибит не в результате выгрузки библиотеки. Я не нашел, чтобы квик явно где-то файлы закрывал или даже чтобы явно выгружал библиотеки, хотя может где и есть. Принимаем как данность. Строго говоря, финализаторы это не деструкторы, никакой порядок их выполнения не гарантируется, так что теоретически все ок, а как практически это обходить, надо думать.
local gcrunner = (function()
local t = {}
setmetatable(t, { __gc = function()
local t = tid
tid = nil
if t then DestroyTable(t) end
message('file type is ' .. io.type(file))
file:write("__gc\n")
file:close()
end })
return t
end)()
Владимир написал: Я чуть не всю жизнь занимался сложными базами данных, и утверждаю, что ЕСТЬ "в базе данных терминала адреса строки или чего то ещё, описывающей сущность". По той простой причине, что доступ к любому элементу данных ДОЛЖЕН быть обеспечен.
Особенно в trie, там одни строки и есть. Но есть нюанс.
Владимир написал: можно просто передать указатель на запись
... и держать лок хранилища, чтобы оно не дай бог что-нибудь там внутри не подвинуло, пока юзер в колбеке ковыряется. Вариант выше упомянут и признан идиотским, каковым он и является. Прочие варианты тоже перечислены и это полная группа, отлаживай не отлаживай. Раз хранилище асинхронное, оно вольно асинхронно очиститься в том числе, и индекс тоже инвалидирутеся. Крэша он не вызовет, а нил вернет, и объяснять придется.
Не в этом случае. Хранилище асинхронное. Можно либо сразу вытащить всю запись и передать по значению, как и сделано, либо держать лок до возврата колбека, что есть идиотизм, либо передать индекс записи и объяснять потом пользователям, почему иногда возвращается ничего. Максимальное улучшение тут видимое - посмотреть при старте скрипта на сигнатуру колбека и не заполнять табличку, если она ему не нужна. Но это из области полировки.
Владимир написал: а теперь там же стал ещё и убивать окно своей таблицы
Код
local run = true
local tid = nil
local gcrunner = (function()
local t = {}
setmetatable(t, { __gc = function()
local t = tid
tid = nil
if t then DestroyTable(t) end
end })
return t
end)()
function main()
tid = AllocTable()
AddColumn(tid, 1, '1', true, QTABLE_INT_TYPE, 1)
CreateWindow(tid)
...
Незнайка написал: Что выгодней с точки зрения производительности
Думаю, в приведенном виде вариант с колбеком будет медленнее в целом, сначала квик лезет в таблицу, чтобы дернуть колбек, потом мейн лезет в таблицу, чтобы получить данные. Этот вариант более интересен, когда мейн не крутится в цикле, а стоит на событии, тогда в неактивное время он вообще не просыпается. Но это уже подразумевает длл, в луа таких сущностей нет. А так вообще ворос более архитектурный, если охота event-driven, кроме колбека и вариантов нет, пусть бы и медленнее.
Владимир написал: при нажатии кнопки "добавить" при загрузке скриптов он открывает не папку "своего" Квика, а ту, из которой была последняя загрузка.
В этом конкретном случае квики путает винда. Квик просто открывает стандартный диалог, а тот сам помнит, кто что открывал последнее. Но помнит абы как, по имени программы без полного пути, вот и получается путаница. По-хорошему надо к диалогу прицеплять уникальный идентификатор, но квик этого не делает.
2.2.25 OnInit Функция вызывается терминалом QUIK перед вызовом функции main(). В качестве параметра принимает значение полного пути к запускаемому скрипту.
Latrop написал: относительно много тратится квиком на перекладывание примитивов с стек луа.
Думаю, причина в синхронизации, каждый вызов lua_push*, lua_set* захватывает-выпускает лок, на большой таблице сотня туда-сюда вполне может произойти. Есть куда улучшить. Сколько-то будет выиграно, когда выпилят рудименты 5.1, сколько-то можно выиграть, захватывая явно лок перед заполнением таблицы (но выпуская перед вызовом колбека). Либо можно было бы создать один стейт без синхронизации на весь квик, заполнять табличку в нем и потом lua_xmove ее в стейт скрипта. В моменте, очевидно, более животрепещущие есть задачи, может когда-нибудь и до этого доберутся.
_sk_ написал: Осталось окно скрипта куда-нибудь спрятать
Если полосы прокрутки выключены, можно спрятать в позицию (-1000, -1000), со включенными не придумал куда. С помощью длл можно скрыть, но тогда можно и совсем без окна обойтись.
_sk_ написал: Костыли типа отправки какой-нибудь транзакции на сервер, чтобы OnTransReply сработал, не предлагать.
А если без сетевого обмена костылик? Поглядите
Код
local run = true
local tid = nil
function main()
tid = AllocTable()
AddColumn(tid, 1, '1', true, QTABLE_INT_TYPE, 1)
CreateWindow(tid)
InsertRow(tid, -1)
InsertRow(tid, -1)
SetWindowPos(tid, 0, 0, -1000, -1000)
SetSelectedRow(tid, 1)
SetTableNotificationCallback(tid, function(t, m, w, l)
if QTABLE_SELCHANGED == m then
if 2 == w then
SetSelectedRow(t, 1)
message('!')
end
end
end)
while run do
sleep(1000)
SetSelectedRow(tid, 2)
end
OnStop()
end
function OnStop()
local t = tid
tid = nil
if t then
DestroyTable(t)
end
run = false
end
Создаем окошко с двумя строчками, выделяем первую, ставим колбек на изменение выделения, в колбеке возвращаем выделение в первую строку. Когда надо колбек выполнить в квиковском потоке, меняем выделение на вторую строку и вуаля, тут же отрабатывает колбек, чего мы и хотели. Вместо message(), естественно, вызываем что-то свое полезное.
Там никаких потоков и нет, поток для мейна любезно qlua.dll нам создает. То есть будет один поток квика, что достигается и в луа, если в мейне ничего не делать, окромя периодической проверки флага выхода. Немножко синхронизации и заполнения (да, иногда ненужных) луа-табличек погоды не делают, там счет на сотни наносекунд на круг. По одной ягодки сам квик вытаскивает из очереди своей и тут же незамедлительно колбек и дергает, если он определен, и, как тысячу раз нам говорили, никогда, НИКОГДА воробьянинов не протягивал руки он не будет ничего откладывать и пересортировывать. Что действительно очень правильно и хорошо.
Пункт 2 я предлагал в несколько другой редакции, сейчас передумал. Причина: единственное ожидаемое применение - обойти зависание при работе с таблицами, пока квик ждет завершения мейна. Но сам заказ колбека придется посылать той же SendMessage (а как иначе), в результате вместо зависания на таблице зависнем на отправке колбека.
Пункт 1 по-моему так должен формулироваться
Код
BOOLEAN setTimer(INTEGER timeout_ms, FUNCTION callback)
и прототип колбека
Код
INTEGER CALLBACK timerCallback(VOID)
Если колбек возвращает отрицательное число, данный таймер прибивается. Если ноль или больше - меняется период таймера на возвращенное значение. Интересный крайний случай при периоде 0 - это тот же немедленный вызов колбека в потоке квика.
Latrop написал: почему вы до сих пор не раскрутили этот qlua.dll и не написали свой плагин qapi.dll
Потому что это нарушение eula? Тем более что и смысла нет. Все почему-то думают, что "у квике усе есть, это злая qlua.dll целенаправленно гадит". Ничего там радикально другого нет, без луа будут ровно те же колбеки дергаться ровно в том же порядке.
новичок написал: такого кайфого сетапа давно не видел
В целом тоже понравилось, я, правда, ставил минималку консольную и потом по одному накатывал чудеса. Видюхи не пробовал никакие, интеловской встройки за глаза хватило.
В том, что пытались снять несуществующий стоп. Он уже исполнился, в смысле "настоящая" заявка ушла в стакан, сам стоп уже прекратил свое существование, о чем синяя подсветка и говорит. Сегодня он исчез не потому, что на сутки был выставлен, а потому, что новая сессия началась, весь мусор от старой забыт. Если не хотите видеть исполненные в таблице, там есть настройка их скрывать.
Иван написал: А в чем, собственно, такая ценность ордерлога?
Ну как бы raw data, как у фотографов, фотку в джипеге можно даром скачать, а ее же в raw уже покупать надо. С точки зрения полезности для трейдера - построить моделек, поизучать их динамику, погенерировать исходов, повычислять вероятностей. Многим не нужно в принципе, для них и полезность близится к нулю.
- Здравствуйте, Сэр! Ваше обращение получено, проблема изучается. Постараемся в ближайшее время дать ответ.
И дальше две модели - синхронная и асинхронная. Синхронная - это как форум, задал вопрос, через некоторое время зашел, поглядел, что ответили, если ничего - попозже еще зашел и так далее. Асинхронная - это оставил мыло (колбек), как проблему изучили, прислали ответ туда. Тут тоже возможны варианты, можно написать "ответ на ваш вопрос появился на форуме", то есть просто напомнить, что пора пойти и синхронно глянуть, либо прислать ответ целиком, чтобы никуда уже не ходить. В квике есть все эти варианты для разных вопросов. В идеальном мире были бы возможны все варианты для любого вопроса по выбору скрипта. Скажем, мне вот не нужна в OnAllTrade вся эта табличка с трейдом, мне нужен только факт, что туда что-то приехало, а бедный квик каждый раз табличку заполняет, в которую я даже не погляжу. А кому-то табличка нужна.
Иван написал: По моим представлениям, они должны быть равны, так как если кто-то продал N лотов, то кто-то должен был купить эти N лотов.
В твс одна запись на сделку, в отличие от ордерлога. Если есть запись "продажа N лотов", то молчаливо предполагается, что другая сторона эти N лотов купила. На вопрос, почему отличается от ордерлога, ответ в том, что в ордерлоге не только сделки, но и заявки, там есть разница, какая из заявок купила, а какая продала, в твс же было бы просто две идентичных записи с разным направлением, бессмысленное удвоение.
валерий написал: разве нельзя было просто ввести дополнительный параметр - идентификатор ядра?
Не готов ответить обоснованно. Возможно, были другие варианты, возможно нет. В любом случае назад дороги нет и надо привыкать жить вот в такой реальности.
(Государство это я). Вроде и юзер это я, а вроде и скрипт это не я, хотя вроде как и я, налицо некое раздвоение личности. Лично я (пап, а кто это?) предпочитаю юзера считать некой третьей силой, неуправляемой и непредсказуемой, что там ему взбредет в данную микросекунду, сие тайна великая есть, так что готовимся к худшему. Но это тксть лирика, а физика
Цитата
меняет ли физически открытый стакан значение функции IsSubscribed_Level_II_Quotes.
вот она. Сейчас нет возможности это проверить, так что готовому ответу порадовался бы.
валерий написал: Кстати интересно, может кто скажет зачем? Зачем 19 знаков?
Грубая картина для понимания зачем. (Предположим, что) раньше на фортсе вся торговля шла в одно ядро. То есть все заявки со всех шлюзов стекались на один комп, там сводились в стакан и сделки, сделкам назначались последовательные номера и результаты опять разбрасывались по всем шлюзам. Теперь решили это дело распараллелить. Побочный эффект такого решения - номера теперь нельзя назначать последовательно, т.к. если ядра будут получать очередной номер сделки откуда-то из центрального источника, это будет то же одно ядро вид сбоку. Поэтому ядрам назначили диапазоны номеров, номер теперь содержит локальный номер сделки на ядре и идентификатор ядра, на котором эта сделка произошла, всего 64 бита, а это как раз 19 десятичных знаков. Второй побочный эффект - номера сделок на разных ядрах больше не будут строго последовательными, теперь сортировать ТВС по номеру сделки и надеяться, что вот в таком порядке они и происходили, уже нельзя.
Приходилось работать с приложениями, прожившими долгую жизнь. Когда в одном файле MFC образца девяностых, а рядом уже какой-то юный прогрессор натыкал c++14, и все это собирается с намеком на то, что работать не будет, но в итоге работает и все довольны, "только вот тут поправить", а куда там смотреть, чтобы поправить, это еще догадаться надо, бо каша. Думаю, в квике примерно та же история творится, хотя очевидно, что постепенно там все переписывается.
TGB написал: Вы согласны что описанная выше ситуация может возникнуть?
А я вроде с этим и не спорил. Хотя Владимир прав, если колбеки виснут, они рано или поздно довиснут до крэша, сколько бы в системе ресурсов ни было. Я только отметил, что предложенный способ решения таковым не является, ничего кроме новых глюков в итоге не появится. И да, мне бы не хотелось, чтобы кто-нибудь в арке повелся на "легкие решения" и нагородил еще кучу "особенностей поведения", пытаясь это реализовать костыль за костылем.
Если окно никуда не прицеплять, будет просто отдельное от квика окно. Чтобы окно было внутри квика, надо SetParent ему сделать с MDIClient'ом квика в качестве родителя. А это прицепит поток с пользовательским окном все к тому же основному потоку квика и работать они будут как один поток. И ради чего тогда вся возня с созданием окон в отдельных потоках? Или что-то другое имелось в виду здесь
Цитата
TGB написал: Давно надо было бы сделать обработку обсуждаемых таблиц в отдельном потоке.
TGB написал: Я же пишу о потоках и не вижу особых проблем взаимодействия между ними.
Просто так создать свое окно верхнего уровня в своем потоке нет проблем, в квике же окно придется прицепить к MDIClient'у главного окна, который будет в другом потоке, а это приведет к автоматическому AttachThreadInput, а это даст а) никаких из ожидаемых преимуществ, т.к. потоки будут синхронизироваться и ждать обработки сообщений друг друга; б) еще кучку дедлоков, странных глюков и прочего подобного. Цитата из того же автора
Цитата
A lot of people use AttachThreadInput thinking that it’s a Get Out of Jail Free card, letting them manipulate windows of other programs and bypass the normal rules for focus and activation. But in fact it’s a Get Into the Same Jail card, because you tied your thread’s fate to that other thread. If that thread has stopped responding to messages, then your thread will also stop responding to messages, since you are sharing the same input queue and operations within an input queue are synchronous.
Владимир написал: Так тела колбеков и расположены в мейне!
Тела колбеков расположены в скрипте, а когда и кем они будут вызваны - это совсем другое дело, вон выше пример, если OnStop вызывает квик, он выполняется в потоке квика, а если я сам дергаю - то в потоке мейна. Тксть трюк исполнен профессионалами, не повторяйте дома.
Цитата
Владимир написал: если я нажму на кнопку останова, а скрипт в это время будет обрабатывать прерывание OnTrade, то управление должно вернуться КУДА?
Чтобы вернуть управление, надо сначала получить управление. Вы жмете кнопку остановить, но главный поток квика занят в OnTrade, сообщения не обрабатываются. Вот выйдет скрипт из OnTrade, квик подергает сообщения из очереди, увидит нажатую кнопку и приступит к остановке скрипта.
Владимир написал: в момент останова скрипт может выполнять код какого-то обработчика
Не может, если OnStop вызван, ни один колбек ни в одном скрипте сейчас не выполняется. Но могут работать мейны. В том и суть, прогарантировать, что ни одного колбека скрипт больше не получит, выйти из обертки над OnStop и дать мейну доработать в нормальном окружении, а через возвращенное из OnStop число секунд убедиться, что мейн завершился, прибить если надо и закончить очистку скрипта.
_sk_ написал: хороший мануал с примерами и объяснениями, почему именно так надо
Весь мануал сведется к тому, что после OnStop квик синхронно ожидает завершения мейна, так что любая попытка вызвать функцию qlua, приводящую к отправке оконных сообщений, приведет к зависанию и принудительному завершению скрипта. Чтобы готового с примерами - вроде нет такого.
По-хорошему квик должен бы после OnStop отменять для скрипта колбеки, ставить таймер и возвращать управление вызвавшему коду, а уже по таймеру проверять состояние мейна и прибивать его по необходимости. Но один крайний случай есть, когда скрипт завершается в процессе закрытия квика, как только квик выйдет из WM_DESTROY, главное окно будет прибито и схема не сработает и, вполне вероятно, еще и наглючит. Поэтому, думаю, так и не делают.
_sk_, спасибо на добром слове. Все ж впопыхах про особенности национальной DestroyTable забыл, надо getClassParametersTest.lua поменять:
Код
require('getClassParameters')
local run = true
local tid = nil
function OnStop()
local t = tid
tid = nil
if t then
DestroyTable(t)
end
run = false
end
local function newwnd()
local tid = AllocTable()
if not tid then error('AllocTable failed') end
if not AddColumn(tid, 1, 'class code', true, QTABLE_STRING_TYPE, 15) then
DestroyTable(tid)
error('AddColumn failed')
end
if not AddColumn(tid, 2, 'formal name', true, QTABLE_STRING_TYPE, 25) then
DestroyTable(tid)
error('AddColumn failed')
end
if not AddColumn(tid, 3, 'short name', true, QTABLE_STRING_TYPE, 30) then
DestroyTable(tid)
error('AddColumn failed')
end
if not AddColumn(tid, 4, 'long name', true, QTABLE_STRING_TYPE, 60) then
DestroyTable(tid)
error('AddColumn failed')
end
if not AddColumn(tid, 5, 'enum values', true, QTABLE_STRING_TYPE, 240) then
DestroyTable(tid)
error('AddColumn failed')
end
if not CreateWindow(tid) then
DestroyTable(tid)
error('CreateWindow failed')
end
if not SetWindowCaption(tid, 'All available parameters') then
DestroyTable(tid)
error('SetWindowCaption failed')
end
return tid
end
local function popwnd(tid)
local t = getClassesList()
local clst = {}
for s in string.gmatch(t, '([^,]+)') do
table.insert(clst, s)
end
table.sort(clst)
for k, v in ipairs(clst) do
local prm = getClassParameters(v)
for kk, vv in ipairs(prm) do
local r = InsertRow(tid, -1)
SetCell(tid, r, 1, v)
SetCell(tid, r, 2, vv.formal)
SetCell(tid, r, 3, vv.short)
SetCell(tid, r, 4, vv.long)
SetCell(tid, r, 5, vv.enum)
end
end
SetSelectedRow(tid, 1)
end
function main()
tid = newwnd()
popwnd(tid)
while run do
if IsWindowClosed(tid) then
OnStop()
break
end
sleep(500)
end
end
В общем, не стал я ждать милостей от природы, взял их и вся недолга. Тксть представляем новый скрипт, добавляющий в qlua функцию getClassParameters. Текущий вариант назовем бета-версией, тестирование было, но надо больше. О найденных глюках пишите здесь. Поддерживается квик 8.5 и новее.
Где взять. Взять здесь. Ссылка будет живой две недели, кто не успел тот опоздал, пишите письма. После скачивания и распаковки архива рекомендуется проверить хотя бы один из хэшей файла getClassParameters.dll во избежание подмены длл злыми кулхацкерами.
Как проверить. Прежде чем кидаться кодить, проверьте, оно у вас вообще работает ли. Распакуйте весь архив куда-нибудь, главное чтобы все в одной папке лежало, и запустите скрипт getClassParametersTest.lua. Он покажет окно квика со всеми доступными параметрами всех доступных классов. Или не покажет, тогда просьба отписаться здесь.
Как использовать в своем скрипте. 1. Положить getClassParameters.dll и getClassParameters.lua рядом со своим скриптом (где бы он ни был). 2. В своем скрипте подключить длл: require('getClassParameters'). 3. По мере необходимости дергать появившуюся глобальную функцию getClassParameters.
Описание функции.
Код
TABLE getClassParameters(STRING classcode)
Аргумент classcode - код класса, для которого нужно получить параметры. Возьмите его из getClassesList, разобрав полученную строку по запятым, или захардкодьте. Функция возвращает луа-таблицу со всеми параметрами для запрошенного класса. Обратите внимание, что элементы считаются с 1, как принято в луа:
Поля каждого элемента: formal - формальное название параметра. short - короткое название параметра. long - длинное название параметра. enum - для параметров, чьи возможные значения предопределены, список этих значений через вертикальную черту, для прочих пустая строка.
В случае ошибки функция не возвращает nil, а кидает луа-ошибку. Если это нежелательно, вызывайте с помощью pcall. Как пример использования смотрите скрипт getClassParametersTest.lua из комплекта.
Гарантии производителя. Автор заверяет, что никаких вредоносных включений в длл не интегрировал, никакая информация не собирается и никуда не отправляется. Тем не менее, поверхность поражения имеется. Во-первых, код не подписан и потому может быть подвержен внешним воздействиям, проверяйте хэши длл. Во-вторых, от ошибок в коде никто не застрахован, все последствия оных пользователь принимает на себя. В-третьих, код основывается на недокументированных особенностях квика, которые могут измениться или вообще исчезнуть в очередном его обновлении.
Поддержка. Поддержка продукта, включая ответы на вопросы, может быть, а может и не быть, в зависимости от настроения левой пятки автора.
Владимир написал: Интерпретатор же просто квантует время и периодически что-то там ему выполняет "в свободное от работы время".
Мы ж договорились, что интерпретатор загружает контекст обработчика и отдает ему всю вычислительную мощность до победного завершения, как принято в кооперативной модели. А уже раз и квантует время, а это уже модель вытесняющая. Концепция поменялась?
Цитата
Владимир написал: А потому, огранизовав аналогичный стек на данных и запрограммировав интерпретацию команд некоего подмножества ассемблера,
мы получаем эмулятор некого процессора, существующего или гипотетического. Так же, как луа симулирует некую выдуманную стековую машину. Так же, как ява. Так же, как сишарп. Или как qemu симулирует существующие процессоры. Или как virtualbox симулирует ажно целый компьютер со всей периферией. И опять подкрадывается тот же вопрос - а главное зачем.