Jak si vedly investice v roce 2023 a co přinese rok 2024?

15. 1. 2024
Doba čtení: 9 minut

Sdílet

Autor: Depositphotos
Kde bylo možné v uplynulém roce nejlépe vydělat a jaké investice nejvíce ztrácely? Co čekat v roce 2024 od nemovitostí, akcií, dluhopisů, zlata a dalších investic?

V roce 2023 se dařilo takřka všem investicím. Nejvyšší zhodnocení přinesly akcie (kolem 20 %). Nadprůměrné výnosy zaznamenaly také dluhopisové fondy (8–10 %). Ceny bytů poklesly, ale většina nemovitostních fondů díky nájmům reportovala výnosy kolem 5–8 %. Cena zlata stoupla o 13 % a bitcoinu o 155 %. Loni v podstatě nešlo šlápnout vedle, s výjimkou Číny nebo komoditních fondů.

Inflace na ústupu, úrokové sazby budou klesat

Meziroční míra inflace klesla na 6,88 %, přičemž za posledních 11 měsíců už stoupl index spotřebitelských cen o pouhých 0,82 %. Přichází čas na snižování úrokových sazeb ze strany ČNB. Úroky na spořicích účtech budou klesat. Stejně tak i výnosy depozitních fondů nebo protiinflačních dluhopisů. Kdo bude chtít úspory zhodnotit, bude muset investovat.

V lednu výrazně poklesne meziroční míra inflace. Očekává se, že během roku 2024 budou centrální banky postupně snižovat úrokové sazby. Takové prostředí by mělo být příznivé zejména pro dluhopisové fondy, nemovitostní fondy a private equity fondy. Mohlo by se dařit i akciím, ale to je nejisté. Hlavní rizika nyní vnímám hlavně v další eskalaci geopolitického napětí (hrozba blokády Taiwanu, hrozba války v Perském zálivu apod.)

Inflace a úrokové sazby v České republice v letech 2007 - 2023

Inflace a úrokové sazby v České republice v letech 2007–2023

Autor: FINEZ Investment Management, s.r.o.

Akcie zpět na vrcholu

V roce 2023 se velmi dařilo akciím, a to prakticky všude ve světě a napříč všemi sektory. Hodnoty předních akciových indexů se vyšplhaly na stejnou úroveň jako na svých maximech z konce roku 2021. Hodnota globálního akciového indexu MSCI World stoupla v uplynulém roce o 17,6 % (v eurech).

Tabulka: Vývoj na akciových trzích v roce 2023

Vývoj na akciových trzích v roce 2023
MSCI World (v EUR) 18 %
S&P 500 (v USD) 24 %
MSCI Europe (v EUR) 13 %
MSCI China (v EUR) −16 %
PX (v CZK) 18 %

Extrémně nadprůměrně se dařilo zejména americkým technologickým firmám (Apple +48 %, Microsoft +57 %, Alphabet +58 %, Amazon +81 %, Meta Platforms +194 %, NVIDIA +239 %). Také v Japonsku a na Taiwanu v průměru stouply ceny akcií o 26 %. Skvělé zhodnocení o 30 % přinesly i akcie v Polsku. Naopak v Číně loni v průměru klesly ceny akcií o 16 %. V ČR v průměru stouply ceny akcií o 18 %.

Očekávání pro rok 2024 jsou u akcií hodně různorodá. Hodnoty akcií se primárně odvíjí od ziskovosti firem. Těžko odhadovat, co přinese zrovna rok 2024. Můžeme pouze říct, že dlouhodobě s inflací firmám porostou tržby a zisky. Akciové trhy mají prostor k dalšímu růstu. Na druhou stranu v porovnání s úroky u dluhopisů dnes akcie nejsou levné, průměrný hrubý dividendový výnos u akcií z amerického indexu S&P 500 je pouhých 1,5 %.

Vývoj hodnoty indexu S&P 500 a ziskovosti firem v letech 1990 - 2023 .

Vývoj hodnoty indexu S&P 500 a ziskovosti firem v letech 1990–2023 .

Autor: FINEZ Investment Management, s.r.o.

Zlaté časy pro dluhopisy

