{"spec_id":"indicator-ema","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// indicator-ema: Exponential Moving Average (EMA) Indicator Chart\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 91/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic fixed-seed LCG) -------------------------\n// LCG PRNG (Numerical Recipes constants) — browser has no seeded RNG.\nlet seed = 42;\nfunction nextRandom() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\n\nconst numPeriods = 120;\nconst dates = [];\nconst closePrices = [];\n\nconst startDate = Date.UTC(2024, 0, 2);\nlet price = 148;\n// Piecewise drift so the price wanders through a downtrend, an uptrend, then\n// a rollover — enough regime change to produce a handful of real EMA crosses.\nfor (let i = 0; i < numPeriods; i++) {\n  dates.push(startDate + i * 86400000);\n  let drift;\n  if (i < 35) drift = -0.35;\n  else if (i < 80) drift = 0.55;\n  else drift = -0.25;\n  price += drift + (nextRandom() - 0.5) * 3.2;\n  price = Math.max(price, 20);\n  closePrices.push(Math.round(price * 100) / 100);\n}\n\nfunction computeEma(values, period) {\n  const k = 2 / (period + 1);\n  const ema = new Array(values.length);\n  ema[0] = values[0];\n  for (let i = 1; i < values.length; i++) {\n    ema[i] = values[i] * k + ema[i - 1] * (1 - k);\n  }\n  return ema;\n}\n\nconst shortPeriod = 12;\nconst longPeriod = 26;\nconst emaShort = computeEma(closePrices, shortPeriod);\nconst emaLong = computeEma(closePrices, longPeriod);\n\n// Crossover points: golden cross (short over long, bullish) vs death cross\n// (short under long, bearish) — the spec explicitly calls out highlighting\n// these, so they carry real information rather than decorative annotation.\n// Scan starts once both EMAs have warmed up past the long period — both\n// series seed from the same first close, so an earlier start would flag\n// that shared seed as a spurious \"cross\".\nconst goldenCrosses = [];\nconst deathCrosses = [];\nfor (let i = longPeriod; i < numPeriods; i++) {\n  const prevDiff = emaShort[i - 1] - emaLong[i - 1];\n  const diff = emaShort[i] - emaLong[i];\n  if (prevDiff <= 0 && diff > 0) goldenCrosses.push([dates[i], emaShort[i]]);\n  else if (prevDiff >= 0 && diff < 0)\n    deathCrosses.push([dates[i], emaShort[i]]);\n}\n\nconst closeSeries = closePrices.map((v, i) => [dates[i], v]);\nconst shortSeries = emaShort.map((v, i) => [\n  dates[i],\n  Math.round(v * 100) / 100,\n]);\nconst longSeries = emaLong.map((v, i) => [dates[i], Math.round(v * 100) / 100]);\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: \"indicator-ema · 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  },\n  yAxis: {\n    title: {\n      text: \"Price (USD)\",\n      style: { color: t.inkSoft, fontSize: \"16px\" },\n    },\n    gridLineColor: t.grid,\n    labels: {\n      style: { color: t.inkSoft, fontSize: \"14px\" },\n      formatter() {\n        return \"$\" + this.value;\n      },\n    },\n  },\n  legend: {\n    itemStyle: { color: t.inkSoft, fontSize: \"14px\" },\n    itemHoverStyle: { color: t.ink },\n  },\n  tooltip: { enabled: false },\n  plotOptions: {\n    series: { animation: false, marker: { enabled: false } },\n  },\n  series: [\n    {\n      name: \"Close price\",\n      type: \"line\",\n      data: closeSeries,\n      color: t.palette[0],\n      lineWidth: 3,\n    },\n    {\n      name: `EMA ${shortPeriod}`,\n      type: \"line\",\n      data: shortSeries,\n      color: t.palette[1],\n      lineWidth: 2,\n    },\n    {\n      name: `EMA ${longPeriod}`,\n      type: \"line\",\n      data: longSeries,\n      color: t.palette[2],\n      lineWidth: 2,\n    },\n    {\n      name: \"Golden cross\",\n      type: \"scatter\",\n      data: goldenCrosses,\n      color: t.palette[0],\n      marker: {\n        enabled: true,\n        radius: 7,\n        symbol: \"triangle\",\n        lineWidth: 1,\n        lineColor: t.ink,\n      },\n    },\n    {\n      name: \"Death cross\",\n      type: \"scatter\",\n      data: deathCrosses,\n      color: \"#AE3030\",\n      marker: {\n        enabled: true,\n        radius: 7,\n        symbol: \"triangle-down\",\n        lineWidth: 1,\n        lineColor: t.ink,\n      },\n    },\n  ],\n});\n"}