options(repos = c(CRAN = "https://cloud.r-project.org"))

Stock Price

The Stock price is collected from March 1st, a month ahead of liberation day to May 10th.

stocks <- tq_get(c("ATCO-A.ST", "SINCH.ST", "INVE-B.st", "SSAB-B.ST", "SAAB-B.ST", "AXFO.ST", "VOLCAR-B.ST", "NDA-SE.ST", "ERIC-B.ST", "CAST.ST", "ORRON.ST", "AZN.ST"),
                 get = "stock.prices",
                 from = "2025-03-01",
                 to = "2025-05-10")
stocks
## # A tibble: 564 × 8
##    symbol    date        open  high   low close  volume adjusted
##    <chr>     <date>     <dbl> <dbl> <dbl> <dbl>   <dbl>    <dbl>
##  1 ATCO-A.ST 2025-03-03  183.  184.  181.  181. 5851286     179.
##  2 ATCO-A.ST 2025-03-04  178.  179.  174.  175. 5849265     174.
##  3 ATCO-A.ST 2025-03-05  180.  185.  178.  184. 6462212     183.
##  4 ATCO-A.ST 2025-03-06  186.  186.  179.  183. 5369088     181.
##  5 ATCO-A.ST 2025-03-07  181.  184.  179.  183. 6084395     182.
##  6 ATCO-A.ST 2025-03-10  184.  185.  179.  179. 3915631     177.
##  7 ATCO-A.ST 2025-03-11  182.  182.  175.  175. 4600879     173.
##  8 ATCO-A.ST 2025-03-12  178.  179.  176.  177. 6365358     175.
##  9 ATCO-A.ST 2025-03-13  175.  178.  174.  176. 3017065     174.
## 10 ATCO-A.ST 2025-03-14  175.  177.  175.  177. 3440299     175.
## # ℹ 554 more rows

Stock Prices Graphed

stocks %>%
    
    ggplot(aes(x = date, y = adjusted, color = symbol)) +
    geom_line()

Improved Graph

# Load data
tickers <- c("ATCO-A.ST", "SINCH.ST", "INVE-B.st", "SSAB-B.ST", "SAAB-B.ST", "AXFO.ST", "VOLCAR-B.ST", "NDA-SE.ST", "ERIC-B.ST", "CAST.ST", "ORRON.ST", "AZN.ST")

df_stocks <- yf_get(tickers = tickers, 
                        first_date = as.Date("2025-03-01"), 
                        last_date = as.Date("2025-05-10"))
## 
## ── Running yfR for 12 stocks | 2025-03-01 --> 2025-05-10 (70 days) ──
## 
## ℹ Downloading data for benchmark ticker ^GSPC
## ℹ (1/12) Fetching data for ATCO-A.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Nice!
## ℹ (2/12) Fetching data for AXFO.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Good stuff!
## ℹ (3/12) Fetching data for AZN.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Good stuff!
## ℹ (4/12) Fetching data for CAST.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Youre doing good!
## ℹ (5/12) Fetching data for ERIC-B.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Well done vincentsmac!
## ℹ (6/12) Fetching data for INVE-B.st
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Good job vincentsmac!
## ℹ (7/12) Fetching data for NDA-SE.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Good stuff!
## ℹ (8/12) Fetching data for ORRON.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Good stuff!
## ℹ (9/12) Fetching data for SAAB-B.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Looking good!
## ℹ (10/12) Fetching data for SINCH.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Good job vincentsmac!
## ℹ (11/12) Fetching data for SSAB-B.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Well done vincentsmac!
## ℹ (12/12) Fetching data for VOLCAR-B.ST
## !    - not cached
## ✔    - cache saved successfully
## ✔    - got 47 valid rows (2025-03-03 --> 2025-05-09)
## ✔    - got 96% of valid prices -- Good stuff!
## ℹ Binding price data
## 
## ── Diagnostics ─────────────────────────────────────────────────────────────────
## ✔ Returned dataframe with 564 rows -- Nice!
## ℹ Using 78.4 kB at /var/folders/xv/5h23j4p10m94jp1pvq6296x00000gn/T//Rtmpm7nz1R/yf_cache for 13 cache files
## ℹ Out of 12 requested tickers, you got 12 (100%)
# Calculate % change from first available price
df_pct_change <- df_stocks %>%
  group_by(ticker) %>%
  arrange(ref_date) %>%
  mutate(pct_change = 100 * (price_adjusted / first(price_adjusted) - 1))

