{"spec_id":"kagi-basic","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' kagi-basic: Basic Kagi Chart\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 95/100 | Created: 2026-05-17\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tibble)\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\"\n\n# Kagi-specific colors: yang (uptrend) uses brand green, yin (downtrend) uses vermillion\nCOLOR_YANG  <- \"#009E73\"  # Okabe-Ito position 1 — uptrend\nCOLOR_YIN   <- \"#AE3030\"  # imprint red — downtrend\n\n# --- Generate price data -----------------------------------------------------\n# Daily closing prices over ~6 months with realistic volatility\nn_days <- 150\ndates <- seq(as.Date(\"2025-10-01\"), by = \"day\", length.out = n_days)\n\n# Random walk with slight upward drift\nprice_changes <- rnorm(n_days, mean = 0.003, sd = 0.02)\nprices <- 100 * exp(cumsum(price_changes))\n\ndf_raw <- tibble::tibble(\n  date = dates,\n  close = prices\n)\n\n# --- Calculate Kagi segments -------------------------------------------------\n# Reversal threshold: 4% of current price\nreversal_pct <- 0.04\n\nkagi_data <- list()\nline_idx <- 0\ndirection <- NA  # \"up\" or \"down\"\nline_start_idx <- 1\nline_start_price <- df_raw$close[1]\nlast_reversal_price <- df_raw$close[1]\n\nfor (i in 2:nrow(df_raw)) {\n  price <- df_raw$close[i]\n\n  # Determine trend direction based on reversal threshold\n  if (is.na(direction)) {\n    # Initial direction: assume uptrend if price goes up\n    if (price > line_start_price * (1 + reversal_pct)) {\n      direction <- \"up\"\n      last_reversal_price <- line_start_price\n    } else if (price < line_start_price * (1 - reversal_pct)) {\n      direction <- \"down\"\n      last_reversal_price <- line_start_price\n    }\n  } else if (direction == \"up\") {\n    # In uptrend, check if price reverses down\n    if (price < last_reversal_price * (1 - reversal_pct)) {\n      # Reversal: end current up line, start down line\n      line_idx <- line_idx + 1\n      kagi_data[[line_idx]] <- tibble::tibble(\n        line_id = line_idx,\n        line_num = line_idx,\n        price_start = line_start_price,\n        price_end = last_reversal_price,\n        idx_start = line_start_idx,\n        idx_end = i - 1,\n        trend = \"up\"\n      )\n\n      direction <- \"down\"\n      line_start_price <- last_reversal_price\n      line_start_idx <- i - 1\n      last_reversal_price <- price\n    } else if (price > last_reversal_price) {\n      # Continue up, update high\n      last_reversal_price <- price\n    }\n  } else {  # direction == \"down\"\n    # In downtrend, check if price reverses up\n    if (price > last_reversal_price * (1 + reversal_pct)) {\n      # Reversal: end current down line, start up line\n      line_idx <- line_idx + 1\n      kagi_data[[line_idx]] <- tibble::tibble(\n        line_id = line_idx,\n        line_num = line_idx,\n        price_start = line_start_price,\n        price_end = last_reversal_price,\n        idx_start = line_start_idx,\n        idx_end = i - 1,\n        trend = \"down\"\n      )\n\n      direction <- \"up\"\n      line_start_price <- last_reversal_price\n      line_start_idx <- i - 1\n      last_reversal_price <- price\n    } else if (price < last_reversal_price) {\n      # Continue down, update low\n      last_reversal_price <- price\n    }\n  }\n}\n\n# Add final segment\nif (!is.na(direction)) {\n  line_idx <- line_idx + 1\n  kagi_data[[line_idx]] <- tibble::tibble(\n    line_id = line_idx,\n    line_num = line_idx,\n    price_start = line_start_price,\n    price_end = last_reversal_price,\n    idx_start = line_start_idx,\n    idx_end = nrow(df_raw),\n    trend = direction\n  )\n}\n\nkagi_df <- bind_rows(kagi_data)\n\n# --- Create line segments for plotting ----------------------------------------\nsegments <- list()\nseg_idx <- 1\n\nfor (i in 1:nrow(kagi_df)) {\n  segment <- kagi_df[i, ]\n\n  # Create vertical line segment\n  segments[[seg_idx]] <- tibble::tibble(\n    x = segment$line_num,\n    y_start = segment$price_start,\n    y_end = segment$price_end,\n    trend = segment$trend,\n    color = if_else(segment$trend == \"up\", COLOR_YANG, COLOR_YIN),\n    width = if_else(segment$trend == \"up\", 1.2, 0.6)\n  )\n\n  seg_idx <- seg_idx + 1\n\n  # Add horizontal segment (shoulder/waist) connecting to next line\n  if (i < nrow(kagi_df)) {\n    next_start <- kagi_df$price_start[i + 1]\n    segments[[seg_idx]] <- tibble::tibble(\n      x = c(segment$line_num, segment$line_num + 0.5),\n      y_start = c(segment$price_end, segment$price_end),\n      y_end = c(segment$price_end, segment$price_end),\n      trend = NA,\n      color = INK_SOFT,\n      width = 0.3\n    )\n    seg_idx <- seg_idx + 1\n  }\n}\n\n# --- Plot -------------------------------------------------------------------\n# Build base data for geom_segment calls\nplot_lines <- list()\n\n# Draw vertical lines\nfor (i in 1:nrow(kagi_df)) {\n  segment <- kagi_df[i, ]\n  color <- if_else(segment$trend == \"up\", COLOR_YANG, COLOR_YIN)\n  width <- if_else(segment$trend == \"up\", 1.2, 0.6)\n\n  plot_lines[[i]] <- tibble::tibble(\n    x = segment$line_num,\n    xend = segment$line_num,\n    y = segment$price_start,\n    yend = segment$price_end,\n    trend = segment$trend,\n    color = color,\n    linewidth = width\n  )\n}\n\nplot_data <- bind_rows(plot_lines)\n\np <- ggplot() +\n  geom_segment(\n    data = plot_data,\n    aes(x = x, xend = xend, y = y, yend = yend, color = trend, linewidth = trend),\n    show.legend = FALSE\n  ) +\n  scale_color_manual(values = c(\"up\" = COLOR_YANG, \"down\" = COLOR_YIN)) +\n  scale_linewidth_manual(values = c(\"up\" = 1.2, \"down\" = 0.6)) +\n  labs(\n    title = \"kagi-basic · ggplot2 · anyplot.ai\",\n    x = \"Line Index\",\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    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  )\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"}