{"spec_id":"ohlc-bar","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' ohlc-bar: OHLC Bar Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 87/100 | Created: 2026-05-17\n\nlibrary(ggplot2)\nlibrary(dplyr)\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\"\nBORDER_COL  <- if (THEME == \"light\") \"#D0CEC4\" else \"#3F3D37\"\nGRID_COL    <- if (THEME == \"light\") \"#E8E6DC\" else \"#2A2824\"\nIMPRINT   <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                 \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# --- Data: Realistic stock prices over 50 trading days ----------------------\ndates <- seq(as.Date(\"2024-01-01\"), by = \"1 day\", length.out = 50)\n# Filter to trading days (Mon-Fri)\ntrading_dates <- dates[weekdays(dates) %in% c(\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\")]\ntrading_dates <- trading_dates[1:45]\n\n# Generate realistic OHLC data with trend\nn <- length(trading_dates)\nreturns <- rnorm(n, mean = 0.001, sd = 0.015)\nclose_prices <- 150 * cumprod(1 + returns)\n\nohlc_data <- data.frame(\n  date = trading_dates,\n  open = close_prices + rnorm(n, mean = -0.5, sd = 0.8),\n  close = close_prices,\n  high = pmax(close_prices, close_prices + abs(rnorm(n, mean = 1.5, sd = 0.6))),\n  low = pmin(close_prices, close_prices - abs(rnorm(n, mean = 1.5, sd = 0.6)))\n) %>%\n  mutate(\n    direction = ifelse(close > open, \"up\", \"down\"),\n    color_val = if_else(direction == \"up\", \"#009E73\", \"#AE3030\"),  # imprint green / red\n    x_pos = as.numeric(date),\n    volatility = (high - low) / low,\n    opacity_val = 0.6 + 0.4 * min(volatility / max(volatility), 1.0)\n  )\n\n# --- Build plot segments for high-low and open-close ticks ------------------\n# Main high-low vertical lines\nhl_segments <- ohlc_data %>%\n  mutate(\n    y_min = low,\n    y_max = high\n  ) %>%\n  select(x_pos, y_min, y_max, direction, color_val, opacity_val)\n\n# Open tick marks (left side, small horizontal)\nopen_segments <- ohlc_data %>%\n  mutate(\n    x_start = x_pos - 1.5,\n    x_end = x_pos,\n    y = open\n  ) %>%\n  select(x_start, x_end, y, direction, color_val, opacity_val)\n\n# Close tick marks (right side, small horizontal)\nclose_segments <- ohlc_data %>%\n  mutate(\n    x_start = x_pos,\n    x_end = x_pos + 1.5,\n    y = close\n  ) %>%\n  select(x_start, x_end, y, direction, color_val, opacity_val)\n\n# Compute moving average for trend visualization\nma_period <- 7\nma_close <- rep(NA, nrow(ohlc_data))\nfor (i in ma_period:nrow(ohlc_data)) {\n  ma_close[i] <- mean(ohlc_data$close[(i - ma_period + 1):i])\n}\nohlc_data$ma_close <- ma_close\n\n# --- Create the plot --------------------------------------------------------\np <- ggplot() +\n  # Subtle trend line (moving average)\n  geom_line(\n    data = ohlc_data,\n    aes(x = x_pos, y = ma_close),\n    color = INK_SOFT,\n    linewidth = 0.6,\n    linetype = \"dotted\",\n    alpha = 0.5\n  ) +\n  # High-low vertical lines with opacity variation for volatility\n  geom_segment(\n    data = hl_segments,\n    aes(x = x_pos, xend = x_pos, y = y_min, yend = y_max, color = direction, alpha = opacity_val),\n    linewidth = 1.3,\n    show.legend = FALSE\n  ) +\n  # Open tick marks (left)\n  geom_segment(\n    data = open_segments,\n    aes(x = x_start, xend = x_end, y = y, yend = y, color = direction, alpha = opacity_val),\n    linewidth = 1.1,\n    show.legend = FALSE\n  ) +\n  # Close tick marks (right)\n  geom_segment(\n    data = close_segments,\n    aes(x = x_start, xend = x_end, y = y, yend = y, color = direction, alpha = opacity_val),\n    linewidth = 1.1,\n    show.legend = FALSE\n  ) +\n  scale_color_manual(\n    values = c(\"up\" = \"#009E73\", \"down\" = \"#AE3030\")  # imprint semantic anchors\n  ) +\n  scale_alpha_identity() +\n  scale_x_continuous(\n    breaks = seq(1, nrow(ohlc_data), by = 5),\n    labels = format(ohlc_data$date[seq(1, nrow(ohlc_data), by = 5)], \"%b %d\"),\n    expand = c(0.02, 0)\n  ) +\n  labs(\n    title = \"ohlc-bar · 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.border        = element_rect(fill = NA, color = BORDER_COL, linewidth = 0.5),\n    panel.grid.major.y  = element_line(color = GRID_COL, linewidth = 0.2),\n    panel.grid.major.x  = element_blank(),\n    panel.grid.minor    = element_blank(),\n    axis.ticks          = element_line(color = BORDER_COL, linewidth = 0.3),\n    axis.ticks.length   = unit(4, \"pt\"),\n    axis.title          = element_text(color = INK, size = 20, face = \"bold\"),\n    axis.text           = element_text(color = INK_SOFT, size = 16),\n    axis.text.x         = element_text(angle = 45, hjust = 1),\n    plot.title          = element_text(color = INK, size = 24, face = \"bold\", margin = margin(b = 12))\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"}