Last active
November 22, 2024 01:06
-
-
Save USMortality/c19e926aa84e8c6b57b9f7a022cf8d56 to your computer and use it in GitHub Desktop.
SP500 US Presidency Analysis [USA]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
library(readr) | |
library(dplyr) | |
library(ggplot2) | |
library(scales) | |
library(quantmod) | |
sf <- 2 | |
width <- 600 * sf | |
height <- 335 * sf | |
options(vsc.dev.args = list(width = width, height = height, res = 72 * sf)) | |
sdate <- as.Date("1900-01-01") | |
edate <- Sys.Date() | |
# Helper function to get symbol data and process it | |
get_symbol_data <- function(symbol, close_col) { | |
data <- getSymbols(symbol, from = sdate, to = edate, auto.assign = FALSE) %>% | |
as.data.frame() %>% | |
mutate(date = row.names(.)) %>% | |
select(date, close = !!sym(close_col)) %>% | |
mutate(date = as.Date(date)) | |
return(data) | |
} | |
watermark <- function( | |
x = structure(Inf), | |
y = Inf, | |
latest = "") { | |
ggplot2::annotate("text", | |
y = y, | |
x = x, | |
label = paste("@USMortality", latest), | |
vjust = 1, | |
hjust = 1, | |
col = "#000000", | |
cex = 6, | |
fontface = "bold", | |
alpha = 0.1 | |
) | |
} | |
# Define custom colors for Democrats (blue) and Republicans (red) | |
party_colors <- c( | |
"Democrat" = "#1f78b4", # blue | |
"Republican" = "#e31a1c" # red | |
) | |
# Load the S&P 500 data | |
df <- get_symbol_data("^GSPC", "GSPC.Close") | |
# Create a data frame of election details | |
election_info <- data.frame( | |
election_date = as.Date(c( | |
"1928-11-06", "1932-11-08", "1936-11-03", "1940-11-05", "1944-11-07", | |
"1948-11-02", "1952-11-04", "1956-11-06", "1960-11-08", "1964-11-03", | |
"1968-11-05", "1972-11-07", "1976-11-02", "1980-11-04", "1984-11-06", | |
"1988-11-08", "1992-11-03", "1996-11-05", "2000-11-07", "2004-11-02", | |
"2008-11-04", "2012-11-06", "2016-11-08", "2020-11-03" | |
)), | |
president = c( | |
"Herbert Hoover", "Franklin D. Roosevelt", "Franklin D. Roosevelt", | |
"Franklin D. Roosevelt", "Franklin D. Roosevelt", "Harry S. Truman", | |
"Dwight D. Eisenhower", "Dwight D. Eisenhower", "John F. Kennedy", | |
"Lyndon B. Johnson", "Richard Nixon", "Richard Nixon", "Jimmy Carter", | |
"Ronald Reagan", "Ronald Reagan", "George H.W. Bush", "Bill Clinton", | |
"Bill Clinton", "George W. Bush", "George W. Bush", "Barack Obama", | |
"Barack Obama", "Donald Trump", "Joe Biden" | |
), | |
party = c( | |
"Republican", "Democrat", "Democrat", "Democrat", "Democrat", "Democrat", | |
"Republican", "Republican", "Democrat", "Democrat", "Republican", | |
"Republican", "Democrat", "Republican", "Republican", "Republican", | |
"Democrat", "Democrat", "Republican", "Republican", "Democrat", "Democrat", | |
"Republican", "Democrat" | |
) | |
) | |
days <- 365.25 * 4 | |
# Filter the S&P 500 data for one year after each election date | |
ts <- election_info |> | |
rowwise() |> | |
do({ | |
election_date <- .$election_date | |
president <- .$president | |
party <- .$party | |
df_filtered <- df |> | |
filter(date >= election_date & date <= election_date + days) |> | |
mutate( | |
election_date = election_date, | |
president = president, | |
party = party, | |
days_since_election = as.numeric(date - election_date), | |
pct_change = ((close - first(close)) / first(close)) | |
) | |
df_filtered | |
}) |> | |
bind_rows() |> | |
filter(days_since_election < days) | |
# Plot the data | |
chart <- ggplot(ts, aes( | |
x = days_since_election, | |
y = pct_change, group = election_date | |
)) + | |
geom_line(aes(color = president)) + | |
scale_y_continuous(labels = percent_format(scale = 100)) + | |
labs( | |
title = "S&P 500 Percentage Change by U.S. President", | |
x = "Days Since Election", | |
y = "Percentage Change (%)", | |
color = "President" | |
) + | |
theme_minimal() + | |
watermark() | |
ggplot2::ggsave( | |
filename = "chart1.png", plot = chart, width = width, height = height, | |
units = "px", dpi = 72 * sf, device = grDevices::png, type = c("cairo") | |
) | |
ts2 <- ts |> | |
group_by(party, days_since_election) |> | |
summarise( | |
sd_value = sd(pct_change), | |
pct_change = mean(pct_change), | |
n = n(), | |
.groups = "drop" | |
) |> | |
mutate( | |
error_margin = if_else(!is.na(sd_value) & n > 1, | |
qt(0.975, df = n - 1) * sd_value / sqrt(n), NA_real_ | |
), | |
lower_ci = | |
if_else(!is.na(error_margin), pct_change - error_margin, NA_real_), | |
upper_ci = | |
if_else(!is.na(error_margin), pct_change + error_margin, NA_real_) | |
) | |
# Plot the data with 95% CI | |
chart <- ggplot(ts2, aes( | |
x = days_since_election, | |
y = pct_change, color = party | |
)) + | |
geom_line() + | |
geom_ribbon(aes(ymin = lower_ci, ymax = upper_ci, fill = party), | |
alpha = 0.2, color = NA, show.legend = FALSE | |
) + | |
scale_y_continuous( | |
labels = percent_format(scale = 100), | |
limits = c(-0.25, 0.75), | |
oob = scales::squish | |
) + | |
scale_color_manual(values = party_colors) + | |
scale_fill_manual(values = party_colors) + | |
labs( | |
title = "S&P 500 Percentage Change by Party", | |
subtitle = "Since 1927", | |
x = "Days Since Election", | |
y = "Percentage Change (%)", | |
color = "Party", | |
linetype = "President" | |
) + | |
theme_minimal() + | |
watermark() | |
ggplot2::ggsave( | |
filename = "chart2.png", plot = chart, width = width, height = height, | |
units = "px", dpi = 72 * sf, device = grDevices::png, type = c("cairo") | |
) | |
ts3 <- ts |> | |
group_by(days_since_election) |> | |
summarise( | |
sd_value = sd(pct_change), | |
pct_change = mean(pct_change), | |
n = n(), | |
.groups = "drop" | |
) |> | |
mutate( | |
error_margin = if_else(!is.na(sd_value) & n > 1, | |
qt(0.975, df = n - 1) * sd_value / sqrt(n), NA_real_ | |
), | |
lower_ci = if_else(!is.na(error_margin), pct_change - error_margin, NA_real_), | |
upper_ci = if_else(!is.na(error_margin), pct_change + error_margin, NA_real_) | |
) | |
chart <- ggplot(ts3, aes(x = days_since_election, y = pct_change)) + | |
geom_line(aes(color = "Mean"), linewidth = 1, linetype = "solid") + | |
geom_ribbon(aes(ymin = lower_ci, ymax = upper_ci), | |
alpha = 0.2, color = NA, show.legend = FALSE | |
) + | |
geom_line( | |
data = ts |> filter(president == "Donald Trump"), | |
aes(x = days_since_election, y = pct_change, color = "Trump"), linewidth = 1 | |
) + | |
geom_line( | |
data = ts |> filter(president == "Joe Biden"), | |
aes(x = days_since_election, y = pct_change, color = "Biden"), linewidth = 1 | |
) + | |
scale_y_continuous( | |
labels = scales::percent_format(scale = 100), | |
limits = c(-0.1, 0.75), | |
oob = scales::squish | |
) + | |
scale_color_manual( | |
values = c( | |
"Mean" = "black", | |
"Trump" = unname(party_colors[2]), | |
"Biden" = unname(party_colors[1]) | |
), | |
breaks = c("Mean", "Trump", "Biden"), # Custom order in the legend | |
name = "Legend" | |
) + | |
labs( | |
title = | |
"S&P 500 Mean Change since U.S. Presidential Election - Trump vs. Biden", | |
subtitle = "Since 1927", | |
x = "Days Since Election", | |
y = "Percentage Change (%)" | |
) + | |
theme_minimal() + | |
watermark() | |
ggplot2::ggsave( | |
filename = "chart3.png", plot = chart, width = width, height = height, | |
units = "px", dpi = 72 * sf, device = grDevices::png, type = c("cairo") | |
) | |
# Calculate Gains Normalized by Years in Office | |
presidential_gains <- ts |> | |
group_by(president, party) |> | |
summarize( | |
start_date = min(date), | |
end_date = max(date), | |
start_close = close[which.min(date)], | |
end_close = close[which.max(date)] | |
) |> | |
mutate( | |
total_days = as.numeric(end_date - start_date), | |
years_in_office = total_days / 365.25, # Account for leap years | |
total_gain = ((end_close - start_close) / start_close) * 100, | |
annualized_gain = total_gain / years_in_office | |
) | |
# Plotting the Annualized Gains | |
chart <- ggplot(presidential_gains, aes( | |
x = reorder(president, annualized_gain), | |
y = annualized_gain, fill = party | |
)) + | |
geom_bar(stat = "identity") + | |
geom_text( | |
aes(label = sprintf("%.1f%%", annualized_gain)), | |
hjust = -0.1, # Adjust this value as needed | |
color = "black", | |
size = 3 | |
) + | |
coord_flip() + | |
labs( | |
title = "Annualized Stock Market Gain During Presidency", | |
x = "President", | |
y = "Annualized Percentage Gain (%)" | |
) + | |
scale_fill_manual(values = party_colors) + | |
theme_minimal() + | |
watermark() | |
ggplot2::ggsave( | |
filename = "chart4.png", plot = chart, width = width, height = height, | |
units = "px", dpi = 72 * sf, device = grDevices::png, type = c("cairo") | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://www.mortality.watch/charts/list.html#sp500-us-presidency-analysis-usa