{"spec_id":"ohlc-bar","library":"echarts","language":"javascript","code":"// anyplot.ai\n// ohlc-bar: OHLC Bar Chart\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 93/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\nconst UP_COLOR = t.palette[0]; // #009E73 brand green — profit/up semantic\nconst DOWN_COLOR = t.palette[4]; // #AE3030 matte red — loss/down semantic\n\n// --- Data: 45 trading days of a fictional stock's OHLC prices --------------\nconst MONTHS = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\"];\nconst NUM_DAYS = 45;\n\nfunction seededRandom(seed) {\n  let s = seed;\n  return function () {\n    s = (s * 1103515245 + 12345) & 0x7fffffff;\n    return s / 0x7fffffff;\n  };\n}\nconst rand = seededRandom(42);\n\nconst dateLabels = [];\nconst cursor = new Date(2024, 0, 2); // Tue Jan 2 2024\nwhile (dateLabels.length < NUM_DAYS) {\n  const weekday = cursor.getDay();\n  if (weekday !== 0 && weekday !== 6) {\n    dateLabels.push(`${MONTHS[cursor.getMonth()]} ${cursor.getDate()}`);\n  }\n  cursor.setDate(cursor.getDate() + 1);\n}\n\nconst ohlcRows = [];\nlet lastClose = 182;\nfor (let i = 0; i < NUM_DAYS; i++) {\n  const open = lastClose + (rand() - 0.5) * 3;\n  const drift = (rand() - 0.47) * 6; // slight upward drift over the period\n  const close = open + drift;\n  const high = Math.max(open, close) + rand() * 2.6;\n  const low = Math.min(open, close) - rand() * 2.6;\n  ohlcRows.push([i, +open.toFixed(2), +high.toFixed(2), +low.toFixed(2), +close.toFixed(2)]);\n  lastClose = close;\n}\n\n// --- Find the steepest single-day move for the storytelling callout --------\nlet extremeIndex = 0;\nlet extremeMove = ohlcRows[0][4] - ohlcRows[0][1];\nfor (const [xIndex, open, , , close] of ohlcRows) {\n  const move = close - open;\n  if (Math.abs(move) > Math.abs(extremeMove)) {\n    extremeMove = move;\n    extremeIndex = xIndex;\n  }\n}\n\n// --- Custom render: I-beam OHLC bars ----------------------------------------\n// Bearish bars use a dashed stroke in addition to red, so the up/down signal\n// survives for red-green color-vision-deficient viewers, not just via hue.\nfunction renderOhlcBar(params, api) {\n  const xIndex = api.value(0);\n  const openPoint = api.coord([xIndex, api.value(1)]);\n  const highPoint = api.coord([xIndex, api.value(2)]);\n  const lowPoint = api.coord([xIndex, api.value(3)]);\n  const closePoint = api.coord([xIndex, api.value(4)]);\n  const tickLength = api.size([1, 0])[0] * 0.32;\n  const isUp = api.value(4) >= api.value(1);\n  const lineStyle = isUp\n    ? { stroke: UP_COLOR, lineWidth: 2.6 }\n    : { stroke: DOWN_COLOR, lineWidth: 2.6, lineDash: [6, 3] };\n\n  return {\n    type: \"group\",\n    children: [\n      {\n        type: \"line\",\n        shape: { x1: highPoint[0], y1: highPoint[1], x2: lowPoint[0], y2: lowPoint[1] },\n        style: lineStyle,\n      },\n      {\n        type: \"line\",\n        shape: { x1: openPoint[0] - tickLength, y1: openPoint[1], x2: openPoint[0], y2: openPoint[1] },\n        style: lineStyle,\n      },\n      {\n        type: \"line\",\n        shape: { x1: closePoint[0], y1: closePoint[1], x2: closePoint[0] + tickLength, y2: closePoint[1] },\n        style: lineStyle,\n      },\n    ],\n  };\n}\n\n// --- Init + option -----------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"Aurora Robotics · ohlc-bar · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 24,\n    textStyle: { color: t.ink, fontSize: 22, fontWeight: 500 },\n  },\n  legend: {\n    data: [\"Bullish (close > open)\", \"Bearish (close < open)\"],\n    top: 78,\n    textStyle: { color: t.inkSoft, fontSize: 16 },\n    itemWidth: 26,\n    itemHeight: 3,\n  },\n  grid: { left: 130, right: 60, top: 150, bottom: 110 },\n  xAxis: {\n    type: \"category\",\n    data: dateLabels,\n    boundaryGap: true,\n    axisLabel: { color: t.inkSoft, fontSize: 14, interval: 3 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    scale: true,\n    name: \"Price (USD)\",\n    nameLocation: \"middle\",\n    nameGap: 44,\n    nameRotate: 90,\n    nameTextStyle: { color: t.inkSoft, fontSize: 14 },\n    axisLabel: { color: t.inkSoft, fontSize: 14, formatter: (v) => `$${v}` },\n    axisLine: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      name: \"Bullish (close > open)\",\n      type: \"line\",\n      data: [],\n      symbol: \"none\",\n      itemStyle: { color: UP_COLOR },\n      lineStyle: { color: UP_COLOR, width: 2.6 },\n    },\n    {\n      name: \"Bearish (close < open)\",\n      type: \"line\",\n      data: [],\n      symbol: \"none\",\n      itemStyle: { color: DOWN_COLOR },\n      lineStyle: { color: DOWN_COLOR, width: 2.6, type: \"dashed\" },\n    },\n    {\n      name: \"OHLC\",\n      type: \"custom\",\n      renderItem: renderOhlcBar,\n      encode: { x: 0, y: [1, 2, 3, 4] },\n      data: ohlcRows,\n    },\n  ],\n});\n\n// --- Storytelling callout: annotate the steepest single-day move -----------\nconst extremeRow = ohlcRows[extremeIndex];\nconst anchorPrice = extremeMove >= 0 ? extremeRow[2] : extremeRow[3]; // high : low\nconst anchorPixel = chart.convertToPixel({ xAxisIndex: 0, yAxisIndex: 0 }, [\n  dateLabels[extremeIndex],\n  anchorPrice,\n]);\nconst calloutLabel =\n  extremeMove >= 0\n    ? `Steepest rally: +$${extremeMove.toFixed(2)}`\n    : `Steepest drawdown: -$${Math.abs(extremeMove).toFixed(2)}`;\nconst labelOffset = extremeMove >= 0 ? -38 : 38;\nconst labelY = anchorPixel[1] + labelOffset;\n\nchart.setOption({\n  graphic: {\n    elements: [\n      {\n        type: \"group\",\n        children: [\n          {\n            type: \"line\",\n            shape: {\n              x1: anchorPixel[0],\n              y1: anchorPixel[1],\n              x2: anchorPixel[0],\n              y2: labelY + (extremeMove >= 0 ? 14 : -14),\n            },\n            style: { stroke: t.inkSoft, lineWidth: 1 },\n          },\n          {\n            type: \"text\",\n            x: anchorPixel[0],\n            y: labelY,\n            style: {\n              text: calloutLabel,\n              fill: t.ink,\n              font: \"600 13px sans-serif\",\n              textAlign: \"center\",\n              textVerticalAlign: extremeMove >= 0 ? \"bottom\" : \"top\",\n            },\n          },\n        ],\n      },\n    ],\n  },\n});\n"}