{"spec_id":"indicator-sma","library":"muix","language":"javascript","code":"// anyplot.ai\n// indicator-sma: Simple Moving Average (SMA) Indicator Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-09-02\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { ChartsReferenceLine } from \"@mui/x-charts/ChartsReferenceLine\";\nimport { Box, Typography } from \"@mui/material\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst [BRAND, SHORT_COLOR, MEDIUM_COLOR, LONG_COLOR] = t.palette;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\nconst SYMBOL = \"AURA\";\nconst N = 300;\nconst WINDOW_SHORT = 20;\nconst WINDOW_MEDIUM = 50;\nconst WINDOW_LONG = 200;\nconst START_PRICE = 68;\n\n// Fixed-seed LCG (Numerical Recipes constants) — the browser has no seeded RNG.\nlet seed = 42;\nconst rand = () => {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n};\nconst gaussian = () => {\n  const u1 = Math.max(rand(), 1e-9);\n  const u2 = rand();\n  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n};\n\n// Trading dates (weekdays only)\nconst dates = [];\nconst cursor = new Date(2023, 5, 1);\nwhile (dates.length < N) {\n  const day = cursor.getDay();\n  if (day !== 0 && day !== 6) dates.push(new Date(cursor));\n  cursor.setDate(cursor.getDate() + 1);\n}\n\n// Daily close through three regimes — steady uptrend, a choppy pullback\n// (death cross), then a recovery uptrend (golden cross) — so the SMA\n// crossovers described in the spec's applications are visible.\nconst PULLBACK_START = 110;\nconst PULLBACK_END = 190;\nconst close = [START_PRICE];\nfor (let i = 1; i < N; i += 1) {\n  const pullback = i >= PULLBACK_START && i < PULLBACK_END;\n  const driftPct = pullback ? -0.12 : 0.09;\n  const dailyVolPct = pullback ? 1.3 : 0.9;\n  const changePct = driftPct + dailyVolPct * gaussian();\n  close.push(Math.max(10, close[i - 1] * (1 + changePct / 100)));\n}\n\nconst round2 = (v) => Math.round(v * 100) / 100;\n\nconst sma = (data, window) =>\n  data.map((_, i) => {\n    if (i < window - 1) return null;\n    const windowSlice = data.slice(i - window + 1, i + 1);\n    return round2(windowSlice.reduce((sum, v) => sum + v, 0) / window);\n  });\n\nconst smaShort = sma(close, WINDOW_SHORT);\nconst smaMedium = sma(close, WINDOW_MEDIUM);\nconst smaLong = sma(close, WINDOW_LONG);\nconst closeRounded = close.map(round2);\n\n// Detect 20/50-day SMA crossovers so the chart can call out the death-cross /\n// golden-cross moments described in the spec's Applications section. Only the\n// crossovers that actually confirm each regime change (the first death cross\n// once the pullback starts, the first golden cross once the recovery starts)\n// are annotated, keeping the markers tied to the story instead of every\n// short-term wiggle.\nconst crossovers = [];\nfor (let i = 1; i < N; i += 1) {\n  if (smaShort[i - 1] === null || smaMedium[i - 1] === null) continue;\n  if (smaShort[i] === null || smaMedium[i] === null) continue;\n  const prevDiff = smaShort[i - 1] - smaMedium[i - 1];\n  const diff = smaShort[i] - smaMedium[i];\n  if (prevDiff === 0 || Math.sign(prevDiff) === Math.sign(diff)) continue;\n  crossovers.push({ index: i, date: dates[i], golden: diff > 0 });\n}\nconst deathCross = crossovers.find((c) => c.index >= PULLBACK_START && !c.golden);\nconst goldenCross = crossovers.find((c) => c.index >= PULLBACK_END && c.golden);\nconst signals = [deathCross, goldenCross]\n  .filter(Boolean)\n  .map((c) => ({ date: c.date, label: c.golden ? \"Golden Cross\" : \"Death Cross\" }));\n\n// Explicit y-axis bounds so the four overlapping lines use the full canvas.\nconst allValues = [...closeRounded, ...smaShort, ...smaMedium, ...smaLong].filter(\n  (v) => v !== null,\n);\nconst dataMin = Math.min(...allValues);\nconst dataMax = Math.max(...allValues);\nconst axisPadding = (dataMax - dataMin) * 0.1;\nconst yMin = Math.floor((dataMin - axisPadding) / 5) * 5;\nconst yMax = Math.ceil((dataMax + axisPadding) / 5) * 5;\n\nconst TITLE = `${SYMBOL} · indicator-sma · javascript · muix · anyplot.ai`;\n\n// --- Chart (default-exported component — the harness mounts it) -------------\nexport default function Chart() {\n  const { width, height } = window.ANYPLOT_SIZE;\n  const titleHeight = 70;\n\n  return (\n    <Box sx={{ width, height, display: \"flex\", flexDirection: \"column\" }}>\n      <Typography\n        color=\"text.primary\"\n        sx={{\n          height: titleHeight,\n          lineHeight: `${titleHeight}px`,\n          pl: 1,\n          fontSize: 28,\n          fontWeight: 700,\n        }}\n      >\n        {TITLE}\n      </Typography>\n      <LineChart\n        width={width}\n        height={height - titleHeight}\n        skipAnimation\n        margin={{ top: 72, right: 48, bottom: 76, left: 132 }}\n        xAxis={[\n          {\n            data: dates,\n            scaleType: \"time\",\n            label: \"Trading Date\",\n            labelStyle: { fontSize: 16 },\n            tickLabelStyle: { fontSize: 14 },\n            valueFormatter: (date) =>\n              date.toLocaleDateString(\"en-US\", {\n                month: \"short\",\n                day: \"numeric\",\n              }),\n          },\n        ]}\n        yAxis={[\n          {\n            label: \"Price (USD)\",\n            labelStyle: { fontSize: 16 },\n            tickFontSize: 40,\n            tickLabelStyle: { fontSize: 14 },\n            valueFormatter: (v) => `$${v.toFixed(0)}`,\n            min: yMin,\n            max: yMax,\n          },\n        ]}\n        series={[\n          {\n            id: \"smaLong\",\n            data: smaLong,\n            color: LONG_COLOR,\n            showMark: false,\n            connectNulls: false,\n            curve: \"monotoneX\",\n            label: \"SMA 200\",\n          },\n          {\n            id: \"smaMedium\",\n            data: smaMedium,\n            color: MEDIUM_COLOR,\n            showMark: false,\n            connectNulls: false,\n            curve: \"monotoneX\",\n            label: \"SMA 50\",\n          },\n          {\n            id: \"smaShort\",\n            data: smaShort,\n            color: SHORT_COLOR,\n            showMark: false,\n            connectNulls: false,\n            curve: \"monotoneX\",\n            label: \"SMA 20\",\n          },\n          {\n            id: \"close\",\n            data: closeRounded,\n            color: BRAND,\n            showMark: false,\n            curve: \"monotoneX\",\n            label: `${SYMBOL} Close`,\n          },\n        ]}\n        grid={{ horizontal: true }}\n        slotProps={{\n          legend: {\n            direction: \"row\",\n            position: { vertical: \"top\", horizontal: \"middle\" },\n            labelStyle: { fontSize: 14 },\n          },\n        }}\n        sx={{\n          \"& .MuiLineElement-root\": { strokeWidth: 2 },\n          \"& .MuiLineElement-series-close\": { strokeWidth: 3 },\n          \"& .MuiLineElement-series-smaShort\": {\n            strokeWidth: 2,\n            strokeDasharray: \"8 5\",\n          },\n          \"& .MuiLineElement-series-smaMedium\": {\n            strokeWidth: 2,\n            strokeDasharray: \"2 4\",\n          },\n          \"& .MuiLineElement-series-smaLong\": {\n            strokeWidth: 2.25,\n          },\n        }}\n      >\n        {signals.map((c) => (\n          <ChartsReferenceLine\n            key={c.date.toISOString()}\n            x={c.date}\n            label={c.label}\n            labelAlign=\"start\"\n            lineStyle={{ stroke: t.inkSoft, strokeDasharray: \"4 4\", strokeWidth: 1 }}\n            labelStyle={{ fontSize: 12, fill: t.inkSoft }}\n          />\n        ))}\n      </LineChart>\n    </Box>\n  );\n}\n"}