{"spec_id":"ohlc-bar","library":"muix","language":"javascript","code":"// anyplot.ai\n// ohlc-bar: OHLC Bar Chart\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\nimport { ChartContainer } from \"@mui/x-charts/ChartContainer\";\nimport { ChartsGrid } from \"@mui/x-charts/ChartsGrid\";\nimport { ChartsXAxis } from \"@mui/x-charts/ChartsXAxis\";\nimport { ChartsYAxis } from \"@mui/x-charts/ChartsYAxis\";\nimport { useXScale, useYScale } from \"@mui/x-charts/hooks\";\n\nconst t = window.ANYPLOT_TOKENS;\nconst TITLE = \"ohlc-bar · javascript · muix · anyplot.ai\";\nconst TITLE_HEIGHT = 56;\n\n// --- Data (in-memory, deterministic LCG — no seeded RNG in the browser) -----\nfunction lcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\n\nconst PERIODS = 45;\n\n// Trading-day dates only (skip Sat/Sun), starting the first Tuesday of 2024.\nconst dates = [];\nconst cursor = new Date(2024, 0, 2);\nwhile (dates.length < PERIODS) {\n  const dow = cursor.getDay();\n  if (dow !== 0 && dow !== 6) {\n    dates.push(cursor.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" }));\n  }\n  cursor.setDate(cursor.getDate() + 1);\n}\n\n// Daily OHLC via a mild-drift random walk, expressed as percentage moves so\n// the wick/gap sizes scale naturally with price.\nconst rand = lcg(42);\nconst opens = [];\nconst highs = [];\nconst lows = [];\nconst closes = [];\nlet prevClose = 218;\nfor (let i = 0; i < PERIODS; i++) {\n  const gapPct = (rand() - 0.5) * 0.006;\n  const open = prevClose * (1 + gapPct);\n  const movePct = (rand() - 0.47) * 0.026 + 0.0015;\n  const close = open * (1 + movePct);\n  const wickUpPct = rand() * 0.012;\n  const wickDownPct = rand() * 0.012;\n  const high = Math.max(open, close) * (1 + wickUpPct);\n  const low = Math.min(open, close) * (1 - wickDownPct);\n  opens.push(open);\n  highs.push(high);\n  lows.push(low);\n  closes.push(close);\n  prevClose = close;\n}\n\nconst yMin = Math.min(...lows);\nconst yMax = Math.max(...highs);\nconst yPad = (yMax - yMin) * 0.06;\n\n// --- OHLC bar overlay ---------------------------------------------------\n// No candlestick/OHLC series ships in the community package (7.29.1) — a\n// custom SVG layer positioned via the chart's own band/linear scale hooks\n// reproduces it while staying entirely within the community ChartContainer\n// surface, the same technique used for box-whisker overlays.\nfunction OhlcBars() {\n  const xScale = useXScale();\n  const yScale = useYScale();\n  const bandwidth = xScale.bandwidth();\n  const tickWidth = Math.min(bandwidth * 0.4, 11);\n\n  return (\n    <g>\n      {dates.map((date, i) => {\n        const center = xScale(date) + bandwidth / 2;\n        const isUp = closes[i] >= opens[i];\n        const color = isUp ? t.palette[0] : t.palette[4];\n\n        return (\n          <g key={date} stroke={color} strokeWidth={2.4} strokeLinecap=\"round\">\n            <line\n              x1={center}\n              x2={center}\n              y1={yScale(highs[i])}\n              y2={yScale(lows[i])}\n            />\n            <line\n              x1={center - tickWidth}\n              x2={center}\n              y1={yScale(opens[i])}\n              y2={yScale(opens[i])}\n            />\n            <line\n              x1={center}\n              x2={center + tickWidth}\n              y1={yScale(closes[i])}\n              y2={yScale(closes[i])}\n            />\n          </g>\n        );\n      })}\n    </g>\n  );\n}\n\nfunction LegendSwatch({ color, label }) {\n  return (\n    <div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>\n      <span\n        style={{\n          width: 18,\n          height: 3,\n          borderRadius: 2,\n          backgroundColor: color,\n        }}\n      />\n      <span style={{ fontSize: 14, color: t.inkSoft }}>{label}</span>\n    </div>\n  );\n}\n\nexport default function Chart() {\n  const chartHeight = window.ANYPLOT_SIZE.height - TITLE_HEIGHT;\n\n  return (\n    <div\n      style={{\n        width: window.ANYPLOT_SIZE.width,\n        height: window.ANYPLOT_SIZE.height,\n      }}\n    >\n      <div\n        style={{\n          height: TITLE_HEIGHT,\n          display: \"flex\",\n          alignItems: \"center\",\n          justifyContent: \"space-between\",\n          paddingLeft: 24,\n          paddingRight: 40,\n        }}\n      >\n        <span style={{ fontSize: 22, fontWeight: 500, color: t.ink }}>\n          {TITLE}\n        </span>\n        <div style={{ display: \"flex\", alignItems: \"center\", gap: 20 }}>\n          <LegendSwatch color={t.palette[0]} label=\"Up (close > open)\" />\n          <LegendSwatch color={t.palette[4]} label=\"Down (close < open)\" />\n        </div>\n      </div>\n      <ChartContainer\n        width={window.ANYPLOT_SIZE.width}\n        height={chartHeight}\n        series={[]}\n        skipAnimation\n        margin={{ top: 20, right: 40, bottom: 70, left: 130 }}\n        xAxis={[\n          {\n            id: \"sessions\",\n            data: dates,\n            scaleType: \"band\",\n            label: \"Trading Session (2024)\",\n            labelStyle: { fontSize: 16 },\n            tickLabelStyle: { fontSize: 14 },\n            tickLabelInterval: (_value, index) => index % 5 === 0,\n          },\n        ]}\n        yAxis={[\n          {\n            id: \"price\",\n            min: yMin - yPad,\n            max: yMax + yPad,\n            label: \"Share Price (USD)\",\n            labelStyle: { fontSize: 16 },\n            // tickFontSize drives the label's reserved offset from the axis\n            // (MUI X spaces the rotated label by tickFontSize + tickSize, not\n            // by the actual rendered tick text width) — set generously so the\n            // \"$XXX\" ticks (rendered at tickLabelStyle's 14px) never collide\n            // with the axis label.\n            tickFontSize: 40,\n            tickLabelStyle: { fontSize: 14 },\n            valueFormatter: (v) => `$${v.toFixed(0)}`,\n          },\n        ]}\n      >\n        <ChartsGrid\n          horizontal\n          sx={{\n            \"& .MuiChartsGrid-line\": {\n              opacity: 0.55,\n              strokeDasharray: \"2 5\",\n            },\n          }}\n        />\n        <OhlcBars />\n        <ChartsXAxis axisId=\"sessions\" />\n        <ChartsYAxis axisId=\"price\" />\n      </ChartContainer>\n    </div>\n  );\n}\n"}