Simple Moving Average (SMA) Indicator Chart — Highcharts

A Simple Moving Average (SMA) chart displays price or value data with one or more SMA overlays, typically showing short, medium, and long-term periods (e.g., 20, 50, 200-day). Each SMA line smooths out price fluctuations by averaging the last N data points, revealing underlying trends at different time scales. This multi-period overlay is fundamental to technical analysis, helping traders identify trend direction, support/resistance levels, and potential crossover signals.

Simple Moving Average (SMA) Indicator Chart rendered with Highcharts

Renders

JavaScript source (Highcharts)

// anyplot.ai
// indicator-sma: Simple Moving Average (SMA) Indicator Chart
// Library: highcharts 12.6.0 | JavaScript 22.23.2
// Quality: 91/100 | Created: 2026-09-02

//# anyplot-orientation: landscape
const t = window.ANYPLOT_TOKENS;

// --- Data (in-memory, deterministic) ---------------------------------------
// Small LCG PRNG (the browser has no seeded RNG) feeding a Box-Muller
// transform, so the daily returns look like real market noise.
let seed = 42;
function lcgRandom() {
  seed = (seed * 1664525 + 1013904223) % 4294967296;
  return seed / 4294967296;
}
function gaussian() {
  const u1 = 1 - lcgRandom();
  const u2 = lcgRandom();
  return Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
}

const PERIODS = 300;
const dates = [];
let cursor = Date.UTC(2023, 0, 2);
while (dates.length < PERIODS) {
  const weekday = new Date(cursor).getUTCDay();
  if (weekday !== 0 && weekday !== 6) dates.push(cursor);
  cursor += 24 * 3600 * 1000;
}

const DRIFT = 0.0004;
const VOLATILITY = 0.012;
const closes = [];
let price = 148;
for (let i = 0; i < PERIODS; i++) {
  price *= 1 + DRIFT + VOLATILITY * gaussian();
  closes.push(Math.round(price * 100) / 100);
}

function sma(values, windowSize) {
  return values.map((_, i) => {
    if (i < windowSize - 1) return null;
    let sum = 0;
    for (let j = i - windowSize + 1; j <= i; j++) sum += values[j];
    return Math.round((sum / windowSize) * 100) / 100;
  });
}

const sma20 = sma(closes, 20);
const sma50 = sma(closes, 50);
const sma200 = sma(closes, 200);

const closeSeries = dates.map((d, i) => [d, closes[i]]);
const sma20Series = dates.map((d, i) => [d, sma20[i]]);
const sma50Series = dates.map((d, i) => [d, sma50[i]]);
const sma200Series = dates.map((d, i) => [d, sma200[i]]);

// Golden-cross / death-cross detection (SMA 50 vs SMA 200) — the spec's
// headline application. Only real crossovers found in the generated series
// are annotated, so the callout always matches what the lines actually do.
const crossovers = [];
for (let i = 1; i < PERIODS; i++) {
  const prev50 = sma50[i - 1];
  const prev200 = sma200[i - 1];
  const cur50 = sma50[i];
  const cur200 = sma200[i];
  if (prev50 == null || prev200 == null || cur50 == null || cur200 == null) continue;
  if (prev50 <= prev200 && cur50 > cur200) {
    crossovers.push({ date: dates[i], type: "golden", label: "Golden Cross" });
  } else if (prev50 >= prev200 && cur50 < cur200) {
    crossovers.push({ date: dates[i], type: "death", label: "Death Cross" });
  }
}