V letech 2022–2023 centrální banky po celém světě v reakci na vysokou inflaci výrazně zvedly úrokové sazby. To bylo velmi negativní pro starší dluhopisy nesoucí nízké úroky. Zároveň bylo ale po mnoha letech možné si pořídit dluhopisy se zajímavými úrokovými výnosy a zafixovat si tak na několik let vysoké úroky. Nyní už úroky u dluhopisů začaly klesat, neboť inflace už je na ústupu a úrokové sazby by měly jít dolů.

Vývoj výnosu do splatnosti desetiletých státních dluhopisů ČR.

Vývoj výnosu do splatnosti desetiletých státních dluhopisů ČR.

Autor: FINEZ Investment Management, s.r.o.

Výnos do splatnosti desetiletých českých státních dluhopisů stoupl v roce 2022 až na 6 % p.a., ke konci loňského roku už ale klesl pod 4 % p.a. Podobně v USA za poslední tři měsíce klesl výnos do splatnosti desetiletých dluhopisů z 5 % p.a. na 4 % p.a. a v Německu ze 3 % p.a. na 2 % p.a.

Tabulka: Výnos do splatnosti 10letých státních dluhopisů

Výnos do splatnosti 10letých státních dluhopisů
Německo 2,0 %
USA 3,9 %
Česká republika  3,7 %

Z klesajících úrokových výnosů dluhopisů nyní těží fondy. Dluhopisové fondy v uplynulém roce v průměru přinesly zhodnocení kolem 8–10 %. Rizikovější high yield dluhopisové fondy nebo dluhopisové fondy zaměřené na rozvíjející se trhy měly dokonce výnosy přes 10 %.

Očekává se, že během roku 2024 budou centrální banky postupně snižovat úrokové sazby. Česká národní banka snížila repo sazbu už v prosinci ze 7 % na 6,75 %. Úroky u dluhopisů s delší splatností už nějakou dobu klesají a nyní budou klesat i úroky u dluhopisů s kratší splatností. S klesajícími úroky u nových dluhopisů roste cena starších dluhopisů nesoucích vyšší úrok.

Investoři tak mají nyní v dluhopisových fondech unikátní příležitost inkasovat celkem vysoké úroky, a ještě vydělat na růstu cen dluhopisů. Rok 2024 by proto měl být velmi pozitivní pro dluhopisové fondy. Největší výnosový potenciál mají rizikovější high yield dluhopisy, kde se stále výnos do splatnosti pohybuje kolem 8 % p.a.

Ceny bytů poklesly, ale jen nepatrně

Podle Českého statistického úřadu poklesly v ČR ceny bytů (realizované ceny) za první tři čtvrtletí v průměru o 6 %. Údaje za čtvrté čtvrtletí ještě nebyly zveřejněny, ale podle signálů z realitních kanceláří už se na trh vrací kupci a ceny přestaly klesat. Nejvíce poklesly ceny starších a energeticky méně úsporných bytů, mnohdy až o 30 %. Naopak nabídkové ceny novostaveb od developerů klesly v průměru jen o 4 %.

Tabulka: Vývoj cen bytů v roce 2023 (k 30. 9.)

Vývoj cen bytů v roce 2023 (k 30. 9.)
Praha −5,6 %
ČR bez Prahy 6,1 %
Celá ČR −6,0 %

Očekával jsem už dva roky zpátky výrazně větší pokles cen bytů. Ovšem pak začala válka na Ukrajině a přišlo k nám půl milionu lidí, kteří potřebují někde bydlet. Všechny prázdné nájemní byty se zaplnily. Na trhu tak sice poslední dva roky chyběli kupci, ale nebylo tam ani moc prodávajících. Nebýt Ukrajinců, byl by trh zaplavený prázdnými nájemními byty a ceny by klesly o 20–30 %.

Díky klesajícím úrokům u hypoték a zadržené poptávce z posledních dvou let očekávám v roce 2024 výrazné oživení realitního trhu. Ceny bytů pravděpodobně již dosáhly svého dna a začnou pozvolna zase růst. Nicméně domníváme se, že úroky u hypoték zůstanou dlouhodobě zvýšené někde na úrovni 4–5 % p.a., takže na trh se vrátí jen vážní kupci, nikoliv spekulanti. To by mělo držet růst cen bytů na uzdě.

Vývoj realizovaných cen bytů v ČR v letech 2005 - 2023.

