{"spec_id":"line-stock-comparison","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' line-stock-comparison: Stock Price Comparison Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 88/100 | Created: 2026-05-23\n\nlibrary(ggplot2)\nlibrary(ragg)\n\nset.seed(42)\n\n# Theme tokens\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\n# embed alpha into hex — ggplot2 element_line lacks alpha parameter\nGRID_COLOR  <- adjustcolor(INK_SOFT, alpha.f = if (THEME == \"dark\") 0.22 else 0.35)\n\nIMPRINT <- c(\"#009E73\", \"#C475FD\", \"#AE3030\", \"#4467A3\")\n\n# Data — approximately 1 year of trading days starting Jan 2023\nall_dates     <- seq(as.Date(\"2023-01-03\"), by = \"day\", length.out = 400)\ntrading_dates <- all_dates[!weekdays(all_dates) %in% c(\"Saturday\", \"Sunday\")]\ntrading_dates <- trading_dates[1:252]\n\ntickers <- c(\"NVDA\", \"MSFT\", \"AAPL\", \"SPY\")\nmu      <- c(0.0020, 0.0007, 0.0005, 0.0003)\nsigma   <- c(0.028,  0.015,  0.012,  0.008)\n\nprices_list <- lapply(seq_along(tickers), function(i) {\n    returns <- rnorm(length(trading_dates) - 1, mean = mu[i], sd = sigma[i])\n    data.frame(\n        date    = trading_dates,\n        symbol  = tickers[i],\n        rebased = cumprod(c(1.0, exp(returns))) * 100\n    )\n})\n\ndf        <- do.call(rbind, prices_list)\ndf$symbol <- factor(df$symbol, levels = tickers)\n\n# End-of-line label positions — last observation per symbol\nlast_pts <- do.call(rbind, lapply(tickers, function(s) {\n    sub_df <- df[df$symbol == s, ]\n    sub_df[nrow(sub_df), ]\n}))\n\n# NVDA subset for outperformance ribbon\nnvda_df <- df[df$symbol == \"NVDA\", ]\n\n# Key 2023 market events\nsvb_region <- data.frame(\n    xmin = as.Date(\"2023-03-06\"), xmax = as.Date(\"2023-03-17\"),\n    ymin = -Inf, ymax = Inf\n)\nai_surge_date  <- as.Date(\"2023-05-24\")\nrate_peak_date <- as.Date(\"2023-10-26\")\n\n# Theme composition as named object — idiomatic ggplot2 pattern\nanyplot_theme <- theme_minimal(base_size = 8) +\n    theme(\n        plot.background       = element_rect(fill = PAGE_BG, color = PAGE_BG),\n        panel.background      = element_rect(fill = PAGE_BG, color = NA),\n        panel.grid.major      = element_line(color = GRID_COLOR, linewidth = 0.18),\n        panel.grid.minor      = element_blank(),\n        panel.border          = element_blank(),\n        axis.line             = element_line(color = INK_SOFT, linewidth = 0.35),\n        axis.title            = element_text(color = INK, size = 10),\n        axis.text             = element_text(color = INK_SOFT, size = 8),\n        axis.text.x           = element_text(angle = 30, hjust = 1),\n        plot.title            = element_text(color = INK, size = 12, margin = margin(b = 2)),\n        plot.subtitle         = element_text(color = INK_SOFT, size = 8.5,\n                                              margin = margin(b = 8)),\n        legend.background     = element_rect(fill = ELEVATED_BG, color = NA),\n        legend.box.background = element_blank(),\n        legend.text           = element_text(color = INK_SOFT, size = 8),\n        legend.title          = element_text(color = INK, size = 10),\n        legend.position       = \"right\",\n        plot.margin           = margin(12, 70, 12, 12)\n    )\n\n# Plot\np <- ggplot(df, aes(x = date, y = rebased, color = symbol)) +\n    # SVB collapse period — shaded rect\n    geom_rect(\n        data = svb_region,\n        aes(xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax),\n        fill = \"#AE3030\", alpha = 0.07,\n        inherit.aes = FALSE\n    ) +\n    # Subtle fill ribbon under NVDA to emphasize outperformance\n    geom_ribbon(\n        data = nvda_df,\n        aes(x = date, ymin = 100, ymax = rebased),\n        fill = IMPRINT[1], alpha = 0.12, inherit.aes = FALSE\n    ) +\n    # Reference line at 100\n    geom_hline(yintercept = 100, color = INK_SOFT, linewidth = 0.5,\n               linetype = \"dashed\") +\n    # Event marker lines\n    geom_vline(xintercept = c(ai_surge_date, rate_peak_date),\n               color = INK_SOFT, linewidth = 0.35, linetype = \"dotted\") +\n    # Stock lines\n    geom_line(linewidth = 1.1, alpha = 0.92) +\n    # End-of-line ticker labels — coord_cartesian(clip=\"off\") lets them render outside panel\n    geom_text(\n        data = last_pts,\n        aes(x = date, y = rebased, label = symbol, color = symbol),\n        hjust = -0.2, size = 3.2, fontface = \"bold\",\n        show.legend = FALSE\n    ) +\n    # Styled callout boxes using annotate(geom=\"label\") — distinctively ggplot2\n    # SVB label at y=145 clears the stock cluster; color-coded to match the shaded region\n    annotate(\"label\", x = as.Date(\"2023-03-11\"), y = 145,\n             label = \"SVB Collapse\", color = \"#AE3030\",\n             fill = ELEVATED_BG, label.size = 0.15, size = 3.0,\n             angle = 90, hjust = 0, vjust = 0.4, fontface = \"italic\") +\n    annotate(\"label\", x = ai_surge_date, y = 102,\n             label = \"NVDA AI Surge\", color = IMPRINT[1],\n             fill = ELEVATED_BG, label.size = 0.15, size = 3.0,\n             angle = 90, hjust = 0, vjust = 0.4, fontface = \"italic\") +\n    annotate(\"label\", x = rate_peak_date, y = 102,\n             label = \"Rates Peak\", color = INK_SOFT,\n             fill = ELEVATED_BG, label.size = 0.15, size = 3.0,\n             angle = 90, hjust = 0, vjust = 0.4, fontface = \"italic\") +\n    # clip=\"off\" enables end-of-line labels and off-axis annotations outside the panel\n    coord_cartesian(clip = \"off\") +\n    scale_color_manual(values = setNames(IMPRINT, tickers), name = NULL) +\n    scale_x_date(date_labels = \"%b '%y\", date_breaks = \"2 months\") +\n    labs(\n        title    = \"line-stock-comparison · r · ggplot2 · anyplot.ai\",\n        subtitle = \"Rebased to 100 at start of 2023  ·  shaded region: SVB bank collapse (Mar)\",\n        x        = \"Date\",\n        y        = \"Rebased Price (Start = 100)\"\n    ) +\n    anyplot_theme\n\n# Save\nggsave(\n    filename = sprintf(\"plot-%s.png\", THEME),\n    plot     = p,\n    device   = ragg::agg_png,\n    width    = 8,\n    height   = 4.5,\n    units    = \"in\",\n    dpi      = 400\n)\n"}