{"spec_id":"frontier-efficient","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// frontier-efficient: Efficient Frontier for Portfolio Optimization\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Asset universe (in-memory, deterministic) ------------------------------\nconst ASSET_NAMES = [\"Govt Bonds\", \"Corp Bonds\", \"REITs\", \"US Equity\", \"Intl Equity\", \"Emerging Mkts\"];\nconst MU = [0.03, 0.045, 0.07, 0.09, 0.08, 0.12]; // annualized expected return\nconst VOL = [0.04, 0.06, 0.14, 0.16, 0.18, 0.24]; // annualized std dev\nconst CORR = [\n  [1.0, 0.75, 0.1, 0.05, 0.0, -0.05],\n  [0.75, 1.0, 0.2, 0.15, 0.1, 0.05],\n  [0.1, 0.2, 1.0, 0.55, 0.45, 0.35],\n  [0.05, 0.15, 0.55, 1.0, 0.7, 0.55],\n  [0.0, 0.1, 0.45, 0.7, 1.0, 0.65],\n  [-0.05, 0.05, 0.35, 0.55, 0.65, 1.0],\n];\nconst N_ASSETS = ASSET_NAMES.length;\nconst COV = MU.map((_, i) => MU.map((__, j) => VOL[i] * VOL[j] * CORR[i][j]));\nconst RISK_FREE_RATE = 0.02;\n\n// --- Deterministic PRNG (LCG) — Math.random() is not seedable in the browser\nfunction makeLcg(seed) {\n  let state = seed >>> 0;\n  return () => {\n    state = (1664525 * state + 1013904223) >>> 0;\n    return (state >>> 8) / 16777216; // (0, 1)\n  };\n}\n\n// Uniform sample from the N-simplex: normalize N exponential draws.\nfunction samplePortfolioWeights(rng) {\n  const draws = Array.from({ length: N_ASSETS }, () => -Math.log(1 - rng()));\n  const total = draws.reduce((a, b) => a + b, 0);\n  return draws.map((d) => d / total);\n}\n\nfunction portfolioStats(weights) {\n  const ret = weights.reduce((sum, w, i) => sum + w * MU[i], 0);\n  let variance = 0;\n  for (let i = 0; i < N_ASSETS; i++) {\n    for (let j = 0; j < N_ASSETS; j++) {\n      variance += weights[i] * weights[j] * COV[i][j];\n    }\n  }\n  const risk = Math.sqrt(variance);\n  return { risk, return: ret, sharpe: (ret - RISK_FREE_RATE) / risk };\n}\n\n// --- Displayed scatter cloud (300 portfolios, within the spec's 50-500 range)\nconst displayRng = makeLcg(42);\nconst portfolios = Array.from({ length: 300 }, () => portfolioStats(samplePortfolioWeights(displayRng)));\n\n// --- Frontier trace: a denser hidden simulation gives a smooth upper envelope\nconst frontierRng = makeLcg(1337);\nconst frontierSamples = Array.from({ length: 4000 }, () => portfolioStats(samplePortfolioWeights(frontierRng)));\n\nconst N_BINS = 60;\nconst risks = frontierSamples.map((p) => p.risk);\nconst minRisk = Math.min(...risks);\nconst maxRisk = Math.max(...risks);\nconst binWidth = (maxRisk - minRisk) / N_BINS;\nconst bins = new Array(N_BINS + 1).fill(null);\nfrontierSamples.forEach((p) => {\n  const idx = Math.min(N_BINS, Math.floor((p.risk - minRisk) / binWidth));\n  if (!bins[idx] || p.return > bins[idx].return) bins[idx] = p;\n});\nconst efficientFrontier = [];\nlet runningMaxReturn = -Infinity;\nbins.forEach((b) => {\n  if (b && b.return > runningMaxReturn) {\n    runningMaxReturn = b.return;\n    efficientFrontier.push(b);\n  }\n});\n\nconst minVariancePortfolio = efficientFrontier[0];\nconst tangencyPortfolio = frontierSamples.reduce((best, p) => (p.sharpe > best.sharpe ? p : best));\n\n// Capital market line: tangent from the risk-free rate through the tangency portfolio\nconst cmlMaxRisk = maxRisk * 1.05;\nconst capitalMarketLine = [\n  { x: 0, y: RISK_FREE_RATE },\n  { x: cmlMaxRisk, y: RISK_FREE_RATE + tangencyPortfolio.sharpe * cmlMaxRisk },\n];\n\n// --- Sharpe → color (imprint_seq gradient, continuous single-polarity data) --\nfunction hexToRgb(hex) {\n  const n = parseInt(hex.slice(1), 16);\n  return [(n >> 16) & 255, (n >> 8) & 255, n & 255];\n}\nfunction lerpColor(hexLow, hexHigh, ratio) {\n  const lo = hexToRgb(hexLow);\n  const hi = hexToRgb(hexHigh);\n  const mix = lo.map((c, i) => Math.round(c + (hi[i] - c) * ratio));\n  return `rgb(${mix[0]}, ${mix[1]}, ${mix[2]})`;\n}\nconst sharpeValues = portfolios.map((p) => p.sharpe);\nconst sharpeMin = Math.min(...sharpeValues);\nconst sharpeMax = Math.max(...sharpeValues);\nconst sharpeColors = sharpeValues.map((s) => lerpColor(t.seq[0], t.seq[1], (s - sharpeMin) / (sharpeMax - sharpeMin)));\n\n// --- Mount -------------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Title (scaled to length — see prompts/plot-generator.md) ---------------\nconst TITLE = \"6-Asset Portfolio Universe · frontier-efficient · javascript · chartjs · anyplot.ai\";\nconst TITLE_FONT_SIZE = Math.max(15, Math.round(22 * Math.min(1, 67 / TITLE.length)));\n\n// --- Chart --------------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"scatter\",\n  data: {\n    datasets: [\n      {\n        label: \"Random Portfolios (colored by Sharpe ratio)\",\n        data: portfolios.map((p) => ({ x: p.risk, y: p.return })),\n        pointBackgroundColor: sharpeColors,\n        pointBorderColor: t.pageBg,\n        pointBorderWidth: 1,\n        pointRadius: 6,\n        pointHoverRadius: 6,\n        showLine: false,\n        order: 5,\n      },\n      {\n        label: \"Efficient Frontier\",\n        data: efficientFrontier.map((p) => ({ x: p.risk, y: p.return })),\n        borderColor: t.palette[0],\n        borderWidth: 4.5,\n        pointRadius: 0,\n        showLine: true,\n        fill: false,\n        tension: 0.2,\n        order: 3,\n      },\n      {\n        label: \"Capital Market Line\",\n        data: capitalMarketLine,\n        borderColor: t.ink,\n        borderWidth: 2.5,\n        borderDash: [10, 6],\n        pointRadius: 0,\n        showLine: true,\n        fill: false,\n        order: 4,\n      },\n      {\n        label: \"Min Variance Portfolio\",\n        data: [{ x: minVariancePortfolio.risk, y: minVariancePortfolio.return }],\n        pointStyle: \"rectRot\",\n        pointRadius: 13,\n        pointBackgroundColor: t.palette[1],\n        pointBorderColor: t.ink,\n        pointBorderWidth: 2,\n        showLine: false,\n        order: 1,\n      },\n      {\n        label: \"Max Sharpe (Tangency) Portfolio\",\n        data: [{ x: tangencyPortfolio.risk, y: tangencyPortfolio.return }],\n        pointStyle: \"triangle\",\n        pointRadius: 14,\n        pointBackgroundColor: t.palette[2],\n        pointBorderColor: t.ink,\n        pointBorderWidth: 2,\n        showLine: false,\n        order: 0,\n      },\n      {\n        label: \"Risk-Free Rate\",\n        data: [{ x: 0, y: RISK_FREE_RATE }],\n        pointStyle: \"circle\",\n        pointRadius: 8,\n        pointBackgroundColor: t.inkSoft,\n        pointBorderColor: t.ink,\n        pointBorderWidth: 1.5,\n        showLine: false,\n        order: 2,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { top: 10, right: 24, bottom: 4, left: 4 } },\n    plugins: {\n      title: {\n        display: true,\n        text: TITLE,\n        color: t.ink,\n        font: { size: TITLE_FONT_SIZE, weight: \"500\" },\n        padding: { bottom: 18 },\n      },\n      legend: {\n        position: \"bottom\",\n        labels: { color: t.inkSoft, font: { size: 14 }, usePointStyle: true, boxWidth: 10, padding: 16 },\n      },\n    },\n    scales: {\n      x: {\n        min: 0,\n        title: { display: true, text: \"Risk (Annualized Std Dev)\", color: t.ink, font: { size: 16 } },\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          callback: (value) => `${Math.round(value * 100)}%`,\n        },\n        grid: { color: t.grid },\n      },\n      y: {\n        min: 0,\n        title: { display: true, text: \"Expected Return (Annualized)\", color: t.ink, font: { size: 16 } },\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          callback: (value) => `${Math.round(value * 100)}%`,\n        },\n        grid: { color: t.grid },\n      },\n    },\n  },\n});\n"}