// --- Chart -----------------------------------------------------------------
Highcharts.chart("container", {
  chart: {
    type: "line",
    backgroundColor: "transparent",
    animation: false,
    style: { fontFamily: "inherit" },
  },
  credits: { enabled: false },
  colors: t.palette,
  title: {
    text: "indicator-sma · javascript · highcharts · anyplot.ai",
    style: { color: t.ink, fontSize: "22px", fontWeight: "600" },
  },
  xAxis: {
    type: "datetime",
    lineColor: t.inkSoft,
    tickColor: t.inkSoft,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
    crosshair: { color: t.grid, dashStyle: "Dash" },
    // Distinctive Highcharts feature: declarative plotLines spotlight the
    // real golden-cross / death-cross moments found in the data above.
    plotLines: crossovers.map((c) => ({
      value: c.date,
      color: c.type === "golden" ? t.palette[0] : t.amber,
      dashStyle: "Dash",
      width: 2,
      zIndex: 5,
      label: {
        text: c.label,
        rotation: 0,
        y: 16,
        x: 6,
        style: { color: t.ink, fontSize: "12px", fontWeight: "600" },
      },
    })),
  },
  yAxis: {
    title: {
      text: "Closing Price (USD)",
      style: { color: t.inkSoft, fontSize: "16px" },
    },
    gridLineColor: t.grid,
    labels: { style: { color: t.inkSoft, fontSize: "14px" } },
  },
  legend: {
    itemStyle: { color: t.inkSoft, fontSize: "14px" },
    itemHoverStyle: { color: t.ink },
    margin: 14,
  },
  tooltip: {
    shared: true,
    // Distinctive Highcharts feature: custom formatter reports each SMA's
    // delta versus the Close price, not just the raw line values.
    formatter: function () {
      const closePoint = this.points.find((p) => p.series.name === "Close");
      const lines = [`<b>${Highcharts.dateFormat("%b %e, %Y", this.x)}</b>`];
      this.points.forEach((p) => {
        let delta = "";
        if (closePoint && p.series.name !== "Close") {
          const deltaPct = ((closePoint.y - p.y) / p.y) * 100;
          delta = ` <span style="color:${t.inkSoft}">(${deltaPct >= 0 ? "+" : ""}${deltaPct.toFixed(1)}% vs Close)</span>`;
        }
        lines.push(`<span style="color:${p.color}">●</span> ${p.series.name}: <b>$${p.y.toFixed(2)}</b>${delta}`);
      });
      return lines.join("<br/>");
    },
  },
  plotOptions: {
    series: { animation: false, marker: { enabled: false } },
  },
  series: [
    { name: "Close", data: closeSeries, lineWidth: 2.5, color: t.palette[0], zIndex: 4 },
    { name: "SMA 20", data: sma20Series, lineWidth: 1.5, dashStyle: "Solid", color: t.palette[1], zIndex: 3 },
    { name: "SMA 50", data: sma50Series, lineWidth: 1.5, dashStyle: "ShortDash", color: t.palette[2], zIndex: 2 },
    { name: "SMA 200", data: sma200Series, lineWidth: 1.75, dashStyle: "LongDash", color: t.palette[3], zIndex: 1 },
  ],
});

Retrieve this implementation

Runnable source as JSON, for any HTTP client: https://api.anyplot.ai/specs/indicator-sma/highcharts/code. Any spec id and library id listed in llms-full.txt fit the same URL shape; every URL below is complete and callable.

{
  "spec_id": "indicator-sma",
  "language": "javascript",
  "library": "highcharts",
  "page": "https://anyplot.ai/indicator-sma/javascript/highcharts",
  "hub": "https://anyplot.ai/indicator-sma",
  "code_json": "https://api.anyplot.ai/specs/indicator-sma/highcharts/code",
  "spec_json": "https://api.anyplot.ai/specs/indicator-sma",
  "render_light_png": "https://storage.googleapis.com/anyplot-images/plots/indicator-sma/javascript/highcharts/plot-light.png",
  "render_dark_png": "https://storage.googleapis.com/anyplot-images/plots/indicator-sma/javascript/highcharts/plot-dark.png",
  "interactive_light_html": "https://storage.googleapis.com/anyplot-images/plots/indicator-sma/javascript/highcharts/plot-light.html",
  "interactive_dark_html": "https://storage.googleapis.com/anyplot-images/plots/indicator-sma/javascript/highcharts/plot-dark.html",
  "quality_score": 91.0,
  "license": "MIT",
  "guide": "https://anyplot.ai/llms.txt"
}

Part of Simple Moving Average (SMA) Indicator Chart on anyplot.ai.

Other implementations