# Plot % change
ggplot(df_pct_change, aes(x = ref_date, y = pct_change, color = ticker)) +
  geom_line(lindewidth = 1) +
  labs(title = "Stock Performance (% Change from Start)",
       x = "Date", y = "% Change", color = "Stock") +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  theme_minimal()
## Warning in geom_line(lindewidth = 1): Ignoring unknown parameters: `lindewidth`

ggplot(df_pct_change, aes(x = ref_date, y = pct_change, color = ticker)) +
  geom_line(size = 1.2) +
  scale_color_tableau(name = "Stock") +  # Color-blind friendly and distinct
  labs(title = "Stock Performance (% Change from Start)",
       x = "Date", y = "% Change") +
  theme_economist() +  # or try theme_fivethirtyeight()
  scale_y_continuous(labels = scales::percent_format(scale = 1))
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
## Warning in check_pal_n(n, max_n): This palette can handle a maximum of 10
## values.You have supplied 12.
## Warning: Removed 94 rows containing missing values or values outside the scale range
## (`geom_line()`).

Filtering Stocks in positive change and negative change

# Show final positive change stocks
stocks_positive <- df_pct_change %>%
  group_by(ticker) %>%
  filter(ref_date == max(ref_date)) %>%
  filter(pct_change > 0)

kable(stocks_positive)
ticker ref_date price_open price_high price_low price_close volume price_adjusted ret_adjusted_prices ret_closing_prices cumret_adjusted_prices pct_change
AXFO.ST 2025-05-09 269.20 270.60 267.10 269.90 176075 269.90 0.0055887 0.0055887 1.206617 20.661679
SAAB-B.ST 2025-05-09 464.65 465.90 447.75 449.80 2762459 449.80 -0.0330001 -0.0330001 1.264794 26.479437
SINCH.ST 2025-05-09 26.23 26.77 24.07 24.16 13445247 24.16 -0.0555121 -0.0555121 1.013423 1.342281
stocks_negative <- df_pct_change %>%
  group_by(ticker) %>%
  filter(ref_date == max(ref_date)) %>%
  filter(pct_change < 0)

kable(stocks_negative)
ticker ref_date price_open price_high price_low price_close volume price_adjusted ret_adjusted_prices ret_closing_prices cumret_adjusted_prices pct_change
ATCO-A.ST 2025-05-09 152.500 153.60 150.950 151.500 4320357 151.500 -0.0039448 -0.0039448 0.8442671 -15.573293
AZN.ST 2025-05-09 1311.000 1331.50 1306.500 1323.000 451695 1323.000 0.0022727 0.0022727 0.8232732 -17.672682
CAST.ST 2025-05-09 111.000 114.40 110.250 113.850 2283814 113.850 0.0242915 0.0242915 0.9718484 -2.815157
ERIC-B.ST 2025-05-09 81.240 81.46 80.120 80.460 4388823 80.460 -0.0066667 -0.0066667 0.9174985 -8.250146
INVE-B.st 2025-05-09 283.700 285.85 282.550 284.150 2198796 284.150 0.0026464 0.0026464 0.8864178 -11.358216
NDA-SE.ST 2025-05-09 136.000 136.35 135.350 135.550 2672041 135.550 -0.0011053 -0.0011053 0.9593089 -4.069107
ORRON.ST 2025-05-09 4.178 4.35 4.178 4.254 373123 4.254 0.0201439 0.0201439 0.7771283 -22.287172
SSAB-B.ST 2025-05-09 60.180 60.30 57.880 58.820 3151423 58.820 -0.0199933 -0.0199933 0.9157264 -8.427364
VOLCAR-B.ST 2025-05-09 18.500 18.65 17.820 18.060 4351761 18.060 -0.0131147 -0.0131147 0.8113207 -18.867928

Arranging Stocks from Best to Worst Performance

stocks_ranked <- df_pct_change %>%
  group_by(ticker) %>%
  filter(ref_date == max(ref_date)) %>%
  ungroup() %>%
  arrange(desc(pct_change)) 

