Блог им. morefinances

Выгрузка данных на конец дня по всем акциям Московской Биржи

Иногда бывает необходимым проанализировать не отдельную бумагу, а рынок в целом.

Кто-то смотрит для этого индексы, кто-то различные сантименты, а мне удобнее проводить анализ по динамике всех бумаг (сколько на дату эмитентов в совокупности растет, сколько бумаг выше своих месячных, квартальных или годовых значений и пр.). Каждый по своему может это использовать далее (как общий фильтр принятия решения для входа в сделку, для составления своих индексов, для анализа динамики своего портфеля – особенно если счетов несколько у разных брокеров и пр.).

Получить котировки на конкретную дату можно через сайт Московской Биржи (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 года:

iss.moex.com/iss/history/engines/stock/markets/shares/boards/TQBR/securities.xml?date=2023-06-27&start=0

iss.moex.com/iss/history/engines/stock/markets/shares/boards/TQBR/securities.xml?date=2023-06-27&start=100

iss.moex.com/iss/history/engines/stock/markets/shares/boards/TQBR/securities.xml?date=2023-06-27&start=200

Уже в 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 выгрузка из торгового терминала, кружок авиамоделизма.

★7
16 комментариев
хорошая работа проделана 
avatar
Paulmarko, спасибо!)
avatar
«Небольшой скрипт на qlua проходит по всем возможным вариантам тикеров»

Зачем такое оригинальное решение если можно просто получить список тикеров через getClassSecurities по коду TQBR?


avatar
quant_trader, да, можно, спасибо. Но полученная строка по запросу getClassSecurities у меня заканчивается уже на тикере RTSB:

 
avatar
alfacentavra, может в msgbox не помещается строка?

Делал еще в купайле — там функция выдавала не строку, а список, который потом в цикле перебирался как то так
sec_code_list = get_class_securities(class_code)
for sec_code in sec_code_list
и шел по всем тикерам.
avatar
quant_trader, да, думаю вы правы. Но когда я пару месяцев назад с этим столкнулся подумал, что опять что-то не работает (в квике это бывает с переходом версий, за давность лет и пр.), поэтому и пришлось изобретать велосипед.
Нужно будет покрутить выдачу разными способами, спасибо!
В терминале сейчас посчитал да, ограничение на выдачу сообщения в 899 символов получается. Сама строка по запросу больше 1200 выходит, вот и отсекается.
avatar
quant_trader, спасибо за наводку! Получилось.

В таком варианте проходит по всем тикерам в строке:

sec_list = getClassSecurities(«TQBR»)

for msec in string.gmatch(sec_list, "[^,]+") do
message(msec)
end 

Попозже сделаю для желающих пост с обновленным первым скриптом, он теперь в разы компактнее.

avatar
quant_trader, там только текущие тикеры будут. Архивных нет
avatar
wrmngr, скрипт выгружает из ТТП, архивных там нет в любом варианте.
avatar
quant_trader, а! Так тут скрипт для квика! Я думал для сервера биржи. Не читал весь топик. Ну тогда вы правы
avatar
Надо было уж сразу ссылку на Github 
avatar
Mr Agilent, да, спасибо. Чуть позже буду выкладывать.
avatar
Mr Agilent, по просьбам «зрителей»:) 
https://github.com/morefinances/qlua 
avatar
Для желающих сделал пост с описание выгрузки тикеров штатными методами через getClassSecurities:
smart-lab.ru/blog/917203.php
avatar
А есть скрипт на его перезапуск если лог файл долго не обновляется?
avatar
Evgen Grig, в моем случае просто можно не выключать скрипт, дополнительно поправив:
1.убрать/закомментить ind = 1, ind =2 и OnStop в main.
2. Там же в main заменить в строке
if mHou >= HTimeOut and mMin >= MTimeOut and cnct == 1 then
>= на == в обоих случаях. Чтобы скрипт не уходил в вечную загрузку.
При этом если при наступлении времени выгрузки сервер брокера разорвет соединение, то скрипт просто ничего не выгрузит после автоматического переподключения. В оригинале у меня (при >=) выгружает.

После этих правок скрипт сам отключаться не будет и сам же повторно запустится через сутки (я не отключаю терминал). И его всегда можно будет отключить вручную в терминале в панеле скриптов.
Но логи будут обновляться при этом каждые 5 минут. Можете поставить ограничения по времени, чтобы вне торгов логи не сохранялись.

На форуме квика обсуждались разные варианты перезапусков скриптов. Как я понимаю, исправно работающих и красивых решений на qlua пока не нашли.
avatar

теги блога alfacentavra

....все тэги



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