{"spec_id":"renko-basic","library":"echarts","language":"javascript","code":"// anyplot.ai\n// renko-basic: Basic Renko Chart\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 87/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\nconst UP_COLOR = t.palette[0]; // \"#009E73\" — bullish brick, ALWAYS Imprint position 1\nconst DOWN_COLOR = \"#AE3030\"; // Imprint position 5 — semantic anchor for bearish/loss\n\n// --- Data: simulate daily closes, then convert to Renko bricks -------------\n// Tiny fixed-seed LCG — the browser has no seeded RNG.\nfunction makeLcg(seed) {\n  let state = seed;\n  return () => {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\n\nconst rng = makeLcg(42);\nconst brickSize = 2; // fixed $2 price move per brick\nconst numDays = 220;\n\nconst closes = [150];\nfor (let i = 1; i < numDays; i++) {\n  const step = (rng() - 0.5) * 3 + 0.08; // slight upward drift random walk\n  closes.push(closes[i - 1] + step);\n}\n\n// Renko conversion: a new brick is emitted only once price has moved a full\n// brickSize away from the last brick's boundary — this is what filters out\n// the day-to-day noise and leaves only significant price action.\nconst bricks = [];\nlet level = Math.round(closes[0] / brickSize) * brickSize;\nfor (let i = 1; i < closes.length; i++) {\n  const price = closes[i];\n  while (price - level >= brickSize) {\n    level += brickSize;\n    bricks.push({ low: level - brickSize, high: level, direction: 1 });\n  }\n  while (level - price >= brickSize) {\n    level -= brickSize;\n    bricks.push({ low: level, high: level + brickSize, direction: -1 });\n  }\n}\n\nconst brickData = bricks.map((b, i) => [i, b.low, b.high, b.direction]);\nconst priceLow = Math.min(...bricks.map((b) => b.low));\nconst priceHigh = Math.max(...bricks.map((b) => b.high));\nconst pricePadding = brickSize * 3;\n\n// --- Init --------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option --------------------------------------------------------------\n// Bricks are drawn with a custom series (renderItem) so every brick is a\n// uniform rectangle with a small gap between neighbors — a plain bar series\n// cannot start each bar at an arbitrary \"low\" baseline.\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"renko-basic · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: 22 },\n  },\n  grid: { left: 90, right: 60, top: 90, bottom: 70 },\n  xAxis: {\n    type: \"value\",\n    name: \"Brick Index\",\n    nameLocation: \"middle\",\n    nameGap: 40,\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    min: -0.5,\n    max: brickData.length - 0.5,\n    axisLabel: {\n      color: t.inkSoft,\n      fontSize: 14,\n      showMinLabel: false,\n      showMaxLabel: false,\n    },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"Price ($)\",\n    nameTextStyle: { color: t.inkSoft, fontSize: 16 },\n    min: priceLow - pricePadding,\n    max: priceHigh + pricePadding,\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      type: \"custom\",\n      renderItem: (params, api) => {\n        const idx = api.value(0);\n        const low = api.value(1);\n        const high = api.value(2);\n        const direction = api.value(3);\n        const gap = 0.12; // fraction of one brick-width kept as a visual gap\n        const topLeft = api.coord([idx - 0.5 + gap, high]);\n        const bottomRight = api.coord([idx + 0.5 - gap, low]);\n        const width = bottomRight[0] - topLeft[0];\n        const height = bottomRight[1] - topLeft[1];\n        return {\n          type: \"rect\",\n          shape: { x: topLeft[0], y: topLeft[1], width, height },\n          style: {\n            fill: direction > 0 ? UP_COLOR : DOWN_COLOR,\n            stroke: t.ink,\n            lineWidth: 1,\n          },\n        };\n      },\n      encode: { x: 0, y: [1, 2] },\n      data: brickData,\n    },\n  ],\n});\n"}