{"spec_id":"indicator-macd","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nindicator-macd: MACD Technical Indicator Chart\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 93/100 | Updated: 2026-05-16\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\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\"\n\n# Okabe-Ito palette\nBRAND = \"#009E73\"  # First series (green)\nSECONDARY = \"#BD8233\"  # imprint ochre — signal line (categorical contrast)\n\n# Generate realistic stock price data with momentum\nnp.random.seed(42)\nn_days = 120\n\n# Simulate stock prices with trend and volatility (more realistic)\ndates = pd.date_range(start=\"2024-01-01\", periods=n_days + 35, freq=\"B\")\nreturns = np.random.normal(0.0005, 0.015, n_days + 35)\n# Add trending behavior\ntrend = np.sin(np.linspace(0, 4 * np.pi, n_days + 35)) * 0.012\nreturns = returns + trend\nprices = 100 * np.cumprod(1 + returns)\n\n# Calculate EMAs using pandas ewm\nema_12 = pd.Series(prices).ewm(span=12, adjust=False).mean().values\nema_26 = pd.Series(prices).ewm(span=26, adjust=False).mean().values\n\n# Calculate MACD components\nmacd_line = ema_12 - ema_26\nsignal_line = pd.Series(macd_line).ewm(span=9, adjust=False).mean().values\nhistogram = macd_line - signal_line\n\n# Use the last n_days (after warmup period)\nstart_idx = 35\ndf = pd.DataFrame(\n    {\n        \"date\": dates[start_idx:],\n        \"macd\": macd_line[start_idx:],\n        \"signal\": signal_line[start_idx:],\n        \"histogram\": histogram[start_idx:],\n    }\n)\n\n# Convert dates to numeric for plotting\ndf[\"day_num\"] = range(len(df))\ndf[\"hist_color\"] = np.where(df[\"histogram\"] >= 0, \"Positive\", \"Negative\")\n\n# Create separate dataframes for lines\ndf_lines = pd.melt(\n    df[[\"day_num\", \"macd\", \"signal\"]],\n    id_vars=[\"day_num\"],\n    value_vars=[\"macd\", \"signal\"],\n    var_name=\"line_type\",\n    value_name=\"value\",\n)\ndf_lines[\"line_type\"] = df_lines[\"line_type\"].map({\"macd\": \"MACD Line\", \"signal\": \"Signal Line\"})\n\n# Create the MACD chart\nplot = (\n    ggplot()\n    + geom_bar(\n        data=df, mapping=aes(x=\"day_num\", y=\"histogram\", fill=\"hist_color\"), stat=\"identity\", width=0.8, alpha=0.8\n    )\n    + geom_hline(yintercept=0, color=INK_SOFT, size=0.8, linetype=\"dashed\")\n    + geom_line(data=df_lines, mapping=aes(x=\"day_num\", y=\"value\", color=\"line_type\"), size=1.5)\n    + scale_fill_manual(values={\"Positive\": \"#2ABCCD\", \"Negative\": \"#AE3030\"}, name=\"Histogram\")  # imprint red for negative bars\n    + scale_color_manual(values={\"MACD Line\": BRAND, \"Signal Line\": SECONDARY}, name=\"Lines\")\n    + labs(x=\"Trading Day\", y=\"MACD Value\", title=\"indicator-macd · letsplot · anyplot.ai\")\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG),\n        panel_grid_major_y=element_line(color=INK_SOFT, size=0.3),\n        panel_grid_minor=element_blank(),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_line=element_line(color=INK_SOFT),\n        plot_title=element_text(size=24, color=INK),\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_position=\"right\",\n    )\n    + ggsize(1600, 900)\n)\n\n# Save as PNG (scale=3 gives 4800x2700)\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\n\n# Save interactive HTML version\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}