{"spec_id":"indicator-ema","library":"makie","language":"julia","code":"# anyplot.ai\n# indicator-ema: Exponential Moving Average (EMA) Indicator Chart\n# Library: makie 0.21.9 | Julia 1.11.9\n# Quality: 88/100 | Created: 2026-09-02\n\nusing CairoMakie\nusing Colors\nusing Random\nusing Dates\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 INK      = THEME == \"light\" ? colorant\"#1A1A17\" : colorant\"#F0EFE8\"\nconst INK_SOFT = THEME == \"light\" ? colorant\"#4A4A44\" : colorant\"#B8B7B0\"\nconst IMPRINT_PALETTE = [\n    colorant\"#009E73\", colorant\"#C475FD\", colorant\"#4467A3\", colorant\"#BD8233\",\n    colorant\"#AE3030\", colorant\"#2ABCCD\", colorant\"#954477\", colorant\"#99B314\",\n]\n\n# --- Data -----------------------------------------------------------------\nn_days = 140\ntrading_days = 1:n_days\nstart_date = Date(2024, 1, 2)\ndates = [start_date + Day(round(Int, 1.4 * (i - 1))) for i in trading_days]\n\ndaily_returns = randn(n_days) .* 1.8 .+ 0.08\nclose_price = 150.0 .+ cumsum(daily_returns)\n\n# 12-day EMA (short-term momentum)\nalpha_short = 2 / (12 + 1)\nema_short = similar(close_price)\nema_short[1] = close_price[1]\nfor i in 2:n_days\n    ema_short[i] = alpha_short * close_price[i] + (1 - alpha_short) * ema_short[i - 1]\nend\n\n# 50-day EMA (long-term trend)\nalpha_long = 2 / (50 + 1)\nema_long = similar(close_price)\nema_long[1] = close_price[1]\nfor i in 2:n_days\n    ema_long[i] = alpha_long * close_price[i] + (1 - alpha_long) * ema_long[i - 1]\nend\n\n# Crossover points: sign changes of (short - long)\ndiff_ema = ema_short .- ema_long\ncrossover_idx = Int[]\nfor i in 2:n_days\n    if sign(diff_ema[i]) != sign(diff_ema[i - 1])\n        push!(crossover_idx, i)\n    end\nend\n\n# Golden cross (bullish, short EMA moves above long EMA) vs. death cross (bearish)\ngolden_idx = filter(i -> diff_ema[i] > 0, crossover_idx)\ndeath_idx = filter(i -> diff_ema[i] < 0, crossover_idx)\n\n# Trend-regime segments (for band shading): contiguous runs of one sign\nregime_bounds = sort(unique(vcat(1, crossover_idx, n_days + 1)))\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-ema · julia · makie · anyplot.ai\",\n    titlesize         = 24,\n    titlecolor        = INK,\n    xlabel            = \"Trading Date\",\n    ylabel            = \"Closing Price (USD)\",\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    xtickcolor        = INK_SOFT,\n    ytickcolor        = 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.12),\n)\n\ntick_positions = round.(Int, range(1, n_days; length = 6))\nax.xticks = (tick_positions, [Dates.format(dates[i], \"u d\") for i in tick_positions])\n\n# Trend-regime shading: tint the band between the two EMAs by prevailing\n# direction (bullish short-over-long vs. bearish), so the golden/death cross\n# story reads at a glance before you even spot the marker.\nbullish_fill = RGBAf(IMPRINT_PALETTE[1].r, IMPRINT_PALETTE[1].g, IMPRINT_PALETTE[1].b, 0.10)\nbearish_fill = RGBAf(IMPRINT_PALETTE[5].r, IMPRINT_PALETTE[5].g, IMPRINT_PALETTE[5].b, 0.10)\nfor k in 1:(length(regime_bounds) - 1)\n    seg_start = regime_bounds[k]\n    seg_end = regime_bounds[k + 1] - 1\n    seg_end < seg_start && continue\n    seg = seg_start:seg_end\n    band!(\n        ax, trading_days[seg], ema_long[seg], ema_short[seg];\n        color = diff_ema[seg_start] >= 0 ? bullish_fill : bearish_fill,\n    )\nend\n\nlines!(ax, trading_days, close_price; color = IMPRINT_PALETTE[1], linewidth = 3.0, label = \"Close price\")\nlines!(ax, trading_days, ema_short; color = IMPRINT_PALETTE[2], linewidth = 2.0, label = \"EMA (12)\")\nlines!(ax, trading_days, ema_long; color = IMPRINT_PALETTE[3], linewidth = 2.0, label = \"EMA (50)\")\n\nscatter!(\n    ax, trading_days[golden_idx], ema_short[golden_idx];\n    color = IMPRINT_PALETTE[1], markersize = 18, marker = :utriangle,\n    strokewidth = 1.5, strokecolor = PAGE_BG, label = \"Golden cross\",\n)\nscatter!(\n    ax, trading_days[death_idx], ema_short[death_idx];\n    color = IMPRINT_PALETTE[5], markersize = 18, marker = :dtriangle,\n    strokewidth = 1.5, strokecolor = PAGE_BG, label = \"Death cross\",\n)\n\naxislegend(ax; position = :lt, framevisible = false, labelcolor = INK, backgroundcolor = :transparent)\n\n# --- Save -------------------------------------------------------------------\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}