{"spec_id":"indicator-ichimoku","library":"echarts","language":"javascript","code":"// anyplot.ai\n// indicator-ichimoku: Ichimoku Cloud Technical Indicator Chart\n// Library: echarts 5.5.1 | JavaScript 22.22.3\n// Quality: 87/100 | Created: 2026-06-08\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Tiny LCG for reproducible pseudo-random price generation (no seeded RNG in browser)\nfunction lcg(seed) {\n  let s = seed >>> 0;\n  return () => { s = (Math.imul(1664525, s) + 1013904223) >>> 0; return s / 0xffffffff; };\n}\nconst rng = lcg(42);\n\n// Generate ~200 trading days of OHLC data\nconst N = 200;\nconst dates = [];\nconst opens = [], highs = [], lows = [], closes = [];\n\nlet price = 148.0;\nconst startDate = new Date(2023, 0, 2);\nlet dayIdx = 0;\n\nwhile (dates.length < N) {\n  const d = new Date(startDate);\n  d.setDate(startDate.getDate() + dayIdx);\n  dayIdx++;\n  if (d.getDay() === 0 || d.getDay() === 6) continue;\n  const dateStr = d.toISOString().slice(0, 10);\n  const change = (rng() - 0.48) * 3.8;\n  const open  = price;\n  const close = Math.max(80, Math.min(300, open + change));\n  const wick  = rng() * 2.8 + 0.4;\n  const high  = Math.max(open, close) + wick;\n  const low   = Math.min(open, close) - wick;\n  dates.push(dateStr);\n  opens.push(open);\n  highs.push(high);\n  lows.push(low);\n  closes.push(close);\n  price = close;\n}\n\n// Compute Ichimoku components (standard 9/26/52 parameters)\nfunction periodHL(hiArr, loArr, i, period) {\n  const start = Math.max(0, i - period + 1);\n  let hi = -Infinity, lo = Infinity;\n  for (let j = start; j <= i; j++) { hi = Math.max(hi, hiArr[j]); lo = Math.min(lo, loArr[j]); }\n  return { hi, lo };\n}\n\nconst tenkan = [], kijun = [], spanA = [], spanB = [];\nfor (let i = 0; i < N; i++) {\n  const r9  = periodHL(highs, lows, i, 9);\n  const r26 = periodHL(highs, lows, i, 26);\n  const r52 = periodHL(highs, lows, i, 52);\n  tenkan.push((r9.hi + r9.lo) / 2);\n  kijun.push((r26.hi + r26.lo) / 2);\n  spanA.push((tenkan[i] + kijun[i]) / 2);\n  spanB.push((r52.hi + r52.lo) / 2);\n}\n\n// Build date axis: N history days + 26 projected days for cloud displacement\nconst futureDates = [];\nlet lastDay = new Date(dates[N - 1]);\nlet added = 0;\nwhile (added < 26) {\n  lastDay.setDate(lastDay.getDate() + 1);\n  if (lastDay.getDay() !== 0 && lastDay.getDay() !== 6) {\n    futureDates.push(lastDay.toISOString().slice(0, 10));\n    added++;\n  }\n}\nconst allDates = [...dates, ...futureDates]; // length = N + 26\n\n// Cloud fill: differential stacking so fill is ONLY between SpanA and SpanB\n// -- Bullish band (SpanA >= SpanB): base = SpanB, fill = SpanA - SpanB (green)\n// -- Bearish band (SpanB >  SpanA): base = SpanA, fill = SpanB - SpanA (red)\n// Each series carries null for the other state so the fill breaks at transitions.\nconst cloudBaseBull = [], cloudFillBull = [];\nconst cloudBaseBear = [], cloudFillBear = [];\nfor (let i = 0; i < N; i++) {\n  const xi = i + 26; // displaced 26 periods forward\n  const vA = spanA[i], vB = spanB[i];\n  if (vA >= vB) {\n    cloudBaseBull.push([xi, vB]);\n    cloudFillBull.push([xi, vA - vB]);\n    cloudBaseBear.push([xi, null]);\n    cloudFillBear.push([xi, null]);\n  } else {\n    cloudBaseBull.push([xi, null]);\n    cloudFillBull.push([xi, null]);\n    cloudBaseBear.push([xi, vA]);\n    cloudFillBear.push([xi, vB - vA]);\n  }\n}\n\n// Lines shifted to their correct positions\nconst tenkanData = tenkan.map((v, i) => [i, v]);\nconst kijunData  = kijun.map((v, i) => [i, v]);\n// Chikou span: close plotted 26 periods in the past (index i - 26, skip first 26)\nconst chikouData = closes.map((v, i) => [i - 26, v]).filter(d => d[0] >= 0);\n\n// --- Colors -----------------------------------------------------------------\nconst BULL_GREEN   = t.palette[0]; // #009E73 — up candles, bullish cloud\nconst BEAR_RED     = t.palette[4]; // #AE3030 — down candles, bearish cloud\nconst TENKAN_COLOR = t.palette[6]; // #954477 rose\nconst KIJUN_COLOR  = t.palette[2]; // #4467A3 blue\nconst CHIKOU_COLOR = t.palette[3]; // #BD8233 ochre\nconst CLOUD_BULL_FILL = \"rgba(0,158,115,0.22)\";\nconst CLOUD_BEAR_FILL = \"rgba(174,48,48,0.22)\";\nconst TRANSPARENT  = \"rgba(0,0,0,0)\";\n\n// Title fontsize scaling\nconst titleText = \"indicator-ichimoku · javascript · echarts · anyplot.ai\";\nconst titleFs   = Math.max(14, Math.round(22 * Math.min(1, 67 / titleText.length)));\n\n// --- Init -------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option -----------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  color: t.palette,\n  backgroundColor: t.pageBg,\n\n  title: {\n    text: titleText,\n    left: \"center\",\n    top: 14,\n    textStyle: { color: t.ink, fontSize: titleFs, fontWeight: \"500\" }\n  },\n\n  legend: {\n    top: 50,\n    left: \"center\",\n    itemGap: 18,\n    textStyle: { color: t.inkSoft, fontSize: 13 },\n    data: [\n      { name: \"OHLC\", icon: \"rect\" },\n      { name: \"Tenkan-sen\" },\n      { name: \"Kijun-sen\" },\n      { name: \"Chikou Span\" },\n      { name: \"Kumo (Bull)\", icon: \"rect\", itemStyle: { color: CLOUD_BULL_FILL, borderColor: BULL_GREEN, borderWidth: 1 } },\n      { name: \"Kumo (Bear)\", icon: \"rect\", itemStyle: { color: CLOUD_BEAR_FILL, borderColor: BEAR_RED, borderWidth: 1 } }\n    ]\n  },\n\n  grid: { left: 72, right: 40, top: 106, bottom: 72 },\n\n  xAxis: {\n    type: \"category\",\n    data: allDates,\n    boundaryGap: true,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 12,\n      rotate: 30,\n      interval: Math.floor(allDates.length / 8)\n    },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: false }\n  },\n\n  yAxis: {\n    scale: true,\n    axisLabel: { color: t.inkSoft, fontSize: 13 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid } }\n  },\n\n  series: [\n    // ── Cloud fill: differential stacking (base transparent, diff colored) ──\n    // Bullish base — transparent fill from axis to SpanB; breaks where null\n    {\n      name: \"_cb_base\",\n      type: \"line\", symbol: \"none\", silent: true, z: 1,\n      data: cloudBaseBull,\n      connectNulls: false,\n      stack: \"cloud_bull\",\n      lineStyle: { opacity: 0, width: 0 },\n      areaStyle: { color: TRANSPARENT, origin: \"auto\" }\n    },\n    // Bullish fill — SpanA - SpanB stacked on top of SpanB → fills cloud band\n    {\n      name: \"Kumo (Bull)\",\n      type: \"line\", symbol: \"none\", silent: true, z: 1,\n      data: cloudFillBull,\n      connectNulls: false,\n      stack: \"cloud_bull\",\n      lineStyle: { color: BULL_GREEN, opacity: 0.5, width: 1 },\n      areaStyle: { color: CLOUD_BULL_FILL, origin: \"auto\" }\n    },\n    // Bearish base — transparent fill from axis to SpanA\n    {\n      name: \"_cb_base2\",\n      type: \"line\", symbol: \"none\", silent: true, z: 1,\n      data: cloudBaseBear,\n      connectNulls: false,\n      stack: \"cloud_bear\",\n      lineStyle: { opacity: 0, width: 0 },\n      areaStyle: { color: TRANSPARENT, origin: \"auto\" }\n    },\n    // Bearish fill — SpanB - SpanA stacked on SpanA → fills cloud band\n    {\n      name: \"Kumo (Bear)\",\n      type: \"line\", symbol: \"none\", silent: true, z: 1,\n      data: cloudFillBear,\n      connectNulls: false,\n      stack: \"cloud_bear\",\n      lineStyle: { color: BEAR_RED, opacity: 0.5, width: 1 },\n      areaStyle: { color: CLOUD_BEAR_FILL, origin: \"auto\" }\n    },\n    // ── OHLC Candlesticks ──────────────────────────────────────────────────\n    {\n      name: \"OHLC\",\n      type: \"candlestick\",\n      z: 3,\n      data: dates.map((_, i) => [opens[i], closes[i], lows[i], highs[i]]),\n      itemStyle: {\n        color: BULL_GREEN,\n        color0: BEAR_RED,\n        borderColor: BULL_GREEN,\n        borderColor0: BEAR_RED,\n        borderWidth: 1\n      }\n    },\n    // ── Ichimoku lines ─────────────────────────────────────────────────────\n    {\n      name: \"Tenkan-sen\",\n      type: \"line\", symbol: \"none\", z: 4,\n      data: tenkanData,\n      lineStyle: { color: TENKAN_COLOR, width: 1.5 }\n    },\n    {\n      name: \"Kijun-sen\",\n      type: \"line\", symbol: \"none\", z: 4,\n      data: kijunData,\n      lineStyle: { color: KIJUN_COLOR, width: 2, type: \"dashed\" }\n    },\n    {\n      name: \"Chikou Span\",\n      type: \"line\", symbol: \"none\", z: 4,\n      data: chikouData,\n      lineStyle: { color: CHIKOU_COLOR, width: 1.5, type: \"dotted\" }\n    }\n  ]\n});\n"}