{"spec_id":"kagi-basic","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// kagi-basic: Basic Kagi Chart\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 86/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: synthetic Brent crude oil daily close, USD/bbl (deterministic LCG) ---\nfunction lcg(seed) {\n  let state = seed;\n  return function next() {\n    state = (state * 1664525 + 1013904223) % 4294967296;\n    return state / 4294967296;\n  };\n}\nconst rand = lcg(20260902);\n\nconst n = 260;\nconst closes = [];\nlet price = 74;\nlet momentum = 0;\nfor (let i = 0; i < n; i++) {\n  momentum = momentum * 0.82 + (rand() - 0.5) * 2.6;\n  price = Math.max(38, price + momentum);\n  closes.push(Math.round(price * 100) / 100);\n}\n\n// --- Kagi construction: collapse the close series into turning-point vertices ---\nconst REVERSAL = 0.04; // 4% reversal threshold\n\nconst vertices = [closes[0]];\nlet dir = null; // \"up\" | \"down\"\nlet extreme = closes[0];\nfor (let i = 1; i < closes.length; i++) {\n  const p = closes[i];\n  if (dir === null) {\n    if ((p - closes[0]) / closes[0] >= REVERSAL) {\n      dir = \"up\";\n      extreme = p;\n    } else if ((closes[0] - p) / closes[0] >= REVERSAL) {\n      dir = \"down\";\n      extreme = p;\n    }\n    continue;\n  }\n  if (dir === \"up\") {\n    if (p >= extreme) {\n      extreme = p;\n    } else if ((extreme - p) / extreme >= REVERSAL) {\n      vertices.push(extreme);\n      dir = \"down\";\n      extreme = p;\n    }\n  } else {\n    if (p <= extreme) {\n      extreme = p;\n    } else if ((p - extreme) / extreme >= REVERSAL) {\n      vertices.push(extreme);\n      dir = \"up\";\n      extreme = p;\n    }\n  }\n}\nvertices.push(extreme);\n\n// --- Yang/yin classification: thickness flips only when a column breaks the\n// prior same-direction extreme (the last shoulder for up, the last waist for down) ---\nlet thick = null;\nconst segments = [];\nfor (let i = 1; i < vertices.length; i++) {\n  const goingUp = vertices[i] > vertices[i - 1];\n  if (i === 1) {\n    thick = goingUp;\n  } else {\n    const priorExtreme = vertices[i - 2];\n    if (goingUp && vertices[i] > priorExtreme) thick = true;\n    else if (!goingUp && vertices[i] < priorExtreme) thick = false;\n  }\n  segments.push({\n    data: [\n      [i - 1, vertices[i - 1]],\n      [i, vertices[i]],\n    ],\n    thick,\n  });\n}\n\nconst YANG = t.palette[0]; // \"#009E73\" brand green — uptrend breakout\nconst YIN = t.palette[4]; // \"#AE3030\" matte red — downtrend breakdown\n\n// --- Standout-move callout: find the single largest vertex-to-vertex swing\n// and shade its column with a plotBand tinted in its own yang/yin color, so\n// the reader's eye lands on the move that matters most, not just the trend ---\nlet standout = segments[0];\nlet standoutDelta = 0;\nfor (const seg of segments) {\n  const delta = Math.abs(seg.data[1][1] - seg.data[0][1]);\n  if (delta > standoutDelta) {\n    standoutDelta = delta;\n    standout = seg;\n  }\n}\nconst standoutUp = standout.data[1][1] > standout.data[0][1];\nconst standoutLabel =\n  \"Standout \" + (standoutUp ? \"breakout: +$\" : \"breakdown: -$\") + standoutDelta.toFixed(2);\n\n// --- Chart ---------------------------------------------------------------\nHighcharts.chart(\"container\", {\n  chart: {\n    type: \"line\",\n    backgroundColor: \"transparent\",\n    animation: false,\n    style: { fontFamily: \"inherit\" },\n  },\n  credits: { enabled: false },\n  title: {\n    text: \"kagi-basic · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"Brent crude oil, synthetic daily close · 4% reversal threshold\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    title: { text: \"Kagi Line Index\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    allowDecimals: false,\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    plotBands: [\n      {\n        from: standout.data[0][0],\n        to: standout.data[1][0],\n        color: Highcharts.color(standoutUp ? YANG : YIN)\n          .setOpacity(0.14)\n          .get(),\n        label: {\n          text: standoutLabel,\n          style: { color: t.ink, fontSize: \"12px\", fontWeight: \"600\" },\n          align: \"center\",\n          verticalAlign: \"top\",\n          y: 14,\n        },\n      },\n    ],\n  },\n  yAxis: {\n    title: {\n      text: \"Brent Crude Oil Price (USD/bbl)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  legend: {\n    enabled: true,\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    enabled: true,\n    formatter: function formatTooltip() {\n      return \"Line \" + this.point.x + \"<br>$\" + this.point.y.toFixed(2);\n    },\n  },\n  plotOptions: {\n    series: { animation: false, marker: { enabled: false }, enableMouseTracking: true },\n  },\n  series: [\n    ...segments.map((seg) => ({\n      type: \"line\",\n      step: \"left\",\n      data: seg.data,\n      color: seg.thick ? YANG : YIN,\n      lineWidth: seg.thick ? 6 : 2,\n      showInLegend: false,\n    })),\n    {\n      name: \"Yang (breaks prior high)\",\n      type: \"line\",\n      color: YANG,\n      lineWidth: 6,\n      data: [],\n      showInLegend: true,\n      enableMouseTracking: false,\n    },\n    {\n      name: \"Yin (breaks prior low)\",\n      type: \"line\",\n      color: YIN,\n      lineWidth: 2,\n      data: [],\n      showInLegend: true,\n      enableMouseTracking: false,\n    },\n  ],\n});\n"}