{"spec_id":"indicator-ichimoku","library":"makie","language":"julia","code":"# anyplot.ai\n# indicator-ichimoku: Ichimoku Cloud Technical Indicator Chart\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 87/100 | Created: 2026-06-08\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# Theme tokens\nconst THEME       = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG     = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst ELEVATED_BG = THEME == \"light\" ? colorant\"#FFFDF6\" : colorant\"#242420\"\nconst INK         = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT    = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\n\n# Imprint palette — 8 hues, hybrid-v3 sort order\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\",  # 1 — brand green\n    colorant\"#C475FD\",  # 2 — lavender\n    colorant\"#4467A3\",  # 3 — blue\n    colorant\"#BD8233\",  # 4 — ochre\n    colorant\"#AE3030\",  # 5 — matte red\n    colorant\"#2ABCCD\",  # 6 — cyan\n    colorant\"#954477\",  # 7 — rose\n    colorant\"#99B314\",  # 8 — lime\n]\n\n# Data: synthetic stock OHLC across 180 periods with two trend phases\nn     = 180\nshift = 26  # standard Ichimoku displacement parameter\n\n# Price with uptrend then downtrend for visible cloud colour change\nphase_up   = cumsum(randn(120) .* 1.2 .+ 0.22)\nphase_down = cumsum(randn(60)  .* 1.2 .- 0.38)\nmid_prices = vcat(phase_up, phase_down) .+ 100.0\n\nopen_px  = mid_prices .+ randn(n) .* 0.35\nclose_px = mid_prices .+ randn(n) .* 0.35\nhigh_px  = max.(open_px, close_px) .+ abs.(randn(n) .* 0.65)\nlow_px   = min.(open_px, close_px) .- abs.(randn(n) .* 0.65)\n\n# Ichimoku components (standard 9/26/52 parameters)\ntenkan = [(maximum(high_px[max(1, i - 8):i]) + minimum(low_px[max(1, i - 8):i])) / 2\n          for i in 1:n]\nkijun  = [(maximum(high_px[max(1, i - 25):i]) + minimum(low_px[max(1, i - 25):i])) / 2\n          for i in 1:n]\n\nspan_a_raw = (tenkan .+ kijun) ./ 2\n\nspan_b_raw = [(maximum(high_px[max(1, i - 51):i]) + minimum(low_px[max(1, i - 51):i])) / 2\n              for i in 1:n]\n\n# Cloud is plotted 26 periods ahead\ncloud_x = Float64.((shift + 1):(n + shift))\nspan_a_cld = span_a_raw\nspan_b_cld = span_b_raw\n\n# Chikou Span: current close shifted 26 periods into the past\nchikou_x = Float64.(1:(n - shift))\nchikou_y = close_px[(shift + 1):n]\n\nxs = Float64.(1:n)\n\n# ── Figure ──────────────────────────────────────────────────────────────────\ntitle_str = \"indicator-ichimoku · julia · makie · anyplot.ai\"\ntitle_sz  = Int(round(20 * min(1.0, 67 / length(title_str))))\n\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 13,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title             = title_str,\n    titlesize         = title_sz,\n    titlecolor        = INK,\n    xlabel            = \"Period (days)\",\n    ylabel            = \"Price (USD)\",\n    xlabelsize        = 13,\n    ylabelsize        = 13,\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xticklabelsize    = 10,\n    yticklabelsize    = 10,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\n    xtickcolor        = INK_SOFT,\n    ytickcolor        = INK_SOFT,\n    xticksvisible     = false,\n    yticksvisible     = false,\n    backgroundcolor   = PAGE_BG,\n    topspinevisible   = false,\n    rightspinevisible = false,\n    leftspinecolor    = INK_SOFT,\n    bottomspinecolor  = INK_SOFT,\n    xgridvisible      = false,\n    ygridcolor        = RGBAf(INK.r, INK.g, INK.b, 0.12),\n)\n\n# ── Kumo (cloud) — drawn first so it sits behind price data ─────────────────\n# Bullish region: Span A > Span B → green fill\nbull        = span_a_cld .> span_b_cld\nsa_bull     = copy(span_a_cld); sa_bull[.!bull] .= NaN\nsb_bull     = copy(span_b_cld); sb_bull[.!bull] .= NaN\nband!(ax, cloud_x, sb_bull, sa_bull; color = (colorant\"#009E73\", 0.22))\n\n# Bearish region: Span B > Span A → red fill\nbear        = span_b_cld .> span_a_cld\n# Price trend change at period 120 (bull → bear phase boundary in generated data)\ntrans_x = 120.0\nsa_bear     = copy(span_a_cld); sa_bear[.!bear] .= NaN\nsb_bear     = copy(span_b_cld); sb_bear[.!bear] .= NaN\nband!(ax, cloud_x, sa_bear, sb_bear; color = (colorant\"#AE3030\", 0.22))\n\n# Cloud boundary lines\nlines!(ax, cloud_x, span_a_cld; color = IMPRINT_PALETTE[1], linewidth = 1.0, linestyle = :dash)\nlines!(ax, cloud_x, span_b_cld; color = IMPRINT_PALETTE[5], linewidth = 1.0, linestyle = :dash)\n\n# ── Candlestick wicks ────────────────────────────────────────────────────────\nwick_xs = Float64[]\nwick_ys = Float64[]\nfor i in 1:n\n    append!(wick_xs, [xs[i], xs[i]])\n    append!(wick_ys, [low_px[i], high_px[i]])\nend\nlinesegments!(ax, wick_xs, wick_ys;\n    color     = RGBAf(INK.r, INK.g, INK.b, 0.55),\n    linewidth = 0.7,\n)\n\n# ── Candlestick bodies ───────────────────────────────────────────────────────\n# Semantic colours: up=green (#009E73), down=red (#AE3030)\nbw     = 0.55\nmin_ht = 0.05\nup_idx = findall(close_px .>= open_px)\ndn_idx = findall(close_px .<  open_px)\n\nup_rects = [Rect2f(xs[i] - bw / 2, min(open_px[i], close_px[i]),\n                   bw, max(min_ht, abs(close_px[i] - open_px[i]))) for i in up_idx]\ndn_rects = [Rect2f(xs[i] - bw / 2, min(open_px[i], close_px[i]),\n                   bw, max(min_ht, abs(close_px[i] - open_px[i]))) for i in dn_idx]\n\nisempty(up_rects) || poly!(ax, up_rects; color = (IMPRINT_PALETTE[1], 0.85))\nisempty(dn_rects) || poly!(ax, dn_rects; color = (IMPRINT_PALETTE[5], 0.85))\n\n# ── Ichimoku indicator lines ─────────────────────────────────────────────────\nlines!(ax, xs, tenkan; color = IMPRINT_PALETTE[2], linewidth = 1.8)\nlines!(ax, xs, kijun;  color = IMPRINT_PALETTE[3], linewidth = 1.8)\nlines!(ax, chikou_x, chikou_y; color = IMPRINT_PALETTE[4], linewidth = 1.5, linestyle = :dot)\n\n# ── Data storytelling: mark trend reversal at period 120 ────────────────────\ny_hi = maximum(filter(isfinite, high_px))\ny_lo = minimum(filter(isfinite, low_px))\nvlines!(ax, [trans_x]; color = RGBAf(INK.r, INK.g, INK.b, 0.25), linewidth = 1.2, linestyle = :dash)\ntext!(ax, [trans_x + 1.5], [y_lo + (y_hi - y_lo) * 0.90];\n    text = [\"Trend reversal\"], color = INK_SOFT, fontsize = 9, align = (:left, :top))\n\n# ── Legend ───────────────────────────────────────────────────────────────────\nlegend_elems = [\n    LineElement(color = IMPRINT_PALETTE[2], linewidth = 2.5),\n    LineElement(color = IMPRINT_PALETTE[3], linewidth = 2.5),\n    LineElement(color = IMPRINT_PALETTE[4], linewidth = 2.5, linestyle = :dot),\n    PolyElement(color = (colorant\"#009E73\", 0.45)),\n    PolyElement(color = (colorant\"#AE3030\", 0.45)),\n]\nlegend_labels = [\n    \"Tenkan-sen (9)\",\n    \"Kijun-sen (26)\",\n    \"Chikou Span (−26)\",\n    \"Kumo Bullish\",\n    \"Kumo Bearish\",\n]\n\nLegend(fig[1, 2], legend_elems, legend_labels;\n    framevisible    = false,\n    labelcolor      = INK,\n    backgroundcolor = PAGE_BG,\n    patchsize       = (28, 12),\n    labelsize       = 11,\n)\n\ncolsize!(fig.layout, 1, Relative(0.82))\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}