{"spec_id":"indicator-bollinger","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nindicator-bollinger: Bollinger Bands Indicator Chart\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 67/100 | Updated: 2026-05-17\n\"\"\"\n\nimport numpy as np\nimport pygal\nfrom pygal.style import Style\n\n\n# Seed for reproducibility\nnp.random.seed(42)\n\n# Generate realistic stock price data (120 trading days)\nn_days = 120\nbase_price = 150\n\n# Generate price movements with trend and volatility\nreturns = np.random.normal(0.0005, 0.015, n_days)\nprices = base_price * np.cumprod(1 + returns)\n\n# Calculate Bollinger Bands (20-period SMA with 2 standard deviations)\nwindow = 20\nsma = np.array([np.mean(prices[max(0, i - window + 1) : i + 1]) if i >= window - 1 else None for i in range(n_days)])\nstd = np.array([np.std(prices[max(0, i - window + 1) : i + 1]) if i >= window - 1 else None for i in range(n_days)])\nupper_band = np.array([sma[i] + 2 * std[i] if sma[i] is not None else None for i in range(n_days)])\nlower_band = np.array([sma[i] - 2 * std[i] if sma[i] is not None else None for i in range(n_days)])\n\n# Create x-axis labels (trading days)\nx_labels = [f\"Day {i + 1}\" for i in range(n_days)]\n\n# Custom style for 4800x2700 canvas with subtle grid\ncustom_style = Style(\n    background=\"white\",\n    plot_background=\"white\",\n    foreground=\"#333333\",\n    foreground_strong=\"#333333\",\n    foreground_subtle=\"#CCCCCC\",  # Subtle gray for grid lines\n    colors=(\"#306998\", \"#FFD43B\", \"#5A9BD4\", \"#8BC34A\"),  # Price (blue), SMA (gold), Upper (steel blue), Lower (green)\n    title_font_size=72,\n    label_font_size=42,\n    major_label_font_size=36,\n    legend_font_size=42,\n    value_font_size=32,\n    tooltip_font_size=28,\n    stroke_width=4,\n    opacity=0.9,\n    opacity_hover=1.0,\n    guide_stroke_color=\"#E0E0E0\",  # Very subtle guide lines\n    guide_stroke_dasharray=\"4,4\",\n)\n\n# Create line chart with filled band area\nchart = pygal.Line(\n    width=4800,\n    height=2700,\n    style=custom_style,\n    title=\"indicator-bollinger · pygal · pyplots.ai\",\n    x_title=\"Trading Day\",\n    y_title=\"Price (USD)\",\n    show_x_guides=False,\n    show_y_guides=True,\n    x_label_rotation=45,\n    show_dots=False,\n    stroke_style={\"width\": 4},\n    legend_at_bottom=True,\n    legend_box_size=30,\n    truncate_label=10,\n    show_minor_x_labels=False,\n    x_labels_major_every=20,\n    interpolate=\"cubic\",\n    margin=50,\n    spacing=30,\n)\n\n# Set x labels\nchart.x_labels = x_labels\n\n# Prepare band data with fill between upper and lower bands\nupper_band_list = [float(v) if v is not None else None for v in upper_band]\nlower_band_list = [float(v) if v is not None else None for v in lower_band]\n\n# Add upper band with fill to create visual band area\nchart.add(\"Upper Band\", upper_band_list, stroke_style={\"width\": 3}, fill=True, allow_interruptions=True)\n\n# Add lower band with fill (fills down, but creates visual contrast)\nchart.add(\"Lower Band\", lower_band_list, stroke_style={\"width\": 3}, fill=True, allow_interruptions=True)\n\n# Add SMA as dashed line (middle band)\nchart.add(\n    \"SMA (20)\",\n    [float(v) if v is not None else None for v in sma],\n    stroke_style={\"width\": 4, \"dasharray\": \"10,5\"},\n    fill=False,\n)\n\n# Add close price on top for visibility\nchart.add(\"Close Price\", prices.tolist(), stroke_style={\"width\": 5, \"dasharray\": \"0\"}, fill=False)\n\n# Save as PNG and HTML\nchart.render_to_png(\"plot.png\")\nchart.render_to_file(\"plot.html\")\n"}