{"spec_id":"indicator-macd","library":"echarts","language":"javascript","code":"// anyplot.ai\n// indicator-macd: MACD Technical Indicator Chart\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 87/100 | Created: 2026-09-05\n\n//# anyplot-orientation: landscape\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic LCG price walk) -------------------------\nconst FAST_PERIOD = 12;\nconst SLOW_PERIOD = 26;\nconst SIGNAL_PERIOD = 9;\nconst N_DAYS = 120;\n\nlet seed = 42;\nfunction nextRandom() {\n  seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n  return seed / 0x7fffffff;\n}\n\nfunction ema(values, period) {\n  const k = 2 / (period + 1);\n  const out = [values[0]];\n  for (let i = 1; i < values.length; i++) {\n    out.push(values[i] * k + out[i - 1] * (1 - k));\n  }\n  return out;\n}\n\nfunction tradingDayLabels(count) {\n  const labels = [];\n  const start = new Date(Date.UTC(2024, 0, 2));\n  const cursor = new Date(start);\n  while (labels.length < count) {\n    const day = cursor.getUTCDay();\n    if (day !== 0 && day !== 6) {\n      labels.push(cursor.toISOString().slice(0, 10));\n    }\n    cursor.setUTCDate(cursor.getUTCDate() + 1);\n  }\n  return labels;\n}\n\nconst dates = tradingDayLabels(N_DAYS);\n\nlet price = 182;\nconst closes = [];\nfor (let i = 0; i < N_DAYS; i++) {\n  const drift = Math.sin(i / 14) * 0.6;\n  const shock = (nextRandom() - 0.5) * 4.2;\n  price = Math.max(60, price + drift + shock);\n  closes.push(price);\n}\n\nconst emaFast = ema(closes, FAST_PERIOD);\nconst emaSlow = ema(closes, SLOW_PERIOD);\nconst macdLine = emaFast.map((v, i) => v - emaSlow[i]);\nconst signalLine = ema(macdLine, SIGNAL_PERIOD);\nconst histogram = macdLine.map((v, i) => v - signalLine[i]);\n\n// Bullish/bearish crossover points (histogram sign flips) for markPoint annotations.\nconst crossovers = [];\nfor (let i = 1; i < histogram.length; i++) {\n  if (histogram[i - 1] < 0 && histogram[i] >= 0) {\n    crossovers.push({\n      name: \"Bullish crossover\",\n      coord: [dates[i], macdLine[i]],\n      symbol: \"triangle\",\n      symbolSize: 14,\n      itemStyle: { color: t.palette[0] },\n      label: { show: false },\n    });\n  } else if (histogram[i - 1] > 0 && histogram[i] <= 0) {\n    crossovers.push({\n      name: \"Bearish crossover\",\n      coord: [dates[i], macdLine[i]],\n      symbol: \"triangle\",\n      symbolRotate: 180,\n      symbolSize: 14,\n      itemStyle: { color: t.palette[4] },\n      label: { show: false },\n    });\n  }\n}\n\n// --- Init --------------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option --------------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  color: [t.palette[2], t.palette[3], t.palette[0]],\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"indicator-macd · javascript · echarts · anyplot.ai\",\n    subtext: \"12-day EMA − 26-day EMA, 9-day signal · MSFT daily close\",\n    left: \"center\",\n    textStyle: { color: t.ink, fontSize: 22 },\n    subtextStyle: { color: t.inkSoft, fontSize: 15 },\n  },\n  legend: {\n    data: [\n      { name: \"MACD\", itemStyle: { color: t.palette[2] } },\n      { name: \"Signal\", itemStyle: { color: t.palette[3] } },\n      { name: \"Histogram\", itemStyle: { color: t.palette[0] } },\n    ],\n    top: 84,\n    textStyle: { color: t.ink, fontSize: 16 },\n  },\n  tooltip: {\n    trigger: \"axis\",\n    axisPointer: { type: \"cross\", label: { color: t.ink, backgroundColor: t.inkSoft } },\n    backgroundColor: t.elevatedBg,\n    borderColor: t.grid,\n    textStyle: { color: t.ink },\n    valueFormatter: (v) => Number(v).toFixed(2),\n  },\n  grid: { left: 90, right: 60, top: 150, bottom: 110 },\n  xAxis: {\n    type: \"category\",\n    data: dates,\n    boundaryGap: true,\n    axisLabel: { color: t.inkSoft, fontSize: 13, rotate: 45, interval: 9 },\n    axisLine: { lineStyle: { color: t.inkSoft } },\n    axisTick: { show: false },\n    splitLine: { show: false },\n  },\n  yAxis: {\n    type: \"value\",\n    name: \"MACD value\",\n    nameTextStyle: { color: t.inkSoft, fontSize: 14 },\n    axisLabel: { color: t.inkSoft, fontSize: 14 },\n    axisLine: { show: false },\n    splitLine: { lineStyle: { color: t.grid } },\n  },\n  series: [\n    {\n      name: \"Histogram\",\n      type: \"bar\",\n      data: histogram.map((v) => v.toFixed(4)),\n      itemStyle: {\n        color: (params) => (params.value >= 0 ? t.palette[0] : t.palette[4]),\n      },\n      markLine: {\n        silent: true,\n        symbol: \"none\",\n        label: { show: false },\n        lineStyle: { color: t.inkSoft, type: \"dashed\", width: 1.5 },\n        data: [{ yAxis: 0 }],\n      },\n      z: 1,\n    },\n    {\n      name: \"MACD\",\n      type: \"line\",\n      data: macdLine.map((v) => v.toFixed(4)),\n      showSymbol: false,\n      lineStyle: { width: 3, color: t.palette[2] },\n      emphasis: { focus: \"series\" },\n      markPoint: {\n        symbolSize: 14,\n        data: crossovers,\n      },\n      z: 2,\n    },\n    {\n      name: \"Signal\",\n      type: \"line\",\n      data: signalLine.map((v) => v.toFixed(4)),\n      showSymbol: false,\n      lineStyle: { width: 3, color: t.palette[3] },\n      emphasis: { focus: \"series\" },\n      z: 2,\n    },\n  ],\n});\n"}