Блог им. jatotrade_com

QLua скринер, или то, о чём мечтал Weddy!

Всем, привет!
Мечты сбываются (и не только у Газпрома)! Weddy, надеюсь этим постом я полностью закрываю ваше ТЗ.
QLua скринер, или то, о чём мечтал Weddy!
Суть скринера в вышеизложенном пожелании. Т.к. первый и второй скринеры не удовлетворяли требованиям ТЗ Weddy.
Надеюсь, теперь все ок. Выглядит так:
QLua скринер, или то, о чём мечтал Weddy!
Яндекс — красавчик, однако.
Пользоваться так:
В переменную tickers записываете пары код инструмента = код класса, для тикеров за которыми ведете наблюдение.
tickers = {SiU0 = «SPBFUT», RIU0 = «SPBFUT», BRQ0 = «SPBFUT», GZU0 = «SPBFUT», SRU0 = «SPBFUT», GAZP = «TQBR», SBER = «TQBR», YNDX = «TQBR»,GMKN = «TQBR», MGNT = «TQBR», SU26207RMFS9 = «TQOB»}

В список dates в формате «ДД/ММ/ГГГГ» даты, где от значений хая и лоу тикера в эту конкретную дату и будет рассчитываться текущее процентное изменение.
dates = {«10/07/2020», «02/07/2020», «19/03/2020», «20/01/2020»}
Третий список events, должен соответствовать описанию дат, чтобы понятнее было, что это за дата такая. Например, «01/07/2020» — «Zeroing». Но торги не проводились. В нашем случае мы взяли начало недели, начало месяца и даты, когда индекс ММВБ показал минимум года, и максимум года. Главное, чтобы количество строк описания совпадало с количеством дат. Эти описания и появятся в заголовке столбцов.
events = {«Week », «Month », «MICEX YrL », «MICEX YrH „}
После этого в Квике можно запускать луа-скрипт.
Из приятного: ускорилась загрузка источников данных. Т.к. перенес определение коллбэка из OnInitв Main, давая загрузиться свечам с сервера. Иначе, если сразу определить коллбэк после CreateDataSource, то по мере загрузки свечей, он будет вызываться для каждой загружаемой свечи, т.е. примерно 3000*кол-во_тикеров*2_таблицы раз. Сейчас “работает как часики». Кода, как обычно не много:

-- ©2020 by Evgeny Shibaev for Weddy
-- Таблица, отображающая в процентах рост(падение) инструмента финансового рынка от заданных дат
-- Какие инструменты(тикеры) отслеживаем. Таблица пар тикер - площадка
tickers = {SiU0 = "SPBFUT", RIU0 = "SPBFUT", BRQ0 = "SPBFUT", GZU0 = "SPBFUT", SRU0 = "SPBFUT", GAZP = "TQBR", SBER = "TQBR", YNDX = "TQBR",
           GMKN = "TQBR", MGNT = "TQBR", SU26207RMFS9 = "TQOB"} --IMOEX = "SNDX"
-- От конкретных дат   start month     year low      year high   MICEX INDEX
dates = {"10/07/2020", "02/07/2020", "19/03/2020", "20/01/2020"}
events = {"Week          ", "Month        ", "MICEX YrL ", "MICEX YrH "} -- Список событий, соответствующих заданной выше дате
sources = {} -- Список источников данных по количеству тикеров
rows = {} -- Список строк в таблице LOW по количеству тикеров
screener = AllocTable() -- Указатель на таблицу FromLow
rowsH = {} -- Список строк в таблице HIGH по количеству тикеров
screenerH = AllocTable() -- Указатель на таблицу FromHigh
stopped = false -- Остановка скрипта
col_shift = 1 -- смещение основных столбцов в таблице
local max = math.max  -- локальная ссылка на math.max
local min = math.min  -- локальная ссылка на math.min
     
