Редактирование настроек графика - Диаграмма - Внешний вид - Порядок отображения слоёв данных
Обзор изменений в новых версиях, Обзор изменений в новых версиях
Пользователь
Сообщений: Регистрация: 20.03.2023
24.04.2025 21:25:54
VCLua версия 1.0 для графического интерфейса (GUI) в QUIK
Пользователь
Сообщений: Регистрация: 20.03.2023
18.04.2025 16:03:28
Мои скрипты имеют общую структуру:
Код
-- если VCLua установлена локально для скрипта!
package.cpath=getScriptPath()..'/'.._VERSION:sub(5)..'/?.dll;'..package.cpath
VCL = require "vcl.core"
local app = VCL.Application()
local ProcessMessages = app.ProcessMessages
-- необходимая магическая строка, без которой ничего работать не будет
app:Initialize()
-- если внутри VCLua или в ваших колбэках случится ошибка, вызовется эта функция
local function print_error(s)
message(s, 3)
PrintDbgStr(s..'\n'..debug.traceback(nil,2))
end
VCL.SetErrorReporter(print_error)
U = VCL.UTF8
W = VCL.WinCP
-- никакого обращения к VCLua не должно быть в колбэках QUIK, если сделали form:Show() в main
function OnStop()
is_run = false
end
local function CreateFormStaticPart()
-- статическая часть формы, вручную или через json
end
local function CreateForm()
CreateFormStaticPart()
form.OnCl ose = function()
OnStop() -- можно убрать и тогда форма будет сворачиваться на закрытии
return 'caMinimize'
end
-- динамическая часть формы, обработчики сообщений
...
--
form:Show()
end
local function CloseForm(form, method)
-- в середине работы можно вызывать Hide вместо Free; после Hide можно сделать form:Show(), чтобы форма снова стала видимой
form[method](form)
end
local function CloseAllForms()
CloseForm(form_1, 'Free')
...
CloseForm(form_n, 'Free')
end
function main()
-- создаём форму в потоке main, а не в основном потоке квика
CreateForm()
while is_run do
-- обновляем GUI, делаем свои дела
...
-- отрисовываем обновление стакана и обрабатываем события
ProcessMessages(app)
-- для простоты спим, в реальном скрипте используются сообщения и MsgWaitForMultipleObjectsEx
sleep(1)
end
CloseAllForms()
-- обрабатываем события закрытия форм
app:ProcessMessages()
end
Пример стакана с программированием формы вручную, без json
Скрытый текст
Код
-- если VCLua установлена локально для скрипта
package.cpath=getScriptPath()..'/'.._VERSION:sub(5)..'/?.dll;'..package.cpath
VCL = require "vcl.core"
local app = VCL.Application()
local ProcessMessages = app.ProcessMessages
-- необходимая магическая строка
app:Initialize()
-- если внутри VCLua или в ваших колбэках случится ошибка, вызовется эта функция
local function print_error(s)
message(s, 3)
PrintDbgStr(s..'\n'..debug.traceback(nil,2))
end
VCL.SetErrorReporter(print_error)
U = VCL.UTF8
W = VCL.WinCP -- в данном скрипте не используется
local tickers = {
['MXM5'] = {class='SPBFUT', sec='MXM5'},
['CRM5'] = {class='SPBFUT', sec='CRM5'},
['CNYRUB_TOM'] = {class='CETS', sec='CNYRUB_TOM'},
}
local ticker = tickers['MXM5']
local depths = {SPBFUT = 20, CETS = 20, TQBR = 10}
local fmt = string.format
local is_run = true
local function Unsub()
local cnt = 0
while not Unsubscribe_Level_II_Quotes(ticker.class,ticker.sec) and cnt<50 do sleep(1); cnt = cnt + 1 end
message(ticker.sec..fmt(' статус подписки %s после %d попыток',IsSubscribed_Level_II_Quotes(ticker.class,ticker.sec),cnt), 1)
end
-- никакого обращения к VCLua не должно быть в колбэках QUIK, если сделали form:Show() в main
function OnStop()
is_run = false
Unsub()
end
local function CreateFormStaticPart(sec)
by_name = {}
quotes = VCL.Form(nil, "quotes")
by_name.quotes = quotes
grid = VCL.StringGrid(quotes, "grid")
by_name.grid = grid
stat = VCL.Label(quotes, "stat")
by_name.stat = stat
lPrice = VCL.Label(quotes, "lPrice")
by_name.lPrice = lPrice
ePrice = VCL.FloatSpinEdit(quotes, "ePrice")
by_name.ePrice = ePrice
eQuantity = VCL.SpinEdit(quotes, "eQuantity")
by_name.eQuantity = eQuantity
lQuantity = VCL.Label(quotes, "lQuantity")
by_name.lQuantity = lQuantity
quotes._ = {
FormStyle = 'fsStayOnTop',
Width = 319,
Top = 577,
Font = {
Size = 9,
},
Left = 1563,
Height = 400,
}
grid._ = {
Columns = {
{
Color = 8446885,
Title = {
Caption = U('Своя'),
Alignment = 'taCenter',
},
Font = {
Color = 16711935,
},
Alignment = 'taRightJustify',
Width = 34,
},
{
Width = 45,
Alignment = 'taRightJustify',
Color = 8446885,
Title = {
Caption = U('Сумма'),
Alignment = 'taLeftJustify',
},
},
{
Width = 45,
Alignment = 'taRightJustify',
Color = 8446885,
Title = {
Caption = U('Лоты'),
Alignment = 'taCenter',
},
},
{
Width = 35,
Alignment = 'taRightJustify',
Color = 8446885,
Title = {
Caption = U('Цена'),
Alignment = 'taCenter',
},
},
{
Width = 35,
Alignment = 'taRightJustify',
Color = 10791167,
Title = {
Caption = U('Цена'),
Alignment = 'taCenter',
},
},
{
Width = 45,
Alignment = 'taRightJustify',
Color = 10791167,
Title = {
Caption = U('Лоты'),
Alignment = 'taCenter',
},
},
{
Width = 45,
Alignment = 'taRightJustify',
Color = 10791167,
Title = {
Caption = U('Сумма'),
Alignment = 'taLeftJustify',
},
},
{
Color = 10791167,
Title = {
Caption = U('Своя'),
Alignment = 'taCenter',
},
Font = {
Color = 16711935,
},
Alignment = 'taRightJustify',
Width = 35,
},
},
DefaultRowHeight = 17,
Align = 'alNone',
GridLineColor = 'clBtnFace',
FixedColor = 'clWindow',
AnchorSideBottom = {
Control = stat,
},
BorderStyle = 'bsNone',
AutoEdit = false,
Width = 319,
AnchorSideTop = {
Control = quotes,
},
AnchorSideRight = {
Control = quotes,
Side = 'asrBottom',
},
AnchorSideLeft = {
Control = quotes,
},
Options = '[goFixedVertLine,goVertLine,goColSizing,goDblClickAutoSize,goSmoothScroll,goRowHighlight]',
Anchors = '[akTop,akLeft,akRight,akBottom]',
Flat = true,
FixedCols = 0,
RowCount = 21,
}
stat._ = {
AnchorSideBottom = {
Control = eQuantity,
Side = 'asrTop',
},
AnchorSideLeft = {
Control = quotes,
},
AnchorSideRight = {
Control = quotes,
Side = 'asrBottom',
},
Alignment = 'taCenter',
Anchors = '[akLeft,akRight,akBottom]',
}
lPrice._ = {
Caption = U('Цена'),
AnchorSideBottom = {
Control = ePrice,
Side = 'asrCenter',
},
AnchorSideLeft = {
Control = quotes,
},
Anchors = '[akLeft,akBottom]',
}
ePrice._ = {
AnchorSideBottom = {
Control = eQuantity,
Side = 'asrBottom',
},
Increment = 0.001,
BorderSpacing = {
Left = 5,
Right = 5,
},
MaxValue = 2147483647.0,
Anchors = '[akLeft,akRight,akBottom]',
AnchorSideLeft = {
Control = lPrice,
Side = 'asrBottom',
},
AnchorSideRight = {
Control = lQuantity,
},
Caption = '0,00',
DecimalPlaces = 3,
MinValue = 0.0,
}
eQuantity._ = {
Width = 75,
AnchorSideBottom = {
Control = quotes,
Side = 'asrBottom',
},
BorderSpacing = {
Left = 5,
Right = 5,
},
Caption = '1',
AnchorSideRight = {
Control = quotes,
Side = 'asrBottom',
},
MaxValue = 2147483647.0,
Anchors = '[akRight,akBottom]',
MinValue = 1.0,
}
lQuantity._ = {
AnchorSideRight = {
Control = eQuantity,
},
AnchorSideBottom = {
Control = eQuantity,
Side = 'asrCenter',
},
Caption = U('Лот'),
Anchors = '[akRight,akBottom]',
}
form = quotes
end
-- переменные для связи между потоками скрипта
local cb_t, recv_t = {}, {}
local function CreateForm()
local sec = ticker.sec
CreateFormStaticPart(sec)
-- U чтобы не было кракозябр
form.Caption = ticker.class..'-'..sec..U(' стакан')
form.OnCl ose = function()
OnStop() -- можно убрать и тогда стакан будет сворачиваться на закрытии
return 'caMinimize'
end
local info = getSecurityInfo(ticker.class, ticker.sec)
if info then
-- настраиваем поле ввода цены согласно параметрам инструмента
by_name.ePrice.DecimalPlaces = info.scale
by_name.ePrice.Increment = info.min_price_step
end
local grid = by_name.grid
-- по нажатию левой кнопки мыши на некоторых ячейках стакана поле цены будет меняться
local function setp(c,r)
local price = grid:Cells(c <= 1 and 3 or 4, r)
if price ~= "" then by_name.ePrice.Value = price end
end
local col_funcs = {setp, setp, [7] = setp, [8] = setp}
grid.OnMouseD own = function(_,Button,Shift,X,Y)
local col, row = grid:MouseToCell2(X,Y)
if row == 0 then return end
if Button == "mbLeft" and (col < 2 or col > 5) then
-- нумерация в Lua от 1, а в Free Pascal от 0, поэтому col+1
col_funcs[col+1](col, row)
end
end
-- глубина стакана зависит от инструмента и от сервера, + 1 для заголовка
grid.RowCount = depths[ticker.class] + 1
-- некоторые параметры удобнее менять при исполнении, чем искать способ в LCL сделать автоматически как хочется
local function on_change_width()
local mid = form.Width // 2
by_name.eQuantity.Width = by_name.eQuantity.Left - mid + by_name.eQuantity.Width
end
-- вызывается при изменении ширины заголовков, делает ширины симметричными
grid.OnHeaderSi zed = function(o,_,index)
local c = o.Columns
c:Items(c.Count-1-index).Width = c:Items(index).Width
local rect = o:CellRect(o.ColCount-1, o.RowCount-1)
form.Width = rect.right
on_change_width()
end
-- высота стакана подстраивается по нажатию мыши на поле статистики
by_name.stat.OnMouseD own = function(_,Button,Shift,X,Y)
on_change_width()
local rect = grid:CellRect(grid.ColCount-1, grid.RowCount-1)
local h = form.Height + rect.bottom - grid.Height
form.Constraints.MinHeight = h
form.Height = h
end
-- НЕ модально показываем форму, поэтому потребуется регулярный вызов ProcessMessages
form:Show()
local was = IsSubscribed_Level_II_Quotes(ticker.class,sec)
local subscribed = Subscribe_Level_II_Quotes(ticker.class,sec)
local cnt = 0
if subscribed then
while not IsSubscribed_Level_II_Quotes(ticker.class,sec) and is_run do sleep(1); cnt = cnt + 1 end
end
message(sec..fmt(' подписка %s изначально %s после %d попыток',subscribed,was,cnt), cnt > 5 and 3 or 1)
cb_t[ticker.sec] = {sent=0}
recv_t[ticker.sec] = {updates=0,received=0,class=ticker.class,depth=depths[ticker.class],by_name=by_name}
-- в поле цены ставим цену последней сделки, если есть
local last = getParamEx(ticker.class, sec, "LAST")
if last then by_name.ePrice.Value = tonumber(last.param_value) end
end
local function CloseForm(form, method)
-- в середине работы можно вызывать Hide вместо Free; после Hide можно сделать form:Show(), чтобы форма снова стала видимой
form[method](form)
end
local function CloseAllForms()
CloseForm(form, 'Free')
end
-- никакого обращения к VCLua не должно быть в колбэках QUIK, если сделали form:Show() в main
function OnQuote(class_code, sec_code)
local sent = cb_t[sec_code]
-- сохраняем номер срабатывания колбэка для данного sec_code
if sent then sent.sent = sent.sent + 1 end
end
-- чисто для скорости работы
local sc = VCL.GetCallable('TStringGrid','SetCells')
local BeginUpdate = VCL.GetCallable('TStringGrid','BeginUpdate')
local EndUpdate = VCL.GetCallable('TStringGrid','EndUpdate')
local tointeger = math.tointeger
local getQuoteLevel2 = getQuoteLevel2
-- реализация обновления стакана
local function updateGrid()
local sec = ticker.sec
local recv = recv_t[sec]
local s = cb_t[sec].sent
local p,pq,q,last,pqs,quantity
-- если номер срабатывания колбэка вырос...
if recv.received < s then
-- ...сохраним новый и обновим стакан
recv.received = s
local depth = recv.depth
local by_name = recv.by_name
local updates = recv.updates + 1
recv.updates = updates
updates = s - updates
-- выводим потери обновлений стакана
by_name.stat.Caption = fmt('%d/%d=%.1f%%',updates,s,updates/s*100)
local grid = by_name.grid
local qt = getQuoteLevel2(recv.class, sec)
pqs = qt.bid
BeginUpdate(grid)
if pqs then
last = tointeger(qt.bid_count)
q = 0
local last1 = last + 1
for i = 1,last do
pq = pqs[last1 - i]
p = pq.price
sc(grid,3,i,p)
quantity = pq.quantity
sc(grid,2,i,quantity)
q = q + tointeger(quantity)
sc(grid,1,i,q)
end
if last < depth then
for i = last1,depth do
sc(grid,3,i,'')
sc(grid,2,i,'')
sc(grid,1,i,'')
end
end
end
pqs = qt.offer
if pqs then
last = tointeger(qt.offer_count)
q = 0
for i = 1,last do
pq = pqs[i]
p = pq.price
sc(grid,4,i,p)
quantity = pq.quantity
sc(grid,5,i,quantity)
q = q + tointeger(quantity)
sc(grid,6,i,q)
end
if last < depth then
for i = last+1,depth do
sc(grid,4,i,'')
sc(grid,5,i,'')
sc(grid,6,i,'')
end
end
end
EndUpdate(grid)
end
end
function main()
-- создаём форму в потоке main, а не в основном потоке квика
CreateForm()
while is_run do
-- обновляем стакан
updateGrid()
-- отрисовываем обновление стакана и обрабатываем события
ProcessMessages(app)
sleep(1)
end
CloseAllForms()
-- обрабатываем события закрытия форм
app:ProcessMessages()
end
Пример стакана с загружаемой и обновляемой через json формой json:
-- если VCLua установлена локально для скрипта
package.cpath=getScriptPath()..'/'.._VERSION:sub(5)..'/?.dll;'..package.cpath
VCL = require "vcl.core"
local app = VCL.Application()
local ProcessMessages = app.ProcessMessages
-- необходимая магическая строка
app:Initialize()
-- если внутри VCLua или в ваших колбэках случится ошибка, вызовется эта функция
local function print_error(s)
message(s, 3)
PrintDbgStr(s..'\n'..debug.traceback(nil,2))
end
VCL.SetErrorReporter(print_error)
U = VCL.UTF8
W = VCL.WinCP -- в данном скрипте не используется
local tickers = {
['MXM5'] = {class='SPBFUT', sec='MXM5'},
['CRM5'] = {class='SPBFUT', sec='CRM5'},
['CNYRUB_TOM'] = {class='CETS', sec='CNYRUB_TOM'},
}
local ticker = tickers['MXM5']
local depths = {SPBFUT = 20, CETS = 20, TQBR = 10}
local fmt = string.format
local is_run = true
local function Unsub()
local cnt = 0
while not Unsubscribe_Level_II_Quotes(ticker.class,ticker.sec) and cnt<50 do sleep(1); cnt = cnt + 1 end
message(ticker.sec..fmt(' статус подписки %s после %d попыток',IsSubscribed_Level_II_Quotes(ticker.class,ticker.sec),cnt), 1)
end
-- никакого обращения к VCLua не должно быть в колбэках QUIK, если сделали form:Show() в main
function OnStop()
is_run = false
Unsub()
end
require "loader"
local path_prefix = getScriptPaths()
local function FormFilename(sec)
return path_prefix..sec..'.json'
end
-- устанавливает глобальные переменные by_name, form
local function CreateFormStaticPart(sec)
local fn, ok = FormFilename(sec)
ok, form, _, by_name = pcall(jsonFormLoad,fn)
if not ok then
local e1 = form
fn = path_prefix..'quote-example.json'
ok, form, _, by_name = pcall(jsonFormLoad,fn)
if not ok then error(e1..'\n'..form) end
end
end
local function UpdateForm(sec, form)
local fn = FormFilename(sec)
local ok, js = pcall(fileToJson, fn)
if not ok then
local e1 = js
ok, js = pcall(fileToJson, path_prefix..'quote-example.json')
if not ok then error(e1..'\n'..js) end
end
jsonUpdateWithForm(js, form)
jsonToFile(js, fn, table.tojson)
end
-- переменные для связи между потоками скрипта
local cb_t, recv_t = {}, {}
local function CreateForm()
local sec = ticker.sec
CreateFormStaticPart(sec)
-- U чтобы не было кракозябр
form.Caption = ticker.class..'-'..sec..U(' стакан')
form.OnCl ose = function()
OnStop() -- можно убрать и тогда стакан будет сворачиваться на закрытии
return 'caMinimize'
end
local info = getSecurityInfo(ticker.class, ticker.sec)
if info then
-- настраиваем поле ввода цены согласно параметрам инструмента
by_name.ePrice.DecimalPlaces = info.scale
by_name.ePrice.Increment = info.min_price_step
end
local grid = by_name.grid
-- по нажатию левой кнопки мыши на некоторых ячейках стакана поле цены будет меняться
local function setp(c,r)
local price = grid:Cells(c <= 1 and 3 or 4, r)
if price ~= "" then by_name.ePrice.Value = price end
end
local col_funcs = {setp, setp, [7] = setp, [8] = setp}
grid.OnMouseD own = function(_,Button,Shift,X,Y)
local col, row = grid:MouseToCell2(X,Y)
if row == 0 then return end
if Button == "mbLeft" and (col < 2 or col > 5) then
-- нумерация в Lua от 1, а в Free Pascal от 0, поэтому col+1
col_funcs[col+1](col, row)
end
end
-- глубина стакана зависит от инструмента и от сервера, + 1 для заголовка
grid.RowCount = depths[ticker.class] + 1
-- некоторые параметры удобнее менять при исполнении, чем искать способ в LCL сделать автоматически как хочется
local function on_change_width()
local mid = form.Width // 2
by_name.eQuantity.Width = by_name.eQuantity.Left - mid + by_name.eQuantity.Width
end
-- вызывается при изменении ширины заголовков, делает ширины симметричными
grid.OnHeaderSi zed = function(o,_,index)
local c = o.Columns
c:Items(c.Count-1-index).Width = c:Items(index).Width
local rect = o:CellRect(o.ColCount-1, o.RowCount-1)
form.Width = rect.right
on_change_width()
end
-- высота стакана подстраивается по нажатию мыши на поле статистики
by_name.stat.OnMouseD own = function(_,Button,Shift,X,Y)
on_change_width()
local rect = grid:CellRect(grid.ColCount-1, grid.RowCount-1)
local h = form.Height + rect.bottom - grid.Height
form.Constraints.MinHeight = h
form.Height = h
end
-- НЕ модально показываем форму, поэтому потребуется регулярный вызов ProcessMessages
form:Show()
local was = IsSubscribed_Level_II_Quotes(ticker.class,sec)
local subscribed = Subscribe_Level_II_Quotes(ticker.class,sec)
local cnt = 0
if subscribed then
while not IsSubscribed_Level_II_Quotes(ticker.class,sec) and is_run do sleep(1); cnt = cnt + 1 end
end
message(sec..fmt(' подписка %s изначально %s после %d попыток',subscribed,was,cnt), cnt > 5 and 3 or 1)
cb_t[ticker.sec] = {sent=0}
recv_t[ticker.sec] = {updates=0,received=0,class=ticker.class,depth=depths[ticker.class],by_name=by_name}
-- в поле цены ставим цену последней сделки, если есть
local last = getParamEx(ticker.class, sec, "LAST")
if last then by_name.ePrice.Value = tonumber(last.param_value) end
end
local function CloseForm(form, method)
-- при закрытии сохраняем json с обновлениями сохранённых в json свойств
UpdateForm(ticker.sec, form)
-- в середине работы можно вызывать Hide вместо Free; после Hide можно сделать form:Show(), чтобы форма снова стала видимой
form[method](form)
end
local function CloseAllForms()
CloseForm(form, 'Free')
end
-- никакого обращения к VCLua не должно быть в колбэках QUIK, если сделали form:Show() в main
function OnQuote(class_code, sec_code)
local sent = cb_t[sec_code]
-- сохраняем номер срабатывания колбэка для данного sec_code
if sent then sent.sent = sent.sent + 1 end
end
-- чисто для скорости работы
local sc = VCL.GetCallable('TStringGrid','SetCells')
local BeginUpdate = VCL.GetCallable('TStringGrid','BeginUpdate')
local EndUpdate = VCL.GetCallable('TStringGrid','EndUpdate')
local tointeger = math.tointeger
local getQuoteLevel2 = getQuoteLevel2
-- реализация обновления стакана
local function updateGrid()
local sec = ticker.sec
local recv = recv_t[sec]
local s = cb_t[sec].sent
local p,pq,q,last,pqs,quantity
-- если номер срабатывания колбэка вырос...
if recv.received < s then
-- ...сохраним новый и обновим стакан
recv.received = s
local depth = recv.depth
local by_name = recv.by_name
local updates = recv.updates + 1
recv.updates = updates
updates = s - updates
-- выводим потери обновлений стакана
by_name.stat.Caption = fmt('%d/%d=%.1f%%',updates,s,updates/s*100)
local grid = by_name.grid
local qt = getQuoteLevel2(recv.class, sec)
pqs = qt.bid
BeginUpdate(grid)
if pqs then
last = tointeger(qt.bid_count)
q = 0
local last1 = last + 1
for i = 1,last do
pq = pqs[last1 - i]
p = pq.price
sc(grid,3,i,p)
quantity = pq.quantity
sc(grid,2,i,quantity)
q = q + tointeger(quantity)
sc(grid,1,i,q)
end
if last < depth then
for i = last1,depth do
sc(grid,3,i,'')
sc(grid,2,i,'')
sc(grid,1,i,'')
end
end
end
pqs = qt.offer
if pqs then
last = tointeger(qt.offer_count)
q = 0
for i = 1,last do
pq = pqs[i]
p = pq.price
sc(grid,4,i,p)
quantity = pq.quantity
sc(grid,5,i,quantity)
q = q + tointeger(quantity)
sc(grid,6,i,q)
end
if last < depth then
for i = last+1,depth do
sc(grid,4,i,'')
sc(grid,5,i,'')
sc(grid,6,i,'')
end
end
end
EndUpdate(grid)
end
end
function main()
-- создаём форму в потоке main, а не в основном потоке квика
CreateForm()
while is_run do
-- обновляем стакан
updateGrid()
-- отрисовываем обновление стакана и обрабатываем события
ProcessMessages(app)
sleep(1)
end
CloseAllForms()
-- обрабатываем события закрытия форм
app:ProcessMessages()
end
Файлы на диске расположены так:
Код
vcl-quotes\5.3\vcl\core.dll -- это мне для тестов, у вас нету
vcl-quotes\5.4\vcl\core.dll
vcl-quotes\CRM5.json -- эти два файла создаются при исполнении quote-example-json.lua, в зависимости от настроек внутри
vcl-quotes\MXM5.json
vcl-quotes\quote-example.json
vcl-quotes\quote-example.lua
vcl-quotes\quote-example-json.lua
Все эти json можно загрузить в vt-form и поковыряться.
VCLua версия 1.0 для графического интерфейса (GUI) в QUIK
Пользователь
Сообщений: Регистрация: 20.03.2023
18.04.2025 15:51:17
Скачивание
Я настоятельно не рекомендую пользоваться Lua 5.3 в квике для скриптов, которые используют много объектов, созданных в DLL, поэтому публично выкладываю пока что только версию для 5.4. Качать её на данный момент можно (Download raw file) . На пока что немного более старая версия, хоть и тоже 1.0. Документация к библиотеке см. выше. Можно скачать class reference. Скачать vt-form можно (Download raw file)
Установка VCLua 1.0
Рекомендуемый вариант -- установка для каждого скрипта отдельно.
В папке со скриптом, откуда будет запускаться единственный использующий VCLua 1.0 скрипт, создайте иерархию папок 5.4/vcl и положите внутрь неё файл core.dll.
Альтернативный вариант, годится, если у вас только один скрипт будет использовать VCLua 1.0, или все ваши гуи скрипты уже отлажены и не ломаются.
Создайте скрипт со строкой "message(package.cpath, 1)" и запустите его в квике.
В окне сообщений появится строка, по которой можно понять, куда можно положить vcl\core.dll: в любую папку, предшествующую знаку вопроса.
Например, при строке "C:\BCS_Work\QUIK_BCS\?.dll;C:\BCS_Work\QUIK_BCS\. .\lib\lua\5.4\?.dll;C:\BCS_Work\QUIK_BCS\loadall.dll;.\?.dll" можно использовать папки C:\BCS_Work\QUIK_BCS, C:\BCS_Work\QUIK_BCS\. .\lib\lua\5.4 (я использую эту) и текущую папку скрипта (.).
В выбранной папке создаёте папку vcl и кладёте туда core.dll.
Пример использования в следующем сообщении.
Установка vt-form (0.9.2).
Распакуйте архив vt-form.zip в отдельную папку
Сделайте доступным для этой папки core.dll одним из двух вариантов выше.
Добавьте main.lua из той папки в квик и запустите в Lua 5.4.
Вообще говоря, это обычный Lua скрипт, не имеющий связи с квиком, поэтому запускать его можно отдельно, если стоит интерпретатор Lua, главное установить core.dll так, чтобы скрипт её видел. После этого можно в командной строке сделать так (если архив распакован в f:\Work\Dev\vt-form\vt-form\src):
Если нужно использовать в своих скриптах json, полученный из vt-form, нужно глобально установить в квик Файлы copy.lua, json.lua, и loader.lua:
Создайте скрипт со строкой "message(package.path, 1)" и запустите его в квике.
В окне сообщений появится строка, по которой можно понять, куда можно положить эти три файла: в любую папку, предшествующую знаку вопроса.
Например, при строке "C:\BCS_Work\QUIK_BCS\lua\?.lua;C:\BCS_Work\QUIK_BCS\lua\?\init.lua;C:\BCS_Work\QUIK_BCS\?.lua;C:\BCS_Work\QUIK_BCS\?\init.lua;C:\BCS_Work\QUIK_BCS\. .\share\lua\5.4\?.lua;C:\BCS_Work\QUIK_BCS\. .\share\lua\5.4\?\init.lua;.\?.lua;.\?\init.lua" их можно положить в папки C:\BCS_Work\QUIK_BCS\lua, C:\BCS_Work\QUIK_BCS, C:\BCS_Work\QUIK_BCS\. .\share\lua\5.4 (у меня они тут) или в текущю папку скрипта (.).
Пример использования в следующем сообщении.
VCLua версия 1.0 для графического интерфейса (GUI) в QUIK
Пользователь
Сообщений: Регистрация: 20.03.2023
18.04.2025 15:42:53
Сразу предупрежу, использовать GUI в QUIK через сторонние библиотеки -- это не для нубов Откуда качать, как устанавливать и примеры кода -- в следующих двух сообщениях.
VCLua - это название графической библиотеки для Lua.
У неё есть несколько версий и они все между собой не совместимы (то есть код на Lua, годящийся для создания GUI с использованием одной из версий VCLua типа 0.5, 0.6.2, 0.9.2 и 1.0, не будет работать с другой версией VCLua). Причины этого среди прочего в том, что разные версии VCLua используют внутри разные версии LCL - графической библиотеки для Free Pascal.
Я значительно расширил VCLua 0.9.2, почти полностью переписав, и зарелизил , использующую LCL 3.2. Общие принципы программирования остались те же.
Последние полгода она стабильно работает на QUIK 11 и 12 под Lua 5.4.
Я использую для рисования стаканов
и управления скриптом
Доступна очень подробная , но она на английском и про добавление GUI в отдельностоящий скрипт на Lua, про QUIK там ни слова.
Есть графическая утилита для рисования формы , которую потом можно сохранить в json и использовать (в т.ч. менять) в своих скриптах.
Здесь я попытаюсь дополнительно задокументировать на русском то, что важно для программирования GUI через VCLua 1.0 для QUIK.
Библиотека основана на визуальных контролах LCL 3.2, поэтому всю документацию этих контролов следует искать в (для более новой версии, чем 3.2), а не в документации к VCLua. Однако то, какие из этих контролов доступны, и какие их поля и методы доступны, как их вызывать из Lua, следует искать в (нажмите Download raw file) к VCLua. Как контролы выглядят и что делают их поля, можно вживую увидеть или в , или установив , или почитав
LCL уже давно использует кодировку символов UTF8, в отличие от ранних версий VCLua и в отличие от QUIK, который до сих пор на 1251. Поэтому, чтобы видеть русские символы в VCLua, нужно конвертировать из квиковской 1251 в UTF8 с помощью функции VCL.UTF8. Обратно через VCL.WinCP, что нужно, например, для сохранения файлов, путь к которым получен из диалога VCLua.
Если у вас несколько скриптов будут использовать VCLua, то лучше использовать разные файлы core.dll. В противном случае после закрытия одного из скриптов вы не сможете его запустить второй раз, пока не закроете все остальные скрипты (и тем самым не выгрузите библиотеку core.dll из памяти). Также при использовании одного файла core.dll есть ограничение на то, что главные формы разных скриптов должны иметь разные названия (у всех компонентов LCL есть внутренние названия).
LCL является однопоточной библиотекой, а скрипты в квике как минимум двухпоточные. Поэтому, чтобы избежать падений VCLua, а вместе с ней и квика, следует обновлять GUI только из того же потока, где была запущена основная форма. Начиная с версии VCLua 1.0 стало возможным запускать GUI из потока квика main вместо главного потока. Я делаю только так, вариант со стартом в главном потоке квика не тестирую. В частности, я не пытался стартовать GUI из кода индикатора, но это возможно.
Необдуманная установка обработчиков событий может привести к , особенно если устанавливать их из разных потоков.
В документации большой посвящён тому, как обрабатывать ошибки при использовании библиотеки и почему для этого нельзя использовать GUI самой библиотеки. Для квика я во всех скриптах использую один и тот же код как в примере стакана ниже.
Размер dll 6.7 мегабайт, все LCL dll большие. Даже если вы выкинете из библиотеки те контролы, которые вы не используете, и перекомпилируете, сильно меньше она не станет.
Как отлаживать скрипты?
Пользователь
Сообщений: Регистрация: 20.03.2023
18.04.2025 15:29:12
QUIK активно противодействует отладке, падая при любом подключённом отладчике.
Создание окна и кнопок в Quik.
Пользователь
Сообщений: Регистрация: 20.03.2023
17.04.2025 13:05:54
Цитата
Vasiliy написал: Сразу напишу, что я нуб, прошу писать на соответствующем уровне)
Любое решение через DLL не для нубов)
соответственно, только как VPM написал.
Новая версия VCLua - библиотеки для GUI
Пользователь
Сообщений: Регистрация: 20.03.2023
17.04.2025 12:44:13
Не, ну sql либы могли бы в другой ветке обсуждать...
Цитата
Nikolay написал: Ответ - достаточно табличных форм qlua. Главная цель же - это скрипт и его работа, а форма управления, даже если она не имеет выпадающих списков, колесиков и прочей, позволяющая редактировать параметры, вполне достаточна.
Для меня главное было гуи иметь вне главного потока QUIK, что моя либа позволяет сделать. Таблицы квика отрисовываются в главном потоке.
Запустить скрипт Lua другим скриптом, Запустить скрипт Lua другим скриптом
Пользователь
Сообщений: Регистрация: 20.03.2023
01.02.2025 01:17:42
Вы имеете ввиду что-то, отличное от Lua команды dofile?
Как в Quik включить получать обезличенные сделки юр. лиц., Заметил что сделки на срочном рынке приходят только физ лиц как включить отображение сделок юр лиц?
Пользователь
Сообщений: Регистрация: 20.03.2023
04.10.2024 23:22:35
Как Вы поняли, что там нет сделок юрлиц?
Выставить сделку с переносом из стакана, быстрые сделки
Пользователь
Сообщений: Регистрация: 20.03.2023
28.09.2024 00:49:25
Цитата
ДмитрийР написал: Открыто окно котировок. Выставлены опции панель торговли и быстрое выставление/снятие заявок. Заявка выставляется без переноса через ночь. Как её выставить так, что бы она переносилась до какой то даты . Я так понял это не возможно?
Быстро -- никак. А так - как обычно, клавиатурной кнопкой вызываю контекстное меню, там в алго заявках есть пункт про GTC, если брокер поддерживает.
Как создать глобальную константу доступную многим скриптам и индикаторам?, Как создать глобальную константу доступную многим скриптам и индикаторам?
У меня квик на пару секунд затупил в приёме данных сейчас на пробое CRU4 11.75 в 12:07 ровно после того как оповещение выскочило. Версия 11.1.4.2, брокер БКС.
[BUG] getFuturesHolding: ошибка в работе
Пользователь
Сообщений: Регистрация: 20.03.2023
22.08.2024 14:39:31
Цитата
VPM написал: local holdings = nil --getFuturesHolding(self.firmid, self.account, symbol, self.type)
Хоть к теме и не относится, но что-то часто такие ошибки у Вас в коде...
А так -- ну да, getFuturesHolding даёт nil, если не было ордеров по тикеру с прошлого клиринга и одновременно если нет позиции. Наверняка это деталь реализации для какого-нибудь ускорения...
Мне тоже хотелось бы знать, безопасно ли использовать getFuturesHolding в скрипте из разных потоков (main и основной поток квика), т.к. у меня мистические краши квика происходят в скрипте, в котором есть такие вызовы.
При смене инструмента графика в Lua индикаторе OnDestroy() не вызывается
Пользователь
Сообщений: Регистрация: 20.03.2023
13.08.2024 23:07:03
На каждом I == 1 проверяете инструмент, если он не совпадает с предыдущим, удаляете метку. Т.е. удаляете после смены инструмента, не до.
текущий современный способ сделать робота без ежемесячных расходов?
Пользователь
Сообщений: Регистрация: 20.03.2023
04.08.2024 11:39:40
Цитата
Oleg Vazhnev написал: Забыл самое важно сказать, что хочу на C++ писать, т.к. у меня уже много полезного кода на C++
Ну так пишите) Lua C API и/или любой из мостов C++-Lua. Я использую .
текущий современный способ сделать робота без ежемесячных расходов?
Пользователь
Сообщений: Регистрация: 20.03.2023
31.07.2024 23:26:10
API брокеров бесплатны, как я понимаю. Есть у тинька и финама, но у финама альфа версия и можно забыть про миллисекунды. Для прототипов я использую sol2 либу для C++-Lua, но потом надо переписывать части на Lua C API (или sol::stack...) для скорости
Хочу заказать скрипт., Хочу заказать скрипт по поиску роботов, которые покупают или продают рыночными заявками.
Пользователь
Сообщений: Регистрация: 20.03.2023
22.07.2024 13:54:46
Конечно же смотрел, потому и спрашиваю.
Хочу заказать скрипт., Хочу заказать скрипт по поиску роботов, которые покупают или продают рыночными заявками.
Пользователь
Сообщений: Регистрация: 20.03.2023
22.07.2024 11:18:52
SellBuyStyle, приведённый пример показывает поведение непериодического робота. Как скрипт, по ТЗ ищущий периодических роботов, должен работать в моменты, когда такой робот активен? Скрипт будет видеть равные объёмы очень регулярно, и определит их или как (условно) 10 роботов, работающих через равные промежутки времени, или никак (если интервалы между бросками слишком разные).
Вылетает квик 50 раз за сессию!, вылетает квик при работе в период торговой сессии
Пользователь
Сообщений: Регистрация: 20.03.2023
20.07.2024 22:10:53
Дмитрий Б., а какое количество индикаторов и областей на каждом графике? У меня уже при 13 стаканах и 30 инструментах на графиках по 50+ линий индикаторов в каждом были ситуации, когда один стакан не обновлялся на одной вкладке (а на другой вкладке, где было мало графиков, стакан по тому же инструменту обновлялся) в период большой рыночной активности. Когда я свой гуи для стаканов написал, выяснилось, что стаканы квика медленные (т.к. в том же потоке рисуются, что и всё остальное, включая расчёт индикаторов и показ графиков).
Хочу заказать скрипт., Хочу заказать скрипт по поиску роботов, которые покупают или продают рыночными заявками.
Пользователь
Сообщений: Регистрация: 20.03.2023
19.07.2024 17:18:52
SellBuyStyle, можете посмотреть работу реального робота сегодня в валюте (продажи по 20 лотов в рандомные интервалы два-три раза внутри секунды в районе 17 часов)
Вылетает квик 50 раз за сессию!, вылетает квик при работе в период торговой сессии
Вылетает квик 50 раз за сессию!, вылетает квик при работе в период торговой сессии
Пользователь
Сообщений: Регистрация: 20.03.2023
19.07.2024 12:13:07
Помимо очевидного совета сменить тормозную тёмную тему на светлую, и чистку dat файлов в archive (что Вы и так делаете), можете добавить в диспетчере задач на вкладке "Подробности" столбец "Объекты GDI" и сказать сюда число объектов GDI для процесса info.exe. Если там 10000, то надо уменьшать количество окон. Также описанные проблемы могут встречаться, если у Вас есть пользовательские скрипты, использующие DLL, особенно гуишные (с багами). И уточните, не англоязычную ли версию квика Вы используете. Там тоже были проблемы гуи
Как в Quik открыть старый файл с данными
Пользователь
Сообщений: Регистрация: 20.03.2023
17.07.2024 01:12:05
Цитата
VargoR написал: Может быть, все-таки, чтобы не мучать пользователей тестирующих свои стратегии в Quik, добавить функционал загрузки старых данных в график Quik?
Этой проблеме сто лет в обед, она навряд ли будет решена, к сожалению. Вы быстрее освоите другие платформы для тестирования автоматических стратегий (tradingview, backtrader, ...). Альтернативно Вы можете автоматизировать устранение перемешивания, если умеете редактировать DAT файлы с свечами (я умею, но это не бесплатно и с ограничениями, наложенными форматом), и далее адаптировать свои скрипты, чтобы они работали с ограниченной по времени частью свечей.
Хочу заказать скрипт., Хочу заказать скрипт по поиску роботов, которые покупают или продают рыночными заявками.
Пользователь
Сообщений: Регистрация: 20.03.2023
13.07.2024 16:18:33
SellBuyStyle, как Вы собираетесь проверять, что скрипт нашёл всех подразумеваемых Вами роботов? По таблице обезличенных сделок? Или через подключение к потоку заявок за большие бабки?
Снять защиту с робота на LUA, Снять защиту с робота на lua, привязанного к закрытому счёту
Пользователь
Сообщений: Регистрация: 20.03.2023
08.07.2024 20:06:22
Maater, Вам же nikolz выше ответил с исправленным кодом робота
Надо кое-что исправить в документации
Пользователь
Сообщений: Регистрация: 20.03.2023
06.07.2024 01:02:10
Anton Belonogov, в документации 11.3 уточнения внесены, но в примерах там такое:
Цитата
Есть спрос, но нет предложения: {'bid_count': '3.000000', 'offer_count': '0.000000', 'bid': {{'price':'11.3', 'quantity':'10'}, {'price':'11.4', 'quantity':'20'}, {'price':'11.5', 'quantity':'30'}, }, 'offer': '' }
То есть утверждается, что если предложения нет, то приходит таблица, в которой есть(!) ключ 'offer' и он указывает на строку длины 0. Неужели это правда так стало в новой версии?)
Куда все подевались?
Пользователь
Сообщений: Регистрация: 20.03.2023
04.07.2024 01:41:09
Может быть, потому все и разбежались, что в любом топике море оффтопа :)
Индикаторы
Пользователь
Сообщений: Регистрация: 20.03.2023
02.07.2024 21:55:35
Цитата
Дмитрий Квази написал: Можно это как то исправить, чтобы на всех выбранных инструментах дельта отрисовывалась не зависимо открыт график или нет.
Без крупного исправления кода индикатора - нет. Но обычно это делается парой скрипт-индикатор (скрипт вычисляет, что нужно, независимо от того, открыты графики или нет, а индикатор рисует вычисленное, но нужно уметь передавать данные между скриптом и индикатором).
Цитата
Дмитрий Квази написал: Одновременно на всех 10 инструментах дельта не отрисуется так как активно я выбираю только 1 инструмент я правильно понял?
Да.
Индикаторы
Пользователь
Сообщений: Регистрация: 20.03.2023
02.07.2024 19:19:10
Цитата
Дмитрий Квази написал: Причина в загрузке перед каждым переходом на новый инструмент, ещё раз повторюсь, ощущение что загрузка данных начинается при открытии графика с инструментом.
Ну так и есть же и так и должно быть, ведь индикатор, если специально сильно не извращаться, работает ровно на одном инструменте. А если это не так, то от кода индикатора сильно зависит, что происходит при смене инструмента, поэтому следует обратиться к автору.
Индикаторы
Пользователь
Сообщений: Регистрация: 20.03.2023
02.07.2024 17:51:30
А, ну я вижу таблицу обезличенных сделок как nikolz советовал. Значит, индикатор так долго считает. Попробуйте удалить часовой график, он дольше всего у Вас на видео грузится. Там или очень много свечей, и/или индикатор слишком неоптимально написан для этого тф.
"Установлен он на 20 инструментах" на видео в каждый момент времени он установлен на ровно одном инструменте на трёх таймфреймах.
Индикаторы
Пользователь
Сообщений: Регистрация: 20.03.2023
02.07.2024 16:42:35
Судя по скринам, вкладка у вас одна и Вы переключаетесь не между вкладками, а просто по таблице текущих торгов. Конечно, как только Вы уходите с одного инструмента, индикатор на нём перестаёт работать и начинает работать на том, на который перешли, возможно подгружая пропущенные данные (зависит от кода индикатора). Даже если Вы последуете совету nikolz, индикатор не вынудит квик запросить данные для инструмента, пока Вы не включите график с индикатором на данном инструменте (хотя, конечно, внутри индикатора может быть хитрая логика, что он просто запоминает весь набор инструментов, на которых он был включён сегодня, и подгружает данные по всем ним сам, независимо от текущего инструмента в ТТТ, но это изврат и тормоза, навряд ли так делалось)
Индикаторы
Пользователь
Сообщений: Регистрация: 20.03.2023
02.07.2024 16:32:51
Цитата
Дмитрий Квази написал: Вы мне просто ответьте на вопрос, для экономии ресурсов программа приостанавливает работу на не активных вкладках или нет? Это всё что мне нужно от вас узнать!
индикаторы на неактивных вкладках совершенно точно продолжают работать
Куда все подевались?
Пользователь
Сообщений: Регистрация: 20.03.2023
02.07.2024 10:05:35
Тоже интересен этот вопрос. Неужели все торгуют через мобильные приложения и веб-интерфейсы брокеров? Знаю, что частные фонды своих прогеров имеют, которым достаточно документации. Ещё заметил, что ТП ARQA периодически предлагает сразу на почту им писать про баги, поэтому часть обсуждений могла туда уйти.
Получить информацию об изменении цены активов с течением времени не в графическом виде
Пользователь
Сообщений: Регистрация: 20.03.2023
26.06.2024 23:40:15
Например, если график открыт, нажать правой кнопкой мыши на свечке и выбрать "Сохранить данные в файл".
Получить историю параметра через CreateDataSource
Пользователь
Сообщений: Регистрация: 20.03.2023
25.06.2024 14:56:44
Я на старте скрипта гружу что мне нужно, это же один раз делается. Ещё мне кажется, что сервер "привыкает" со временем, что клиент с таким-то ID хочет такие-то данные и будет отдавать их быстрее.
Получить историю параметра через CreateDataSource
Пользователь
Сообщений: Регистрация: 20.03.2023
25.06.2024 14:17:16
Сначала надо сделать так, чтобы ParamRequest на этот параметр вернул true. Потом, после CreateDataSource, дождаться ненулевого размера (или повесить колбэк и получить первый вызов). Далее работа как со свечами.
Как на графике нарисовать свечу?
Пользователь
Сообщений: Регистрация: 20.03.2023
23.06.2024 17:05:42
Конечно нет)
Баг: Заменить инструмент не работает если слишком много инструментов
Сообщение "Слишком много графиков. Возможно снижение производительности" возникает по причине того, что превышено ограничение на количество графических объектов, допустимых для одной диаграммы. Для устранения ошибки достаточно либо удалить ненужные графики из существующей диаграммы, либо добавлять новые в другое окно.
Можете добавить настройку для игнорирования этого ограничения? Никакого "железного" ограничения на количество графических объектов нет же, это искусственное ограничение квика.
Баг: Заменить инструмент не работает если слишком много инструментов
Пользователь
Сообщений: Регистрация: 20.03.2023
19.06.2024 18:09:35
И да, можно ли проигнорировать это предупреждение и всё равно добавить инструмент/индикатор? Пока что он просто не добавляется и всё.
Ещё что-то из индикаторов поудалять, то всё равно не добавляется с той же ошибкой. Какое условие появление ошибки "Слишком много графиков"? Как отключить эту фигню?
Баг: Заменить инструмент не работает если слишком много инструментов
Пользователь
Сообщений: Регистрация: 20.03.2023
19.06.2024 18:04:08
Создаём график цены и объёма
В окне редактирования настроек добавляем на него разные инструменты до тех пор, пока не вылезет окно с ошибкой: "Слишком много графиков. Возможно снижение производительности." В моём случае 5 областей, в каждой по инструменту и 5-6 индикаторов
Закрываем окно настроек, нажимаем правой кнопкой мыши на цене любого инструмента и выбираем пункт "Заменить инструмент"
Выбираем новый инструмент, нажимаем Ок и ничего не происходит (price не меняется на новый инструмент, как должна)
версия 11.1.4.2
Русские буквы выводятся аброкадаброй
Пользователь
Сообщений: Регистрация: 20.03.2023
18.06.2024 01:05:10
Сохраняйте скрипт в кодировке Win1251, а не в UTF8
Удаление не используемых счетов и перенос настроек, Удаление не используемых счетов и перенос настроек
Пользователь
Сообщений: Регистрация: 20.03.2023
11.06.2024 23:34:35
Вкладка, сохранённая в 10-м квике, правильно открывается в 11-м квике, и настройки таблиц в ней сохраняются.
Что изменилось в 11.1.1.11, что перестали приходить данные по CreateDataSource?
Пользователь
Сообщений: Регистрация: 20.03.2023
06.06.2024 01:07:03
Цитата
Georgii написал: Текст ошибки: " attempt to call a nil value (method 'size') "
Написано ясно - нет метода "size". Lua имеет регистрозависимый синтаксис, следует использовать "Size".
Глобальные переменные индикатора, не работает код индикатора на lua при попытке обратится к переменной объявленной в функции Init()
Пользователь
Сообщений: Регистрация: 20.03.2023
29.05.2024 02:14:10
VuEma после конца первого прохода OnCalculate остаётся равным последнему значению. Из-за того, что проходов обычно три, следует всё изменяемое состояние индикатора инициализировать внутри условия по == 1.
Access Violation at adress и Unknown exception at adress, Прошу помощи с ошибкой Lua
Пользователь
Сообщений: Регистрация: 20.03.2023
27.05.2024 21:14:16
Скомпилируйте с дебаг информацией и посмотрите стектрейс. Я так всегда и ловлю.
Quik ОЧЕНЬ долго загружается на виртуальной машине.
Пользователь
Сообщений: Регистрация: 20.03.2023
16.05.2024 21:35:37
Цитата
Кирилл написал: сделав файл info.log с нулевым размером и read-only
ого а замеряли ли Вы, сколько это дало дополнительного прироста в сравнении с "просто перенести на рамдиск"?
SciTE
Пользователь
Сообщений: Регистрация: 20.03.2023
14.05.2024 13:32:03
Цитата
Nikolay написал: Я скрипты компилирую через терминал, с использованием специально написанного (на том же Lua) скрипта компиляции. У меня проект - это много файлов, каждый раз разные зависимости. Руками это компилировать?
А результатом становится один luac файл и все require внутри него ведут каким-то образом внутрь него же, или дерево luac файлов и все require находят модули в сгенерированном дереве, или ещё как?