{"spec_id":"indicator-rsi","library":"bokeh","language":"python","code":"\"\"\" anyplot.ai\nindicator-rsi: RSI Technical Indicator Chart\nLibrary: bokeh 3.9.0 | Python 3.13.13\nQuality: 80/100 | Updated: 2026-05-16\n\"\"\"\n\nimport numpy as np\nimport pandas as pd\nfrom bokeh.io import export_png, save\nfrom bokeh.models import BoxAnnotation, ColumnDataSource, Legend, LegendItem, Span\nfrom bokeh.plotting import figure\nfrom bokeh.resources import CDN\n\n\n# Data - Generate realistic stock price data and calculate RSI\nnp.random.seed(42)\nn_days = 120\n\n# Generate price data with realistic movements\ndates = pd.date_range(start=\"2024-01-01\", periods=n_days, freq=\"B\")\nreturns = np.random.normal(0.0005, 0.02, n_days)\nreturns[30:40] = np.random.normal(0.02, 0.01, 10)  # Strong uptrend (will push RSI high)\nreturns[60:75] = np.random.normal(-0.015, 0.01, 15)  # Downtrend (will push RSI low)\nreturns[95:105] = np.random.normal(0.018, 0.01, 10)  # Another uptrend\nprice = 100 * 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 initial average gain/loss\navg_gain = np.zeros(len(price))\navg_loss = np.zeros(len(price))\navg_gain[period] = np.mean(gains[:period])\navg_loss[period] = np.mean(losses[:period])\n\n# Smoothed RSI calculation\nfor i in range(period + 1, len(price)):\n    avg_gain[i] = (avg_gain[i - 1] * (period - 1) + gains[i - 1]) / period\n    avg_loss[i] = (avg_loss[i - 1] * (period - 1) + losses[i - 1]) / period\n\nrs = np.divide(avg_gain, avg_loss, out=np.zeros_like(avg_gain), where=avg_loss != 0)\nrsi = 100 - (100 / (1 + rs))\nrsi[:period] = np.nan  # RSI not valid for first 'period' values\n\n# Prepare data\ndf = pd.DataFrame({\"date\": dates, \"rsi\": rsi})\ndf = df.dropna()\nsource = ColumnDataSource(df)\n\n# Create figure\np = figure(\n    width=4800,\n    height=2700,\n    title=\"indicator-rsi · bokeh · pyplots.ai\",\n    x_axis_label=\"Date\",\n    y_axis_label=\"RSI (14-Period)\",\n    x_axis_type=\"datetime\",\n    y_range=(0, 100),\n)\n\n# Styling\np.title.text_font_size = \"32pt\"\np.xaxis.axis_label_text_font_size = \"24pt\"\np.yaxis.axis_label_text_font_size = \"24pt\"\np.xaxis.major_label_text_font_size = \"18pt\"\np.yaxis.major_label_text_font_size = \"18pt\"\n\n# Add shaded zones for overbought and oversold\noverbought_zone = BoxAnnotation(bottom=70, top=100, fill_alpha=0.15, fill_color=\"#FF6B6B\", level=\"underlay\")\noversold_zone = BoxAnnotation(bottom=0, top=30, fill_alpha=0.15, fill_color=\"#4ECDC4\", level=\"underlay\")\np.add_layout(overbought_zone)\np.add_layout(oversold_zone)\n\n# Add threshold lines\noverbought_line = Span(location=70, dimension=\"width\", line_color=\"#E74C3C\", line_width=3, line_dash=\"dashed\")\noversold_line = Span(location=30, dimension=\"width\", line_color=\"#27AE60\", line_width=3, line_dash=\"dashed\")\ncenterline = Span(location=50, dimension=\"width\", line_color=\"#95A5A6\", line_width=2, line_dash=\"dotted\")\np.add_layout(overbought_line)\np.add_layout(oversold_line)\np.add_layout(centerline)\n\n# Plot RSI line\nrsi_line = p.line(x=\"date\", y=\"rsi\", source=source, line_width=4, line_color=\"#306998\", alpha=0.9)\n\n# Add scatter points at extremes for emphasis\nextreme_high = df[df[\"rsi\"] >= 70].copy()\nextreme_low = df[df[\"rsi\"] <= 30].copy()\n\nif not extreme_high.empty:\n    source_high = ColumnDataSource(extreme_high)\n    p.scatter(x=\"date\", y=\"rsi\", source=source_high, size=18, color=\"#E74C3C\", alpha=0.8)\n\nif not extreme_low.empty:\n    source_low = ColumnDataSource(extreme_low)\n    p.scatter(x=\"date\", y=\"rsi\", source=source_low, size=18, color=\"#27AE60\", alpha=0.8)\n\n# Create legend manually\nlegend_items = [LegendItem(label=\"RSI (14)\", renderers=[rsi_line])]\nlegend = Legend(items=legend_items, location=\"top_right\", label_text_font_size=\"18pt\")\np.add_layout(legend)\n\n# Grid styling\np.grid.grid_line_alpha = 0.3\np.grid.grid_line_dash = [6, 4]\n\n# Background\np.background_fill_color = \"#FAFAFA\"\n\n# Save\nexport_png(p, filename=\"plot.png\")\nsave(p, filename=\"plot.html\", resources=CDN, title=\"indicator-rsi · bokeh · pyplots.ai\")\n"}