{"spec_id":"indicator-macd","library":"makie","language":"julia","code":"# anyplot.ai\n# indicator-macd: MACD Technical Indicator Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 84/100 | Created: 2026-09-05\n\nusing CairoMakie\nusing Colors\nusing Random\n\nRandom.seed!(42)\n\n# Theme tokens (see prompts/default-style-guide.md \"Theme-adaptive Chrome\")\nconst THEME    = get(ENV, \"ANYPLOT_THEME\", \"light\")\nconst PAGE_BG  = THEME == \"light\" ? colorant\"#FAF8F1\" : colorant\"#1A1A17\"\nconst INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\n\n# Imprint palette — MACD line uses blue so it never blends into the green\n# gain bars; histogram keeps the sanctioned green/red finance semantic.\nconst MACD_COLOR   = colorant\"#4467A3\"  # Imprint position 3 — blue\nconst SIGNAL_COLOR = colorant\"#C475FD\"  # Imprint position 2 — lavender\nconst GAIN_COLOR   = colorant\"#009E73\"  # semantic: bullish histogram bars (finance profit/up → green)\nconst LOSS_COLOR   = colorant\"#AE3030\"  # semantic: bearish histogram bars (finance loss/down → red)\n\n# Data: simulate 120 trading days of closing prices, derive MACD/signal/histogram\nn_days = 120\ndaily_returns = randn(n_days) .* 1.2\ntrend = 8 .* sin.(range(0, 4π, length = n_days))\nclosing_prices = 150 .+ cumsum(daily_returns) .+ trend\n\nfunction ema(values, span)\n    alpha = 2 / (span + 1)\n    smoothed = similar(values, Float64)\n    smoothed[1] = values[1]\n    for i in 2:length(values)\n        smoothed[i] = alpha * values[i] + (1 - alpha) * smoothed[i - 1]\n    end\n    return smoothed\nend\n\nema_fast = ema(closing_prices, 12)\nema_slow = ema(closing_prices, 26)\nmacd_line = ema_fast .- ema_slow\nsignal_line = ema(macd_line, 9)\nmacd_histogram = macd_line .- signal_line\n\ntrading_day = 1:n_days\nbar_colors = [value >= 0 ? GAIN_COLOR : LOSS_COLOR for value in macd_histogram]\n\n# Crossover points (MACD line crosses the signal line) reinforce the\n# buy/sell-signal narrative called out in the spec's Applications section.\ncrossover_idx = [i for i in 2:n_days if sign(macd_histogram[i - 1]) != sign(macd_histogram[i])]\ncrossover_x = trading_day[crossover_idx]\ncrossover_y = macd_line[crossover_idx]\ncrossover_bullish = [macd_histogram[i] >= 0 for i in crossover_idx]\ncrossover_colors = [bullish ? GAIN_COLOR : LOSS_COLOR for bullish in crossover_bullish]\ncrossover_markers = [bullish ? :utriangle : :dtriangle for bullish in crossover_bullish]\n\n# Plot\nfig = Figure(\n    size            = (1600, 900),\n    fontsize        = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title             = \"indicator-macd · julia · makie · anyplot.ai\",\n    titlesize         = 20,\n    titlecolor        = INK,\n    xlabel            = \"Trading Day\",\n    ylabel            = \"MACD Value\",\n    xlabelsize        = 14,\n    ylabelsize        = 14,\n    xlabelcolor       = INK,\n    ylabelcolor       = INK,\n    xticklabelsize    = 12,\n    yticklabelsize    = 12,\n    xticklabelcolor   = INK_SOFT,\n    yticklabelcolor   = INK_SOFT,\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.15),\n)\n\nbarplot!(ax, trading_day, macd_histogram; color = bar_colors, width = 0.8, strokewidth = 0)\nhlines!(ax, [0]; color = INK_SOFT, linewidth = 1.5, linestyle = :dash)\nlines!(ax, trading_day, macd_line; color = MACD_COLOR, linewidth = 3, label = \"MACD (12, 26)\")\nlines!(ax, trading_day, signal_line; color = SIGNAL_COLOR, linewidth = 3, label = \"Signal (9)\")\nscatter!(\n    ax, crossover_x, crossover_y;\n    color = crossover_colors, marker = crossover_markers,\n    markersize = 16, strokewidth = 1.5, strokecolor = PAGE_BG,\n)\n\naxislegend(ax; position = :lt, framevisible = false, labelcolor = INK, labelsize = 12)\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}