Can the FED Control Inflation and Maintain Full Employment? Has the FED been able to fulfill the mandate given to it by Congress?

Data Collection/Preprocessing

library(httr)
library(jsonlite)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ readr     2.1.5
## ✔ ggplot2   3.4.4     ✔ stringr   1.5.1
## ✔ lubridate 1.9.3     ✔ tibble    3.2.1
## ✔ purrr     1.0.2     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter()  masks stats::filter()
## ✖ purrr::flatten() masks jsonlite::flatten()
## ✖ dplyr::lag()     masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2)
library(scales)
## 
## Attaching package: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
library(tidyr)
# Fetching the FED Funds Rate from FRED API

fred_response <- GET(
  "https://api.stlouisfed.org/fred/series/observations",
  query = list(
    series_id = 'FEDFUNDS',
    api_key = '*',
    file_type = 'json',
    frequency = 'm',
    observation_start = '1999-02-15',
    observation_end = '2024-02-15'
  )
)

# Parse the data
fred_data <- content(fred_response, "text")
fred_data <- fromJSON(fred_data)

# Create data frame for FFR
ffr_df <- data.frame(
  Date = as.Date(paste0(substr(fred_data$observations$date, 1, 7), "-01")),
  FedFundsRate = as.numeric(fred_data$observations$value)
)

# Convert Date to "YYYY-MM" format for consistency with your requirements
ffr_df$Date <- format(ffr_df$Date, "%Y-%m")
# BLS API Key
bls_api_key <- "*"

# Function to perform API request, process the response, and create a dataframe
get_df <- function(series_id, start_year, end_year) {
  response <- GET(paste0("https://api.bls.gov/publicAPI/v2/timeseries/data/", series_id),
                  query = list(
                    registrationkey = bls_api_key,
                    startyear = as.character(start_year),
                    endyear = as.character(end_year)
                  ))
  data <- fromJSON(content(response, "text"), flatten = TRUE)$Results$series$data[[1]]
  data$Date <- paste0(data$year, "-", sub("M", "", data$period))
  

  column_name <- ifelse(grepl("LNS14000000", series_id), "UnemploymentRate", "CPI")
  
  df <- setNames(data.frame(
    Date = data$Date,
    Value = as.numeric(data$value)
  ), c("Date", column_name))
  
  return(df)
}

# API calls for CPI data
cpi_df1 <- get_df("CUUR0000SA0L1E", 1999, 2018)
cpi_df2 <- get_df("CUUR0000SA0L1E", 2019, 2024)

# Combine and sort the CPI data frames
cpi_df <- rbind(cpi_df1, cpi_df2)
cpi_df$Date <- as.Date(paste0(cpi_df$Date, "-01"))
cpi_df <- cpi_df[order(cpi_df$Date), ]
cpi_df$Date <- format(cpi_df$Date, "%Y-%m")

# API calls for Unemployment data
unemp_df1 <- get_df("LNS14000000", 1999, 2018)
unemp_df2 <- get_df("LNS14000000", 2019, 2024)

# Combine and sort the Unemployment data frames
unemployment_df <- rbind(unemp_df1, unemp_df2)
unemployment_df$Date <- as.Date(paste0(unemployment_df$Date, "-01"))
unemployment_df <- unemployment_df[order(unemployment_df$Date), ]
unemployment_df$Date <- format(unemployment_df$Date, "%Y-%m")

cpi_df$CPI <- as.numeric(cpi_df$CPI)
# Calculate the inflation rate y-o-y
cpi_df <- cpi_df %>%
  mutate(
    inflation = (CPI - lag(CPI,12)) / lag(CPI,12) * 100
  )


cpi_df$inflation[is.na(cpi_df$inflation)] <- 0

# Merging the dataframes on the 'Date' column
merged_df <- reduce(list(ffr_df, cpi_df, unemployment_df), function(x, y) merge(x, y, by = "Date", all = TRUE))

# Filtering the dataframe to include only dates from 1999-02 to 2024-01
master_df <- merged_df %>%
  filter(Date >= "1999-02" & Date <= "2024-01")
tail(master_df)
if (file.exists("master_df_cache.rds")) {
  master_df <- readRDS("master_df_cache.rds")
} else {
  saveRDS(master_df, "master_df_cache.rds")
}

Overview of Economic Indicators

master_df$Date <- as.Date(paste0(master_df$Date, "-01"))


long_df <- pivot_longer(master_df, cols = c(FedFundsRate, inflation, UnemploymentRate),
                        names_to = "Indicator", values_to = "Value")

# Define a data frame for the economic events
events_df <- data.frame(
  xmin = as.Date(c("1999-01-01", "2001-01-01", "2007-01-01", "2020-01-01")),
  xmax = as.Date(c("2000-12-31", "2001-12-31", "2009-12-31", "2021-12-31")),
  Event = c("Dot-com Bubble", "9/11 Attacks", "Global Financial Crisis", "COVID-19 Pandemic"),
  fill = c("blue", "orange", "red", "purple")
)

