{"spec_id":"indicator-sma","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nindicator-sma: Simple Moving Average (SMA) Indicator Chart\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 86/100 | Updated: 2026-05-19\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import (\n    LetsPlot,\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_line,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    scale_color_manual,\n    scale_x_datetime,\n    theme,\n    theme_minimal,\n)\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\"\nGRID_COLOR = \"#C0BFBA\" if THEME == \"light\" else \"#484844\"\n\n# Okabe-Ito palette — first series always #009E73\nCOLORS = {\"Close\": \"#009E73\", \"SMA 20\": \"#C475FD\", \"SMA 50\": \"#4467A3\", \"SMA 200\": \"#BD8233\"}\n\n# Data\nnp.random.seed(42)\nn_periods = 300\ndates = pd.date_range(start=\"2024-01-02\", periods=n_periods, freq=\"B\")\nreturns = np.random.normal(0.0005, 0.015, n_periods)\nprice_series = 100 * np.cumprod(1 + returns)\n\nsma_20 = pd.Series(price_series).rolling(window=20).mean()\nsma_50 = pd.Series(price_series).rolling(window=50).mean()\nsma_200 = pd.Series(price_series).rolling(window=200).mean()\n\ndf = pd.DataFrame({\"date\": dates, \"Close\": price_series, \"SMA 20\": sma_20, \"SMA 50\": sma_50, \"SMA 200\": sma_200})\n\ndf_long = df.melt(\n    id_vars=[\"date\"], value_vars=[\"Close\", \"SMA 20\", \"SMA 50\", \"SMA 200\"], var_name=\"series\", value_name=\"price\"\n)\n\n# Separate data for line-weight hierarchy: Close thicker, SMAs thinner\ndf_sma = df_long[df_long[\"series\"] != \"Close\"]\ndf_close = df_long[df_long[\"series\"] == \"Close\"]\n\n# Plot\nanyplot_theme = 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=GRID_COLOR, size=0.5),\n    panel_grid_major_x=element_blank(),\n    panel_grid_minor=element_blank(),\n    axis_title=element_text(color=INK, size=20),\n    axis_text=element_text(color=INK_SOFT, size=16),\n    axis_line=element_line(color=INK_SOFT),\n    plot_title=element_text(color=INK, size=24),\n    plot_subtitle=element_text(color=INK_SOFT, size=16),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(color=INK_SOFT, size=16),\n    legend_title=element_text(color=INK, size=18),\n)\n\nplot = (\n    ggplot(df_long, aes(x=\"date\", y=\"price\", color=\"series\"))\n    + geom_line(data=df_sma, size=1.3)\n    + geom_line(data=df_close, size=2.0)\n    + scale_color_manual(values=COLORS)\n    + scale_x_datetime(format=\"%b '%y\")\n    + labs(\n        title=\"indicator-sma · python · letsplot · anyplot.ai\",\n        subtitle=\"Close price (bold) vs. 20 / 50 / 200-day SMAs — watch for golden/death cross signals\",\n        x=\"Date\",\n        y=\"Price (USD)\",\n        color=\"Series\",\n    )\n    + theme_minimal()\n    + anyplot_theme\n    + ggsize(1600, 900)\n)\n\n# Save\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}