Vývoj realizovaných cen bytů v ČR v letech 2005–2023.

Autor: FINEZ Investment Management, s.r.o.

Nemovitostní fondy ustály tlak

Nemovitostní fondy zaměřené na rezidenční i komerční nemovitosti se v roce 2023 potýkali s růstem yieldů a s rostoucími úroky u úvěrů. Přesto díky rostoucím nájmům dokázaly nemovitostní fondy ve většině případů reportovat solidní zhodnocení kolem 5–8 %.

Rok 2024 by měl být velmi příznivý pro nemovitostní fondy. A další roky rovněž. V uplynulých dvou letech nemovitostním fondům díky vysoké inflaci výrazně vyskočily nahoru na straně příjmů nájmy, ale zároveň na straně výdajů úroky u úvěrů. Zatímco úroky by měly postupně klesat, nájmy už zůstanou zvýšené.

Provozní zisky tedy porostou, a to se bude pozitivně promítat i do účetního ocenění spravovaných nemovitostí, které teď bude pár let tak trochu dohánět inflaci. Perspektiva nemovitostních fondů pro příští roky je proto velmi dobrá a měly by nyní zase několik let stabilně porážet inflaci.

Koruna mírně oslabila k euru a posílila k dolaru

S očekáváním poklesu úrokových sazeb v ČR začala od léta pozvolna oslabovat koruna vůči euru, neboť Evropská centrální banka naopak v létě ještě zvedala sazby. Úrokový diferenciál mezi korunou a eurem se během roku postupně snižoval a koruna přestala být pro investory tak atraktivní. Dokud korunové vklady (a dluhopisy) nesly 6 % ročně a eurové v podstatě žádný úrok, byla koruna v Evropě velmi žádaná.

Tabulka: Vybrané kurzy měn v roce 2023

Vybrané kurzy měn v roce 2023
EUR/CZK 24,70 2,50 %
USD/CZK 22,40 −1,10 %
GBP/CZK 28,50 4,60 %

Další vývoj české měny vůči euru se bude do značné míry odvíjet od toho, jak synchronizovaně budou centrální banky snižovat úrokové sazby. Bude-li ČNB snižovat sazby dříve než ECB, bude se úrokový diferenciál dál snižovat a koruna může dál oslabovat nad 25 korun za euro. Pakliže ale ECB začne brzy rovněž sazby snižovat a korunové vklady si zachovají vyšší úroky než vklady v eurech, má naopak koruna potenciál znovu posílit pod 24 korun za jedno euro.

Vývoj kurzu EUR/CZK.

Vývoj kurzu EUR/CZK.

Autor: FINEZ Investment Management, s.r.o.

Energie prudce zlevňují

Většina komodit na burze v uplynulém roce výrazně zlevnila. Cena ropy klesla o 8,5 %. Díky tomu zlevnily i pohonné hmoty na čerpacích stanicích. Zejména ale výrazně poklesla cena elektřiny a plynu na burze Power Exchange Central Europe. Díky levnějším energiím a pohonným hmotám klesá i inflace.

Tabulka: Vývoj cen komodit v roce 2023

Tabulka: Vývoj cen komodit v roce 2023
Zlato (USD/Oz) 2063 13 %
Ropa WTI (USD/b) 72 −8 %
Elektřina (EUR/MWh) 98 −55 %

O co jsou levnější samotné energie, o to bohužel od ledna stoupla cena za distribuci a poplatky za OZE, takže ve finále se na fakturách zlevnění nedočkáme. Nicméně pozitivní je, že se Evropa dokázala odtrhnout od závislosti na ruských zdrojích a energetická krize se nyní zdá být zažehnána, pokud se nestane nic dalšího mimořádného, co by znovu ohrozilo dodávky ropy a plynu do Evropy (např. válka v Perském zálivu apod.)

Vývoj ceny elekřiny (EUR/MWh).

Vývoj ceny elektřiny (EUR/MWh).

Autor: FINEZ Investment Management, s.r.o.

Zlato i bitcoin výrazně podražily

Cena bitcoinu se v prosinci přešvihla přes 40 tisíc dolarů a za celý rok 2023 stoupla cena bitcoinu o 155 %. Cena bitcoinu rostla zejména v očekávaní schválení prvních bitcoinových ETF na americkém trhu. Cenu podporuje také plánovaný halving, který by měl nastat letos v dubnu.

