{"spec_id":"renko-basic","library":"chartjs","language":"javascript","code":"// anyplot.ai\n// renko-basic: Basic Renko Chart\n// Library: chartjs 4.4.7 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: deterministic LCG price walk with alternating trend regimes -----\nfunction lcg(seed) {\n  let state = seed >>> 0;\n  return () => {\n    state = (state * 1664525 + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n}\nconst rand = lcg(20240315);\n\nconst NUM_OBSERVATIONS = 260;\nconst REGIME_LENGTH = 30;\nconst BRICK_SIZE = 2;\nconst START_DATE = Date.UTC(2024, 0, 2);\n\nconst closes = [];\nlet price = 100;\nfor (let day = 0; day < NUM_OBSERVATIONS; day++) {\n  const regime = Math.floor(day / REGIME_LENGTH) % 2 === 0 ? 1 : -1;\n  const drift = regime * 0.35;\n  const noise = (rand() - 0.5) * 2.4;\n  price += drift + noise;\n  closes.push(price);\n}\n\n// --- Renko brick construction (fixed brick size, direction on reversal) ----\nconst bricks = [];\nlet lastBrickClose = closes[0];\nfor (let day = 1; day < closes.length; day++) {\n  const observedPrice = closes[day];\n  while (Math.abs(observedPrice - lastBrickClose) >= BRICK_SIZE) {\n    const direction = observedPrice > lastBrickClose ? 1 : -1;\n    const newClose = lastBrickClose + direction * BRICK_SIZE;\n    bricks.push({ open: lastBrickClose, close: newClose, direction, day });\n    lastBrickClose = newClose;\n  }\n}\n\nconst labels = bricks.map((b) => {\n  const d = new Date(START_DATE + b.day * 86400000);\n  return d.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" });\n});\nconst values = bricks.map((b) => [Math.min(b.open, b.close), Math.max(b.open, b.close)]);\n\n// Price range is far from zero — anchor the axis to the brick range, not the origin.\nconst allPrices = bricks.flatMap((b) => [b.open, b.close]);\nconst priceRange = Math.max(...allPrices) - Math.min(...allPrices);\nconst yPadding = priceRange * 0.1;\nconst yMin = Math.min(...allPrices) - yPadding;\nconst yMax = Math.max(...allPrices) + yPadding;\n\n// Finance semantic exception (default-style-guide.md): up -> brand green, down -> matte red\nconst BULLISH = t.palette[0];\nconst BEARISH = t.palette[4];\n\n// Thin edge highlight: a darker shade of each brick's own hue for subtle depth.\nfunction shade(hex, factor) {\n  const r = Math.round(parseInt(hex.slice(1, 3), 16) * factor);\n  const g = Math.round(parseInt(hex.slice(3, 5), 16) * factor);\n  const b = Math.round(parseInt(hex.slice(5, 7), 16) * factor);\n  return `rgb(${r}, ${g}, ${b})`;\n}\nconst BULLISH_EDGE = shade(BULLISH, 0.7);\nconst BEARISH_EDGE = shade(BEARISH, 0.7);\n\n// Find the longest run of consecutive same-direction bricks - the strongest\n// trend move in the series - to call it out with a bolder ink-color edge.\nlet streakStart = 0;\nlet streakEnd = 0;\nlet runStart = 0;\nfor (let i = 1; i <= bricks.length; i++) {\n  if (i === bricks.length || bricks[i].direction !== bricks[i - 1].direction) {\n    if (i - runStart > streakEnd - streakStart + 1) {\n      streakStart = runStart;\n      streakEnd = i - 1;\n    }\n    runStart = i;\n  }\n}\nconst streakLength = streakEnd - streakStart + 1;\nconst streakLabel = bricks[streakStart].direction === 1 ? \"bullish\" : \"bearish\";\n\n// --- Mount -------------------------------------------------------------\nconst canvas = document.createElement(\"canvas\");\ndocument.getElementById(\"container\").appendChild(canvas);\n\n// --- Chart ---------------------------------------------------------------\nnew Chart(canvas, {\n  type: \"bar\",\n  data: {\n    labels,\n    datasets: [\n      {\n        label: \"Brick\",\n        data: values,\n        backgroundColor: (ctx) => (bricks[ctx.dataIndex].direction === 1 ? BULLISH : BEARISH),\n        borderColor: (ctx) => {\n          const i = ctx.dataIndex;\n          if (i >= streakStart && i <= streakEnd) return t.ink;\n          return bricks[i].direction === 1 ? BULLISH_EDGE : BEARISH_EDGE;\n        },\n        borderWidth: (ctx) => (ctx.dataIndex >= streakStart && ctx.dataIndex <= streakEnd ? 3 : 1),\n        barPercentage: 0.82,\n        categoryPercentage: 0.92,\n      },\n    ],\n  },\n  options: {\n    responsive: true,\n    maintainAspectRatio: false,\n    animation: false,\n    plugins: {\n      title: {\n        display: true,\n        text: \"renko-basic · javascript · chartjs · anyplot.ai\",\n        color: t.ink,\n        font: { size: 22 },\n      },\n      subtitle: {\n        display: true,\n        text: `Brick size: $${BRICK_SIZE} · Longest run: ${streakLength} ${streakLabel} bricks`,\n        color: t.inkSoft,\n        font: { size: 14, style: \"italic\" },\n        padding: { bottom: 16 },\n      },\n      legend: {\n        display: true,\n        labels: {\n          color: t.ink,\n          font: { size: 16 },\n          generateLabels: () => [\n            { text: \"Bullish (up)\", fillStyle: BULLISH, strokeStyle: BULLISH, lineWidth: 0 },\n            { text: \"Bearish (down)\", fillStyle: BEARISH, strokeStyle: BEARISH, lineWidth: 0 },\n          ],\n        },\n      },\n    },\n    scales: {\n      x: {\n        ticks: { color: t.inkSoft, font: { size: 14 }, autoSkip: true, maxTicksLimit: 14, maxRotation: 0 },\n        grid: { display: false },\n        title: { display: true, text: \"Brick close date (est.)\", color: t.ink, font: { size: 16 } },\n      },\n      y: {\n        min: yMin,\n        max: yMax,\n        ticks: { color: t.inkSoft, font: { size: 14 } },\n        grid: { color: t.grid },\n        title: { display: true, text: \"Price ($)\", color: t.ink, font: { size: 16 } },\n      },\n    },\n  },\n});\n"}