kable(stocks_ranked)
ticker ref_date price_open price_high price_low price_close volume price_adjusted ret_adjusted_prices ret_closing_prices cumret_adjusted_prices pct_change
SAAB-B.ST 2025-05-09 464.650 465.90 447.750 449.800 2762459 449.800 -0.0330001 -0.0330001 1.2647944 26.479437
AXFO.ST 2025-05-09 269.200 270.60 267.100 269.900 176075 269.900 0.0055887 0.0055887 1.2066168 20.661679
SINCH.ST 2025-05-09 26.230 26.77 24.070 24.160 13445247 24.160 -0.0555121 -0.0555121 1.0134228 1.342281
CAST.ST 2025-05-09 111.000 114.40 110.250 113.850 2283814 113.850 0.0242915 0.0242915 0.9718484 -2.815157
NDA-SE.ST 2025-05-09 136.000 136.35 135.350 135.550 2672041 135.550 -0.0011053 -0.0011053 0.9593089 -4.069107
ERIC-B.ST 2025-05-09 81.240 81.46 80.120 80.460 4388823 80.460 -0.0066667 -0.0066667 0.9174985 -8.250146
SSAB-B.ST 2025-05-09 60.180 60.30 57.880 58.820 3151423 58.820 -0.0199933 -0.0199933 0.9157264 -8.427364
INVE-B.st 2025-05-09 283.700 285.85 282.550 284.150 2198796 284.150 0.0026464 0.0026464 0.8864178 -11.358216
ATCO-A.ST 2025-05-09 152.500 153.60 150.950 151.500 4320357 151.500 -0.0039448 -0.0039448 0.8442671 -15.573293
AZN.ST 2025-05-09 1311.000 1331.50 1306.500 1323.000 451695 1323.000 0.0022727 0.0022727 0.8232732 -17.672682
VOLCAR-B.ST 2025-05-09 18.500 18.65 17.820 18.060 4351761 18.060 -0.0131147 -0.0131147 0.8113207 -18.867928
ORRON.ST 2025-05-09 4.178 4.35 4.178 4.254 373123 4.254 0.0201439 0.0201439 0.7771283 -22.287172
#Bar Chart 

ggplot(stocks_ranked, aes(x = reorder(ticker, pct_change), y = pct_change, fill = pct_change > 0)) +
  geom_col(show.legend = FALSE) +
  coord_flip() +
  labs(title = "Final % Change in Stock Prices (2025-03-01 to 2025-05-10)",
       x = "Stock", y = "% Change") +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  scale_fill_manual(values = c("TRUE" = "forestgreen", "FALSE" = "red")) +
  theme_minimal()

Selected Columns - Ticker & Final % Change

stocks_ranked %>%
  select(Stock = ticker, `Final % Change` = pct_change) %>%
  kable()
Stock Final % Change
SAAB-B.ST 26.479437
AXFO.ST 20.661679
SINCH.ST 1.342281
CAST.ST -2.815157
NDA-SE.ST -4.069107
ERIC-B.ST -8.250146
SSAB-B.ST -8.427364
INVE-B.st -11.358216
ATCO-A.ST -15.573293
AZN.ST -17.672682
VOLCAR-B.ST -18.867928
ORRON.ST -22.287172

Added Column - Positive Or Negative

df_pct_change %>%
  group_by(ticker) %>%
  filter(ref_date == max(ref_date)) %>%
  ungroup() %>%
  mutate(growth_category = ifelse(pct_change > 0, "Positive", "Negative")) %>%
  select(ticker, pct_change, growth_category) %>%
  kable()
ticker pct_change growth_category
ATCO-A.ST -15.573293 Negative
AXFO.ST 20.661679 Positive
AZN.ST -17.672682 Negative
CAST.ST -2.815157 Negative
ERIC-B.ST -8.250146 Negative
INVE-B.st -11.358216 Negative
NDA-SE.ST -4.069107 Negative
ORRON.ST -22.287172 Negative
SAAB-B.ST 26.479437 Positive
SINCH.ST 1.342281 Positive
SSAB-B.ST -8.427364 Negative
VOLCAR-B.ST -18.867928 Negative

Summarize Groups by Average % change in each growth category

avg_growth <- df_pct_change %>%
  group_by(ticker) %>%
  filter(ref_date == max(ref_date)) %>%
  ungroup() %>%
  mutate(growth_category = ifelse(pct_change > 0, "Positive", "Negative")) %>%
  group_by(growth_category) %>%
  summarise(avg_change = mean(pct_change))

ggplot(avg_growth, aes(x = growth_category, y = avg_change, fill = growth_category)) +
  geom_col() +
  geom_text(aes(label = paste0(round(avg_change, 1), "%")),  # add % label
            vjust = -0.5, size = 4) +  # position above the bar
  labs(title = "Average % Change by Growth Category",
       x = "Growth Category",
       y = "Average % Change") +
  scale_y_continuous(labels = percent_format(scale = 1)) +
  theme_minimal()