-- Функция вызывается перед вызовом main
function OnInit(path)
   -- "Ticker"- название первого столбца в таблице
   AddColumn(screener, 0, "Ticker", true, QTABLE_STRING_TYPE, 15)
   AddColumn(screener, 1, "Price", true, QTABLE_DOUBLE_TYPE, 12) 
   -- Названия остальных столбцов в таблице по количеству dates
   for column, date in ipairs(dates) do
       AddColumn(screener, column + col_shift, events[column].." "..date, true, QTABLE_DOUBLE_TYPE, 10)
   end
   CreateWindow(screener)
  -- Даем название  таблице
   SetWindowCaption(screener, "Percent change from LOW from date")
  -- Вторая таблица - от максимумов за период.
   AddColumn(screenerH, 0, "Ticker", true, QTABLE_STRING_TYPE, 15)
   AddColumn(screenerH, 1, "Price", true, QTABLE_DOUBLE_TYPE, 12)   
   -- Названия остальных столбцов в таблице по количеству dates
   for column, date in ipairs(dates) do
       AddColumn(screenerH, column + col_shift, events[column].." "..date, true, QTABLE_DOUBLE_TYPE, 10)
   end
   CreateWindow(screenerH)
   SetWindowCaption(screenerH, "Percent change from HIGH from date")
   for ticker, board in pairs(tickers) do
       --Для каждого тикера создаем источник данных - "дневки"
       sources[ticker] = CreateDataSource(board, ticker, INTERVAL_D1)
       --ИСПРАВЛЕНО! Когда я в OnInit устанавливал коллбэк, то загрузка была долгой, из за того что при приходе очередной свечи 
       -- каждый раз вызывался этот коллбэк. Я перенес определение коллбэка в main с небольшой задержкой sleep(100).
       -- После этого тормоза исчезли. Строка ниже в этом месте тормозила:
       -- sources[ticker]:SetUpdateCallback(function(index) InvalidateCallback(index, ticker) end)
       --Для каждого тикера определяем строку в таблице и запоминаем ее в rows
       rows[ticker] = InsertRow(screener, -1)
       rowsH[ticker] = InsertRow(screenerH, -1)
       --В первом столбце каждой строки устанавливаем имя тикера
       SetCell(screener, rows[ticker], 0, ticker)
       SetCell(screenerH, rowsH[ticker], 0, ticker)
   end
end

-- Коллбэк функция вызывается при изменении значения текущей цены тикера. Обновляет строку тикера в таблице сразу, как происходит изменение.
function InvalidateCallback(index, ticker)
   price = sources[ticker]:C(sources[ticker]:Size())
   ap = getSecurityInfo(tickers[ticker], ticker).scale
   if ap == 0 then price = math.floor(price) end 
   SetCell(screener, rows[ticker], 1, tostring(price))
   SetCell(screenerH, rows[ticker], 1, tostring(price))
   for column, date in ipairs(dates) do
      -- Определяем процентр изменения цены тикера от минимальных значений заданной даты
      percent = DateLow(ticker, date)
      -- Определяем процентр изменения цены тикера за days-дней от максимальных значений  заданной даты
      percentH = DateHigh(ticker, date)
      SetCell(screener, rows[ticker], column + col_shift, string.format("%.2f", percent))
      SetCell(screenerH, rowsH[ticker], column + col_shift, string.format("%.2f", percentH))
      -- Подкрашиваем ячейку соответственно росту(падению) и величины роста(падения)
      SetColor(screener, rows[ticker], column + col_shift, BCellColor(percent), FCellColor(percent), BCellColor(percent), FCellColor(percent))
      SetColor(screenerH, rowsH[ticker], column + col_shift, BCellColor(percentH), FCellColor(percentH), BCellColor(percentH), FCellColor(percentH))
   end
end

-- Определяет процент изменения текущей цены от минимума дня указанной даты
function DateLow(ticker, date)
  local len = sources[ticker]:Size() --Сколько всего "дневных" свечей в источнике данных конкретного тикера
  local dt = StrToDate(date)
  for i = len, 1, -1 do
    local candle_time = sources[ticker]:T(i)
    if dt.day == candle_time.day and dt.month == candle_time.month and dt.year == candle_time.year then
       return (sources[ticker]:C(len) - sources[ticker]:L(i)) / sources[ticker]:L(i) * 100 end
  end
  return 0
end

-- Определяет процент изменения текущей цены от максимума дня указанной даты
function DateHigh(ticker, date)
  local len = sources[ticker]:Size() --Сколько всего "дневных" свечей в источнике данных конкретного тикера
  local dt = StrToDate(date)
  for i = len, 1, -1 do
    local candle_time = sources[ticker]:T(i)
    if dt.day == candle_time.day and dt.month == candle_time.month and dt.year == candle_time.year then
       return (sources[ticker]:C(len) - sources[ticker]:H(i)) / sources[ticker]:H(i) * 100 end
  end
  return 0
end

-- Цвет текста в ячейке. Если рост - то цвет "зеленый", падение - "красный"
--function FCellColor(change) if change > 0 then return RGB(0,128,0) else return RGB(158,0,0) end end
function FCellColor(change) if change > 0 then return RGB(0,0,0) else return RGB(0,0,0) end end

-- Маленькая "тепловая карта". Делает фон ячейки более интенсивным, взависимости от величины роста(падения)
function BCellColor(change)
  bright = math.floor(255 - min(math.abs(change*5), 235),1)  --10 110
  if change > 0 then return RGB(bright,255,bright) else return RGB(255,bright,bright) end
end

-- Преобразует строку вида "05/07/2020" в таблицу {year = 2020, month = 7, day = 5}
function StrToDate(str)
   dt = {}
   dt.day,dt.month,dt.year = string.match(str, "(%d*)/(%d*)/(%d*)")
   for key,val in pairs(dt) do dt[key] = tonumber(val) end
   return dt
end

-- Функция вызывается перед остановкой скрипта
function OnStop(signal) stopped = true end

