{"spec_id":"histogram-kde","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// histogram-kde: Histogram with KDE Overlay\n// Library: chartjs 4.4.7 | JavaScript 22.23.1\n// Quality: 83/100 | Created: 2026-08-05\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: simulated daily portfolio returns (%), deterministic LCG ---------\nfunction makeLcg(seed) {\n  let state = seed >>> 0;\n  return function rand() {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = makeLcg(42);\n\nfunction randNormal() {\n  let u = 0;\n  let v = 0;\n  while (u === 0) u = rand();\n  while (v === 0) v = rand();\n  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v);\n}\n\nconst n = 600;\nconst returns = [];\nfor (let i = 0; i < n; i++) {\n  // 92% calm-market days, 8% drawdown days (fat left tail)\n  const isDrawdown = rand() < 0.08;\n  returns.push(\n    isDrawdown ? -3.2 + randNormal() * 2.0 : 0.15 + randNormal() * 1.1,\n  );\n}\n\n// --- Histogram (density-scaled) ---------------------------------------------\nconst dataMin = Math.min(...returns);\nconst dataMax = Math.max(...returns);\nconst binCount = 24;\nconst binWidth = (dataMax - dataMin) / binCount;\nconst counts = new Array(binCount).fill(0);\nreturns.forEach((v) => {\n  const idx = Math.min(binCount - 1, Math.floor((v - dataMin) / binWidth));\n  counts[idx] += 1;\n});\nconst histData = counts.map((c, i) => ({\n  x: dataMin + (i + 0.5) * binWidth,\n  y: c / (n * binWidth),\n}));\n\n// --- KDE overlay (Gaussian kernel, Silverman's rule bandwidth) --------------\nconst mean = returns.reduce((a, b) => a + b, 0) / n;\nconst variance = returns.reduce((a, b) => a + (b - mean) ** 2, 0) / (n - 1);\nconst std = Math.sqrt(variance);\nconst bandwidth = 1.06 * std * Math.pow(n, -1 / 5);\n\nfunction gaussianKernel(z) {\n  return Math.exp(-0.5 * z * z) / Math.sqrt(2 * Math.PI);\n}\nfunction kde(x) {\n  let sum = 0;\n  for (let i = 0; i < n; i++)\n    sum += gaussianKernel((x - returns[i]) / bandwidth);\n  return sum / (n * bandwidth);\n}\n\nconst gridPoints = 200;\nconst gridMin = dataMin - 3 * bandwidth;\nconst gridMax = dataMax + 3 * bandwidth;\nconst gridStep = (gridMax - gridMin) / (gridPoints - 1);\nconst kdeData = Array.from({ length: gridPoints }, (_, i) => {\n  const x = gridMin + i * gridStep;\n  return { x, y: kde(x) };\n});\n\n// --- Mount -------------------------------------------------------------------\nfunction hexToRgba(hex, alpha) {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart -------------------------------------------------------------------\nnew Chart(canvas, {\n  data: {\n    datasets: [\n      {\n        type: \"bar\",\n        label: \"Histogram (density)\",\n        data: histData,\n        backgroundColor: hexToRgba(t.palette[0], 0.5),\n        borderColor: t.palette[0],\n        borderWidth: 1,\n        barPercentage: 1.0,\n        categoryPercentage: 1.0,\n        order: 1,\n      },\n      {\n        type: \"line\",\n        label: \"KDE\",\n        data: kdeData,\n        borderColor: t.palette[1],\n        backgroundColor: \"transparent\",\n        borderWidth: 3,\n        pointRadius: 0,\n        tension: 0.3,\n        order: 0,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: \"histogram-kde · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n      },\n      legend: {\n        labels: { color: t.ink, font: { size: 16 } },\n      },\n    },\n    scales: {\n      x: {\n        type: \"linear\",\n        min: gridMin,\n        max: gridMax,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { display: false },\n        title: {\n          display: true,\n          text: \"Daily Return (%)\",\n          color: t.ink,\n          font: { size: 16 },\n        },\n      },\n      y: {\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: {\n          display: true,\n          text: \"Density\",\n          color: t.ink,\n          font: { size: 16 },\n        },\n        beginAtZero: true,\n      },\n    },\n  },\n});\n"}