Tabulka: Vývoj cen drahých kovů a kryptoměn v roce 2023

Vývoj cen drahých kovů a kryptoměn v roce 2023
Zlato (USD/Oz) 2063 13 %
Stříbro (USD/Oz 23,78 −1 %
Bitcoin (USD/BTC) 42 265 155 %

Cena zlata v důsledku inflace a geopolitického napětí v roce 2023 stoupla o 13 % na nové maximum. V prosinci se dokonce krátce dostala nad 2100 dolarů za unci. Naopak cena stříbra v prosinci o 6 % poklesla, a ke konci roku tak byla zhruba na stejné úrovni jako před rokem.

Cena zlata (USD/Oz)

Cena zlata (USD/Oz)

Autor: FINEZ Investment Management, s.r.o.

Půda, private equity a jiné FKI

Nadprůměrné zhodnocení za rok 2023 očekávám od fondů zaměřených na zemědělskou půdu, obnovitelné zdroje energií, pohledávky, úvěry a také od některých private equity fondů. Naopak slabší hospodářské výsledky budou zcela jistě reportovat mnohé realitní a developerské fondy.

U většiny FKI fondů ale zatím známe neúplné výsledky za 10–11 měsíců nebo tři čtvrtletí loňského roku. Ocenění k 31. 12. zveřejní až po auditech, tj. zpravidla v březnu/dubnu. Přehled a výsledky různě zaměřených FKI fondů v ČR zobrazuje specializovaný portál FKI-fondy.cz.

skoleni_12_6

Pro rok 2024 vidím zajímavý potenciál u private equity fondů. Zde se totiž bude podobně jako u nemovitostních fondů pozitivně do ocenění projevovat pokles úrokových sazeb, jednak díky levnějším úvěrům, ale také v rámci samotných oceňovacích modelů při diskontování budoucího cash flow.

Disclaimer: Článek a informace v něm obsažené nejsou investičním doporučením či analýzou investičních příležitostí ani nepředstavují veřejnou nabídku investičních nástrojů ani jakoukoli jinou nabídku či výzvu vůči veřejnosti k transakci s investičními nástroji. Data uvedená v článku pochází z materiálů FINEZ Investment Management a jsou platná k 31. 12. 2023, není-li uvedeno jinak.

Autor článku

Autor je privátní investiční poradce a ředitel FINEZ Investment Management.

'; document.getElementById('preroll-iframe').onload = function () { setupIframe(); } prerollContainer = document.getElementsByClassName('preroll-container-iframe')[0]; } function setupIframe() { prerollDocument = document.getElementById('preroll-iframe').contentWindow.document; let el = prerollDocument.createElement('style'); prerollDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:20px;right:25px}"; videoContent = prerollDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('PREROLL sound allowed'); // setUpIMA(true); videoContent.volume = 1; videoContent.muted = false; setUpIMA(); }).catch(function () { console.log('PREROLL sound forbidden'); videoContent.volume = 0; videoContent.muted = true; setUpIMA(); }); } } function setupDimensions() { prerollWidth = Math.min(iinfoPrerollPosition.offsetWidth, 480); prerollHeight = Math.min(iinfoPrerollPosition.offsetHeight, 320); } function setUpIMA() { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Preroll advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = prerollWidth; // adsRequest.linearAdSlotHeight = prerollHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. prerollDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( prerollDocument.getElementById('adContainer'), videoContent); } function unmutePrerollAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); prerollDocument.getElementById('adMuteBtn').innerHTML = ''; } } function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(prerollWidth, prerollHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } function onAdEvent(adEvent) { const ad = adEvent.getAd(); console.log('Preroll event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: if (!ad.isLinear()) { videoContent.play(); } prerollDocument.getElementById('adContainer').style.width = '100%'; prerollDocument.getElementById('adContainer').style.maxWidth = '640px'; prerollDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); if (ad.isLinear()) { intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } prerollDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (prerollLastError === 303) { playYtVideo(); } break; case google.ima.AdEvent.Type.COMPLETE: if (ad.isLinear()) { clearInterval(intervalTimer); } playYtVideo(); break; } } function onAdError(adErrorEvent) { console.log(adErrorEvent.getError()); prerollLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { playYtVideo(); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoPrerollPosition.remove(); playPrerollAd(); } else { return false; } adVolume = 1; return true; } function onContentPauseRequested() { videoContent.pause(); } function onContentResumeRequested() { videoContent.play(); } function onActiveView() { if (prerollContainer) { const containerOffset = prerollContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (prerollPaused) { adsManager.resume(); prerollPaused = false; } return true; } else { if (!prerollPaused) { adsManager.pause(); prerollPaused = true; } } } return false; } function playYtVideo() { iinfoPrerollPosition.remove(); youtubeIframe.style.display = 'block'; youtubeIframe.src += '&autoplay=1&mute=1'; } }
Upozorníme vás na články, které by vám neměly uniknout (maximálně 2x týdně).
'; document.getElementById('outstream-iframe').onload = function () { setupIframe(); } replayScreen = document.getElementById('iinfoOutstreamReplay'); iinfoOutstreamPosition = document.getElementById('iinfoOutstreamPosition'); outstreamContainer = document.getElementsByClassName('outstream-container')[0]; setupReplayScreen(); } function setupIframe() { outstreamDocument = document.getElementById('outstream-iframe').contentWindow.document; let el = outstreamDocument.createElement('style'); outstreamDocument.head.appendChild(el); el.innerText = "#adContainer>div:nth-of-type(1),#adContainer>div:nth-of-type(1) > iframe { width: 99% !important;height: 99% !important;max-width: 100%;}#videoContent,body{ width:100vw;height:100vh}body{ font-family:'Helvetica Neue',Arial,sans-serif}#videoContent{ overflow:hidden;background:#000}#adMuteBtn{ width:35px;height:35px;border:0;background:0 0;display:none;position:absolute;fill:rgba(230,230,230,1);bottom:-5px;right:25px}"; videoContent = outstreamDocument.getElementById('contentElement'); videoContent.style.display = 'none'; videoContent.volume = 1; videoContent.muted = false; if ( location.href.indexOf('rejstriky.finance.cz') !== -1 || location.href.indexOf('finance-rejstrik') !== -1 || location.href.indexOf('firmy.euro.cz') !== -1 || location.href.indexOf('euro-rejstrik') !== -1 || location.href.indexOf('/rejstrik/') !== -1 || location.href.indexOf('/rejstrik-firem/') !== -1) { outstreamDirectPlayed = true; soundAllowed = true; iinfoVastUrlIndex = 0; } if (!outstreamDirectPlayed) { console.log('OUTSTREAM direct'); setUpIMA(true); } else { if (soundAllowed) { const playPromise = videoContent.play(); if (playPromise !== undefined) { playPromise.then(function () { console.log('OUTSTREAM sound allowed'); setUpIMA(false); }).catch(function () { console.log('OUTSTREAM sound forbidden'); renderBanner(); }); } } else { renderBanner(); } } } function getWrapper() { let articleWrapper = document.querySelector('.rs-outstream-placeholder'); // Outstream Placeholder from RedSys manipulation if (articleWrapper && articleWrapper.style.display !== 'block') { articleWrapper.innerHTML = ""; articleWrapper.style.display = 'block'; } // Don't render OutStream on homepages if (articleWrapper === null) { if (document.querySelector('body.p-index')) { return null; } } if (articleWrapper === null) { articleWrapper = document.getElementById('iinfo-outstream'); } if (articleWrapper === null) { articleWrapper = document.querySelector('.layout-main__content .detail__article p:nth-of-type(6)'); } if (articleWrapper === null) { // Euro, Autobible, Zdravi articleWrapper = document.querySelector('.o-article .o-article__text p:nth-of-type(6)'); } if (articleWrapper === null) { articleWrapper = document.getElementById('sidebar'); } if (!articleWrapper) { console.error("Outstream wrapper of article was not found."); } return articleWrapper; } function setupDimensions() { outstreamWidth = Math.min(iinfoOutstreamPosition.offsetWidth, 480); outstreamHeight = Math.min(iinfoOutstreamPosition.offsetHeight, 320); } /** * Sets up IMA ad display container, ads loader, and makes an ad request. */ function setUpIMA(direct) { google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true); google.ima.settings.setLocale('cs'); google.ima.settings.setNumRedirects(10); // Create the ad display container. createAdDisplayContainer(); // Create ads loader. adsLoader = new google.ima.AdsLoader(adDisplayContainer); // Listen and respond to ads loaded and error events. adsLoader.addEventListener( google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onAdsManagerLoaded, false); adsLoader.addEventListener( google.ima.AdErrorEvent.Type.AD_ERROR, onAdError, false); // An event listener to tell the SDK that our content video // is completed so the SDK can play any post-roll ads. const contentEndedListener = function () { adsLoader.contentComplete(); }; videoContent.onended = contentEndedListener; // Request video ads. const adsRequest = new google.ima.AdsRequest(); if (direct) { adsRequest.adTagUrl = directVast; console.log('Outstream DIRECT CAMPAING advert: ' + directVast); videoContent.muted = true; videoContent.volume = 0; outstreamDirectPlayed = true; } else { adsRequest.adTagUrl = iinfoVastUrls[iinfoVastUrlIndex]; console.log('Outstream advert: ' + iinfoVastUrls[iinfoVastUrlIndex]); videoContent.muted = false; videoContent.volume = 1; } // Specify the linear and nonlinear slot sizes. This helps the SDK to // select the correct creative if multiple are returned. // adsRequest.linearAdSlotWidth = outstreamWidth; // adsRequest.linearAdSlotHeight = outstreamHeight; adsRequest.nonLinearAdSlotWidth = 0; adsRequest.nonLinearAdSlotHeight = 0; adsLoader.requestAds(adsRequest); } function setupReplayScreen() { replayScreen.addEventListener('click', function () { iinfoOutstreamPosition.remove(); iinfoVastUrlIndex = 0; outstreamInit(); }); } /** * Sets the 'adContainer' div as the IMA ad display container. */ function createAdDisplayContainer() { // We assume the adContainer is the DOM id of the element that will house // the ads. outstreamDocument.getElementById('videoContent').style.display = 'none'; adDisplayContainer = new google.ima.AdDisplayContainer( outstreamDocument.getElementById('adContainer'), videoContent); } function unmuteAdvert() { adVolume = !adVolume; if (adVolume) { adsManager.setVolume(0.3); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } else { adsManager.setVolume(0); outstreamDocument.getElementById('adMuteBtn').innerHTML = ''; } } /** * Loads the video content and initializes IMA ad playback. */ function playAds() { // Initialize the container. Must be done through a user action on mobile // devices. videoContent.load(); adDisplayContainer.initialize(); // setupDimensions(); try { // Initialize the ads manager. Ad rules playlist will start at this time. adsManager.init(1920, 1080, google.ima.ViewMode.NORMAL); // Call play to start showing the ad. Single video and overlay ads will // start at this time; the call will be ignored for ad rules. adsManager.start(); // window.addEventListener('resize', function (event) { // if (adsManager) { // setupDimensions(); // adsManager.resize(outstreamWidth, outstreamHeight, google.ima.ViewMode.NORMAL); // } // }); } catch (adError) { // An error may be thrown if there was a problem with the VAST response. // videoContent.play(); } } /** * Handles the ad manager loading and sets ad event listeners. * @param { !google.ima.AdsManagerLoadedEvent } adsManagerLoadedEvent */ function onAdsManagerLoaded(adsManagerLoadedEvent) { // Get the ads manager. const adsRenderingSettings = new google.ima.AdsRenderingSettings(); adsRenderingSettings.restoreCustomPlaybackStateOnAdBreakComplete = true; adsRenderingSettings.loadVideoTimeout = 12000; // videoContent should be set to the content video element. adsManager = adsManagerLoadedEvent.getAdsManager(videoContent, adsRenderingSettings); // Add listeners to the required events. adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onAdError); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, onContentPauseRequested); adsManager.addEventListener( google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, onContentResumeRequested); adsManager.addEventListener( google.ima.AdEvent.Type.ALL_ADS_COMPLETED, onAdEvent); // Listen to any additional events, if necessary. adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, onAdEvent); adsManager.addEventListener(google.ima.AdEvent.Type.COMPLETE, onAdEvent); playAds(); } /** * Handles actions taken in response to ad events. * @param { !google.ima.AdEvent } adEvent */ function onAdEvent(adEvent) { // Retrieve the ad from the event. Some events (for example, // ALL_ADS_COMPLETED) don't have ad object associated. const ad = adEvent.getAd(); console.log('Outstream event: ' + adEvent.type); switch (adEvent.type) { case google.ima.AdEvent.Type.LOADED: // This is the first event sent for an ad - it is possible to // determine whether the ad is a video ad or an overlay. if (!ad.isLinear()) { // Position AdDisplayContainer correctly for overlay. // Use ad.width and ad.height. videoContent.play(); } outstreamDocument.getElementById('adContainer').style.width = '100%'; outstreamDocument.getElementById('adContainer').style.maxWidth = '640px'; outstreamDocument.getElementById('adContainer').style.height = '360px'; break; case google.ima.AdEvent.Type.STARTED: window.addEventListener('scroll', onActiveView); // This event indicates the ad has started - the video player // can adjust the UI, for example display a pause button and // remaining time. if (ad.isLinear()) { // For a linear ad, a timer can be started to poll for // the remaining time. intervalTimer = setInterval( function () { // Example: const remainingTime = adsManager.getRemainingTime(); // adsManager.pause(); }, 300); // every 300ms } outstreamDocument.getElementById('adMuteBtn').style.display = 'block'; break; case google.ima.AdEvent.Type.ALL_ADS_COMPLETED: if (ad.isLinear()) { clearInterval(intervalTimer); } if (outstreamLastError === 303) { if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } } break; case google.ima.AdEvent.Type.COMPLETE: // This event indicates the ad has finished - the video player // can perform appropriate UI actions, such as removing the timer for // remaining time detection. if (ad.isLinear()) { clearInterval(intervalTimer); } if (isBanner) { renderBanner(); } else { replayScreen.style.display = 'flex'; } break; } } /** * Handles ad errors. * @param { !google.ima.AdErrorEvent } adErrorEvent */ function onAdError(adErrorEvent) { // Handle the error logging. console.log(adErrorEvent.getError()); outstreamLastError = adErrorEvent.getError().getErrorCode(); if (!loadNext()) { renderBanner(); } } function renderBanner() { if (isBanner) { console.log('Outstream: Render Banner'); iinfoOutstreamPosition.innerHTML = ""; iinfoOutstreamPosition.style.height = "330px"; iinfoOutstreamPosition.appendChild(bannerDiv); } else { console.log('Outstream: Banner is not set'); } } function loadNext() { iinfoVastUrlIndex++; if (iinfoVastUrlIndex < iinfoVastUrls.length) { iinfoOutstreamPosition.remove(); outstreamInit(); } else { return false; } adVolume = 1; return true; } /** * Pauses video content and sets up ad UI. */ function onContentPauseRequested() { videoContent.pause(); // This function is where you should setup UI for showing ads (for example, // display ad timer countdown, disable seeking and more.) // setupUIForAds(); } /** * Resumes video content and removes ad UI. */ function onContentResumeRequested() { videoContent.play(); // This function is where you should ensure that your UI is ready // to play content. It is the responsibility of the Publisher to // implement this function when necessary. // setupUIForContent(); } function onActiveView() { if (outstreamContainer) { const containerOffset = outstreamContainer.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight/1 && containerOffset.bottom > 0.0) { if (outstreamPaused) { adsManager.resume(); outstreamPaused = false; } return true; } else { if (!outstreamPaused) { adsManager.pause(); outstreamPaused = true; } } } return false; } let outstreamInitInterval; if (typeof cpexPackage !== "undefined") { outstreamInitInterval = setInterval(tryToInitializeOutstream, 100); } else { const wrapper = getWrapper(); if (wrapper) { let outstreamInitialized = false; window.addEventListener('scroll', () => { if (!outstreamInitialized) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { outstreamInit(); outstreamInitialized = true; } } }); } } function tryToInitializeOutstream() { const wrapper = getWrapper(); if (wrapper) { const containerOffset = wrapper.getBoundingClientRect(); const windowHeight = window.innerHeight; if (containerOffset.top < windowHeight / 1 && containerOffset.bottom > 0.0) { if (cpexPackage.adserver.displayed) { clearInterval(outstreamInitInterval); outstreamInit(); } } } else { clearInterval(outstreamInitInterval); } } }
OSZAR »