{"spec_id":"indicator-ichimoku","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// indicator-ichimoku: Ichimoku Cloud Technical Indicator Chart\n// Library: chartjs 4.4.7 | JavaScript 22.22.3\n// Quality: 88/100 | Created: 2026-06-08\n//# anyplot-orientation: landscape\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (deterministic OHLC + Ichimoku indicators) -----------------------\nconst N      = 180;   // trading periods\nconst FUTURE = 26;    // Ichimoku look-forward shift\nconst TOTAL  = N + FUTURE;\n\n// Seeded LCG for reproducible pseudo-random data (no seedable Math.random in browser)\nlet seed = 42;\nfunction rng() {\n  seed = (seed * 1664525 + 1013904223) & 0xffffffff;\n  return (seed >>> 0) / 0xffffffff;\n}\nfunction randn() {\n  const u = rng() + 1e-10;\n  return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * rng());\n}\n\n// Simulated equity OHLC price series\nconst opens  = new Array(N);\nconst closes = new Array(N);\nconst highs  = new Array(N);\nconst lows   = new Array(N);\nlet price = 150;\nfor (let i = 0; i < N; i++) {\n  const drift = randn() * 2.5;\n  const range = (Math.abs(randn()) + 0.5) * 1.8;\n  opens[i]  = price;\n  closes[i] = price + drift;\n  highs[i]  = Math.max(opens[i], closes[i]) + range * rng();\n  lows[i]   = Math.min(opens[i], closes[i]) - range * rng();\n  price     = closes[i];\n}\n\n// Period high/low helpers for Ichimoku computation\nfunction hiN(arr, end, n) {\n  let m = -Infinity;\n  for (let j = Math.max(0, end - n + 1); j <= end; j++) m = Math.max(m, arr[j]);\n  return m;\n}\nfunction loN(arr, end, n) {\n  let m = Infinity;\n  for (let j = Math.max(0, end - n + 1); j <= end; j++) m = Math.min(m, arr[j]);\n  return m;\n}\n\n// Ichimoku components (standard 9 / 26 / 52 parameters)\nconst tenkan = new Array(TOTAL).fill(null);\nconst kijun  = new Array(TOTAL).fill(null);\nconst spanA  = new Array(TOTAL).fill(null);\nconst spanB  = new Array(TOTAL).fill(null);\nconst chikou = new Array(TOTAL).fill(null);\n\nfor (let i = 0; i < N; i++) {\n  if (i >= 8)\n    tenkan[i] = (hiN(highs, i, 9)  + loN(lows, i, 9))  / 2;\n  if (i >= 25)\n    kijun[i]  = (hiN(highs, i, 26) + loN(lows, i, 26)) / 2;\n  if (tenkan[i] !== null && kijun[i] !== null)\n    spanA[i + FUTURE] = (tenkan[i] + kijun[i]) / 2;\n  if (i >= 51)\n    spanB[i + FUTURE] = (hiN(highs, i, 52) + loN(lows, i, 52)) / 2;\n  if (i >= FUTURE)\n    chikou[i - FUTURE] = closes[i];\n}\n\n// X-axis labels (sequential calendar dates starting 2023-01-03)\nconst dateStart = new Date('2023-01-03');\nconst labels = Array.from({ length: TOTAL }, (_, i) => {\n  const d = new Date(dateStart);\n  d.setDate(d.getDate() + i);\n  return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\n});\n\n// Floating-bar arrays for candlestick simulation\nconst wickData   = Array.from({ length: TOTAL }, (_, i) =>\n  i < N ? [lows[i], highs[i]] : null);\nconst bodyUpData = Array.from({ length: TOTAL }, (_, i) =>\n  i < N && closes[i] >= opens[i] ? [opens[i], closes[i]] : null);\nconst bodyDnData = Array.from({ length: TOTAL }, (_, i) =>\n  i < N && closes[i] <  opens[i] ? [closes[i], opens[i]] : null);\n\n// Per-bar wick colour (Imprint: green = up / matte-red = down, semantic exception)\nconst wickColors = Array.from({ length: TOTAL }, (_, i) =>\n  i < N ? (closes[i] >= opens[i] ? t.palette[0] : t.palette[4]) : 'transparent');\n\n// --- Mount ------------------------------------------------------------------\ndocument.getElementById('container').style.backgroundColor = t.pageBg;\nconst canvas = document.createElement('canvas');\ndocument.getElementById('container').appendChild(canvas);\n\n// --- Chart ------------------------------------------------------------------\nconst titleText = 'indicator-ichimoku · javascript · chartjs · anyplot.ai';\nconst titleSize = Math.max(14, Math.round(22 * Math.min(1, 67 / titleText.length)));\n\nnew Chart(canvas, {\n  type: 'bar',\n  plugins: [{\n    id: 'bgFill',\n    beforeDraw(chart) {\n      const ctx = chart.ctx;\n      ctx.save();\n      ctx.fillStyle = t.pageBg;\n      ctx.fillRect(0, 0, chart.width, chart.height);\n      ctx.restore();\n    },\n  }],\n  data: {\n    labels,\n    datasets: [\n      // ── Ichimoku Kumo cloud (rendered behind price data) ─────────────────\n      {\n        type: 'line',\n        label: 'Senkou Span B',\n        data: spanB,\n        borderColor: t.palette[3],        // #BD8233 ochre — cloud boundary\n        borderWidth: 1,\n        borderDash: [4, 4],\n        pointRadius: 0,\n        spanGaps: false,\n        fill: false,\n        order: 10,\n      },\n      {\n        type: 'line',\n        label: 'Senkou Span A',\n        data: spanA,\n        borderColor: t.palette[7],        // #99B314 lime — cloud boundary\n        borderWidth: 1,\n        borderDash: [4, 4],\n        pointRadius: 0,\n        spanGaps: false,\n        // Fill between Span A and Span B (dataset index 0):\n        // above = Span A > Span B = bullish Kumo (green)\n        // below = Span A < Span B = bearish Kumo (red)\n        fill: {\n          target: 0,\n          above: 'rgba(0,158,115,0.22)',\n          below: 'rgba(174,48,48,0.22)',\n        },\n        order: 10,\n      },\n      // ── Candlestick wicks (thin floating bars: low → high) ───────────────\n      {\n        type: 'bar',\n        label: '_wick',\n        data: wickData,\n        backgroundColor: wickColors,\n        borderWidth: 0,\n        barThickness: 1,\n        order: 5,\n      },\n      // ── Candlestick bodies (floating bars: open → close) ─────────────────\n      {\n        type: 'bar',\n        label: 'Bullish',\n        data: bodyUpData,\n        backgroundColor: 'rgba(0,158,115,0.85)',\n        borderColor: t.palette[0],\n        borderWidth: 1,\n        barThickness: 5,\n        order: 4,\n      },\n      {\n        type: 'bar',\n        label: 'Bearish',\n        data: bodyDnData,\n        backgroundColor: 'rgba(174,48,48,0.85)',\n        borderColor: t.palette[4],\n        borderWidth: 1,\n        barThickness: 5,\n        order: 4,\n      },\n      // ── Tenkan-sen & Kijun-sen (primary Ichimoku signal lines) ────────────\n      {\n        type: 'line',\n        label: 'Tenkan-sen',\n        data: tenkan,\n        borderColor: t.palette[0],        // #009E73 brand green — first categorical series\n        borderWidth: 2,\n        pointRadius: 0,\n        spanGaps: false,\n        fill: false,\n        order: 1,\n      },\n      {\n        type: 'line',\n        label: 'Kijun-sen',\n        data: kijun,\n        borderColor: t.palette[1],        // #C475FD lavender\n        borderWidth: 2,\n        pointRadius: 0,\n        spanGaps: false,\n        fill: false,\n        order: 1,\n      },\n      // ── Chikou Span (lagging line: close plotted 26 periods in the past) ──\n      {\n        type: 'line',\n        label: 'Chikou Span',\n        data: chikou,\n        borderColor: t.palette[5],        // #2ABCCD cyan\n        borderWidth: 1.5,\n        borderDash: [6, 3],\n        pointRadius: 0,\n        spanGaps: false,\n        fill: false,\n        order: 1,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    layout: { padding: { top: 10, right: 20, bottom: 10, left: 10 } },\n    plugins: {\n      title: {\n        display: true,\n        text: titleText,\n        color: t.ink,\n        font: { size: titleSize },\n        padding: { top: 10, bottom: 16 },\n      },\n      legend: {\n        labels: {\n          color: t.ink,\n          font: { size: 14 },\n          filter: (item) => !item.text.startsWith('_'),\n          boxWidth: 24,\n        },\n      },\n    },\n    scales: {\n      x: {\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          maxTicksLimit: 12,\n          maxRotation: 0,\n        },\n        grid: { color: t.grid },\n        border: { display: false },\n        title: {\n          display: true,\n          text: 'Trading Date',\n          color: t.ink,\n          font: { size: 13 },\n        },\n      },\n      y: {\n        beginAtZero: false,\n        ticks: {\n          color: t.inkSoft,\n          font: { size: 14 },\n          callback: (v) => '$' + v.toFixed(0),\n        },\n        grid: { color: t.grid },\n        border: { display: false },\n        title: {\n          display: true,\n          text: 'Price (USD)',\n          color: t.ink,\n          font: { size: 13 },\n        },\n      },\n    },\n  },\n});\n"}