{"spec_id":"drawdown-basic","library":"plotly","language":"python","code":"\"\"\" anyplot.ai\ndrawdown-basic: Drawdown Chart\nLibrary: plotly 6.7.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-23\n\"\"\"\n\nimport os\nimport sys\n\n\n# Remove current directory from sys.path to avoid shadowing installed packages\nsys.path = [p for p in sys.path if p not in (\"\", \".\")]\n\nimport numpy as np\nimport pandas as pd\nimport plotly.graph_objects as go\n\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nGRID = \"rgba(26, 26, 23, 0.10)\" if THEME == \"light\" else \"rgba(240, 239, 232, 0.10)\"\n\nLOSS_RED = \"#AE3030\"  # Imprint palette position 3 — semantic: loss/drawdown\nRECOVERY_GREEN = \"#009E73\"  # Imprint palette position 1 — semantic: recovery/new high\n\n# Data — simulated stock price with realistic volatility\nnp.random.seed(42)\nn_days = 500\ndates = pd.date_range(\"2022-01-01\", periods=n_days, freq=\"B\").strftime(\"%Y-%m-%d\").tolist()\n\nreturns = np.random.normal(0.0003, 0.015, n_days)\nreturns[100:150] -= 0.008  # market correction\nreturns[300:350] -= 0.012  # larger drawdown period\nprice = 100 * np.cumprod(1 + returns)\n\n# Calculate drawdown\nrunning_max = np.maximum.accumulate(price)\ndrawdown = (price - running_max) / running_max * 100\n\n# Find maximum drawdown\nmax_dd_idx = np.argmin(drawdown)\nmax_dd_value = drawdown[max_dd_idx]\nmax_dd_date = dates[max_dd_idx]\n\n# Find recovery points (drawdown returns to zero after being negative)\nrecovery_mask = (drawdown == 0) & (np.roll(drawdown, 1) < 0)\nrecovery_mask[0] = False\nrecovery_dates = [d for d, m in zip(dates, recovery_mask, strict=False) if m]\n\n# Calculate max drawdown duration\nmax_dd_duration = 0\ncurrent_duration = 0\nfor dd in drawdown:\n    if dd < 0:\n        current_duration += 1\n        max_dd_duration = max(max_dd_duration, current_duration)\n    else:\n        current_duration = 0\n\n# Plot\nfig = go.Figure()\n\n# Drawdown fill area\nfig.add_trace(\n    go.Scatter(\n        x=dates,\n        y=drawdown,\n        fill=\"tozeroy\",\n        fillcolor=\"rgba(174, 48, 48, 0.30)\",\n        line=dict(color=LOSS_RED, width=2),\n        name=\"Drawdown\",\n        hovertemplate=\"Date: %{x|%Y-%m-%d}<br>Drawdown: %{y:.2f}%<extra></extra>\",\n    )\n)\n\n# Maximum drawdown marker\nfig.add_trace(\n    go.Scatter(\n        x=[max_dd_date],\n        y=[max_dd_value],\n        mode=\"markers+text\",\n        marker=dict(size=14, color=LOSS_RED, symbol=\"diamond\"),\n        text=[f\"Max DD: {max_dd_value:.1f}%\"],\n        textposition=\"bottom center\",\n        textfont=dict(size=10, color=LOSS_RED),\n        name=\"Max Drawdown\",\n        showlegend=False,\n        hovertemplate=(f\"Max Drawdown<br>Date: {max_dd_date}<br>Drawdown: {max_dd_value:.2f}%<extra></extra>\"),\n    )\n)\n\n# Recovery point markers (larger for better visibility)\nif len(recovery_dates) > 0:\n    fig.add_trace(\n        go.Scatter(\n            x=recovery_dates,\n            y=[0] * len(recovery_dates),\n            mode=\"markers\",\n            marker=dict(size=16, color=RECOVERY_GREEN, symbol=\"triangle-up\"),\n            name=\"Recovery (New High)\",\n            hovertemplate=\"Recovery<br>Date: %{x|%Y-%m-%d}<extra></extra>\",\n        )\n    )\n\n# Statistics annotation inside plot (bottom-left, avoids right-edge clipping)\nstats_text = (\n    f\"<b>Drawdown Statistics</b><br>\"\n    f\"Max Drawdown: {max_dd_value:.2f}%<br>\"\n    f\"Max Duration: {max_dd_duration} days<br>\"\n    f\"Recovery Points: {len(recovery_dates)}\"\n)\n\nfig.add_annotation(\n    text=stats_text,\n    xref=\"paper\",\n    yref=\"paper\",\n    x=0.02,\n    y=0.04,\n    xanchor=\"left\",\n    yanchor=\"bottom\",\n    showarrow=False,\n    font=dict(size=10, color=INK_SOFT),\n    align=\"left\",\n    bordercolor=INK_SOFT,\n    borderwidth=1,\n    borderpad=8,\n    bgcolor=ELEVATED_BG,\n)\n\n# Layout\nfig.update_layout(\n    autosize=False,\n    paper_bgcolor=PAGE_BG,\n    plot_bgcolor=PAGE_BG,\n    title=dict(\n        text=\"drawdown-basic · python · plotly · anyplot.ai\", font=dict(size=16, color=INK), x=0.5, xanchor=\"center\"\n    ),\n    xaxis=dict(\n        title=dict(text=\"Date\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        showgrid=True,\n        gridcolor=GRID,\n        linecolor=INK_SOFT,\n        zerolinecolor=INK_SOFT,\n    ),\n    yaxis=dict(\n        title=dict(text=\"Drawdown (%)\", font=dict(size=12, color=INK)),\n        tickfont=dict(size=10, color=INK_SOFT),\n        ticksuffix=\"%\",\n        showgrid=True,\n        gridcolor=GRID,\n        linecolor=INK_SOFT,\n        zeroline=True,\n        zerolinewidth=2,\n        zerolinecolor=INK_SOFT,\n    ),\n    showlegend=True,\n    legend=dict(\n        font=dict(size=10, color=INK_SOFT),\n        bgcolor=ELEVATED_BG,\n        bordercolor=INK_SOFT,\n        borderwidth=1,\n        orientation=\"h\",\n        yanchor=\"bottom\",\n        y=1.02,\n        xanchor=\"center\",\n        x=0.5,\n    ),\n    margin=dict(l=80, r=40, t=80, b=60),\n)\n\n# Save\nfig.write_image(f\"plot-{THEME}.png\", width=800, height=450, scale=4)\nfig.write_html(f\"plot-{THEME}.html\", include_plotlyjs=\"cdn\")\n"}