мне нужно по 30 бумагам получить цену закрытия предыдущего дня из ТТП (через getParamEx(class_code,tiket_code,"prevlegalclosepr").param_value) найти в стакане самый большой объем на продажу и открытие. взять из них цену и вывести процентное соотношение. чтобы не открывать 30 стаканов (а мне еще заявки ставить). пробую сделать через Subscribe_Level_II_Quotes. работает медленно, но я пробую так: из файла в таблицу TIKETS загружен список бумаг. key - код бумаги function main() while is_run do for key, value in spairs(tikets) do Subscribe_Level_II_Quotes(class_code, key) Quotes(key) end end end
function Quotes(code) sleep(250) for i=1, 30 do q = getQuoteLevel2(class_code, code) --получаю стакан, перебираю его. добавляю в таблицу в окне данные. max_bid_price и max_ask_price
prevlegalclosepr=tonumber(getParamEx(class_code, code,"last").param_value) if (max_bid_price==0) or (max_ask_price==0) then delta==0 else if max_bid_price >= max_ask_price then delta = (prevlegalclosepr/max_bid_price) else delta = (prevlegalclosepr) end end
SetCell(Таблица, НомерСтрокиПоБумаге, 6, delta)
и т.д. первая проблема - перебор 30 строк в таблице это долго. вторая - если сделаю меньший sleep то часто не получаю данные стакана.
в этой строке delta = (prevlegalclosepr/max_bid_price) временами ошибка attempt to perform arithmetic on local 'delta ' (a string value) причину отловить не могу. механизмов отладки нет (или я не нашел где они). как мне эффективнее сделать данный алгоритм? или при инициализации, где я загружаю коды тикетов в таблицу сделать Subscribe_Level_II_Quotes по каждой бумаге, а потом просто перебирать через getQuoteLevel2?
function main()
while is_run do
for key, value in spairs(tikets) do
Subscribe_Level_II_Quotes(class_code, key)
Quotes(key)
end
end
end
function Quotes(code)
sleep(250)
for i=1, 30 do
q = getQuoteLevel2(class_code, code)
--получаю стакан, перебираю его. добавляю в таблицу в окне данные.
max_bid_price и max_ask_price
prevlegalclosepr=tonumber(getParamEx(class_code, code,"last").param_value)
if (max_bid_price==0) or (max_ask_price==0) then
delta==0
else
if max_bid_price >= max_ask_price then
delta = (prevlegalclosepr/max_bid_price)
else
delta = (prevlegalclosepr/max_ask_price)
end
end
SetCell(Таблица, НомерСтрокиПоБумаге, 6, delta)
"Eldar пишет: и т.д. первая проблема -перебор 30 строк в таблице это долго.
Вынесите Subscribe_Level_II_Quotes в отдельный цикл, перед строкой "while is_run do" заказывать постоянно одни и те же стаканы нет смысла. Плюс, сам цикл "for i=1, 30 do совершенно не понятен. Зачем он?
Цитата
Eldar пишет: вторая -если сделаю меньший sleep то часто не получаю данные стакана.
На заказ данных по очередному скакану требуется время, но 250 как-то много. обычно хватает 10-50. Быть может терминал чем-то нагружен? Попробуйте провести эксперимент с sleep(50) но при этом закрыв все посторонние окна в терминале.
Цитата
Eldar пишет: временами ошибка attempt to perform arithmetic on local 'delta ' (a string value )
Тут проще, если стакан не полный, то при обращении к пустой строке стакана функция выдаст nil, отсюда и ошибка.
Цитата
Eldar пишет: причину отловить не могу. механизмов отладки нет (или я не нашел где они).
Механизмов отладки много, самый простой это логирование через PrintDbgStr. Также в интернете можно почитать про Decode и ее аналоги.
это по форуму. код нечитабельный стал. не вставился тег CODE. может у меня глюк.
Цитата
Sergey Gorokhov пишет: Вынесите Subscribe_Level_II_Quotes в отдельный цикл, перед строкой "while is_run do" заказывать постоянно одни и те же стаканы нет смысла. Плюс, сам цикл "for i=1, 30 doсовершенно не понятен. Зачем он?
мне надо запросить объемы в стаканах по 30 бумагам. мне Subscribe_Level_II_Quotes запросить один раз на каждую бумагу в отдельном цикле? а потом получать стакан по getQuoteLevel2?
Цитата
Sergey Gorokhov пишет: Тут проще, если стакан не полный, то при обращении к пустой строке стакана функция выдаст nil, отсюда и ошибка.
то есть ставить условие
Код
if (max_bid_price~=nil) or (max_bid_price==0) then
delta==0
долго, потому что наверно в цикле делаю Subscribe_Level_II_Quotes, потом иду обрабатывать данные. но попробую уменьшить количество окон и почистить код
Sergey Gorokhov пишет: Вынесите Subscribe_Level_II_Quotes в отдельный цикл, перед строкой "while is_run do" заказывать постоянно одни и те же стаканы нет смысла. Плюс, сам цикл "for i=1, 30 doсовершенно не понятен. Зачем он?
мне надо запросить объемы в стаканах по 30 бумагам. мне Subscribe_Level_II_Quotes запросить один раз на каждую бумагу в отдельном цикле?а потом получать стакан по getQuoteLevel2?
Посмотрите код внимательней.
Вот Вы гоняете в цикле все бумаги по очереди:
Код
for key, value in spairs(tikets) do
Далее, для каждой из этих бумаг Вы вызываете функцию Quotes в которой вызываете цикл "for i=1, 30 do" Таким образом, если в таблице "tikets" у Вас 30 бумаг, то в общем случае, за одну итерацию, цикл "for i=1, 30 do" приведет к тому что функции внутри него будут вызваны 30*30=900 раз. Конечно, сложно судить не видя полного кода, но на мой взгляд 900 раз вызывать одни и те же функции совершенно излишне.
Sergey Gorokhov пишет: Далее, для каждой из этих бумаг Вы вызываете функцию Quotes в которой вызываете цикл "for i=1, 30 do" Таким образом, если в таблице "tikets" у Вас 30 бумаг, то в общем случае, за одну итерацию, цикл "for i=1, 30 do" приведет к тому что функции внутри него будут вызваны 30*30=900 раз. Конечно, сложно судить не видя полного кода, но на мой взгляд 900 раз вызывать одни и те же функции совершенно излишне.
первый цикл - это запрос стаканов по бумагам. второй цикл - это внесение изменений в таблицу.
Eldar пишет: второй цикл - это внесение изменений в таблицу.
У вас в цикле "for i=1, 30 do" нигде не используется переменная-счётчик i. Отсюда, на первый взгляд, в этом цикле вы делаете 30 раз одни и те же действия.
Надо делать так, как надо. А как не надо - делать не надо.
1) на любой взгляд этот цикл "for i=1, 30 do" лишний.
2) Если надо делать такое лишь один раз в сессию то можно и так, но если это делается всю сессию, то надо ставить колбек OnQuote(STRING class_code, STRING sec_code).
Sergey Gorokhov пишет: Тогда уберите getQuoteLevel2 и все что с ним связано из второго цикла. Ее достаточно взять 1 раз перед циклом "for i=1, 30 do"
но мне надо получать данные стакана регулярно, а не только один раз. плюс все же тут не весь код. мне надо понять логику. получается что обычно OnQuote работает с открытыми стаканами, но через Subscribe_Level_II_Quotes я подписываюсь на изменения. то есть в первом цикле делаю Subscribe_Level_II_Quotesпо всем тикетам, а потом отрабатываю через OnQuote (а внутри через getQuoteLevel2) ? второй цикл не нужен, так как изменить данные нужной строки таблицы вывода я могу через SetCell, где ключем будет код бумаги.
Цитата
Sergey Gorokhov пишет: То есть ограничить просмотр строк в стакане количеством этих строк. Для этого надо использовать параметры "bid_count" и "offer_count"
каким образом? я получаю таблицу через getQuoteLevel2. потом получаю таблицу BID и OFFER
for bid_i=1, bid_count, 1 do
bid_quantity = tonumber( bid[bid_i]["quantity"] )
if (bid_quantity > max_bid_count) then
max_bid_count = bid_quantity
max_bid_price = bid[bid_i]["price"]
end
end
почему я тут могу получить NIL? или если у меня не загрузится стакан, то как раз поэтому у меня NIL и выходит?
--константы
data_file = "s_test.txt" --путь к файлу со списком акций и кодов классов
--переменные скрипта
is_run = true --Режим работы скрипта, работает или остановлен
shares = {} --Список акций
shares_count = 0 --Количество акций
table_data = nil --Данные для визуальной таблицы
class_code = "TQBR" --Код классов
t = nil --Указатель на визуальную таблицу
--Дополнительый поток
function main()
while is_run do
for key, value in spairs(shares) do
i = i + 1
Subscribe_Level_II_Quotes(class_code, key)
Quotes(key)
end
end
end
function OnInit(quik_path)
--читаем из файла акции и коды классов
for line in io.lines(getScriptPath() .. "\\" .. data_file) do
pos = string.len(line)
share = string.sub(line, 0, pos)
share_item = {}
share_item["class"] = class_code
shares[share] = share_item
shares_count = shares_count + 1
end
table.sort(shares)
--содержимое таблицы
table_data = {}
--Создаем структуру, описывающую таблицу
t = AllocTable()
--столбцы
AddColumn(t, 1, "Ticker", true, QTABLE_STRING_TYPE, 10)
AddColumn(t, 2, "Count Bid", true, QTABLE_INT_TYPE, 10)
AddColumn(t, 3, "Bid Price", true, QTABLE_STRING_TYPE, 10)
AddColumn(t, 4, "Count Ask", true, QTABLE_INT_TYPE, 10)
AddColumn(t, 5, "Ask Price", true, QTABLE_STRING_TYPE, 10)
AddColumn(t, 6, "Delta", true, QTABLE_STRING_TYPE, 10)
CreateWindow(t)
SetWindowCaption(t, "Price_Delta")
SetWindowPos(t, 200, 200, 500, 600)
for i=1, shares_count do
InsertRow(t, -1)
end
i = 0
-- заполняем таблицу используя сортировку по ключам
for key, value in spairs(shares) do
i = i + 1
sec_code = key
SetCell(t, i, 1, tostring(sec_code))
SetColor(t, i, 1, RGB(150, 150, 150), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
end
end
function OnStop()
is_run = false
for key, value in spairs(shares) do
Unsubscribe_Level_II_Quotes(class_code, key)
end
end
function Quotes(code)
sleep(150)
for i=1, shares_count do
local row = GetCell(t, i, 1)
row_sec = tostring(row["image"])
if (row_sec == code) then
q = getQuoteLevel2(class_code, code)
bid_count = tonumber(q["bid_count"])
ask_count = tonumber(q["offer_count"])
bid = q["bid"] --покупатели
ask = q["offer"] --продавцы
max_bid_count = 0 --количество лотов (максимальный спрос)
max_ask_count = 0 --количество лотов (максимальное предложение)
max_bid_price = "" --цена максимального спроса
max_ask_price = "" --цена максимального предложения
--перебираем всех покупателей и находим самый большой объем
for ii=1, bid_count, 1 do
bid_quantity = tonumber( bid[ii]["quantity"] )
if (bid_quantity > max_bid_count) then
max_bid_count = bid_quantity
max_bid_price = bid[ii]["price"]
end
end
--перебираем всех продавцов находим самый большой объем
for ii=1, ask_count, 1 do
ask_quantity = tonumber( ask[ii]["quantity"] )
if (ask_quantity > max_ask_count) then
max_ask_count = ask_quantity
max_ask_price = ask[ii]["price"]
end
end
SetCell(t, i, 2, tostring(max_bid_count))
SetColor(t, i, 2, RGB(50, 250, 50), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
SetCell(t, i, 3, tostring(max_bid_price))
SetColor(t, i, 3, RGB(50, 250, 50), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
SetCell(t, i, 4, tostring(max_ask_count))
SetColor(t, i, 4, RGB(250, 50, 50), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
SetCell(t, i, 5, tostring(max_ask_price))
SetColor(t, i, 5, RGB(250, 50, 50), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
if (max_ask_count>max_bid_count) then
price_max=max_ask_price
SetCell(t, i, 2, "")
SetColor(t, i, 2, RGB(255, 255, 255), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
SetCell(t, i, 3, "")
SetColor(t, i, 3, RGB(255, 255, 255), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
elseif (max_ask_count<max_bid_count) then
price_max=max_bid_price
SetCell(t, i, 4, "")
SetColor(t, i, 4, RGB(255, 255, 255), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
SetCell(t, i, 5, "")
SetColor(t, i, 5, RGB(255, 255, 255), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
else
price_max=max_bid_price
end
closeprice=tonumber(getParamEx(class_code, code,"last").param_value)
if (closeprice==0) then
delta=0
else
delta=(tonumber(price_max)/closeprice-1)*100
end
SetCell(t, i, 6, apply_scale(delta,2))
if delta>=2 then
SetColor(t, i, 6, RGB(50, 250, 50), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
elseif delta<=-2 then
SetColor(t, i, 6, RGB(250, 50, 50), QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
else
SetColor(t, i, 6, RGB(255, 255, 255), QTABLE_DEFAULT_COLOR,QTABLE_DEFAULT_COLOR, QTABLE_DEFAULT_COLOR)
end
end
end
end
ну как-то так и что по поводу логики подскажете
Цитата
Eldar пишет: получается что обычно OnQuote работает с открытыми стаканами, но через Subscribe_Level_II_Quotes я подписываюсь на изменения. то есть в первом цикле делаю Subscribe_Level_II_Quotesпо всем тикетам, а потом отрабатываю через OnQuote (а внутри через getQuoteLevel2) ? второй цикл не нужен, так как изменить данные нужной строки таблицы вывода я могу через SetCell, где ключем будет код бумаги.
for bid_i=1, bid_count, 1 do
bid_quantity = tonumber( bid[bid_i]["quantity"] )
if (bid_quantity > max_bid_count) then
max_bid_count = bid_quantity
max_bid_price = bid[bid_i]["price"]
end
end
почему я тут могу получить NIL? или если у меня не загрузится стакан, то как раз поэтому у меня NIL и выходит?
У нас не воспроизводится. Приведите полный текст ошибки.
Eldar пишет: получается что обычно OnQuote работает с открытыми стаканами, но через Subscribe_Level_II_Quotes я подписываюсь на изменения. то есть в первом цикле делаю Subscribe_Level_II_Quotesпо всем тикетам, а потом отрабатываю через OnQuote (а внутри через getQuoteLevel2) ? второй цикл не нужен, так как изменить данные нужной строки таблицы вывода я могу через SetCell, где ключем будет код бумаги.
Вы программист, Вам решать какую логику использовать.
Ошибка плавающая. иногда сразу выскакивает, иногда через час.
Цитата
Sergey Gorokhov пишет: Вы программист, Вам решать какую логику использовать.
дело в том, что не понятно как работает функция Subscribe_Level_II_Quotes: 1. запрашиваем стакан по тикету. получаем единоразово стакан и обрабатываем через getQuoteLevel2 2. помечаем стакан тикета на отслеживание изменений. отслеживаем изменения стакана через OnQuote.обрабатываем через getQuoteLevel2. скуповата документация.
Цитата
Sergey Gorokhov пишет: Да, используйте для этого условие на функцию IsWindowClosed
в этой функции я могу пометить is_run как false и не обрабатывать в main, но скрипт все еще будет запущен. а мне надо остановить его в коде, как через меню "Остановить". скрипт выполнил расчет, нужный тикет мне показал через message и остановился.
Eldar пишет: дело в том, что не понятно как работает функция Subscribe_Level_II_Quotes:
вызов Subscribe_Level_II_Quotes аналогичен открытию стакана в терминале QUIK. Без открытия стакана данные в него не поступают. Соответственно OnQuote и getQuoteLevel2 работать не будут
Цитата
Eldar пишет: в этой функции я могу пометить is_run как false и не обрабатывать в main, но скрипт все еще будет запущен.
Это значит что Вы не понимаете как работает IsWindowClosed Напишите в функции Quotes так:
Код
function Quotes(code)
sleep(150)
for i=1, shares_count do
if IsWindowClosed(t) then
is_run=false
break
end
--остальной код
Спасибо. что-то проясняется. например найден BREAK. но так как документация скудная, появился следующий вопрос. после BREAK не выполнится функция OnStop(), я правильно понимаю?
Eldar пишет: Спасибо. что-то проясняется. например найден BREAK. но так как документация скудная, появился следующий вопрос.
По поводу документации объясняю. Язык LUA придумали НЕ мы. Мы взяли готовый язык и добавили в него несколько наших функций. Поэтому в НАШЕЙ документации только НАШИ функции. Все функции которые являются стандартными для языка LUA описаны в документации на язык LUA, которая есть в интернете: http://www.lua.org/docs.html
BREAK приводит к выходу из цикла. Подробнее в документации на LUA
так у меня вопросы касаются работы именно в сочетании с quik. с break понятно. но тогда все еще не понятен вопрос, как именно ОСТАНОВИТЬ выполнение скрипта. вот как через меню "Доступные скрипты" - Остановить. и вопрос про функцию Subscribe_Level_II_Quotes по моему мнению тоже резонный. спасибо за разьяснения, но вроде вопросов все больше и больше. буду пробовать допиливать.
Eldar пишет: и вопрос про функцию Subscribe_Level_II_Quotes по моему мнению тоже резонный.
На вопрос где лучше использовать Subscribe_Level_II_Quotes у нас нет и не может быть ответа. Так как этот вопрос зависит от Вашего видения работы программы. Можно даже сказать от Вашего вкуса.