Práca s databázou – akcie z indexu S&P 500

Vývoj cien a regresná analýza denných výnosov vybraných akcií

Autor

Livia

Publikované

1. januára 2026

Predmet výskumu

Akcie veľkých amerických spoločností často reagujú na podobné ekonomické správy, ale ich ceny sa nemusia meniť rovnako. V tejto práci skúmam desať vybraných akcií z indexu S&P 500: NVDA, AAPL, MSFT, AMZN, GOOGL, GOOG, META, AVGO, TSLA a BRK.B. Ide o desať akciových titulov deviatich spoločností, pretože GOOGL a GOOG sú dve triedy akcií spoločnosti Alphabet.

Najprv porovnám vývoj ich cien. Hlavná výskumná otázka však znie: Ako súvisí denný výnos akcie NVIDIA (NVDA) s priemerným denným výnosom ostatných deviatich akcií? Následne vyskúšam, či pridaním vnútrodenného cenového rozpätia a zmeny objemu obchodov získam model, ktorý lepšie opisuje pozorované výnosy. Použijem základné funkcie lm(), tidy(), glance() a `predict().

Príprava údajov

Údaje som získala v Google Sheets pomocou funkcie GOOGLEFINANCE a spracovala do excelového súboru S&P500.xlsx. Jeden riadok predstavuje jednu akciu v jeden obchodný deň. Premenné Open, High, Low a Close sú ceny v USD, Volume je objem obchodovania a Name je označenie akcie.

Kód
library(readxl)
library(dplyr)
library(tidyr)
library(ggplot2)
library(broom)
library(knitr)

# Excel je uložený na pracovnej ploche v priečinku modregmet/ekonometria.
# Na Windows môže mať pracovná plocha názov Desktop alebo byť v OneDrive.
domov <- Sys.getenv("USERPROFILE")
cesty <- c(
  file.path(domov, "Desktop", "modregmet", "ekonometria", "S&P500.xlsx"),
  file.path(domov, "OneDrive", "Desktop", "modregmet", "ekonometria", "S&P500.xlsx"),
  file.path(domov, "OneDrive", "Pracovná plocha", "modregmet", "ekonometria", "S&P500.xlsx"),
  file.path(domov, "Pracovná plocha", "modregmet", "ekonometria", "S&P500.xlsx")
)
cesta <- cesty[file.exists(cesty)][1]
if (is.na(cesta)) stop("Excel S&P500.xlsx sa nenašiel v priečinku modregmet/ekonometria na pracovnej ploche.")

udaje <- read_excel(cesta)
names(udaje) <- trimws(names(udaje))

# Dátum môže byť v Exceli uložený ako dátum alebo ako text.
if (inherits(udaje$Date, c("POSIXct", "POSIXt", "Date"))) {
  udaje$Date <- as.Date(udaje$Date)
} else if (is.numeric(udaje$Date)) {
  udaje$Date <- as.Date(udaje$Date, origin = "1899-12-30")
} else {
  udaje$Date <- as.Date(sub(" .*", "", as.character(udaje$Date)),
                        tryFormats = c("%d.%m.%Y", "%Y-%m-%d"))
}
udaje <- udaje |> arrange(Name, Date)

# Denná percentuálna zmena zatváracej ceny
udaje <- udaje |>
  group_by(Name) |>
  mutate(vynos = 100 * (Close / lag(Close) - 1),
         index = 100 * Close / first(Close),
         rozpatie = 100 * (High - Low) / Open,
         zmena_objemu = 100 * (Volume / lag(Volume) - 1)) |>
  ungroup()

Vzorka obsahuje 4990 záznamov pre 10 akcií od 21.10.2024 do 16.10.2026. Každá akcia má 499 cenových pozorovaní. Denný výnos počítam ako percentuálnu zmenu zatváracej ceny oproti predchádzajúcemu obchodnému dňu. Prvý deň každej akcie nemá vypočítaný výnos, pretože chýba predchádzajúca cena.

Kód
kontrola <- data.frame(
  Premenna = c("Date", "Open", "High", "Low", "Close", "Volume", "Name", "vynos"),
  Pocet_NA = c(sum(is.na(udaje$Date)), sum(is.na(udaje$Open)),
               sum(is.na(udaje$High)), sum(is.na(udaje$Low)),
               sum(is.na(udaje$Close)), sum(is.na(udaje$Volume)),
               sum(is.na(udaje$Name)), sum(is.na(udaje$vynos)))
)
kable(kontrola, col.names = c("Premenná", "Chýbajúce hodnoty"),
      caption = "Kontrola chýbajúcich hodnôt")
Kontrola chýbajúcich hodnôt
Premenná Chýbajúce hodnoty
Date 0
Open 0
High 0
Low 0
Close 0
Volume 0
Name 0
vynos 10

Chýbajúce hodnoty výnosu na prvých dňoch sú prirodzeným dôsledkom výpočtu. Pri regresii použijem iba dni, pri ktorých sú všetky potrebné hodnoty dostupné.

Vývoj cien a porovnanie akcií

Na porovnanie cien používam index, pri ktorom prvá cena každej akcie predstavuje hodnotu 100. Takto sa dajú porovnať aj akcie s rôznymi počiatočnými cenami.

Kód
ggplot(udaje, aes(Date, index, color = Name)) +
  geom_line(linewidth = 0.6) +
  geom_hline(yintercept = 100, linetype = "dashed", color = "grey60") +
  labs(x = NULL, y = "Index ceny (začiatok = 100)", color = "Akcia") +
  theme_minimal() +
  theme(legend.position = "bottom")

Relatívny vývoj zatváracích cien (prvá cena = 100).
Kód
poradie <- udaje |>
  group_by(Name) |>
  summarise(zmena = 100 * (last(Close) / first(Close) - 1),
            volatilita = sd(vynos, na.rm = TRUE), .groups = "drop") |>
  arrange(desc(zmena))
kable(poradie, digits = 2,
      col.names = c("Akcia", "Zmena ceny (%)", "Volatilita výnosov (%)"),
      caption = "Porovnanie cenovej výkonnosti a denného kolísania")
Porovnanie cenovej výkonnosti a denného kolísania
Akcia Zmena ceny (%) Volatilita výnosov (%)
GOOGL 107.59 2.02
GOOG 103.20 1.99
AVGO 82.65 3.30
TSLA 62.08 3.75
NVDA 49.00 2.76
AAPL 45.24 1.81
AMZN 32.23 2.15
MSFT 26.33 1.84
META 23.33 2.49
BRK.B 14.62 1.08

Najväčší rast ceny dosiahla GOOGL (107.6 %) a najmenší BRK.B (14.6 %). Volatilita je smerodajná odchýlka denných výnosov: čím je vyššia, tým viac výnosy počas obdobia kolísali. Uvedené zmeny vychádzajú z cien Close, nie z celkového výnosu vrátane dividend.

Graf závislosti výnosov NVDA

Pre každý deň vypočítam jednoduchý priemer výnosov ostatných deviatich akcií. Tento priemer slúži ako porovnávací ukazovateľ; nie je to oficiálny výnos indexu S&P 500. NVDA do priemeru nezahŕňam, aby nevysvetľovala sama seba.

Kód
siroke <- udaje |>
  select(Date, Name, vynos) |>
  pivot_wider(names_from = Name, values_from = vynos)

siroke$trh <- rowMeans(siroke |>
  select(-Date, -NVDA), na.rm = TRUE)

nvda <- udaje |>
  filter(Name == "NVDA") |>
  select(Date, vynos, rozpatie, zmena_objemu) |>
  left_join(siroke |> select(Date, trh), by = "Date") |>
  drop_na()
Kód
ggplot(nvda, aes(trh, vynos)) +
  geom_point(color = "steelblue", alpha = 0.55) +
  geom_smooth(method = "lm", se = FALSE, color = "firebrick") +
  labs(x = "Priemerný výnos ostatných akcií (%)",
       y = "Denný výnos NVDA (%)") +
  theme_minimal()

Denný výnos NVDA a priemerný výnos ostatných akcií.

Každý bod predstavuje jeden obchodný deň. Stúpajúca regresná priamka znamená, že vyšší výnos ostatných akcií sa v dátach spravidla spája aj s vyšším výnosom NVDA. Rozptyl bodov ukazuje, že nejde o dokonalý vzťah.

Jednoduchá lineárna regresia

Odhadnem model

\[ R_{NVDA,t} = \beta_0 + \beta_1 R_{ostatne,t} + \varepsilon_t. \]

Vysvetľovaná premenná je denný výnos NVDA a vysvetľujúca premenná je priemerný denný výnos ostatných akcií. Koeficient \(\beta_1\) vyjadruje, o koľko percentuálnych bodov sa v priemere líši výnos NVDA pri rozdiele jedného percentuálneho bodu vo výnose porovnávacej skupiny.

Kód
model1 <- lm(vynos ~ trh, data = nvda)
tidy(model1) |>
  kable(digits = 3,
        col.names = c("Člen", "Odhad", "Štandardná chyba", "t-hodnota", "p-hodnota"),
        caption = "Jednoduchá regresia výnosu NVDA")
Jednoduchá regresia výnosu NVDA
Člen Odhad Štandardná chyba t-hodnota p-hodnota
(Intercept) -0.007 0.098 -0.071 0.944
trh 1.115 0.064 17.435 0.000

Odhadnutá smernica je 1.115. Keď je výnos ostatných akcií vyšší o jeden percentuálny bod, model spája tento rozdiel s približne 1.115 percentuálneho bodu vyšším výnosom NVDA. Konštanta predstavuje predikovaný výnos NVDA pri nulovom výnose porovnávacej skupiny. Tento vzťah nie je dôkazom príčiny ani zárukou budúceho vývoja.

Viacnásobná lineárna regresia

Do druhého modelu pridám dve premenné, ktoré opisujú obchodovanie s NVDA v tom istom dni: vnútrodenné rozpätie ceny a percentuálnu zmenu objemu obchodov oproti predchádzajúcemu dňu. Cieľom je porovnať, či tieto informácie zlepšujú opis dát, nie predpovedať ich dopredu.

\[ R_{NVDA,t} = \beta_0 + \beta_1 R_{ostatne,t} + \beta_2 Rozpatie_t + \beta_3 ZmenaObjemu_t + \varepsilon_t. \]

Kód
model2 <- lm(vynos ~ trh + rozpatie + zmena_objemu, data = nvda)
tidy(model2) |>
  kable(digits = 4,
        col.names = c("Člen", "Odhad", "Štandardná chyba", "t-hodnota", "p-hodnota"),
        caption = "Viacnásobná regresia výnosu NVDA")
Viacnásobná regresia výnosu NVDA
Člen Odhad Štandardná chyba t-hodnota p-hodnota
(Intercept) 0.0894 0.1990 0.4495 0.6533
trh 1.1122 0.0642 17.3176 0.0000
rozpatie -0.0298 0.0544 -0.5479 0.5840
zmena_objemu -0.0001 0.0008 -0.1782 0.8586

V tomto modeli sa každý koeficient interpretuje pri rovnakých hodnotách ostatných vysvetľujúcich premenných. Koeficient pri trh je 1.112. Vnútrodenné rozpätie aj objem obchodov sa však vytvárajú počas toho istého dňa ako vysvetľovaný výnos, preto ich nemožno chápať ako vopred dostupné prediktory.

Stručný súhrn modelov pomocou glance()

Funkcia tidy() ukazuje jednotlivé koeficienty a glance() poskytuje základné informácie o celom modeli. Porovnám najmä \(R^2\), ktoré udáva, akú časť variability denných výnosov NVDA model zachytáva.

Kód
suhrn <- data.frame(
  Model = c("Jednoduchý", "Viacnásobný"),
  Pocet_dni = c(glance(model1)$nobs, glance(model2)$nobs),
  R2 = c(glance(model1)$r.squared, glance(model2)$r.squared),
  SD_chyby = c(glance(model1)$sigma, glance(model2)$sigma)
)
kable(suhrn, digits = 3,
      col.names = c("Model", "Počet dní", "R²", "Odhad SD chyby (%)"),
      caption = "Porovnanie jednoduchého a viacnásobného modelu")
Porovnanie jednoduchého a viacnásobného modelu
Model Počet dní R² Odhad SD chyby (%)
Jednoduchý 498 0.38 2.175
Viacnásobný 498 0.38 2.178

Jednoduchý model zachytáva približne 38 % variability výnosov NVDA a viacnásobný približne 38 %. Rozdiel medzi modelmi je 0.04 percentuálneho bodu. Vyššie \(R^2\) samo osebe neznamená lepšiu predikciu budúcich dní.

Skutočná hodnota, predikcia a rezíduum

Rezíduum je rozdiel medzi skutočným výnosom a výnosom vypočítaným regresnou priamkou. Kladné rezíduum znamená, že skutočný výnos bol vyšší než hodnota podľa modelu.

Kód
porovnanie <- data.frame(
  Datum = nvda$Date,
  Skutocnost = nvda$vynos,
  Predikcia = fitted(model1),
  Reziduum = residuals(model1)
)
head(porovnanie, 8) |>
  kable(digits = 2,
        col.names = c("Dátum", "Skutočný výnos (%)", "Predikcia (%)", "Rezíduum (p. b.)"),
        caption = "Prvých osem dní v jednoduchom modeli")
Prvých osem dní v jednoduchom modeli
Dátum Skutočný výnos (%) Predikcia (%) Rezíduum (p. b.)
2024-10-22 -0.08 0.40 -0.48
2024-10-23 -2.81 -2.10 -0.71
2024-10-24 0.61 2.67 -2.06
2024-10-25 0.80 1.16 -0.36
2024-10-28 -0.72 0.15 -0.87
2024-10-29 0.52 1.35 -0.83
2024-10-30 -1.35 0.36 -1.71
2024-10-31 -4.72 -3.34 -1.38
Kód
ggplot(porovnanie, aes(Datum, Reziduum)) +
  geom_hline(yintercept = 0, color = "grey60") +
  geom_point(color = "steelblue", alpha = 0.6) +
  labs(x = NULL, y = "Rezíduum (percentuálne body)") +
  theme_minimal()

Rezíduá jednoduchého regresného modelu v čase.

Predikcia pre zadanú hodnotu

Na jednoduchom modeli ukážem výpočet pre tri hypotetické hodnoty priemerného denného výnosu ostatných akcií: −1 %, 0 % a +1 %.

Kód
nove <- data.frame(trh = c(-1, 0, 1))
nove$Predikcia_NVDA <- predict(model1, newdata = nove)
kable(nove, digits = 3,
      col.names = c("Výnos ostatných akcií (%)", "Predikovaný výnos NVDA (%)"),
      caption = "Ukážka bodových predikcií jednoduchého modelu")
Ukážka bodových predikcií jednoduchého modelu
Výnos ostatných akcií (%) Predikovaný výnos NVDA (%)
-1 -1.122
0 -0.007
1 1.108

Pri priemernom výnose ostatných akcií +1 % model predikuje výnos NVDA 1.11 %. Ide o hodnotu na regresnej priamke, nie o istý výsledok ani investičné odporúčanie.

Malé cvičenia

Na záver si na krátkych otázkach overím, ako rozumiem výsledkom regresnej analýzy.

1. Ktorá premenná je vysvetľovaná a ktoré sú vysvetľujúce?

Vysvetľovanou premennou je denný percentuálny výnos akcie NVDA (vynos). V jednoduchom modeli ho vysvetľujem priemerným výnosom ostatných deviatich akcií (trh). Vo viacnásobnom modeli pridávam vnútrodenné cenové rozpätie (rozpatie) a zmenu objemu obchodovania (zmena_objemu).

2. Čo ukazuje regresná priamka v bodovom grafe?

Smernica jednoduchej regresie je 1.115. Pri zvýšení priemerného denného výnosu ostatných akcií o jeden percentuálny bod sa predikovaný výnos NVDA mení približne o 1.115 percentuálneho bodu. Ide o štatistickú súvislosť, nie o dokázaný príčinný vzťah.

3. Prečo sa jednoduchá a viacnásobná regresia líšia?

Jednoduchý model používa jednu vysvetľujúcu premennú, viacnásobný tri. Hodnota \(R^2\) je v jednoduchom modeli 0.38 a vo viacnásobnom 0.38. Pri viacnásobnej regresii interpretujem koeficient jednej premennej pri rovnakých hodnotách ostatných zahrnutých premenných.

4. Ako overím predikciu pre výnos ostatných akcií +1 %?

Pomocou funkcie predict() dostávam predikciu 1.108 %. Ručne ju overím dosadením do rovnice: konštanta + smernica × 1 = -0.007 + 1.115 × 1, čo po výpočte dáva 1.108 %.

5. Čo znamenajú kladné a záporné rezíduá?

V nasledujúcej tabuľke vyberiem jeden deň s kladným a jeden so záporným rezíduom. Kladné rezíduum znamená, že skutočný výnos bol vyšší než predikcia; záporné znamená, že bol nižší.

Kód
priklady <- porovnanie[c(which.max(porovnanie$Reziduum),
                          which.min(porovnanie$Reziduum)), ]
kable(priklady, digits = 2,
      col.names = c("Dátum", "Skutočný výnos (%)", "Predikcia (%)", "Rezíduum (p. b.)"),
      caption = "Príklad kladného a záporného rezídua")
Príklad kladného a záporného rezídua
Dátum Skutočný výnos (%) Predikcia (%) Rezíduum (p. b.)
463 2026-08-27 8.74 0.70 8.03
65 2025-01-27 -16.97 -2.76 -14.20

6. Znamená kladný koeficient, že rast ostatných akcií spôsobuje rast NVDA?

Nie. Kladný koeficient ukazuje, že sa výnosy v sledovanom období pohybovali podobným smerom. Príčinou môžu byť aj spoločné reakcie na ekonomické správy alebo vývoj akciového trhu.

Záver

Cieľom práce bolo preskúmať vývoj desiatich vybraných akcií a ukázať použitie jednoduchej a viacnásobnej lineárnej regresie na denných údajoch. V sledovanom období najviac vzrástla cena akcie GOOGL a najmenej cena BRK.B.

Pri NVDA sa ukázala kladná súvislosť medzi jej denným výnosom a priemerným výnosom ostatných sledovaných akcií. Jednoduchý model vysvetlil približne 38 % variability výnosov. Po pridaní rozpätia a zmeny objemu sa hodnota \(R^2\) zvýšila iba nepatrne.

Analýza ukazuje, že regresia je užitočná na opis štatistických súvislostí, ale nevysvetľuje všetky denné cenové pohyby. Výsledky sa týkajú iba vybraných akcií a daného obdobia. Pri porovnaní sa používa jednoduchý priemer deviatich akcií, nie celý index S&P 500. Práca sa zameriava na pochopenie regresie, nie na diagnostické testy alebo obchodnú stratégiu.