{"spec_id":"indicator-rsi","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nindicator-rsi: RSI Technical Indicator Chart\nLibrary: letsplot 4.9.0 | Python 3.13.13\nQuality: 90/100 | Updated: 2026-05-16\n\"\"\"\n# ruff: noqa: F405\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom lets_plot import *\n\n\nLetsPlot.setup_html()\n\n# Theme tokens\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nELEVATED_BG = \"#FFFDF6\" if THEME == \"light\" else \"#242420\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_SOFT = \"#4A4A44\" if THEME == \"light\" else \"#B8B7B0\"\nBRAND = \"#009E73\"\n\n# Data - Generate realistic stock price data and calculate RSI\nnp.random.seed(42)\n\nn_days = 120\ndates = pd.date_range(\"2024-06-01\", periods=n_days, freq=\"B\")\n\n# Generate volatile price movements to demonstrate overbought/oversold zones\nreturns = np.random.normal(0.0005, 0.025, n_days)  # Increased volatility\nprice = 150 * np.exp(np.cumsum(returns))\n\n# Calculate RSI with 14-period lookback\nperiod = 14\ndelta = np.diff(price)\ngains = np.where(delta > 0, delta, 0)\nlosses = np.where(delta < 0, -delta, 0)\n\n# Calculate average gains/losses using exponential moving average\navg_gain = np.zeros(len(delta))\navg_loss = np.zeros(len(delta))\n\navg_gain[period - 1] = np.mean(gains[:period])\navg_loss[period - 1] = np.mean(losses[:period])\n\nfor i in range(period, len(delta)):\n    avg_gain[i] = (avg_gain[i - 1] * (period - 1) + gains[i]) / period\n    avg_loss[i] = (avg_loss[i - 1] * (period - 1) + losses[i]) / period\n\nrs = np.where(avg_loss != 0, avg_gain / avg_loss, 100)\nrsi = 100 - (100 / (1 + rs))\n\n# Align RSI with dates (first period-1 values are NaN)\nrsi_values = np.full(n_days, np.nan)\nrsi_values[period:] = rsi[period - 1 :]\n\ndf = pd.DataFrame({\"date\": dates, \"rsi\": rsi_values}).dropna()\n\ndf[\"date_num\"] = range(len(df))\n\n# Create zones for shading\noverbought_df = pd.DataFrame(\n    {\"xmin\": [df[\"date_num\"].min()], \"xmax\": [df[\"date_num\"].max()], \"ymin\": [70], \"ymax\": [100]}\n)\n\noversold_df = pd.DataFrame({\"xmin\": [df[\"date_num\"].min()], \"xmax\": [df[\"date_num\"].max()], \"ymin\": [0], \"ymax\": [30]})\n\n# Create the RSI chart with theme-adaptive styling\nplot = (\n    ggplot()\n    # Overbought zone\n    + geom_rect(\n        data=overbought_df, mapping=aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\"), fill=\"#DC2626\", alpha=0.1\n    )\n    # Oversold zone\n    + geom_rect(\n        data=oversold_df, mapping=aes(xmin=\"xmin\", xmax=\"xmax\", ymin=\"ymin\", ymax=\"ymax\"), fill=\"#16A34A\", alpha=0.1\n    )\n    # Horizontal threshold lines\n    + geom_hline(yintercept=70, color=\"#DC2626\", size=1.2, linetype=\"dashed\")\n    + geom_hline(yintercept=30, color=\"#16A34A\", size=1.2, linetype=\"dashed\")\n    + geom_hline(yintercept=50, color=INK_SOFT, size=0.8, linetype=\"dotted\")\n    # RSI line in brand color\n    + geom_line(data=df, mapping=aes(x=\"date_num\", y=\"rsi\"), color=BRAND, size=1.8)\n    # Labels and styling\n    + labs(title=\"indicator-rsi · letsplot · anyplot.ai\", x=\"Trading Day\", y=\"RSI (14-period)\")\n    + scale_y_continuous(limits=[0, 100], breaks=[0, 30, 50, 70, 100])\n    + scale_x_continuous(breaks=[0, 25, 50, 75, 100], labels=[\"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\"])\n    + theme_minimal()\n    + theme(\n        plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n        panel_grid_major=element_line(color=INK_SOFT, size=0.4),\n        panel_grid_minor=element_blank(),\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n        plot_title=element_text(size=24, face=\"bold\", color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n    )\n    + ggsize(1600, 900)\n)\n\n# Save as PNG (scale 3x for 4800 × 2700 px)\nggsave(plot, f\"plot-{THEME}.png\", scale=3)\n\n# Save as HTML for interactive viewing\nggsave(plot, f\"plot-{THEME}.html\")\n"}