{"spec_id":"indicator-bollinger","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' indicator-bollinger: Bollinger Bands Indicator Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 90/100 | Created: 2026-05-17\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(ragg)\n\nset.seed(42)\n\n# --- Theme tokens -----------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nIMPRINT   <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                 \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# --- Data -------------------------------------------------------------------\nn_periods <- 150\ndates <- seq.Date(from = as.Date(\"2024-01-01\"), by = \"1 day\", length.out = n_periods)\n\nprice <- 100 + cumsum(rnorm(n_periods, mean = 0.15, sd = 1.2))\n\nsma_period <- 20\nsma <- numeric(n_periods)\nfor (i in 1:n_periods) {\n  if (i < sma_period) {\n    sma[i] <- mean(price[1:i])\n  } else {\n    sma[i] <- mean(price[(i - sma_period + 1):i])\n  }\n}\n\nstd_dev <- numeric(n_periods)\nfor (i in 1:n_periods) {\n  if (i < sma_period) {\n    std_dev[i] <- sd(price[1:i])\n  } else {\n    std_dev[i] <- sd(price[(i - sma_period + 1):i])\n  }\n}\n\nupper_band <- sma + 2 * std_dev\nlower_band <- sma - 2 * std_dev\n\ndf <- data.frame(\n  date = dates,\n  close = price,\n  sma = sma,\n  upper_band = upper_band,\n  lower_band = lower_band\n)\n\n# --- Plot -------------------------------------------------------------------\np <- ggplot(df, aes(x = date)) +\n  geom_ribbon(aes(ymin = lower_band, ymax = upper_band),\n              fill = IMPRINT[1], alpha = 0.15) +\n  geom_line(aes(y = upper_band), color = IMPRINT[1], linewidth = 0.6,\n            linetype = \"solid\", alpha = 0.6) +\n  geom_line(aes(y = lower_band), color = IMPRINT[1], linewidth = 0.6,\n            linetype = \"solid\", alpha = 0.6) +\n  geom_line(aes(y = sma), color = IMPRINT[1], linewidth = 0.8,\n            linetype = \"dashed\", alpha = 0.7) +\n  geom_line(aes(y = close), color = IMPRINT[1], linewidth = 1.2) +\n  labs(\n    title = \"indicator-bollinger · ggplot2 · anyplot.ai\",\n    x = \"Date\",\n    y = \"Price ($)\"\n  ) +\n  theme_minimal(base_size = 14) +\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 = INK_SOFT, linewidth = 0.3),\n    panel.grid.minor = element_blank(),\n    panel.grid.major.x = element_blank(),\n    axis.title       = element_text(color = INK, size = 20),\n    axis.text        = element_text(color = INK_SOFT, size = 16),\n    plot.title       = element_text(color = INK, size = 24, face = \"plain\"),\n    legend.background = element_rect(fill = PAGE_BG, color = NA),\n    legend.text      = element_text(color = INK_SOFT, size = 16),\n    plot.margin      = unit(c(12, 12, 12, 12), \"mm\")\n  )\n\n# --- Save -------------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot     = p,\n  device   = ragg::agg_png,\n  width    = 16,\n  height   = 9,\n  units    = \"in\",\n  dpi      = 300\n)\n"}