Иногда бывает необходимым проанализировать не отдельную бумагу, а рынок в целом.
Кто-то смотрит для этого индексы, кто-то различные сантименты, а мне удобнее проводить анализ по динамике всех бумаг (сколько на дату эмитентов в совокупности растет, сколько бумаг выше своих месячных, квартальных или годовых значений и пр.). Каждый по своему может это использовать далее (как общий фильтр принятия решения для входа в сделку, для составления своих индексов, для анализа динамики своего портфеля – особенно если счетов несколько у разных брокеров и пр.).
Получить котировки на конкретную дату можно через сайт Московской Биржи (https://www.moex.com/ru/marketdata/#/mode=groups&group=4&collection=3&boardgroup=57&data_type=history&date=2023-06-27&category=main), но это не очень удобно т.к. требуется либо парсить (для чего нужен уже нетривиальный уровень в программировании), либо вручную выдергивать эту страницу, например в excel (тем, кто попробует выгрузить всё по кнопкам скачать Excel / CSV биржа предложит воспользоваться платной подпиской для получения данных).
В качестве альтернативы можно воспользоваться iss запросами (подробно расписано в руководстве разработчика в разделе «Программный интерфейс к ИСС» https://www.moex.com/a2193, все варианты запросов и параметров здесь: https://iss.moex.com/iss/reference/).
Акции можно выгрузить постранично по 100 эмитентов. Для примера на 27 июня 2023 года:
Уже в Excel я объединяю эти XML выгрузки и макросом отсекаю лишнюю информацию.
Эта возможность бесплатная (пока), но ничто не вечно: биржа может перестать поддерживать этот сервис (как ранее случилось с запросами ISSRPC) или сделает его платным.
Поэтому в какой-то момент я задумался о том, как это всё можно выгружать из торгового терминала. И здесь либо нужно работать с массивом тикеров, который самому необходимо ежедневного мониторить и обновлять на предмет новых бумаг или делистинга, либо делать это в автоматическом режиме. Я выбрал второй путь.
Небольшой скрипт на qlua проходит по всем возможным вариантам тикеров (от AAAA до ZZZZ) на предмет их торгуемости (=getParamEx(«TQBR», TIKER, 'STATUS').param_value), префы проверяются добавлением к тикеру «P». Отдельно приходится проверять торгуемость VEON-RX, т.к. она единственная выбивается из общего списка.
Результат записывается в файл tickers.csv на C:\files\ (папку files необходимо создать на C:\ до запуска скрипта, либо заменить в коде на свою).
function main() message('[ = = = = = = = = = = start = = = = = = = = = = ]') datetime = os.date("!*t",os.time()) message('Определение тикеров торгуемых бумаг на фондовой секции') message(os.date("%d.%m.%Y")) message('Начало работы скрипта '..os.date("%X",os.time())) DirectionSaveFile=tostring("C:\\files\\tickers.csv") my_csv=io.open(DirectionSaveFile,"w") t = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'} ind = 1 ind_two = 0 sprint = "" for i=1, #t do for j=1, #t do for n=1, #t do for m=1, #t do TIKER = tostring(t[i])..tostring(t[j])..tostring(t[n])..tostring(t[m]) TIKERP = tostring(t[i])..tostring(t[j])..tostring(t[n])..tostring(t[m]).."P" find_status=tonumber(getParamEx("TQBR",TIKER,'STATUS').param_value) find_status2=tonumber(getParamEx("TQBR",TIKERP,'STATUS').param_value) if find_status == 1 then my_csv:write(TIKER) my_csv:write("\n") sprint = sprint..tostring(ind).."/ "..TIKER.." " ind_two = ind_two + 1 ind = ind + 1 sleep(5) end if find_status2 == 1 then my_csv:write(TIKERP) my_csv:write("\n") sprint = sprint..tostring(ind).."/ "..TIKERP.." " ind_two = ind_two + 1 ind = ind + 1 sleep(5) end if ind_two >= 6 or (i==#t and i==j and n==m and i==n) then message(sprint) sprint="" ind_two = 0 end end end end end if tonumber(getParamEx("TQBR","VEON-RX",'STATUS').param_value) == 1 then message(tostring(ind).." / ".."VEON-RX") my_csv:write("VEON-RX") sleep(5) end message('Завершение работы скрипта '.. os.date("%X",os.time())) my_csv:flush() my_csv:close() message('Общее количество торгуемых бумаг : '..tostring(ind)) message("[ = = = = = = = = = = end = = = = = = = = = = ]") end
Минус этого скрипта – если появится тикер с цифрами, с большим количеством букв или c тире (как это случилось с VEON-RX), то такую бумагу скрипт не увидит, нужно будет добавить как исключение (как и сделано с VEONом) и далее скрипт будет точечно проверять эту бумагу на торгуемость.
Дополнительно результаты выводятся в терминале в таблице системных сообщениях (в настройках лучше отключить показ сообщений в отдельном окне).
Следующий скрипт запускается и уже по таймеру (HTimeOut – час выгрузки по МСК, MTimeOut — минуты) делает выгрузку всей необходимой для меня информации из таблицы текущих торгов. Тикеры берутся из только что полученного файла C:\files\tickers.csv (поэтому если меняли папку выше, то и в следующем скрипте нужно подправить). Я запускаю параллельно 2 подобных скрипта, один делает выгрузку в 18:55 под закрытие основной сессии, после чего сам выключается, второй в 23:55 и также выключается.
Статусы текущих состояний работы скриптов выводятся в отдельные таблички.
function OnInit() --параметры таймера: HTimeOut=18 MTimeOut=55 NameTableColumns = {'старт', 'таргет', 'текущий статус', 'время начала выгрузки'} QCTanbles = { 9, 7, 55, 33 } YPosConsts = {447, 526} if HTimeOut <= 19 then YPosTables = YPosConsts[1] else YPosTables = YPosConsts[2] end indTimer = 1680 -- таймер отключит скрипт через 7 часов (4*60*7=1680) if HTimeOut < 10 then otxt = "0" else otxt = "" end if MTimeOut < 10 then ttxt = "0" else ttxt = "" end ProgramSmallName="dwnlds "..otxt..HTimeOut..":"..ttxt..MTimeOut ProgramName="выгрузка цен закрытия и объемов торгов" ProgramVersion="1.0.5. от 23.06.2023" end function OnStop() DirectionSaveFile=tostring("C:\\files\\logs.csv") my_csv=io.open(DirectionSaveFile,"a+") if ind == 1 then message("Скрипт "..ProgramSmallName.." остановлен по завершению работы", 2) my_csv:write("Скрипт "..ProgramSmallName.." остановлен по завершению работы\n") SetCell(m_t, 1, 3, "Скрипт остановлен по завершению работы") Highlight(m_t, 1, 3, RGB(149, 200, 216), RGB(0,0,0), 600) SetColor(m_t, 1, QTABLE_NO_INDEX, RGB(166, 220, 243), RGB(0,0,0), RGB(149,200,216), RGB(0,0,0)) end if ind == 0 then message("Скрипт "..ProgramSmallName.." остановлен пользователем") my_csv:write("Скрипт "..ProgramSmallName.." остановлен пользователем\n") SetCell(m_t, 1, 3, "Скрипт остановлен пользователем") Highlight(m_t, 1, 3, RGB(149, 200, 216), RGB(0,0,0), 600) SetColor(m_t, 1, QTABLE_NO_INDEX, RGB(197, 220, 243), RGB(0,0,0), RGB(149,200,216), RGB(0,0,0)) end if ind == -1 then message("Скрипт "..ProgramSmallName.." остановлен в период разрыва соединения", 3) my_csv:write("Скрипт "..ProgramSmallName.." остановлен в период разрыва соединения\n") SetCell(m_t, 1, 3, "Скрипт остановлен в период разрыва соединения") Highlight(m_t, 1, 3, RGB(160, 160, 160), RGB(0,0,0), 600) SetColor(m_t, 1, QTABLE_NO_INDEX, RGB(224, 224, 224), RGB(0,0,0), RGB(149,200,216), RGB(0,0,0)) end if ind == 2 then message("Скрипт "..ProgramSmallName.." остановлен по таймеру [ n >= "..indtre.." ] ", 3) my_csv:write("Скрипт "..ProgramSmallName.." остановлен по таймеру [ n >= "..indtre.." ] \n") SetCell(m_t, 1, 3, "Скрипт остановлен по таймеру [ n >= "..indtre.." ]") Highlight(m_t, 1, 3, RGB(160, 160, 160), RGB(0,0,0), 600) SetColor(m_t, 1, QTABLE_NO_INDEX, RGB(224, 224, 224), RGB(0,0,0), RGB(149,200,216), RGB(0,0,0)) end myRun=false message("<--- завершение "..ProgramSmallName..": "..ProgramName.." --->") message("===========================================") my_csv:write("<--- завершение "..ProgramSmallName..": "..ProgramName.." --->\n") my_csv:write("===========================================\n") my_csv:flush() my_csv:close() sleep(10) end function downloadsdata() message("<-- "..ProgramSmallName.." -->") message("<-- выгрузка данных -->") message("подгрузка тикеров с С:/files/tickers.csv") -- выгрузка тикеров С:\files\tickers.csv t = {} DirectionOpenFile=tostring("C:\\files\\tickers.csv") f = io.open(DirectionOpenFile, "r"); for i=1, 300 do t[i] = f:read("*l") if t[i]==nil then break end end f:close() message(ProgramSmallName.." Выгружено "..tostring(#t).." тикеров") -- создание файла datetime = os.date("!*t",os.time()) Hou_one = datetime.hour Min_one = datetime.min Sec_one = datetime.sec Total_one = Hou_one * 60 * 60 + Min_one * 60 + Sec_one mdate=getTradeDate() if mdate.day<10 then clockdate = " 0"..mdate.day else clockdate = mdate.day end if mdate.month < 10 then clockdate = clockdate.." 0"..mdate.month.." "..mdate.year else clockdate = clockdate.." "..mdate.month.." "..mdate.year end if (Hou_one + 3) < 10 then clocknamefile = "0"..tostring(Hou_one + 3) else clocknamefile = tostring(Hou_one + 3) end if Min_one < 10 then clocknamefile = clocknamefile.." ".."0"..Min_one else clocknamefile = clocknamefile.." "..Min_one end if Sec_one < 10 then clocknamefile = clocknamefile.." ".."0"..Sec_one else clocknamefile = clocknamefile.." "..Sec_one end DirectionSaveFile=tostring("C:\\files\\downloads "..clockdate.." "..clocknamefile..".csv") my_csv=io.open(DirectionSaveFile,"a+") --LEGALCLOSEPRICE 18:45 / Цена закрытия основной сессии --CLOSE 23:50 / Цена закрытия дневной свечи (+ вечерняя сессия, если есть) if (Hou_one + 3) < 23 then my_csv:write("Тикер; OPEN; HIGH; LOW; LASTPRICE; Оборот (на 18:50); LCP; preLCP; preCP; LCP = LegalClosePrice (18:50); CP = ClosePrice (23:50) \n") else my_csv:write("Тикер; Open; High; Low; LastPrice; Оборот (на 23:50); LCP; CP ; preCP; LCP = LegalClosePrice (18:50); CP = ClosePrice (23:50)\n") end openprice = {} highprice={} lowprice = {} lastprice = {} closeprice = {} legalcloseprice = {} prevlegalcloseprice = {} value = {} for i = 1, #t do -- проверка на торгуемость if getParamEx("TQBR",t[i],'STATUS').param_value == 0 then message(ProgramSmallName..' Тикер '..t[i]..' не торгуется') my_csv:write(t[i].."; Не торгуется;\n") sleep(5) else openprice[i] = tonumber(getParamEx("TQBR",t[i],'OPEN').param_value) if openprice[i] == 0 then for u=1, 10 do sleep(100) openprice[i] = tonumber(getParamEx("TQBR",t[i],'OPEN').param_value) message(ProgramSmallName.." "..t[i]..": openprice_"..u) if openprice[i]~=0 then break end end end highprice[i] = tonumber(getParamEx("TQBR",t[i],'HIGH').param_value) if highprice[i] == 0 then for u=1, 10 do sleep(100) highprice[i] = tonumber(getParamEx("TQBR",t[i],'HIGH').param_value) message(ProgramSmallName.." "..t[i]..": highprice_"..u) if highprice[i]~=0 then break end end end lowprice[i] = tonumber(getParamEx("TQBR",t[i],'LOW').param_value) if lowprice[i] == 0 then for u=1, 10 do sleep(100) lowprice[i] = tonumber(getParamEx("TQBR",t[i],'LOW').param_value) message(ProgramSmallName.." "..t[i]..": lowprice_"..u) if lowprice[i]~=0 then break end end end lastprice[i] = tonumber(getParamEx("TQBR",t[i],'LAST').param_value) if lastprice[i] == 0 then for u=1, 10 do sleep(100) lastprice[i] = tonumber(getParamEx("TQBR",t[i],'LAST').param_value) message(ProgramSmallName.." "..t[i]..": lastprice_"..u) if lastprice[i]~=0 then break end end end legalcloseprice[i] = tonumber(getParamEx("TQBR",t[i],'LCLOSEPRICE').param_value) if legalcloseprice[i] == 0 and (Hou_one + 3) >= 18 then for u=1, 10 do sleep(1000) legalcloseprice[i] = tonumber(getParamEx("TQBR",t[i],'LCLOSEPRICE').param_value) message(ProgramSmallName.." "..t[i]..": legalcloseprice_"..u) if tonumber(legalcloseprice[i]~=0) then break end end end closeprice[i] = tonumber(getParamEx("TQBR",t[i],'PREVPRICE').param_value) if closeprice[i] == 0 then for u=1, 10 do sleep(1000) closeprice[i] = tonumber(getParamEx("TQBR",t[i],'PREVPRICE').param_value) message(ProgramSmallName.." "..t[i]..": prevprice~close"..u) if closeprice[i]~=0 then break end end end prevlegalcloseprice[i] = tonumber(getParamEx("TQBR",t[i],'PREVLEGALCLOSEPR').param_value) -- and ((Hou_one + 3) < 10 or (Hou_one + 3) >= 23) if prevlegalcloseprice[i] == 0 then for u=1, 10 do sleep(1000) prevlegalcloseprice[i] = tonumber(getParamEx("TQBR",t[i],'PREVLEGALCLOSEPR').param_value) message(ProgramSmallName.." "..t[i]..": prevlegalcloseprice"..u) if closeprice[i]~=0 then break end end end value[i] = tonumber(getParamEx("TQBR",t[i],'VALTODAY').param_value) if value[i] == 0 then for u=1, 10 do sleep(250) value[i] = tonumber(getParamEx("TQBR",t[i],'VALTODAY').param_value) message(ProgramSmallName.." "..t[i]..": value_"..u) if tonumber(value[i]~=0) then break end end end message(ProgramSmallName.." "..t[i].." : OPEN="..tostring(myint(openprice[i],2)).." / HIGH="..tostring(myint(highprice[i],2)).." / LOW="..tostring(lowprice[i]).."/ LAST= "..tostring(myint(lastprice[i],0))) message(ProgramSmallName.." "..t[i].." : legalCP="..tostring(myint(legalcloseprice[i],2)).." / CP="..tostring(myint(closeprice[i],2)).." / PLCP="..tostring(prevlegalcloseprice[i]).."/ V= "..tostring(myint(value[i],0))) my_csv:write(t[i]..";"..openprice[i]..";"..highprice[i]..";"..lowprice[i]..";"..lastprice[i]..";"..value[i]..";"..legalcloseprice[i]..";"..prevlegalcloseprice[i]..";"..closeprice[i]..";\n") sleep(10) end sleep(10) end my_csv:flush() my_csv:close() datetime = os.date("!*t",os.time()) Hou_two = datetime.hour Min_two = datetime.min Sec_two = datetime.sec Total_two = Hou_two * 60 * 60 + Min_two * 60 + Sec_two message(ProgramSmallName.." Время выгрузки: "..tostring(Total_two-Total_one).." сек.") message(ProgramSmallName.." <-- завершение процедуры выгрузки -->") end -- вывод чисел: отсекаем хвосты 000 function myint(numb, qual) if qual == 0 then return(tostring(math.floor(numb))) end if qual == 1 then b = math.floor(numb) c = math.ceil((numb - b)*10) --message(b.."."..c) return (b.."."..c) end if qual == 2 then b = math.floor(numb) c = math.ceil((numb - b)*100) if c == 0 then return(b.."."..c.."0") else return(b.."."..c) end end end function main() -- создаём таблицу if m_t==nil then m_t=AllocTable() for m=1, 4 do AddColumn(m_t, m, NameTableColumns[m], true, QTABLE_STRING_TYPE, QCTanbles[m]) end CreateWindow(m_t) SetWindowPos(m_t, 0, YPosTables, 706, 80) SetWindowCaption(m_t, ProgramSmallName..": "..ProgramName.." "..ProgramVersion) InsertRow(m_t,-1) end myRun=true message("===========================================", 2) message("<--- cтарт "..ProgramSmallName..": "..ProgramName.." --->", 2) message("<--- версия: "..ProgramVersion.." --->", 2) message('Дата запуска: '..os.date("%d.%m.%Y"), 2) message('Время старта: '..os.date("%X",os.time()), 2) SetCell(m_t, 1, 1, tostring(os.date("%X",os.time()))) SetCell(m_t, 1, 2, otxt..HTimeOut..":"..ttxt..MTimeOut) SetCell(m_t, 1, 3, "старт программы") Highlight(m_t, 1, QTABLE_NO_INDEX, RGB(149,200,216), RGB(0,0,0), 600) sleep(10) --дублируем в логи DirectionSaveFile=tostring("C:\\files\\logs.csv") my_csv=io.open(DirectionSaveFile,"a+") my_csv:write("===========================================\n") my_csv:write("<--- cтарт "..ProgramSmallName..": "..ProgramName.." --->\n") my_csv:write("<--- версия: "..ProgramVersion.." --->\n") my_csv:write('Дата запуска: '..os.date("%d.%m.%Y").."\n") my_csv:write('Время старта: '..os.date("%X",os.time()).."\n") my_csv:flush() my_csv:close() sleep(10) ind = 0 -- счетчик выключения программы indtwo = 0 -- индикатор первой итерации MM = 0 -- запись минут indtre = 0 -- счетчик для отключения по таймеру while myRun do -- обработка таймера старта datetime = os.date("!*t",os.time()) mHou = datetime.hour + 3 mMin = datetime.min mSec = datetime.sec TimerVar = mHou * 3600 + mMin * 60 + mSec cnct = isConnected() if indtwo == 0 then DirectionSaveFile=tostring("C:\\files\\logs.csv") my_csv=io.open(DirectionSaveFile,"a+") if cnct == 1 then if ind >= 0 then message(ProgramSmallName.." <-- Связь с сервером на старте установлена -->", 2) my_csv:write(ProgramSmallName.." <-- Связь с сервером на старте установлена -->\n") SetCell(m_t, 1, 3, "Связь с сервером на старте установлена") Highlight(m_t, 1, 3, RGB(149, 200, 216), RGB(0,0,0), 600) sleep(10) end else message(ProgramSmallName.." <-- Связь с сервером на старте разорвана -->", 3) my_csv:write(ProgramSmallName.." <-- Связь с сервером на старте разорвана -->\n") SetCell(m_t, 1, 3, "Связь с сервером на старте разорвана") Highlight(m_t, 1, 3, RGB(160, 160, 160), RGB(0,0,0), 600) ind = -1 OnStop() myRun = false end my_csv:flush() my_csv:close() sleep(10) end if ind == -1 and cnct == 1 then DirectionSaveFile=tostring("C:\\files\\logs.csv") my_csv=io.open(DirectionSaveFile,"a+") if mMin<10 then stxtone="0" else stxtone="" end if mSec<10 then stxttwo="0" else stxttwo="" end message(ProgramSmallName..": Связь восстановлена в "..mHou..":"..stxtone..mMin..":"..stxttwo..mSec, 2) my_csv:write(ProgramSmallName..": Связь восстановлена в "..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.."\n") SetCell(m_t, 1, 3, "Связь восстановлена в "..mHou..":"..stxtone..mMin..":"..stxttwo..mSec) Highlight(m_t, 1, 3, RGB(149, 200, 216), RGB(0,0,0), 600) my_csv:flush() my_csv:close() sleep(10) ind = 0 indtwo = 0 MM = 0 end if cnct == 0 and indtwo == 1 then if mMin<10 then stxtone="0" else stxtone="" end if mSec<10 then stxttwo="0" else stxttwo="" end DirectionSaveFile=tostring("C:\\files\\logs.csv") my_csv=io.open(DirectionSaveFile,"a+") message(ProgramSmallName..": <-- Связь с сервером прервана "..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.." -->", 3) message(ProgramSmallName.." Переход в спящий режим, ждём возобновления связи", 3) SetCell(m_t, 1, 3, "Связь с сервером прервана "..mHou..":"..stxtone..mMin..":"..stxttwo..mSec) Highlight(m_t, 1, 3, RGB(160, 160, 160), RGB(0,0,0), 600) my_csv:write(ProgramSmallName..": <-- Связь с сервером прервана "..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.." -->\n") my_csv:write(ProgramSmallName.." Переход в спящий режим, ждём возобновления связи\n") my_csv:flush() my_csv:close() sleep(10) ind = -1 indtwo = -1 end if indtwo == 0 then MM = TimerVar indtwo = 1 end if mHou >= HTimeOut and mMin >= MTimeOut and cnct == 1 then if mMin<10 then stxtone="0" else stxtone="" end if mSec<10 then stxttwo="0" else stxttwo="" end SetCell(m_t, 1, 3, "Процедура выгрузки данных") Highlight(m_t, 1, 3, RGB(149, 200, 216), RGB(0,0,0), 600) DirectionSaveFile=tostring("C:\\files\\logs.csv") my_cs=io.open(DirectionSaveFile,"a+") message(ProgramSmallName.." Timer downloads: "..mHou..":"..stxtone..tostring(mMin)..":"..stxttwo..tostring(mSec), 2) my_cs:write(ProgramSmallName.." Timer downloads: "..mHou..":"..stxtone..tostring(mMin)..":"..stxttwo..tostring(mSec).."\n") SetCell(m_t, 1, 4, " Timer downloads: "..mHou..":"..stxtone..tostring(mMin)..":"..stxttwo..tostring(mSec)) my_cs:flush() my_cs:close() sleep(10) downloadsdata() ind = 1 end if TimerVar >= (MM + 5*60) then if mMin<10 then stxtone="0" else stxtone="" end if mSec<10 then stxttwo="0" else stxttwo="" end --открываем файл для записи логов DirectionSaveFile=tostring("C:\\files\\logs.csv") my_csv=io.open(DirectionSaveFile,"a+") if cnct == 1 then message(ProgramSmallName.." timer ["..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.."]") -- фиксируем логи my_csv:write(ProgramSmallName.." timer ["..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.."]\n") SetCell(m_t, 1, 3, "режим ожидания ["..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.."]") Highlight(m_t, 1, 3, RGB(149, 200, 216), RGB(0,0,0), 600) sleep(10) end if cnct == 0 then message(ProgramSmallName.." timer ["..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.."] без сервера", 3) -- фиксируем логи my_csv:write(ProgramSmallName.." timer ["..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.."] без сервера\n") SetCell(m_t, 1, 3, " режим ожидания ["..mHou..":"..stxtone..mMin..":"..stxttwo..mSec.."] без сервера") Highlight(m_t, 1, 3, RGB(160, 160, 160), RGB(0,0,0), 600) sleep(10) end -- закрываем файл my_csv:flush() my_csv:close() sleep(10) MM = TimerVar end if ind == 1 then OnStop() end -- счетчик в базовом варианте будет выставлен на 7 часов if indtre >= indTimer then ind = 2 OnStop() end --ожидание 15 секунд для разгрузки процессора sleep(15000) indtre = indtre + 1 end end
Файл записывает логи по которым можно смотреть всё ли прошло нормально в logs.csv.
В процессе выгрузки если получены нулевые значения, то скрипт ожидает 100 мс и перезапрашивает данные, если за 10 итераций ничего не получено (значит ничего и нет), то переходит далее. В списке (tickers) всегда есть бумаги, по которым торги не проводятся, но биржа их при этом отражает как торгуемые (через iss запросы они аналогично выгружаются с нулевыми объемами, но индикативными ценами).
Предусмотрено, что может отключаться сервер, скрипт ждет восстановления связи. Если далее связь возобновляется скрипт продолжает дожидаться нужного времени и штатно делает выгрузку. Если через 7 часов после старта (я запускаю в начале 6го) выгрузка не состоялась скрипт по таймеру (indTimer) выключает работу. Если ваш компьютер уходит в спящий режим, то скорее всего и терминал «засыпает». Поэтому у меня стоит в настройках никогда не уходить в спящий режим, чтобы всё корректно работало.
Результаты записываются в файлы downloads с датой и временем выгрузки в названии файла всё в той же C:\files\ директории.
Теги: выгрузки котировок на конец дня, qlua выгрузка из торгового терминала, кружок авиамоделизма.
Зачем такое оригинальное решение если можно просто получить список тикеров через getClassSecurities по коду TQBR?
Делал еще в купайле — там функция выдавала не строку, а список, который потом в цикле перебирался как то так
sec_code_list = get_class_securities(class_code)
for sec_code in sec_code_list
и шел по всем тикерам.
Нужно будет покрутить выдачу разными способами, спасибо!
В терминале сейчас посчитал да, ограничение на выдачу сообщения в 899 символов получается. Сама строка по запросу больше 1200 выходит, вот и отсекается.
В таком варианте проходит по всем тикерам в строке:
sec_list = getClassSecurities(«TQBR»)
for msec in string.gmatch(sec_list, "[^,]+") do
message(msec)
end
Попозже сделаю для желающих пост с обновленным первым скриптом, он теперь в разы компактнее.
https://github.com/morefinances/qlua
smart-lab.ru/blog/917203.php
1.убрать/закомментить ind = 1, ind =2 и OnStop в main.
2. Там же в main заменить в строке
>= на == в обоих случаях. Чтобы скрипт не уходил в вечную загрузку.
При этом если при наступлении времени выгрузки сервер брокера разорвет соединение, то скрипт просто ничего не выгрузит после автоматического переподключения. В оригинале у меня (при >=) выгружает.
После этих правок скрипт сам отключаться не будет и сам же повторно запустится через сутки (я не отключаю терминал). И его всегда можно будет отключить вручную в терминале в панеле скриптов.
Но логи будут обновляться при этом каждые 5 минут. Можете поставить ограничения по времени, чтобы вне торгов логи не сохранялись.
На форуме квика обсуждались разные варианты перезапусков скриптов. Как я понимаю, исправно работающих и красивых решений на qlua пока не нашли.