{"spec_id":"drawdown-basic","library":"makie","language":"julia","code":"# anyplot.ai\n# drawdown-basic: Drawdown Chart\n# Library: makie 0.22.10 | Julia 1.11.9\n# Quality: 87/100 | Created: 2026-05-23\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# Drawdown = loss → semantic red (Imprint palette position 3)\nconst DRAW_RED = colorant\"#AE3030\"\n# Recovery = new high → anyplot green (palette position 1)\nconst RECOVER_GREEN = colorant\"#009E73\"\n\n# Data: 756 trading days with two distinct drawdown episodes.\n# Phase structure: growth → first correction → full recovery → growth → major correction → partial recovery\n# Ensures at least one complete recovery point is detectable.\nn_days = 756\nphase_returns = vcat(\n    0.0012 .+ 0.010 .* randn(120),   # bull phase: establish initial ATH\n    -0.0030 .+ 0.016 .* randn(90),   # first correction: ~-24% drawdown\n    0.0025 .+ 0.012 .* randn(160),   # strong recovery: surpasses previous ATH\n    0.0008 .+ 0.009 .* randn(60),    # consolidation at new high\n    -0.0035 .+ 0.015 .* randn(140),  # major correction: deeper ~-38% max DD\n    0.0010 .+ 0.011 .* randn(186),   # partial recovery (unresolved)\n)\nnav = 100.0 .* cumprod(1.0 .+ phase_returns)\n\n# Drawdown: percentage decline from running all-time high\nrunning_peak = accumulate(max, nav)\ndrawdown = (nav .- running_peak) ./ running_peak .* 100.0\n\nmax_dd_idx = argmin(drawdown)\nmax_dd_val = drawdown[max_dd_idx]\n\ndays = collect(1:n_days)\n\n# Detect recovery points: days where drawdown returns to near-zero after a ≥5% episode\n# Use let block to avoid Julia soft-scope issues in top-level scripts\nrecovery_days = let in_ep = false, rdays = Int[]\n    for i in 1:n_days\n        if drawdown[i] < -5.0\n            in_ep = true\n        elseif in_ep && drawdown[i] >= -0.5\n            push!(rdays, i)\n            in_ep = false\n        end\n    end\n    rdays\nend\n\n# Max DD duration: walk back from trough to episode start\nmax_dd_start = let s = max_dd_idx\n    while s > 1 && drawdown[s - 1] < -0.5\n        s -= 1\n    end\n    s\nend\nmax_dd_duration = max_dd_idx - max_dd_start + 1\n\n# Recovery time from max DD trough (nothing if unresolved)\nmax_dd_end_idx = findfirst(i -> i > max_dd_idx && drawdown[i] >= -0.5, 1:n_days)\nrecovery_label = max_dd_end_idx !== nothing ? \"$(max_dd_end_idx - max_dd_idx) days\" : \"pending\"\n\n# Figure\nfig = Figure(\n    resolution = (1600, 900),\n    fontsize = 14,\n    backgroundcolor = PAGE_BG,\n)\n\nax = Axis(\n    fig[1, 1];\n    title = \"drawdown-basic · julia · makie · anyplot.ai\",\n    titlesize = 20,\n    titlecolor = INK,\n    xlabel = \"Trading Day\",\n    ylabel = \"Drawdown (%)\",\n    xlabelsize = 14,\n    ylabelsize = 14,\n    xticklabelsize = 12,\n    yticklabelsize = 12,\n    xlabelcolor = INK,\n    ylabelcolor = INK,\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    xticks = ([1, 252, 504, 756], [\"Start\", \"Year 1\", \"Year 2\", \"Year 3\"]),\n    xgridvisible = false,\n    ygridcolor = RGBAf(INK.r, INK.g, INK.b, 0.10),\n    yminorgridvisible = false,\n    xminorgridvisible = false,\n)\n\n# Filled drawdown area — more opaque on dark theme for visibility\nfill_alpha = THEME == \"light\" ? 0.28 : 0.48\nband!(ax, days, drawdown, zeros(n_days); color = (DRAW_RED, fill_alpha))\n\n# Drawdown line\nlines!(ax, days, drawdown; color = DRAW_RED, linewidth = 2.2)\n\n# Zero baseline reference\nhlines!(ax, [0.0]; color = INK_SOFT, linewidth = 1.0)\n\n# Recovery point markers: green upward triangles at zero baseline (new all-time highs)\nif !isempty(recovery_days)\n    scatter!(ax, Float64.(recovery_days), zeros(length(recovery_days));\n        marker = :utriangle,\n        color = RECOVER_GREEN,\n        markersize = 14,\n        strokewidth = 0,\n    )\nend\n\n# Max DD marker — hollow circle at the trough\nscatter!(ax, [Float64(max_dd_idx)], [max_dd_val];\n    color = PAGE_BG, markersize = 16,\n    strokewidth = 2.5, strokecolor = DRAW_RED)\n\n# Dotted leader line connecting trough to annotation\nann_x = min(Float64(max_dd_idx) + 30, Float64(n_days) - 160)\nann_y = max_dd_val * 0.55\nlines!(ax, [Float64(max_dd_idx), ann_x], [max_dd_val, ann_y];\n    color = INK_SOFT, linewidth = 0.8, linestyle = :dot)\n\n# Multi-line annotation: max DD percentage, duration, and recovery time\nstats_text = \"Max DD: $(round(max_dd_val, digits=1))%\\nDuration: $(max_dd_duration) days\\nRecovery: $(recovery_label)\"\ntext!(ax, ann_x, ann_y;\n    text = stats_text,\n    align = (:left, :center),\n    color = INK,\n    fontsize = 13,\n)\n\n# Save\nsave(\"plot-$(THEME).png\", fig; px_per_unit = 2)\n"}