{"spec_id":"indicator-sma","library":"altair","language":"python","code":"\"\"\" anyplot.ai\nindicator-sma: Simple Moving Average (SMA) Indicator Chart\nLibrary: altair 6.1.0 | Python 3.13.13\nQuality: 87/100 | Updated: 2026-05-19\n\"\"\"\n\nimport os\n\nimport altair as alt\nimport numpy as np\nimport pandas as pd\n\n\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\nIMPRINT = [\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\"]\n\nnp.random.seed(42)\nn_days = 300\ndates = pd.date_range(\"2025-01-01\", periods=n_days, freq=\"D\")\n\nreturns = np.random.normal(0.0003, 0.015, n_days)\nprice = 100 * np.cumprod(1 + returns)\ntrend = np.linspace(0, 20, n_days)\nprice = price + trend\n\ndf = pd.DataFrame({\"date\": dates, \"close\": price})\ndf[\"sma_20\"] = df[\"close\"].rolling(window=20).mean()\ndf[\"sma_50\"] = df[\"close\"].rolling(window=50).mean()\ndf[\"sma_200\"] = df[\"close\"].rolling(window=200).mean()\n\nprice_df = df[[\"date\", \"close\"]].copy()\nprice_df[\"series\"] = \"Close Price\"\nprice_df = price_df.rename(columns={\"close\": \"value\"})\n\nsma20_df = df[[\"date\", \"sma_20\"]].copy()\nsma20_df[\"series\"] = \"SMA 20\"\nsma20_df = sma20_df.rename(columns={\"sma_20\": \"value\"})\n\nsma50_df = df[[\"date\", \"sma_50\"]].copy()\nsma50_df[\"series\"] = \"SMA 50\"\nsma50_df = sma50_df.rename(columns={\"sma_50\": \"value\"})\n\nsma200_df = df[[\"date\", \"sma_200\"]].copy()\nsma200_df[\"series\"] = \"SMA 200\"\nsma200_df = sma200_df.rename(columns={\"sma_200\": \"value\"})\n\ncombined_df = pd.concat([price_df, sma20_df, sma50_df, sma200_df], ignore_index=True)\n\nSERIES = [\"Close Price\", \"SMA 20\", \"SMA 50\", \"SMA 200\"]\n\ncolor_scale = alt.Scale(domain=SERIES, range=IMPRINT)\nstroke_dash_scale = alt.Scale(domain=SERIES, range=[[0], [8, 4], [4, 4], [2, 2]])\nstroke_width_scale = alt.Scale(domain=SERIES, range=[3.5, 1.5, 1.5, 1.5])\n\nchart = (\n    alt.Chart(combined_df)\n    .mark_line()\n    .encode(\n        x=alt.X(\"date:T\", title=\"Date\", axis=alt.Axis(format=\"%b %Y\", labelAngle=-45)),\n        y=alt.Y(\"value:Q\", title=\"Price ($)\", scale=alt.Scale(zero=False)),\n        color=alt.Color(\n            \"series:N\",\n            scale=color_scale,\n            legend=alt.Legend(\n                title=\"Series\",\n                orient=\"top-right\",\n                titleFontSize=18,\n                labelFontSize=16,\n                symbolSize=200,\n                symbolStrokeWidth=3,\n            ),\n        ),\n        strokeDash=alt.StrokeDash(\"series:N\", scale=stroke_dash_scale, legend=None),\n        strokeWidth=alt.StrokeWidth(\"series:N\", scale=stroke_width_scale, legend=None),\n        tooltip=[\n            alt.Tooltip(\"date:T\", title=\"Date\", format=\"%Y-%m-%d\"),\n            alt.Tooltip(\"series:N\", title=\"Series\"),\n            alt.Tooltip(\"value:Q\", title=\"Price\", format=\"$.2f\"),\n        ],\n    )\n    .properties(width=1600, height=900, title=\"indicator-sma · python · altair · anyplot.ai\", background=PAGE_BG)\n    .configure_title(fontSize=28, anchor=\"middle\", color=INK)\n    .configure_axis(\n        labelFontSize=18,\n        titleFontSize=22,\n        gridOpacity=0.10,\n        domainColor=INK_SOFT,\n        tickColor=INK_SOFT,\n        gridColor=INK,\n        labelColor=INK_SOFT,\n        titleColor=INK,\n    )\n    .configure_view(fill=PAGE_BG, stroke=INK_SOFT)\n    .configure_legend(fillColor=ELEVATED_BG, strokeColor=INK_SOFT, labelColor=INK_SOFT, titleColor=INK)\n)\n\nchart.save(f\"plot-{THEME}.png\", scale_factor=3.0)\nchart.save(f\"plot-{THEME}.html\")\n"}