{"spec_id":"line-yield-curve","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' line-yield-curve: Yield Curve (Interest Rate Term Structure)\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 87/100 | Created: 2026-06-10\n\nlibrary(ggplot2)\nlibrary(scales)\nlibrary(ragg)\n\n# Theme tokens (Imprint palette + adaptive chrome)\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\"\nINK_MUTED   <- if (THEME == \"light\") \"#6B6A63\" else \"#A8A79F\"\nGRID_COLOR  <- scales::alpha(INK, 0.15)\n\n# Imprint categorical palette — positions 1, 2, 3 for three yield curves\nIMPRINT_PALETTE <- c(\"#009E73\", \"#C475FD\", \"#4467A3\")\n\n# Data: U.S. Treasury yield curves on three historically significant dates\nmaturities     <- c(\"1M\",  \"3M\",  \"6M\",  \"1Y\", \"2Y\", \"3Y\", \"5Y\", \"7Y\", \"10Y\", \"20Y\", \"30Y\")\nmaturity_years <- c(1/12, 0.25,  0.5,   1.0,  2.0,  3.0,  5.0,  7.0,  10.0,  20.0,  30.0)\n\n# Jan 2021: Normal steep curve (near-zero short rates, post-pandemic QE)\nyields_jan2021 <- c(0.05, 0.06, 0.08, 0.09, 0.12, 0.20, 0.45, 0.80, 1.08, 1.57, 1.83)\n# Jul 2023: Deeply inverted (peak of Fed hiking cycle, 5.25-5.50% fed funds)\nyields_jul2023 <- c(5.30, 5.43, 5.52, 5.44, 4.87, 4.55, 4.28, 4.18, 3.97, 4.11, 3.96)\n# Jan 2024: Still inverted, beginning to normalize\nyields_jan2024 <- c(5.52, 5.43, 5.36, 5.12, 4.43, 4.17, 4.00, 4.01, 3.97, 4.35, 4.22)\n\ndf <- data.frame(\n  maturity       = rep(maturities, 3),\n  maturity_years = rep(maturity_years, 3),\n  yield_pct      = c(yields_jan2021, yields_jul2023, yields_jan2024),\n  curve_date     = factor(\n    rep(c(\"Jan 2021\", \"Jul 2023\", \"Jan 2024\"), each = length(maturities)),\n    levels = c(\"Jan 2021\", \"Jul 2023\", \"Jan 2024\")\n  )\n)\n\ny_top   <- max(df$yield_pct)\ny_annot <- y_top + 0.30\nplot_title <- \"line-yield-curve · r · ggplot2 · anyplot.ai\"\n\n# Data-driven ribbon: front-end region (1M–2Y) between Jan 2021 baseline and\n# Jul 2023 peak — shows the full magnitude of the hiking cycle at the short end\nfront_end_mask <- maturity_years <= 2.0\ninversion_df <- data.frame(\n  maturity_years = maturity_years[front_end_mask],\n  ymin           = yields_jan2021[front_end_mask],\n  ymax           = yields_jul2023[front_end_mask]\n)\n\np <- ggplot(df, aes(x = maturity_years, y = yield_pct, color = curve_date)) +\n  geom_ribbon(\n    data = inversion_df,\n    aes(x = maturity_years, ymin = ymin, ymax = ymax),\n    fill = INK_MUTED, alpha = 0.10, inherit.aes = FALSE\n  ) +\n  geom_line(linewidth = 1.2, lineend = \"round\") +\n  geom_point(size = 3.5) +\n  annotate(\"text\",\n    x     = 0.7,   y     = y_annot,\n    label = \"Inversion zone\",\n    color = INK_MUTED, size = 3.5, hjust = 0.5, vjust = 0\n  ) +\n  scale_color_manual(values = IMPRINT_PALETTE, name = NULL) +\n  scale_x_log10(\n    breaks = maturity_years,\n    labels = maturities\n  ) +\n  scale_y_continuous(\n    labels = function(x) sprintf(\"%.1f%%\", x),\n    expand = expansion(mult = c(0.05, 0.22))\n  ) +\n  labs(\n    title = plot_title,\n    x     = \"Maturity\",\n    y     = \"Yield (%)\"\n  ) +\n  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.3),\n    panel.grid.minor  = element_blank(),\n    panel.border      = element_blank(),\n    axis.title        = element_text(color = INK,      size = 10),\n    axis.text         = element_text(color = INK_SOFT, size = 8),\n    axis.line         = element_line(color = INK_SOFT, linewidth = 0.5),\n    axis.ticks        = element_blank(),\n    plot.title        = element_text(color = INK,      size = 12, margin = margin(b = 12)),\n    legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.3),\n    legend.text       = element_text(color = INK_SOFT, size = 9),\n    legend.title      = element_blank(),\n    legend.key.width  = unit(1.5, \"cm\"),\n    legend.position   = \"right\",\n    plot.margin       = margin(20, 20, 20, 20, \"pt\")\n  )\n\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"}