library(shiny) library(tidyverse) library(plotly)
sp500_data <- read.csv(“sp500_sampled data_2020_2024.txt”)
roe_long <- sp500_data %>% pivot_longer( cols = starts_with(“ROE_”), names_to = “Year”, values_to = “ROE”, names_prefix = “ROE_” ) %>% mutate(Year = as.numeric(Year))
sp500_data <- sp500_data %>% mutate( Growth_Status = ifelse(EPS_2024 > EPS_2020, “Growing”, “Declining”), EPS_Growth_5Yr = ((EPS_2024 - EPS_2020) / abs(EPS_2020)) * 100, Avg_ROE = (ROE_2020 + ROE_2021 + ROE_2022 + ROE_2023 + ROE_2024) / 5, ROE_Stability = sd(c(ROE_2020, ROE_2021, ROE_2022, ROE_2023, ROE_2024)) )
ui <- fluidPage( titlePanel(“📊 Value Investing Stock Screener - S&P 500 Compounders”),
sidebarLayout( sidebarPanel( width = 3, h4(“Company Selection”), selectInput( “company”, “Select Company:”, choices = setNames(sp500_data\(Symbol, paste0(sp500_data\)Company, ” (“, sp500_data$Symbol,”)“)), selected =”AAPL” ), hr(), h4(“Selected Company Details”), uiOutput(“company_info”), hr(), helpText(“This screener identifies ‘Compounders’ - stocks with consistent earnings growth and stable ROE over 5 years.”) ),
mainPanel(
width = 9,
# Top Panel: ROE Trend Line Chart
h3("5-Year Return on Equity (ROE) Trend"),
plotlyOutput("roe_trend", height = "350px"),
hr(),
# Bottom Panel: PE vs EPS Growth Scatter
h3("Value Analysis: P/E Ratio vs. 5-Year EPS Growth"),
plotlyOutput("pe_growth_scatter", height = "400px"),
hr(),
# Summary Statistics Table
h4("Top Compounders (Growing EPS + Stable ROE)"),
tableOutput("top_compounders")
)
) )
server <- function(input, output, session) {
# Reactive: Selected company data selected_company <- reactive({ sp500_data %>% filter(Symbol == input$company) })
# Reactive: Selected company ROE trend data selected_roe <- reactive({ roe_long %>% filter(Symbol == input$company) })
# Output: Company information sidebar output\(company_info <- renderUI({ company <- selected_company() tagList( p(strong("Company:"), company\)Company), p(strong(“Sector:”), company\(Sector), p(strong("Current P/E:"), round(company\)PE_Current, 1)), p(strong(“Growth Status:”), span(company\(Growth_Status, style = paste0("color: ", ifelse(company\)Growth_Status == “Growing”, “green”, “red”), “; font-weight: bold;”))), p(strong(“5-Yr EPS Growth:”), paste0(round(company\(EPS_Growth_5Yr, 1), "%")), p(strong("Avg ROE:"), paste0(round(company\)Avg_ROE * 100, 1), “%”)) ) })
# Output: ROE Trend Line Chart output$roe_trend <- renderPlotly({ data <- selected_roe() company <- selected_company()
plot_ly(data, x = ~Year, y = ~ROE, type = 'scatter', mode = 'lines+markers',
line = list(color = '#1f77b4', width = 3),
marker = list(size = 10, color = '#1f77b4')) %>%
layout(
title = paste0(company$Company, " - ROE Trend (2020-2024)"),
xaxis = list(title = "Year", dtick = 1),
yaxis = list(title = "Return on Equity (ROE)", tickformat = ".0%"),
hovermode = "x unified",
plot_bgcolor = '#f8f9fa',
paper_bgcolor = 'white'
)
})
# Output: P/E vs EPS Growth Scatter Plot output$pe_growth_scatter <- renderPlotly({ data <- sp500_data %>% filter(!is.na(PE_Current) & is.finite(EPS_Growth_5Yr))
selected <- selected_company()
plot_ly(data, x = ~EPS_Growth_5Yr, y = ~PE_Current,
type = 'scatter', mode = 'markers',
marker = list(size = 10, opacity = 0.6),
color = ~Growth_Status,
colors = c("Growing" = "#2ecc71", "Declining" = "#e74c3c"),
text = ~paste0(Company, " (", Symbol, ")<br>",
"P/E: ", round(PE_Current, 1), "<br>",
"EPS Growth: ", round(EPS_Growth_5Yr, 1), "%<br>",
"Avg ROE: ", round(Avg_ROE * 100, 1), "%"),
hoverinfo = 'text') %>%
add_markers(
data = selected,
x = ~EPS_Growth_5Yr,
y = ~PE_Current,
marker = list(size = 20, color = 'gold',
line = list(color = 'black', width = 2)),
name = "Selected",
showlegend = FALSE
) %>%
layout(
xaxis = list(title = "5-Year EPS Growth (%)"),
yaxis = list(title = "Current P/E Ratio"),
hovermode = "closest",
plot_bgcolor = '#f8f9fa',
paper_bgcolor = 'white',
annotations = list(
list(x = 0.5, y = -0.15, text = "← Lower Growth | Higher Growth →",
xref = "paper", yref = "paper", showarrow = FALSE,
font = list(size = 11, color = "gray"))
)
)
})
# Output: Top Compounders Table output$top_compounders <- renderTable({ sp500_data %>% filter(Growth_Status == “Growing” & !is.na(PE_Current)) %>% arrange(desc(EPS_Growth_5Yr)) %>% select(Symbol, Company, Sector, PE_Current, EPS_Growth_5Yr, Avg_ROE) %>% head(10) %>% mutate( PE_Current = round(PE_Current, 1), EPS_Growth_5Yr = paste0(round(EPS_Growth_5Yr, 1), “%”), Avg_ROE = paste0(round(Avg_ROE * 100, 1), “%”) ) %>% rename( “Ticker” = Symbol, “Company” = Company, “Sector” = Sector, “P/E” = PE_Current, “5Y EPS Growth” = EPS_Growth_5Yr, “Avg ROE” = Avg_ROE ) }, striped = TRUE, hover = TRUE) }
shinyApp(ui = ui, server = server)