{"spec_id":"indicator-ema","library":"matplotlib","language":"python","code":"\"\"\" anyplot.ai\nindicator-ema: Exponential Moving Average (EMA) Indicator Chart\nLibrary: matplotlib 3.10.9 | Python 3.13.13\nQuality: 92/100 | Updated: 2026-05-19\n\"\"\"\n\nimport os\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom matplotlib.dates import DateFormatter, MonthLocator\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\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# imprint palette — categorical EMAs + semantic crossover markers\nEMA_SHORT_COLOR = \"#4467A3\"  # imprint blue — 12-day EMA\nEMA_LONG_COLOR = \"#BD8233\"  # imprint ochre — 26-day EMA\nGOLDEN_COLOR = \"#009E73\"  # green — bullish crossover (semantic)\nDEATH_COLOR = \"#AE3030\"  # red — bearish crossover (semantic)\n\n# Data\nnp.random.seed(42)\nn_days = 120\n\ndates = pd.date_range(\"2024-01-02\", periods=n_days, freq=\"B\")\nreturns = np.random.normal(0.0005, 0.02, n_days)\nprice = 150 * np.cumprod(1 + returns)\n\ndf = pd.DataFrame({\"date\": dates, \"close\": price})\ndf[\"ema_12\"] = df[\"close\"].ewm(span=12, adjust=False).mean()\ndf[\"ema_26\"] = df[\"close\"].ewm(span=26, adjust=False).mean()\n\n# Identify crossover signals\nsignal = np.sign(df[\"ema_12\"].values - df[\"ema_26\"].values)\ncrossover_idx = np.where(np.diff(signal))[0]\n\n# Plot\nfig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG)\nax.set_facecolor(PAGE_BG)\n\n# Shaded regime regions between EMA lines\nax.fill_between(\n    df[\"date\"],\n    df[\"ema_12\"],\n    df[\"ema_26\"],\n    where=df[\"ema_12\"] >= df[\"ema_26\"],\n    alpha=0.12,\n    color=EMA_SHORT_COLOR,\n    interpolate=True,\n)\nax.fill_between(\n    df[\"date\"],\n    df[\"ema_12\"],\n    df[\"ema_26\"],\n    where=df[\"ema_12\"] < df[\"ema_26\"],\n    alpha=0.12,\n    color=EMA_LONG_COLOR,\n    interpolate=True,\n)\n\n# Price line (most prominent)\nax.plot(df[\"date\"], df[\"close\"], linewidth=2.5, color=INK, label=\"Price\", alpha=0.85, zorder=3)\n\n# EMA lines (slightly thinner per spec)\nax.plot(df[\"date\"], df[\"ema_12\"], linewidth=2, color=EMA_SHORT_COLOR, label=\"EMA 12\", alpha=0.9, zorder=4)\nax.plot(df[\"date\"], df[\"ema_26\"], linewidth=2, color=EMA_LONG_COLOR, label=\"EMA 26\", alpha=0.9, zorder=4)\n\n# Crossover markers — golden cross (^) vs death cross (v)\nfor idx in crossover_idx:\n    x_date = df[\"date\"].iloc[idx]\n    y_val = df[\"ema_12\"].iloc[idx]\n    is_golden = signal[idx] < 0  # EMA12 was below, now crossing above\n    ax.scatter(\n        x_date,\n        y_val,\n        s=280,\n        color=GOLDEN_COLOR if is_golden else DEATH_COLOR,\n        marker=\"^\" if is_golden else \"v\",\n        zorder=6,\n        edgecolors=PAGE_BG,\n        linewidths=2,\n    )\n\n# Proxy artists for crossover legend entries\nax.scatter([], [], s=200, color=GOLDEN_COLOR, marker=\"^\", edgecolors=PAGE_BG, linewidths=2, label=\"Golden Cross\")\nax.scatter([], [], s=200, color=DEATH_COLOR, marker=\"v\", edgecolors=PAGE_BG, linewidths=2, label=\"Death Cross\")\n\n# Annotate first golden cross and first death cross with arrows\nfirst_golden = True\nfirst_death = True\nfor idx in crossover_idx:\n    x_date = df[\"date\"].iloc[idx]\n    y_val = df[\"ema_12\"].iloc[idx]\n    is_golden = signal[idx] < 0\n    if is_golden and first_golden:\n        ax.annotate(\n            \"Golden\\nCross\",\n            xy=(x_date, y_val),\n            xytext=(38, 22),\n            textcoords=\"offset points\",\n            fontsize=11,\n            color=GOLDEN_COLOR,\n            fontweight=\"semibold\",\n            arrowprops={\"arrowstyle\": \"->\", \"color\": GOLDEN_COLOR, \"lw\": 1.5},\n            bbox={\"facecolor\": ELEVATED_BG, \"edgecolor\": GOLDEN_COLOR, \"alpha\": 0.88, \"boxstyle\": \"round,pad=0.3\"},\n            zorder=8,\n        )\n        first_golden = False\n    elif not is_golden and first_death:\n        ax.annotate(\n            \"Death\\nCross\",\n            xy=(x_date, y_val),\n            xytext=(38, -38),\n            textcoords=\"offset points\",\n            fontsize=11,\n            color=DEATH_COLOR,\n            fontweight=\"semibold\",\n            arrowprops={\"arrowstyle\": \"->\", \"color\": DEATH_COLOR, \"lw\": 1.5},\n            bbox={\"facecolor\": ELEVATED_BG, \"edgecolor\": DEATH_COLOR, \"alpha\": 0.88, \"boxstyle\": \"round,pad=0.3\"},\n            zorder=8,\n        )\n        first_death = False\n\n# Style\nax.set_xlabel(\"Date\", fontsize=20, color=INK)\nax.set_ylabel(\"Price (USD)\", fontsize=20, color=INK)\nax.set_title(\"indicator-ema · python · matplotlib · anyplot.ai\", fontsize=24, fontweight=\"medium\", color=INK, pad=30)\n# Subtitle for typographic hierarchy\nax.text(\n    0.5,\n    1.012,\n    \"EMA crossover signals with regime shading  |  120 trading days  |  periods: 12 & 26\",\n    transform=ax.transAxes,\n    fontsize=13,\n    color=INK_MUTED,\n    ha=\"center\",\n    va=\"bottom\",\n)\nax.tick_params(axis=\"both\", labelsize=16, colors=INK_SOFT, labelcolor=INK_SOFT, length=0)\n\nax.spines[\"top\"].set_visible(False)\nax.spines[\"right\"].set_visible(False)\nfor s in (\"left\", \"bottom\"):\n    ax.spines[s].set_color(INK_SOFT)\n\nax.yaxis.grid(True, alpha=0.10, linewidth=0.8, color=INK)\n\n# Date locator/formatter for clean month labels\nax.xaxis.set_major_locator(MonthLocator())\nax.xaxis.set_major_formatter(DateFormatter(\"%b %Y\"))\nplt.setp(ax.get_xticklabels(), rotation=30, ha=\"right\", fontsize=16, color=INK_SOFT)\n\nleg = ax.legend(fontsize=14, loc=\"upper right\", framealpha=0.9, ncol=2)\nleg.get_frame().set_facecolor(ELEVATED_BG)\nleg.get_frame().set_edgecolor(INK_SOFT)\nplt.setp(leg.get_texts(), color=INK_SOFT)\n\nplt.tight_layout()\nplt.savefig(f\"plot-{THEME}.png\", dpi=300, bbox_inches=\"tight\", facecolor=PAGE_BG)\n"}