{"spec_id":"stock-event-flags","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// stock-event-flags: Stock Chart with Event Flags\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 92/100 | Created: 2026-08-26\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Helpers -----------------------------------------------------------------\nfunction makeLcg(seed) {\n  let state = seed >>> 0;\n  return function lcg() {\n    state = (1103515245 * state + 12345) >>> 0;\n    return state / 4294967296;\n  };\n}\nfunction hexToRgba(hex, alpha) {\n  const r = parseInt(hex.slice(1, 3), 16);\n  const g = parseInt(hex.slice(3, 5), 16);\n  const b = parseInt(hex.slice(5, 7), 16);\n  return `rgba(${r}, ${g}, ${b}, ${alpha})`;\n}\n\n// --- Data: one trading year for a fictional tech company ---------------------\nconst TRADING_DAYS = 190;\nconst tradingDates = [];\nconst cursor = new Date(Date.UTC(2024, 0, 2));\nwhile (tradingDates.length < TRADING_DAYS) {\n  const weekday = cursor.getUTCDay();\n  if (weekday !== 0 && weekday !== 6) tradingDates.push(cursor.getTime());\n  cursor.setUTCDate(cursor.getUTCDate() + 1);\n}\n\nconst rng = makeLcg(20260826);\nlet price = 148;\nconst closePrices = tradingDates.map(() => {\n  const drift = 0.00065;\n  const shock = (rng() - 0.5) * 4.4;\n  price = Math.max(30, price * (1 + drift) + shock * 0.4);\n  return Math.round(price * 100) / 100;\n});\n\nconst EVENTS = [\n  { i: 10, type: \"earnings\", label: \"Q4 Earnings Beat\" },\n  { i: 34, type: \"news\", label: \"AI Chip Unveiled\" },\n  { i: 49, type: \"dividend\", label: \"Ex-Div $0.24\" },\n  { i: 71, type: \"earnings\", label: \"Q1 Earnings Miss\" },\n  { i: 96, type: \"split\", label: \"2-for-1 Split\" },\n  { i: 119, type: \"dividend\", label: \"Ex-Div $0.26\" },\n  { i: 141, type: \"earnings\", label: \"Q2 Earnings Beat\" },\n  { i: 169, type: \"news\", label: \"Analyst Upgrade\" },\n];\n\nconst EVENT_STYLE = {\n  earnings: { name: \"Earnings\", color: t.palette[1], symbol: \"triangle\" },\n  dividend: { name: \"Dividend\", color: t.palette[2], symbol: \"diamond\" },\n  split: { name: \"Stock Split\", color: t.palette[3], symbol: \"square\" },\n  news: { name: \"News\", color: t.palette[5], symbol: \"circle\" },\n};\n\nconst eventSeriesData = { earnings: [], dividend: [], split: [], news: [] };\nEVENTS.forEach((event, idx) => {\n  eventSeriesData[event.type].push({\n    x: tradingDates[event.i],\n    y: closePrices[event.i],\n    label: event.label,\n    dataLabels: { y: idx % 2 === 0 ? -60 : -96 },\n  });\n});\n\n// Dashed connector from each flag down to its trading date, theme-adaptive.\nconst connectorColor = hexToRgba(t.ink, 0.3);\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  colors: t.palette,\n  title: {\n    text: \"stock-event-flags · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  xAxis: {\n    type: \"datetime\",\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    plotLines: EVENTS.map((event) => ({\n      value: tradingDates[event.i],\n      color: connectorColor,\n      dashStyle: \"Dash\",\n      width: 1,\n      zIndex: 2,\n    })),\n  },\n  yAxis: {\n    title: {\n      text: \"Close Price (USD)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    maxPadding: 0.38,\n    minPadding: 0.08,\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: {\n    backgroundColor: t.elevatedBg,\n    borderColor: t.inkSoft,\n    style: { color: t.ink },\n    xDateFormat: \"%b %e, %Y\",\n  },\n  plotOptions: {\n    series: { animation: false },\n    line: { marker: { enabled: false } },\n    scatter: {\n      marker: { radius: 7, lineWidth: 1.5, lineColor: t.pageBg },\n      dataLabels: {\n        enabled: true,\n        format: \"{point.label}\",\n        verticalAlign: \"bottom\",\n        align: \"center\",\n        backgroundColor: t.elevatedBg,\n        borderRadius: 4,\n        borderWidth: 1,\n        padding: 6,\n        style: {\n          color: t.ink,\n          fontSize: \"13px\",\n          fontWeight: \"600\",\n          textOutline: \"none\",\n        },\n      },\n    },\n  },\n  series: [\n    {\n      name: \"Close Price\",\n      type: \"line\",\n      data: tradingDates.map((date, i) => [date, closePrices[i]]),\n      color: t.palette[0],\n      lineWidth: 2.5,\n      zIndex: 1,\n    },\n    {\n      name: EVENT_STYLE.earnings.name,\n      type: \"scatter\",\n      data: eventSeriesData.earnings,\n      color: EVENT_STYLE.earnings.color,\n      marker: { symbol: EVENT_STYLE.earnings.symbol },\n      dataLabels: { borderColor: EVENT_STYLE.earnings.color },\n      zIndex: 3,\n    },\n    {\n      name: EVENT_STYLE.dividend.name,\n      type: \"scatter\",\n      data: eventSeriesData.dividend,\n      color: EVENT_STYLE.dividend.color,\n      marker: { symbol: EVENT_STYLE.dividend.symbol },\n      dataLabels: { borderColor: EVENT_STYLE.dividend.color },\n      zIndex: 3,\n    },\n    {\n      name: EVENT_STYLE.split.name,\n      type: \"scatter\",\n      data: eventSeriesData.split,\n      color: EVENT_STYLE.split.color,\n      marker: { symbol: EVENT_STYLE.split.symbol },\n      dataLabels: { borderColor: EVENT_STYLE.split.color },\n      zIndex: 3,\n    },\n    {\n      name: EVENT_STYLE.news.name,\n      type: \"scatter\",\n      data: eventSeriesData.news,\n      color: EVENT_STYLE.news.color,\n      marker: { symbol: EVENT_STYLE.news.symbol },\n      dataLabels: { borderColor: EVENT_STYLE.news.color },\n      zIndex: 3,\n    },\n  ],\n});\n"}