{"spec_id":"line-stock-comparison","library":"muix","language":"javascript","code":"// anyplot.ai\n// line-stock-comparison: Stock Price Comparison Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-08-26\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 (in-memory, deterministic) ----------------------------------------\n// One trading year (~252 sessions, weekends skipped) of three growth stocks\n// plus the S&P 500 ETF (SPY) as a passive benchmark, all rebased to 100 at\n// the first session so relative performance reads directly off the y-axis.\nconst NUM_SESSIONS = 252;\nconst START_DATE = new Date(2025, 0, 2);\n\nconst tradingDates = [];\nconst cursor = new Date(START_DATE);\nwhile (tradingDates.length < NUM_SESSIONS) {\n  const weekday = cursor.getDay();\n  if (weekday !== 0 && weekday !== 6) tradingDates.push(new Date(cursor));\n  cursor.setDate(cursor.getDate() + 1);\n}\n\n// Small fixed-seed LCG — the browser has no seeded RNG.\nconst makeLcg = (seed) => {\n  let state = seed >>> 0;\n  return () => {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n};\n\nconst buildPriceSeries = (startPrice, dailyDrift, dailyVolatility, seed) => {\n  const rng = makeLcg(seed);\n  const prices = [startPrice];\n  for (let i = 1; i < NUM_SESSIONS; i += 1) {\n    const dailyReturn = dailyDrift + (rng() - 0.5) * dailyVolatility;\n    prices.push(prices[i - 1] * (1 + dailyReturn));\n  }\n  return prices;\n};\n\nconst rebaseToHundred = (prices) =>\n  prices.map((price) => (price / prices[0]) * 100);\n\nconst stocks = [\n  {\n    symbol: \"AAPL\",\n    name: \"Apple\",\n    startPrice: 182,\n    drift: 0.0009,\n    volatility: 0.016,\n    seed: 11,\n  },\n  {\n    symbol: \"GOOGL\",\n    name: \"Alphabet\",\n    startPrice: 141,\n    drift: 0.0004,\n    volatility: 0.019,\n    seed: 22,\n  },\n  {\n    symbol: \"MSFT\",\n    name: \"Microsoft\",\n    startPrice: 378,\n    drift: 0.0011,\n    volatility: 0.014,\n    seed: 33,\n  },\n].map((stock) => ({\n  ...stock,\n  rebased: rebaseToHundred(\n    buildPriceSeries(\n      stock.startPrice,\n      stock.drift,\n      stock.volatility,\n      stock.seed,\n    ),\n  ),\n}));\n\nconst benchmark = {\n  symbol: \"SPY\",\n  name: \"S&P 500 (benchmark)\",\n  rebased: rebaseToHundred(buildPriceSeries(468, 0.0005, 0.009, 44)),\n};\n\nconst TITLE = \"line-stock-comparison · javascript · muix · anyplot.ai\";\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: 40, bottom: 24, left: 24 };\n  const titleBlockHeight = 56;\n  // MUI X's y-axis `label` prop offsets from a hardcoded tick-width guess\n  // rather than the real measured width of 3-digit tick labels, so it\n  // collides with them. A hand-rotated label in its own column sidesteps\n  // that and gives predictable, collision-free spacing.\n  const yLabelWidth = 32;\n  const chartWidth = size.width - padding.left - padding.right - yLabelWidth;\n  const chartHeight =\n    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\n        sx={{\n          fontSize: 22,\n          fontWeight: 600,\n          color: \"text.primary\",\n          mb: \"20px\",\n          lineHeight: 1,\n        }}\n      >\n        {TITLE}\n      </Typography>\n      <Box sx={{ display: \"flex\", flexDirection: \"row\", height: chartHeight }}>\n        <Box\n          sx={{\n            width: yLabelWidth,\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              transform: \"rotate(-90deg)\",\n            }}\n          >\n            Rebased price (start = 100)\n          </Typography>\n        </Box>\n        <LineChart\n          width={chartWidth}\n          height={chartHeight}\n          skipAnimation\n          series={[\n            ...stocks.map((stock, index) => ({\n              id: stock.symbol,\n              label: `${stock.symbol} · ${stock.name}`,\n              data: stock.rebased,\n              showMark: false,\n              color: t.palette[index],\n              curve: \"monotoneX\",\n              valueFormatter: (v) => `${v.toFixed(1)}`,\n            })),\n            {\n              id: benchmark.symbol,\n              label: `${benchmark.symbol} · ${benchmark.name}`,\n              data: benchmark.rebased,\n              showMark: false,\n              color: t.ink,\n              curve: \"monotoneX\",\n              valueFormatter: (v) => `${v.toFixed(1)}`,\n            },\n          ]}\n          xAxis={[\n            {\n              data: tradingDates,\n              scaleType: \"time\",\n              label: \"Date\",\n              valueFormatter: (date) =>\n                date.toLocaleDateString(\"en-US\", {\n                  month: \"short\",\n                  day: \"numeric\",\n                }),\n              tickLabelStyle: { fontSize: 14 },\n              labelStyle: { fontSize: 16 },\n            },\n          ]}\n          yAxis={[\n            {\n              tickLabelStyle: { fontSize: 14 },\n            },\n          ]}\n          grid={{ horizontal: true }}\n          slotProps={{\n            legend: {\n              direction: \"row\",\n              labelStyle: { fontSize: 13 },\n              itemMarkWidth: 18,\n              itemMarkHeight: 10,\n              markGap: 8,\n              itemGap: 20,\n            },\n          }}\n          sx={{\n            \"& .MuiLineElement-root\": { strokeWidth: 2.5 },\n            [`& .MuiLineElement-series-${benchmark.symbol}`]: {\n              strokeWidth: 2,\n              strokeDasharray: \"8 5\",\n            },\n            \"& .MuiChartsGrid-line\": { stroke: t.grid, strokeOpacity: 0.2 },\n          }}\n        >\n          <ChartsReferenceLine\n            y={100}\n            lineStyle={{\n              stroke: t.inkSoft,\n              strokeDasharray: \"3 4\",\n              strokeOpacity: 0.6,\n            }}\n            label=\"Start (100)\"\n            labelStyle={{ fontSize: 12, fill: t.inkSoft }}\n          />\n        </LineChart>\n      </Box>\n    </Box>\n  );\n}\n"}