# Create the plots with separate facets for each indicator
p <- ggplot() +
  geom_line(data = long_df, aes(x = Date, y = Value, colour = Indicator), size=1) +
  scale_colour_manual(values = c("FedFundsRate" = "green", "inflation" = "blue", "UnemploymentRate" = "red"), guide = FALSE) +
  facet_wrap(~Indicator, scales = "free_y", ncol = 1) +
  
  # Add rectangles for major economic events using geom_rect
  geom_rect(data = events_df, aes(xmin = xmin, xmax = xmax, ymin = -Inf, ymax = Inf, fill = Event), alpha = 0.18) +
  scale_fill_manual(values = c("Dot-com Bubble" = "blue", "9/11 Attacks" = "orange", "Global Financial Crisis" = "red", "COVID-19 Pandemic" = "purple")) +
  
  theme_minimal() +
  theme(legend.position = "right",
        panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        legend.background = element_rect(fill = "white"),
        axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title=element_text(hjust=0.5, face='bold'))+
  labs(x = "", y = "Rate %", title = "     Economic Indicators Over Time (1999-2024)") +
  scale_x_date(date_labels = "%Y", date_breaks = "2 years") +
  guides(fill = guide_legend(title = "Major Economic Events")) # Set the title for the fill legend
## 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.
# Print the plot
p
## Warning: The `guide` argument in `scale_*()` cannot be `FALSE`. This was deprecated in
## ggplot2 3.3.4.
## ℹ Please use "none" instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Closely Examine

master_df$Date <- as.Date(master_df$Date, format = "%Y-%m")

# Calculate correlations
corr_unemployment <- cor(master_df$UnemploymentRate, master_df$FedFundsRate, use = "complete.obs")

# Plot
ggplot(data = master_df, aes(x = Date)) +
  geom_line(aes(y = UnemploymentRate, colour = "Unemployment Rate"), size = 1) +  
  geom_line(aes(y = FedFundsRate, colour = "Fed Funds Rate"), size = 1) + 
  scale_colour_manual(values = c("Unemployment Rate" = "red", "Fed Funds Rate" = "green")) +
  theme_minimal() +
  theme(
    legend.position = "right",  
    legend.title = element_blank(),  
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.background = element_blank(),
    axis.title.x = element_blank(),
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(hjust = 0.5, face = "bold", size=16)
  ) +
  labs(
    y = " ",
    title = "     Unemployment and Fed Funds Rate over the Last 25 years"
  ) +
  annotate("text", x = min(master_df$Date), y = max(master_df$FedFundsRate, na.rm = TRUE), label = paste("Correlation: ", round(corr_unemployment, 2)), hjust = 0, vjust = -6, size = 6, face='bold', colour = "black") + # Add correlation text to the plot
  scale_y_continuous(labels = percent_format(scale = 1)) +
  scale_x_date(date_breaks = "2 years", date_labels = "%Y") + 
  guides(colour = guide_legend(title = "Legend")) 
## Warning in annotate("text", x = min(master_df$Date), y =
## max(master_df$FedFundsRate, : Ignoring unknown parameters: `face`

master_df$Date <- as.Date(master_df$Date, format = "%Y-%m")

# Calculate correlations
corr_inflation <- cor(master_df$inflation, master_df$FedFundsRate, use = "complete.obs")

# Plot
ggplot(data = master_df, aes(x = Date)) +
  geom_line(aes(y = inflation, colour = "Inflation Rate"), size = 1) +  # Increase line size
  geom_line(aes(y = FedFundsRate, colour = "Fed Funds Rate"), size = 1) +  # Increase line size
  scale_colour_manual(values = c("Inflation Rate" = "blue", "Fed Funds Rate" = "green")) +
  theme_minimal() +
  theme(
    legend.position = "right",  # Adjust legend position back to bottom
    legend.title = element_blank(),  # Remove legend title
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    panel.background = element_blank(),
    axis.title.x = element_blank(),
    axis.text.x = element_text(angle = 45, hjust = 1),
    plot.title = element_text(hjust = 0.5, face = "bold", size=16)
  ) +
  labs(
    y = "",
    title = "     Inflation and Fed Funds Rate over the Last 25 years"
  ) +
  annotate("text", x = min(master_df$Date), y = max(master_df$FedFundsRate, na.rm = TRUE), label = paste("Correlation: ", round(corr_inflation, 2)), hjust = 0, vjust = 0, size = 6, face='bold', colour = "black") + # Add correlation text to the plot
  scale_y_continuous(labels = percent_format(scale = 1)) +
  scale_x_date(date_breaks = "2 years", date_labels = "%Y") + # Adjust date breaks and labels
  guides(colour = guide_legend(title = "Legend"))  # Add legend title back
## Warning in annotate("text", x = min(master_df$Date), y =
## max(master_df$FedFundsRate, : Ignoring unknown parameters: `face`