Fetch stock prices

31 July 2025 - Written by ML

Highlights

  • This report provides work flow and code to gather a selection of shares share prices and store the result ina csv file.

Aim

  • Our aim is to fetch year end closing prices for a selection of stocks. Then save the result in a csv format.

  • Code has been created with a mixture of models including kimi.ai, deep seek, mistral.ai, chatgpt, and, cursor.

Script

###############################################################################
# Script Name: year_end_prices.R
#
# Description:
# This script downloads adjusted closing prices for a specified set of stock 
# tickers from Yahoo Finance, covering a time range from 2017-01-01 to today. 
# It extracts the closing prices for the end of each year from 2017 through 2024, 
# appends the latest available closing price, reshapes the data to wide format, 
# and writes the result to a CSV file.
#
# Output:
# - year_end_prices_2017_2024_latest.csv : A wide-format CSV with one row per ticker 
#   and columns for each year-end and the latest price.
###############################################################################

# --- Packages ------------------------------------------------------------
# Ensure required packages are installed and loaded
required <- c("quantmod", "dplyr", "lubridate")
if (!all(required %in% installed.packages()[, "Package"]))
  install.packages(setdiff(required, installed.packages()[, "Package"]))
lapply(required, library, character.only = TRUE)

# --- Tickers & Date Range -----------------------------------------------
# Define the tickers to analyze and the historical date range
tickers <- c("AJG", "AON", "MUV2.DE", "^GSPC", "^DJI")
from <- "2017-01-01"
to   <- Sys.Date()

# --- Download Historical Prices -----------------------------------------
# Download adjusted closing prices for each ticker from Yahoo Finance
# Use try() to continue if a ticker fails
raw_list <- setNames(vector("list", length(tickers)), tickers)

for (tk in tickers) {
  cat("Fetching", tk, "...")
  raw_list[[tk]] <- try(
    getSymbols(tk, from = from, to = to, src = "yahoo", auto.assign = FALSE),
    silent = TRUE
  )
  cat(ifelse(inherits(raw_list[[tk]], "try-error"), "FAILED\n", "OK\n"))
}

# Extract the closing prices from each ticker that downloaded successfully
prices_xts <- do.call(
  merge, 
  lapply(raw_list[!sapply(raw_list, inherits, "try-error")], Cl)
)

# --- Extract Year-End Prices --------------------------------------------
# Define target year-end dates
year_ends <- as.Date(paste0(2017:2024, "-12-31"))

# Get last available closing price on or before each year-end
ye_prices <- lapply(year_ends, function(d) {
  na.omit(prices_xts[paste0("/", d)])[NROW(na.omit(prices_xts[paste0("/", d)])), , drop = FALSE]
})

# Convert year-end prices to data frame
ye_df <- do.call(rbind, ye_prices) %>%
  as.data.frame() %>%
  mutate(date = year_ends) %>%
  select(date, everything())

# --- Latest Close Price --------------------------------------------------
# Extract most recent available closing price
latest_price <- tail(prices_xts, 1)
latest_df <- data.frame(
  date = as.Date(index(latest_price)),
  setNames(
    as.list(coredata(latest_price)),
    sub("Close\\.", "Close.", colnames(latest_price))
  )
)

# --- Combine Year-End and Latest Prices ----------------------------------
# Combine into single long-format data frame and annotate year
out <- bind_rows(ye_df, latest_df) %>%
  arrange(date) %>%
  mutate(year = lubridate::year(date)) %>%
  select(year, date, everything())

# Display the combined result
print(out)

# --- Reshape to Wide Format ----------------------------------------------
# Convert long format to wide with one row per ticker, columns per date
library(tidyr)
library(dplyr)

wide_prices <- out %>%
  select(-year) %>%                         # Remove numeric year column
  select(date, ends_with(".Close")) %>%    # Keep only date and Close columns
  pivot_longer(-date, names_to = "ticker", values_to = "price") %>%
  mutate(ticker = sub("\\.Close$", "", ticker)) %>%
  pivot_wider(names_from = date, values_from = price, names_prefix = "D_") %>%
  arrange(ticker)

# Display reshaped data
wide_prices

# --- Write to CSV --------------------------------------------------------
# Save wide-format year-end + latest prices to file
write.csv(wide_prices,
          file = "year_end_prices_2017_2024_latest.csv",
          row.names = FALSE)