Settings = {
Name = 'ParamRequest',
line = {
{ Name = 'ParamRequest' }
}
}
function Init()
return #Settings.line
end
local class_code, sec_code
function OnChangeSettings()
OnDestroy()
local DSI = getDataSourceInfo()
class_code = DSI.class_code
sec_code = DSI.sec_code
PrintDbgStr(tostring(sec_code))
ParamRequest(class_code, sec_code, 'LAST')
end
function OnCalculate(index)
if index == 1 then
ParamRequest(class_code, sec_code, 'LAST')
end
return nil
end
function OnDestroy()
if sec_code then
CancelParamRequest(class_code, sec_code, 'LAST')
end
end
При добавлении / удалении индикатора возникают ошибки:
Цитата
Function OnChangeSettings: ACCESS VIOLATION at address 000007FC524B89CC ACCESS VIOLATION at address 000007FC524B89CC
Что не так?
Надо делать так, как надо. А как не надо - делать не надо.
local Labels = {}
function OnCalculate(index)
if index == 1 then
OnDestroy()
end
...
local Label = AddLabel(Settings.tag, Param)
if Label and Label > 0 then
Labels[#Labels+1] = Label
end
...
end
function OnDestroy()
for i = 1, #Labels do
PrintDbgStr('DelLabel(' .. Labels[i] .. '): ' .. tostring(DelLabel(Settings.tag, Labels[i])))
end
Labels = {}
end
При закрытии Квика срабатывает OnDestroy, но метки с графиков не удаляются. И при следующем запуске индикатор ставит новые метки поверх старых, что есть не хорошо. Так и было задумано или же ошибка?
Надо делать так, как надо. А как не надо - делать не надо.
Сделайте уже, наконец, чтобы при сохранении настроек в файл из меню QUIK открывал по умолчанию ту папку, в которой находится info.exe, а не ту, в которой что-то делали в ДРУГОМ квике. Реально, чтобы сохранить настройки, надо пройти целый квест с кучей проверок, как бы не затереть настройки другого квика. И ещё можно утилизировать старый паровоз, а на его место поставить кнопку "Сохранить настройки".
Надо делать так, как надо. А как не надо - делать не надо.
Через Lua отправляю транзакцию на удаление заявок по фьючерсам {TRANS_ID=trans_id, ACTION="KILL_ALL_FUTURES_ORDERS", ACCOUNT=account, CLASSCODE="SPBFUT", BASE_CONTRACT=base_contract} Но удаляются также заявки и по спредам (FUTSPREAD) с тем же базовым активом. Что я делаю не так?
В шлюзе SPECTRA Plaza-2 для метода DelUserOrders есть маска группы инструментов:
Брокер по своему усмотрению может ограничить список инструментов, по которым можно открывать новые позиции. В какой таблице QUIK можно ознакомиться с актуальным списком инструментов с установленными на них ограничениями?
Надо делать так, как надо. А как не надо - делать не надо.
Срочный рынок. Через Lua подаётся транзакция MOVE_ORDERS. В течение более 6 мин. сервер сыпет ошибки: Вы не можете заменить заявку ..., так как ее обработка еще не завершена. Код ошибки: -1065103584 Источник ошибки: Библиотека расчёта лимитов
Сервер не способен обработать заявку более 6 мин.? Что за обработка такая? Зачем он вообще это делает? Обработкой заявок занимается торговая система биржи. А от терминала требуется передать транзакцию от пользователя в торговую систему и вернуть ответ от ТС в терминал пользователя. И сделать это желательно как можно быстрее.
Надо делать так, как надо. А как не надо - делать не надо.
local n = 50000
for i = 1, n do
_G["f"..i] = function () end
end
local class = "SPBFUT"
local sec = "SiZ1"
local param = {"BIDDEPTHT", "OFFERDEPTHT"}
local run = true
function OnStop()
run = nil
end
function main()
assert(Subscribe_Level_II_Quotes(class, sec))
for i = 1, #param do
ParamRequest(class, sec, param[i])
end
while run do sleep(500) end
Unsubscribe_Level_II_Quotes(class, sec)
for i = 1, #param do
CancelParamRequest(class, sec, param[i])
end
end
function OnQuote(class_code, sec_code) end
function OnParam(class_code, sec_code) end
function OnAllTrade(alltrade) end
QUIK 9.3.1.11, Lua 5.4 Открыто, как минимум одно окно: стакан ликвидного инструмента. Конечно, никто не запускает скрипты с тысячами функций, но при нескольких запущенных скриптах с десятками функций при высокой активности на бирже получаем нихилую загрузку CPU.
ЗЫ: У кого "один скрипт на все случаи жизни" с парой функций, может игнорировать эту тему. Без флуда!
Надо делать так, как надо. А как не надо - делать не надо.
local run = true
local id
function OnStop()
run = nil
local t = id
id = nil
if t then DestroyTable(t) end
end
function main()
id = AllocTable()
AddColumn(id, 1, "Row", true, QTABLE_INT_TYPE, 10)
AddColumn(id, 2, "Selected", true, QTABLE_INT_TYPE, 15)
CreateWindow(id)
for i = 1, 4 do
SetCell(id, InsertRow(id, i), 1, tostring(i), i)
end
SetTableNotificationCallback(id, function(t, event, row)
if event == QTABLE_VKEY then
message("SetSelectedRow: " .. tostring(SetSelectedRow(id, 2)))
elseif event == QTABLE_SELCHANGED then
SetCell(id, row, 2, tostring(row))
end
end)
while run do sleep(1000) end
end
Применить к таблице фильтр, как на скриншоте: "Row больше 1" При нажатии на любую клавишу, нужно выделить строку с фактическим порядковым номером 2. Но выделяется строка с номером 3, о чём сообщает событие QTABLE_SELCHANGED.
1. Как выделять строки по фактическому порядковому номеру, с учётом того, что скрипт понятия не имеет о применённых фильтрах и сортировках? 2. Да в руководстве указано: "Функция работает с видимым представлением таблицы, в котором учитываются пользовательские фильтры и сортировка." Но нафига оно онужно? Можете привести хоть один реальный пример, когда нужно выделить строку в таблице, в которой "учитываются пользовательские фильтры и сортировка", принимая во внимание, что скрипт понятия не имеет о применённых фильтрах и сортировках?
Надо делать так, как надо. А как не надо - делать не надо.
Подглядел в другом терминале: с зажатым контролом скрол вниз уменьшает масштаб, скрол вверх - увеличивает. При этом, масштабирование происходит таким образом, что свеча, над которой находится курсор остаётся на месте.
Надо делать так, как надо. А как не надо - делать не надо.
Открыто два стакана по одному инструменту. В каждом стакане включена панель торговли. В ней выбран конкретный торговый счёт. Они разные в каждом стакане. Как сделать, чтобы при открытии формы ввода заявки из стакана (двойной клик) в поле "Торговый счет" автоматически подставлялся тот торговый счет, который выбран в панели торговли?
Надо делать так, как надо. А как не надо - делать не надо.
local class, sec_code = "SPBFUT", "SiU1"
local run = true
function OnStop()
run = nil
end
function main()
local ds = assert(CreateDataSource(class, sec_code, 1))
ds:SetUpdateCallback(function (index)
if index < ds:Size() then return end
for i = 1, index do
ds:T(i)
ds:O(i)
ds:H(i)
ds:L(i)
ds:C(i)
ds:V(i)
end
end)
while run do sleep(1000) end
end
За несколько минут объём занимаемой скриптом памяти вырастает с 300 Кб до Гигабайтов.
Если график раннее не заказывался и убрать строку
Код
if index < ds:Size() then return end
, то сразу при запуске будет 2Гб.
Надо делать так, как надо. А как не надо - делать не надо.
Вы бы в инструкцию включили пример, когда в результате срабатывания Тейк–профита в ТС может выставиться лимитированная заявка с ценой на 954 п. ниже максимума, при том, что отступ+спред = 8.
Надо делать так, как надо. А как не надо - делать не надо.
1. https://forum.quik.ru/messages/forum10/message57044/topic475/#message57044 В целях корректной обработки события закрытия таблицы предлагаю вызывать OnClose, когда только дана команда главному окну терминала на закрытие (нажат крестик, Alt+F4 или "Система -> Выход") до QTABLE_CLOSE. Тогда OnClose можно будет использовать, чтобы определить, что таблица закрыта не пользователем.
К логину было подключено два кода клиента. Скрипт работал по обоим нормально. Потом один код был отключен брокером или пользователь зашел с логина, где подключен только один клиент, не суть. В результате вместо того, чтобы выдать ошибку, что у пользователя нет прав для работы с отключенным кодом клиента, QUIK стал молча подменять его на другой. Т.е., скрипт отправляет транзакцию с CLIENT_CODE = client2//brokerref, а заявка приходит с client_code = client1, brokerref = client1//client2//brokerref Это косяк.
Надо делать так, как надо. А как не надо - делать не надо.
local run = true
function OnStop()
run = nil
end
function main()
local pos
while run do
local fut_pos = getFuturesHolding(FIRMID, ACCOUNT, SEC_CODE, TYPE)
if type(fut_pos) == "table" then
local totalnet = fut_pos.totalnet
if pos ~= totalnet then
message(SEC_CODE .. ": pos = " .. tostring(pos) .. "\n" .. fut_pos.sec_code .. ": totalnet = " .. tostring(totalnet), 3)
pos = totalnet
end
else
message("getFuturesHolding error", 3)
end
sleep(1)
end
end
Скрипт 2
Скрытый текст
Код
local run = true
function OnStop()
run = nil
end
function main()
while run do
getFuturesHolding(FIRMID, ACCOUNT, SEC_CODE, TYPE)
sleep(1)
end
end
Запускаем скрипт 1 и несколько скриптов 2 по разным бумагам. Любуемся результатом.
Возможно, другие функции getЧегоТоТам также работают нестабильно. Проверяйте сами.
Надо делать так, как надо. А как не надо - делать не надо.
Иногда CreateDataSource возвращает неверные данные. Демонстрационный скрипт:
Скрытый текст
Код
class, sec_code = "SPBFUT", "SiH1"
local run = true
function OnStop()
run = nil
return 500
end
function main()
local Volume -- Дневной объём
local ds_D1 = assert(CreateDataSource(class, sec_code, INTERVAL_D1))
ds_D1:SetUpdateCallback(function (index)
if not run then return end
Volume = ds_D1:V(index)
end)
sleep(1000)
local Day = os.date("*t").day
local CreateDS = true
local ds_M1, Index
local function cb(index)
if not run then return end
if Index and index > Index then
run = false return -- Ждём закрытия свечи, на которой обнаружена ошибка
end
if math.floor(os.time() / 60) ~= math.floor(os.time(ds_M1:T(index)) / 60) then return end -- Время свечи не совпадает с текущим
local volume = 0 -- Суммарный объём за текущий день
for i = index, 1, -1 do
local day = ds_M1:T(i).day
if day < Day then break
elseif day == Day then
volume = volume + ds_M1:V(i)
end
end
if volume == Volume then
CreateDS = true -- Если ошибки нет, открываем новый DS
elseif Index == nil then
Index = index -- Если есть ошибка, запомним номер последней свечи
message(string.format("[ERROR] %s: index: %u: %s, %s", sec_code, Index, Volume, volume), 3)
end
end
while run do
if CreateDS then
if ds_M1 then
ds_M1:Close()
sleep(2000)
end
if run then
ds_M1 = assert(CreateDataSource(class, sec_code, INTERVAL_M1))
ds_M1:SetUpdateCallback(cb)
CreateDS = false
end
else sleep(1) end
end
ds_D1:Close()
if Index then
local function canle(ds, index)
local t = ds:T(index)
return string.format("%02u:%02u:%02u: %s; %s; %s; %s; %s", t.hour, t.min, t.sec, ds:O(index), ds:H(index), ds:L(index), ds:C(index), ds:V(index))
end
local s = string.format("%s: index: %u\n%s", sec_code, Index, canle(ds_M1, Index)) -- Выводим свечу, на которой обнаружена ошибка
ds_M1:Close()
sleep(100)
ds_M1 = assert(CreateDataSource(class, sec_code, INTERVAL_M1))
repeat sleep(1000) until ds_M1:Size() > Index
message(s .. "\n" .. canle(ds_M1, Index), 2) -- Сравниваем со свечой с тем же индексом из другого DS
end
ds_M1:Close()
end
Графики должны быть закрыты. Можно запустить несколько скриптов с разными инструментами. Через некоторое время будет сообщение вида:
Скрытый текст
Возможно, большое количество свечей повышает вероятность ошибки. Но это не точно.
Надо делать так, как надо. А как не надо - делать не надо.
QUIK 8.12.0.41 Если в таблице несколько строк, удовлетворяющих параметрам поиска, то по F3 будут показываться значения только в пределах одной строки. В 8.1 поиск осуществлялся по всем строкам таблицы.
Надо делать так, как надо. А как не надо - делать не надо.
function main()
if sendTransaction{
TRANS_ID = tostring(trans_id),
ACTION = "NEW_ORDER",
ACCOUNT = account,
CLIENT_CODE = client,
CLASSCODE = class_code,
SECCODE = sec_code,
OPERATION = "B",
TYPE = "L",
PRICE = price,
QUANTITY = "1"
} == "" then run = true end
while run do sleep(1) end
end
function OnTransReply(trans_reply)
if trans_reply.trans_id ~= trans_id then return end
run = nil
sendTransaction{
TRANS_ID = tostring(trans_id + 1),
ACTION = "KILL_ORDER",
CLASSCODE = class_code,
ORDER_KEY = tostring(trans_reply.order_num)
}
end
function OnStop()
run = nil
end
Надо делать так, как надо. А как не надо - делать не надо.
Заказ полного набора всех сделок происходит все зависимости от установленных в таблице обезличенных сделок фильтров или открытого тикового графика по конкретному инструменту.
Прошу техподдержку прокомментировать, что сие значит.
Надо делать так, как надо. А как не надо - делать не надо.
Видео Описание: Две вкладки, на каждой открыто по окну, в данном случае ТТТ, а также окно "Доступные скрипты", которое перекрывается первым окном. "Доступные скрипты" делаю "Показывать на всех вкладках". Когда активирую окно "Доступные скрипты" на одной вкладке, то на другой положение (перекрывание) окон сохраняется. Но когда запускаю Lua скрипт с созданием окна, этот порядок нарушается. На видео окна на Вкладке 1 расположил в следующем порядке (сверху вниз): Lua-окно, ТТТ, Доступные скрипты. Далее переключился на вкладку 2 и активировал окно "Доступные скрипты". Возвращаюсь на вкладку 1, порядок поменялся: Lua-окно, Доступные скрипты, ТТТ.
Надо делать так, как надо. А как не надо - делать не надо.
1. Перемещать мышкой заявку в стакане транзакцией "Переставить заявки" ("Изменение заявки"), если доступен данный тип транзакции для инструмента. Если по одной цене стоят несколько заявок, то применять операцию для последней выставленной заявки (для двух последних на FORTS).
2. Снимать заявки (панель снятия заявок по условию в стакане) транзакцией "Удалить все заявки по условию", если доступна.
Надо делать так, как надо. А как не надо - делать не надо.
Здесь будут публиковаться кривые ошибки, возникающие, очевидно, из-за двухпоточной схемы работы QLua. Некоторые ошибки указаны здесь: Потокобезопасные функции в Lua 5.3
Надо делать так, как надо. А как не надо - делать не надо.
QUIK 8.8.4.3. Стандартная схема оформления. Иногда после нескольких дней работы зависает. При этом загрузка ЦП процессом 0 Сначала грешил на потокобезопасные функции. Но анализ логов показывает, что зависании происходит вне потокобезопасных функций (но это не точно )) Можно ли по стеку определить последнюю вызванную QLua функцию?
Надо делать так, как надо. А как не надо - делать не надо.
local run = true
function main()
local id = AllocTable()
AddColumn(id, 1, 'Количество', true, QTABLE_INT_TYPE, 13)
CreateWindow(id)
InsertRow(id, -1)
while run do
local len = getNumberOf('all_trades')
SetCell(id, 1, 1, tostring(len), len)
sleep(100)
end
end
function OnStop()
run = nil
end
Скрипт 2:
Скрытый текст
Код
local run = true
local AllTrades = {}
function OnAllTrade(alltrade)
table.sinsert(AllTrades, alltrade)
--[[table.ssort({0, 0}, function()
AllTrades[#AllTrades+1] = alltrade
return true
end)]]
end
function main()
while run do
while #AllTrades > 0 do
table.sremove(AllTrades, 1)
end
sleep(1)
end
end
function OnStop()
run = nil
end
При запущенных обоих скриптах изредка в строке
Код
while #AllTrades > 0 do
возникает странная ошибка:
Цитата
attempt to compare number with function
Для воспроизведения можно Получить заново данные по обезличенным сделкам.
Надо делать так, как надо. А как не надо - делать не надо.
Есть скрипт, который стартует автоматически при запуске QUIK. В самом начале идёт поиск заданного торгового счета:
Код
function main()
local Index = SearchItems("trade_accounts", 0, getNumberOf("trade_accounts")-1, function(trdaccid) return trdaccid == Account end, "trdaccid")
...
end
В настройках стоит "Очищать данные после смены даты: На сервере (при установлении связи)". Т.е., при первичном старте скрипт берёт данные из кэша. Запуск QUIK происходит долго, открыто несколько тиковых графиков и индикаторов. Но обычно скрипт работает нормально. Но сегодня SearchItems не нашла торговый счёт и вернула nil. Повторный запуск скрипта вручную (до подключения к серверу) отработал корректно.
По какой причине данный код мог дать сбой?
Надо делать так, как надо. А как не надо - делать не надо.
Начну из далека. При заказе таблицы котировок используем
Код
Subscribe_Level_II_Quotes(class, sec) -- Для заказа котировок с сервера
getQuoteLevel2(class, sec) -- Для получения котировок
Мы либо получаем актуальные котировки, если стакан открыт, либо незаполненную таблицу, если стакан закрыт или котировки ещё не подгрузились с сервера. Если закрыть стакан (отменить заказ на получение котировок с сервера), то getQuoteLevel2 вернёт незаполненную таблицу. Т.о., мы понимаем, что, если getQuoteLevel2 вернул данные, то они актуальны. Использование простое:
Код
function main()
Subscribe_Level_II_Quotes(class, sec)
-- Получаем актуальные котировки, если они есть:
Quotes = getQuoteLevel2(class, sec)
end
-- Дальнейшие изменения в стакане можно ловить в OnQuote:
function OnQuote(class, sec)
Quotes = getQuoteLevel2(class, sec)
end
С getParamEx2 намного сложнее. Если getParamEx2 вернул данные, то они актуальные или старые? Может, пять часов назад их заказывали, потом подписку отменили, а данные остались в кеше. Если же брать парамтры только в OnParam, то по неликвидному инструменту их можно ждать бесконечно долго.
Надо делать так, как надо. А как не надо - делать не надо.
Ошибка плавающая. Причину установить не удалось. Но замечено, что виснет с большей вероятностью, если одновременно с DestroyTable происходит какое-то событие. Для воспроизведения написал следующий скрипт:
Код
function main()
ID = {}
for i = 1, 20 do
ID[i] = AllocTable()
CreateWindow(ID[i])
SetTableNotificationCallback(ID[i], function(id, event)
if event == QTABLE_CLOSE then
run = false
end
end)
end
run = true
while run do sleep(1) end
for _, id in pairs(ID) do
DestroyTable(id)
end
end
Использование: Запустить скрипт без подключения к серверу. Чтобы посыпались события, установить соединение с сервером, и сразу закрыть одно из окон скрипта.
Надо делать так, как надо. А как не надо - делать не надо.
Если залогиниться в QUIK под другим пользователем (не закрывая программу), то на открытых графиках остаются треугольники от сделок и линии заявок от предыдущего пользователя.
Надо делать так, как надо. А как не надо - делать не надо.
function OnStop()
run = nil
SetCell(id, 1, 1, string.format('Size: %u', GetTableSize(id))) -- Почему-то попадаем в строку 2
end
local alltrade
function OnAllTrade()
if not run then return end
alltrade = true
for i = 1, 30 do
DeleteRow(id, 1)
InsertRow(id, 30)
end
end
function main()
id = AllocTable()
AddColumn(id, 1, 'Size', true, QTABLE_INT_TYPE, 15)
CreateWindow(id)
SetWindowPos(id, 0, 0, 100, 520)
for i = 1, 30 do
InsertRow(id, -1)
end
run = true
while run do
if alltrade then
alltrade = nil
SetCell(id, 30, 1, string.format('%u', GetTableSize(id)))
else sleep(1) end
end
end
Через некоторое время работы появляется 31 строка. Первая строка становится недоступна для редактирования.