{"spec_id":"indicator-rsi","library":"highcharts","language":"javascript","code":"// anyplot.ai\n// indicator-rsi: RSI Technical Indicator Chart\n// Library: highcharts 12.6.0 | JavaScript 22.23.2\n// Quality: 89/100 | Created: 2026-09-05\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data: synthetic daily closes -> 14-period RSI (in-memory, deterministic) ---\nlet seed = 42;\nfunction lcgRandom() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\n\nconst numDays = 120;\nconst lookback = 14;\nconst totalDays = numDays + lookback;\n// Alternate trending regimes so RSI actually breaches both the overbought\n// and oversold thresholds, matching the spec's overbought/oversold use cases.\nconst regimeLength = 22;\nconst closes = [200];\nfor (let i = 1; i < totalDays; i += 1) {\n  const regime = Math.floor(i / regimeLength) % 2;\n  const drift = regime === 0 ? 1.1 : -1.1;\n  const shock = (lcgRandom() - 0.5) * 4;\n  const next = Math.max(20, closes[closes.length - 1] + drift + shock);\n  closes.push(next);\n}\n\nfunction computeRsi(prices, period) {\n  const values = [];\n  let avgGain = 0;\n  let avgLoss = 0;\n  for (let i = 1; i <= period; i += 1) {\n    const change = prices[i] - prices[i - 1];\n    avgGain += Math.max(change, 0);\n    avgLoss += Math.max(-change, 0);\n  }\n  avgGain /= period;\n  avgLoss /= period;\n\n  for (let i = period + 1; i < prices.length; i += 1) {\n    const change = prices[i] - prices[i - 1];\n    const gain = Math.max(change, 0);\n    const loss = Math.max(-change, 0);\n    avgGain = (avgGain * (period - 1) + gain) / period;\n    avgLoss = (avgLoss * (period - 1) + loss) / period;\n    const rs = avgLoss === 0 ? 100 : avgGain / avgLoss;\n    const rsi = avgLoss === 0 ? 100 : 100 - 100 / (1 + rs);\n    values.push(Math.round(rsi * 100) / 100);\n  }\n  return values;\n}\n\nconst rsiValues = computeRsi(closes, lookback);\nconst startDate = Date.UTC(2025, 2, 3);\nconst dayMs = 24 * 60 * 60 * 1000;\nconst rsiSeries = rsiValues.map((value, i) => [startDate + i * dayMs, value]);\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-rsi · javascript · highcharts · anyplot.ai\",\n    style: { color: t.ink, fontSize: \"22px\", fontWeight: \"600\" },\n  },\n  subtitle: {\n    text: \"14-period RSI\",\n    style: { color: t.inkSoft, fontSize: \"14px\" },\n  },\n  xAxis: {\n    type: \"datetime\",\n    lineColor: t.inkSoft,\n    tickColor: t.inkSoft,\n    gridLineWidth: 0,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n  },\n  yAxis: {\n    min: 0,\n    max: 100,\n    tickInterval: 10,\n    title: { text: \"RSI (0–100)\", style: { color: t.inkSoft, fontSize: \"16px\" } },\n    gridLineColor: t.grid,\n    labels: { style: { color: t.inkSoft, fontSize: \"14px\" } },\n    plotBands: [\n      { from: 70, to: 100, color: Highcharts.color(t.palette[4]).setOpacity(0.12).get() },\n      { from: 0, to: 30, color: Highcharts.color(t.palette[2]).setOpacity(0.12).get() },\n    ],\n    plotLines: [\n      { value: 70, color: t.palette[4], width: 1.5, dashStyle: \"Dash\",\n        label: { text: \"Overbought (70)\", style: { color: t.inkSoft, fontSize: \"12px\" }, align: \"right\", x: -4 } },\n      { value: 50, color: t.inkSoft, width: 1, dashStyle: \"Dot\",\n        label: { text: \"Centerline (50)\", style: { color: t.inkSoft, fontSize: \"12px\" }, align: \"right\", x: -4 } },\n      { value: 30, color: t.palette[2], width: 1.5, dashStyle: \"Dash\",\n        label: { text: \"Oversold (30)\", style: { color: t.inkSoft, fontSize: \"12px\" }, align: \"right\", x: -4 } },\n    ],\n  },\n  legend: { enabled: false },\n  plotOptions: {\n    series: { animation: false },\n    line: { lineWidth: 2.5, marker: { enabled: false } },\n  },\n  series: [\n    {\n      name: \"RSI (14)\",\n      data: rsiSeries,\n      color: t.palette[0],\n      // Recolor the line itself while it's inside the oversold/overbought\n      // zones, reinforcing the plotBands shading directly on the data.\n      zoneAxis: \"y\",\n      zones: [\n        { value: 30, color: t.palette[2] },\n        { value: 70, color: t.palette[0] },\n        { color: t.palette[4] },\n      ],\n    },\n  ],\n});\n"}