{"spec_id":"indicator-ema","library":"muix","language":"javascript","code":"// anyplot.ai\n// indicator-ema: Exponential Moving Average (EMA) Indicator Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 88/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;\n\n// --- Data: 120 trading days of daily closes, deterministic LCG walk --------\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1103515245 + 12345) % 2147483648;\n    return state / 2147483648;\n  };\n}\nconst rand = lcg(42);\n\nconst NUM_DAYS = 120;\nconst START_DATE = new Date(2024, 0, 2);\n\nconst dates = Array.from({ length: NUM_DAYS }, (_, day) => {\n  const d = new Date(START_DATE);\n  d.setDate(d.getDate() + day);\n  return d;\n});\n\n// Random-walk closing price with a mild upward drift plus daily noise.\nconst closePrices = [];\nlet price = 148;\nfor (let day = 0; day < NUM_DAYS; day += 1) {\n  const drift = 0.15;\n  const shock = (rand() - 0.5) * 4.2;\n  price = Math.max(80, price + drift + shock);\n  closePrices.push(Math.round(price * 100) / 100);\n}\n\nconst ema = (values, period) => {\n  const k = 2 / (period + 1);\n  const out = [values[0]];\n  for (let i = 1; i < values.length; i += 1) {\n    out.push(values[i] * k + out[i - 1] * (1 - k));\n  }\n  return out;\n};\n\nconst emaShort = ema(closePrices, 12);\nconst emaLong = ema(closePrices, 26);\n\n// Crossover points: where the short EMA changes sign relative to the long EMA.\n// Golden cross = bullish (short moves above long); death cross = bearish.\nconst allCrossovers = [];\nfor (let i = 1; i < NUM_DAYS; i += 1) {\n  const prevDiff = emaShort[i - 1] - emaLong[i - 1];\n  const currDiff = emaShort[i] - emaLong[i];\n  if (prevDiff !== 0 && currDiff !== 0 && Math.sign(prevDiff) !== Math.sign(currDiff)) {\n    allCrossovers.push({ index: i, bullish: currDiff > 0 });\n  }\n}\n// Keep the chart legible: cap the highlighted crossovers to a handful, and\n// require a minimum day-gap so neighboring labels never collide.\nconst MAX_CROSSOVERS = 4;\nconst MIN_GAP_DAYS = 15;\nconst crossovers = [];\nfor (const c of allCrossovers) {\n  const last = crossovers[crossovers.length - 1];\n  if (!last || c.index - last.index >= MIN_GAP_DAYS) {\n    crossovers.push(c);\n  }\n  if (crossovers.length >= MAX_CROSSOVERS) break;\n}\n\nconst TITLE = \"indicator-ema · javascript · muix · anyplot.ai\";\nconst dateFormatter = (date) => date.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\n\n// --- Chart (default-exported component — the harness mounts it) -----------\nexport default function Chart() {\n  const size = window.ANYPLOT_SIZE;\n  const padding = { top: 28, right: 44, bottom: 24, left: 44 };\n  const titleBlockHeight = 56;\n  // MUI X's built-in yAxis `label` positions itself using a fixed\n  // (tickFontSize + tickSize + 10) offset rather than the tick labels'\n  // measured width, so a 4-char dollar-formatted tick (\"$158\") collides\n  // with the rotated title. Render the y-axis title ourselves in a\n  // dedicated column instead, and only reserve chart-internal margin for\n  // the tick labels.\n  const yAxisLabelColWidth = 32;\n  const chartWidth = size.width - padding.left - padding.right - yAxisLabelColWidth;\n  const chartHeight = size.height - padding.top - padding.bottom - titleBlockHeight;\n\n  return (\n    <Box\n      sx={{\n        width: size.width,\n        height: size.height,\n        boxSizing: \"border-box\",\n        padding: `${padding.top}px ${padding.right}px ${padding.bottom}px ${padding.left}px`,\n        display: \"flex\",\n        flexDirection: \"column\",\n      }}\n    >\n      <Typography sx={{ fontSize: 22, fontWeight: 600, color: \"text.primary\", mb: \"20px\", lineHeight: 1 }}>\n        {TITLE}\n      </Typography>\n      <Box sx={{ display: \"flex\", flexDirection: \"row\", alignItems: \"center\" }}>\n        <Box\n          sx={{\n            width: yAxisLabelColWidth,\n            height: chartHeight,\n            display: \"flex\",\n            alignItems: \"center\",\n            justifyContent: \"center\",\n          }}\n        >\n          <Typography\n            sx={{\n              fontSize: 16,\n              color: \"text.secondary\",\n              whiteSpace: \"nowrap\",\n              writingMode: \"vertical-rl\",\n              transform: \"rotate(180deg)\",\n            }}\n          >\n            Price (USD)\n          </Typography>\n        </Box>\n        <LineChart\n          width={chartWidth}\n          height={chartHeight}\n          skipAnimation\n          margin={{ top: 20, right: 30, bottom: 40, left: 64 }}\n          series={[\n            {\n              id: \"close\",\n              label: \"Close Price\",\n              data: closePrices,\n              color: t.palette[0],\n              showMark: false,\n              valueFormatter: (v) => `$${v.toFixed(2)}`,\n            },\n            {\n              id: \"ema12\",\n              label: \"EMA (12-day)\",\n              data: emaShort,\n              color: t.palette[1],\n              showMark: false,\n              valueFormatter: (v) => `$${v.toFixed(2)}`,\n            },\n            {\n              id: \"ema26\",\n              label: \"EMA (26-day)\",\n              data: emaLong,\n              color: t.palette[2],\n              showMark: false,\n              valueFormatter: (v) => `$${v.toFixed(2)}`,\n            },\n          ]}\n          xAxis={[\n            {\n              data: dates,\n              scaleType: \"time\",\n              label: \"Trading Date\",\n              valueFormatter: dateFormatter,\n              tickLabelStyle: { fontSize: 14 },\n              labelStyle: { fontSize: 16 },\n            },\n          ]}\n          yAxis={[\n            {\n              valueFormatter: (v) => `$${v}`,\n              tickLabelStyle: { fontSize: 14 },\n            },\n          ]}\n          grid={{ horizontal: true }}\n          slotProps={{\n            legend: {\n              direction: \"row\",\n              labelStyle: { fontSize: 14 },\n              itemMarkWidth: 18,\n              itemMarkHeight: 10,\n              markGap: 8,\n            },\n          }}\n          sx={{\n            \"& .MuiLineElement-series-close\": { strokeWidth: 3.5 },\n            \"& .MuiLineElement-series-ema12\": { strokeWidth: 2 },\n            \"& .MuiLineElement-series-ema26\": { strokeWidth: 2 },\n          }}\n        >\n          {crossovers.map((c) => (\n            <ChartsReferenceLine\n              key={c.index}\n              x={dates[c.index]}\n              label={c.bullish ? \"Golden cross\" : \"Death cross\"}\n              labelAlign=\"start\"\n              lineStyle={{\n                stroke: c.bullish ? t.palette[0] : t.palette[4],\n                strokeDasharray: \"6 4\",\n                strokeWidth: 1.5,\n              }}\n              labelStyle={{ fill: c.bullish ? t.palette[0] : t.palette[4], fontSize: 13 }}\n            />\n          ))}\n        </LineChart>\n      </Box>\n    </Box>\n  );\n}\n"}