-- Функция вызывается перед закрытием квика
function OnClose() stopped = true end;

-- Основная функция выполнения скрипта
function main()
   for ticker, board in pairs(tickers) do
       sleep(300)
       --Устанавливаем коллбэк функцию для каждого тикера. Она вызывается при изменении цены тикера. "А что, так можно было?"
       sources[ticker]:SetUpdateCallback(function(index) InvalidateCallback(index, ticker) end)
       sleep(50)
       -- Вызываем первый раз явно для полной отрисовки таблицы, следующая отрисовка строк только при изменении цены
       InvalidateCallback(sources[ticker]:Size(), ticker)
   end
  while not stopped do sleep(1) end
end  	  

Всем хороших выходных, и пусть мечты сбываются!
Код вы можете также найти в конце страницы https://www.jatotrade.com/download
Или по прямой ссылке. Подписаться на мой канал в ютьюбе можно здесь. Там много чего интересного.





  • обсудить на форуме:
  • Quik Lua
★24
25 комментариев
Из замечаний.

function OnStop(signal)
stopped =true
sleep(1000) — неплохо бы поставить...
end

sleep(1000) — неплохо бы поставить, чтобы main() могла корректно завершиться.
и в OnClose() тоже хорошо бы
avatar
3Qu, учту
Огонь!
avatar
Еще один момент обнаружил.
Вы делаете подписки в цикле
sources[ticker] = CreateDataSource(...)
В OnStop() и в OnClose() их, по хорошему надо бы уничтожить.
примерно так:
sources[ticker]:Close()
Наверно опять же в цикле.
Можно это дело перенести в main() после завершения его основного цикла.
avatar
3Qu, для полной корректности — да. Правда, насколько я понял, при остановке скрипта все эти ресурсы автоматически очищаются.
Евгений Шибаев, я так понимаю, что подписки идут на уровне терминала, примерно на том же уровне, что и подписки на данные в меню настроек Квик, и наверно остаются до перезагрузки терминала. Не знаю насколько они чему могут помешать, но постель лучше за собой убирать.) На всяк случай.
Это мое понимание, утверждать ничего не берусь.
avatar
Как язык на Си похож. Наверное, Lua потом зайдет.
avatar
Vladimir N., да попроще будет, в смысле — «не строгий»
Vladimir N., пожалуй, по синтаксису он ближе к VB, только без V.)
avatar
А мне lua паскаль напоминает, не один а один, но что-то есть ))
Каждый в луа видит что-то родное. Мне вот даже Лисп напомнил))
Жень, здравствуй, в чём может быть печалька:


Сам скрипт при этом не стартует. Спасибо.
avatar
Chelovekspasibo, день добрый, возможно редактируешь обычным блокнотом, луа это не нравится, а может Квик джунторский не все тикеры подцепляет. Пиши в личку, разберемся.

Здравствуйте, спасибо за ваш труд, но что-то давно у вас не было топиков:) Могли бы вы запилиль qlua скрипт для выгрузки тиковых данных по акциям в sqlite? Был бы крайне признателен!
avatar
Емельянов Иван, спасибо. Если сможете подключить модуль sqlite3 в QLUA, то сделаю, не вопрос.
avatar
Евгений Шибаев, прощу прощения за беспокойство! Но ошибкка выскакивает почему то при запуске данного скрипта вот такая:

Может Вам не сложно сказать в чем дело?)

avatar
GoodBargains, посмотрю на днях в чем дело, если нетрудно скиньте ссылку какой скрипт не запускается
Евгений Шибаев, странно, но уже нет ошибки. Вчера определенно была. 
avatar
Евгений Шибаев, а сейчас опять та же ошибка появилась(. Вот отсюда скрипт smart-lab.ru/blog/630269.php. А еще не подскажите, как доделать, чтобы память не забивалась? Все время растет память, тогда как строк всегда одинаковое количество. вот тут память просто растет прямо пропорционально времени:


avatar
GoodBargains, теперь понятно о каком скрипте идет речь, нужно подумать насчет памяти: можно сделать так чтобы периодически шла скользящая рамка, но тогда история будет теряться. Память растет из-за того что каждое изменение стакана запоминается а их может быть до 10 в секунду для каждого инструмента. Ну и потом это Квик…
Евгений Шибаев, а как сделать, чтобы сохранялись только N последних? Или это и есть скользящая рамка?
avatar
Евгений Шибаев, да, N это и есть ширина скользящей рамки.
Евгений Шибаев, а так можно ограничить в LUA в квике? Может пару строк кода всего  не подскажете?
avatar
Евгений Шибаев, хотел ещё у вас спросить, почему на Лисп быстрее, чем на Питоне было бы? За счет чего быстрее то?
avatar
код так себе, даты нужно переводить в секунды и потом 1 раз сравнивать.

теги блога Евгений Шибаев

....все тэги



UPDONW
Новый дизайн