{"spec_id":"subplot-grid","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// subplot-grid: Subplot Grid Layout\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 88/100 | Created: 2026-09-09\n\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: 60-session trading dashboard (deterministic LCG walk) -----------\nfunction lcg(seed) {\n  let state = seed >>> 0;\n  return function () {\n    state = (1103515245 * state + 12345) & 0x7fffffff;\n    return state / 0x7fffffff;\n  };\n}\nconst rand = lcg(42);\nconst gaussian = () => {\n  let sum = 0;\n  for (let i = 0; i < 12; i++) sum += rand();\n  return sum - 6;\n};\n\nconst numDays = 60;\nconst dayLabels = Array.from({ length: numDays }, (_, i) => `Day ${i + 1}`);\n\nlet price = 150;\nconst prices = [];\nconst volumes = [];\nconst returns = [];\nfor (let i = 0; i < numDays; i++) {\n  const prevPrice = price;\n  const change = gaussian() * 1.1 + 0.15;\n  price = Math.max(price + change, 50);\n  prices.push(Number(price.toFixed(2)));\n  returns.push(((price - prevPrice) / prevPrice) * 100);\n  const volume = Math.round(2_200_000 + Math.abs(change) * 850_000 + rand() * 500_000);\n  volumes.push(volume);\n}\n\n// Binned distribution of daily returns\nconst numBins = 8;\nconst minReturn = Math.min(...returns);\nconst maxReturn = Math.max(...returns);\nconst binWidth = (maxReturn - minReturn) / numBins;\nconst binCounts = new Array(numBins).fill(0);\nconst binLabels = Array.from({ length: numBins }, (_, i) =>\n  (minReturn + i * binWidth).toFixed(1)\n);\nreturns.forEach((r) => {\n  const idx = Math.min(numBins - 1, Math.max(0, Math.floor((r - minReturn) / binWidth)));\n  binCounts[idx]++;\n});\n// profit/loss semantic exception: bins centred above 0% read as gains\nconst binColors = binCounts.map((_, i) => {\n  const binCenter = minReturn + (i + 0.5) * binWidth;\n  return binCenter >= 0 ? t.palette[0] : t.palette[4];\n});\n\nconst priceVolumePoints = prices.map((p, i) => ({ x: p, y: volumes[i] }));\n\n// --- Layout: header + 2x2 grid of independent Chart.js canvases ------------\nconst root = document.createElement(\"div\");\nroot.style.cssText = `\n  width: 100%; height: 100%; box-sizing: border-box;\n  display: flex; flex-direction: column;\n  padding: 22px 26px; background: ${t.pageBg};\n  font-family: -apple-system, \"Segoe UI\", Roboto, sans-serif;\n`;\ndocument.getElementById(\"container\").appendChild(root);\n\nconst header = document.createElement(\"div\");\nheader.textContent = \"subplot-grid · javascript · chartjs · anyplot.ai\";\nheader.style.cssText = `\n  color: ${t.ink}; font-size: 22px; font-weight: 600;\n  text-align: center; flex-shrink: 0; margin-bottom: 6px;\n`;\nroot.appendChild(header);\n\nconst caption = document.createElement(\"div\");\ncaption.textContent =\n  \"Panels 1–2 share the trading-day (x) axis for direct comparison · Panels 3–4 use independent axes fitted to their own scale\";\ncaption.style.cssText = `\n  color: ${t.inkSoft}; font-size: 13px; font-style: italic;\n  text-align: center; flex-shrink: 0; margin-bottom: 16px;\n`;\nroot.appendChild(caption);\n\nconst grid = document.createElement(\"div\");\ngrid.style.cssText = `\n  flex: 1; min-height: 0;\n  display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr;\n  gap: 22px;\n`;\nroot.appendChild(grid);\n\nfunction makeCell() {\n  const cell = document.createElement(\"div\");\n  cell.style.cssText = `\n    position: relative; min-width: 0; min-height: 0; box-sizing: border-box;\n    background: ${t.elevatedBg}; border-radius: 10px; padding: 14px 18px;\n  `;\n  const canvas = document.createElement(\"canvas\");\n  cell.appendChild(canvas);\n  grid.appendChild(cell);\n  return canvas;\n}\n\n// Shared x-axis (trading day) tick/title config reused verbatim by Panels 1 & 2\n// so the two time-series panels line up for direct comparison.\nconst sharedDayTicks = {\n  color: t.inkSoft,\n  font: { size: 11 },\n  autoSkip: false,\n  callback: (_value, index) => (index % 10 === 0 ? dayLabels[index] : null),\n};\nconst sharedDayTitle = { display: true, text: \"Trading Day\", color: t.inkSoft, font: { size: 12 } };\n\nconst commonTitle = (text) => ({\n  display: true,\n  text,\n  color: t.ink,\n  font: { size: 17, weight: \"600\" },\n  padding: { bottom: 10 },\n});\nconst axisTicks = { color: t.inkSoft, font: { size: 11 } };\nconst axisTitle = (text) => ({ display: true, text, color: t.inkSoft, font: { size: 12 } });\n\n// Panel 1: closing price (line)\nnew Chart(makeCell(), {\n  type: \"line\",\n  data: {\n    labels: dayLabels,\n    datasets: [\n      {\n        label: \"Close\",\n        data: prices,\n        borderColor: t.palette[0],\n        backgroundColor: (context) => {\n          const { ctx, chartArea } = context.chart;\n          if (!chartArea) return `${t.palette[0]}00`;\n          const gradient = ctx.createLinearGradient(0, chartArea.top, 0, chartArea.bottom);\n          gradient.addColorStop(0, `${t.palette[0]}40`);\n          gradient.addColorStop(1, `${t.palette[0]}00`);\n          return gradient;\n        },\n        fill: true,\n        borderWidth: 2.5,\n        pointRadius: 0,\n        tension: 0.15,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: { title: commonTitle(\"Closing Price\"), legend: { display: false } },\n    scales: {\n      x: { ticks: sharedDayTicks, grid: { display: false }, title: sharedDayTitle },\n      y: { ticks: axisTicks, grid: { color: t.grid }, title: axisTitle(\"Price ($)\") },\n    },\n  },\n});\n\n// Panel 2: trading volume (bar)\nnew Chart(makeCell(), {\n  type: \"bar\",\n  data: {\n    labels: dayLabels,\n    datasets: [\n      {\n        label: \"Volume\",\n        data: volumes,\n        backgroundColor: t.palette[2],\n        borderWidth: 0,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: { title: commonTitle(\"Trading Volume\"), legend: { display: false } },\n    scales: {\n      x: { ticks: sharedDayTicks, grid: { display: false }, title: sharedDayTitle },\n      y: {\n        ticks: { ...axisTicks, callback: (v) => `${(v / 1e6).toFixed(1)}M` },\n        grid: { color: t.grid },\n        title: axisTitle(\"Shares\"),\n        beginAtZero: true,\n      },\n    },\n  },\n});\n\n// Panel 3: daily-return distribution (histogram)\nnew Chart(makeCell(), {\n  type: \"bar\",\n  data: {\n    labels: binLabels,\n    datasets: [\n      {\n        label: \"Sessions\",\n        data: binCounts,\n        backgroundColor: binColors,\n        borderWidth: 0,\n        categoryPercentage: 1.0,\n        barPercentage: 0.95,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: commonTitle(\"Daily Return Distribution\"),\n      legend: {\n        display: true,\n        position: \"top\",\n        align: \"end\",\n        onClick: () => {},\n        labels: {\n          color: t.inkSoft,\n          font: { size: 11 },\n          boxWidth: 10,\n          boxHeight: 10,\n          generateLabels: () => [\n            { text: \"Gain (≥0%)\", fillStyle: t.palette[0], strokeStyle: t.palette[0] },\n            { text: \"Loss (<0%)\", fillStyle: t.palette[4], strokeStyle: t.palette[4] },\n          ],\n        },\n      },\n    },\n    scales: {\n      x: { ticks: axisTicks, grid: { display: false }, title: axisTitle(\"Return (%)\") },\n      y: { ticks: axisTicks, grid: { color: t.grid }, title: axisTitle(\"Sessions\"), beginAtZero: true },\n    },\n  },\n});\n\n// Panel 4: price vs. volume relationship (scatter)\nnew Chart(makeCell(), {\n  type: \"scatter\",\n  data: {\n    datasets: [\n      {\n        label: \"Session\",\n        data: priceVolumePoints,\n        backgroundColor: t.palette[1],\n        pointRadius: 4.5,\n        pointHoverRadius: 4.5,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: { title: commonTitle(\"Price vs. Volume\"), legend: { display: false } },\n    scales: {\n      x: { ticks: axisTicks, grid: { color: t.grid }, title: axisTitle(\"Price ($)\") },\n      y: {\n        ticks: { ...axisTicks, callback: (v) => `${(v / 1e6).toFixed(1)}M` },\n        grid: { color: t.grid },\n        title: axisTitle(\"Volume\"),\n      },\n    },\n  },\n});\n"}