{"spec_id":"candlestick-volume","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\ncandlestick-volume: Stock Candlestick Chart with Volume\nLibrary: pygal 3.1.0 | Python 3.13.13\nQuality: 83/100 | Created: 2026-05-16\n\"\"\"\n\nimport os\nimport sys\n\nimport numpy as np\nimport pandas as pd\n\n\ncurrent_dir = os.path.dirname(os.path.abspath(__file__))\nsys.path = [p for p in sys.path if os.path.abspath(p) != current_dir]\n\nimport pygal\nfrom pygal.style import Style\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\nUP_COLOR = \"#009E73\"\nDOWN_COLOR = \"#AE3030\"  # imprint red — down days\nVOLUME_COLOR = \"#2ABCCD\"\n\nIMPRINT = (\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\", \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\nnp.random.seed(42)\nn_periods = 60\n\ndates = pd.date_range(\"2024-01-01\", periods=n_periods, freq=\"D\")\ndate_nums = np.arange(n_periods)\n\nclose_prices = 100 + np.cumsum(np.random.normal(0.3, 1.5, n_periods))\nopen_prices = close_prices + np.random.normal(0, 0.4, n_periods)\nhigh_prices = np.maximum(open_prices, close_prices) + np.abs(np.random.normal(0, 1.2, n_periods))\nlow_prices = np.minimum(open_prices, close_prices) - np.abs(np.random.normal(0, 1.2, n_periods))\nvolumes = np.random.uniform(1e6, 4e6, n_periods)\n\nup_mask = close_prices >= open_prices\ndown_mask = ~up_mask\n\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=IMPRINT,\n    title_font_size=28,\n    label_font_size=22,\n    major_label_font_size=18,\n    legend_font_size=16,\n    value_font_size=14,\n    stroke_width=3,\n)\n\nchart = pygal.XY(\n    style=custom_style,\n    width=4800,\n    height=2700,\n    title=\"Tesla Stock OHLC Candlestick with Volume · candlestick-volume · pygal · anyplot.ai\",\n    x_title=\"Trading Date\",\n    y_title=\"Price (USD)\",\n    show_legend=True,\n    show_y_guides=True,\n    show_x_guides=False,\n    range=(low_prices.min() - 5, high_prices.max() + 10),\n)\n\nup_wicks = []\ndown_wicks = []\nup_bodies = []\ndown_bodies = []\n\nfor i in range(n_periods):\n    x = float(date_nums[i])\n    h = float(high_prices[i])\n    low = float(low_prices[i])\n    o = float(open_prices[i])\n    c = float(close_prices[i])\n\n    if up_mask[i]:\n        up_wicks.append((x, low))\n        up_wicks.append((x, h))\n        up_wicks.append(None)\n        body_min = min(o, c)\n        body_max = max(o, c)\n        up_bodies.append((x, body_min))\n        up_bodies.append((x, body_max))\n        up_bodies.append(None)\n    else:\n        down_wicks.append((x, low))\n        down_wicks.append((x, h))\n        down_wicks.append(None)\n        body_min = min(o, c)\n        body_max = max(o, c)\n        down_bodies.append((x, body_min))\n        down_bodies.append((x, body_max))\n        down_bodies.append(None)\n\nif up_wicks:\n    chart.add(\n        \"Up Day Wicks\", up_wicks, stroke_width=5, color=UP_COLOR, dots_size=0, show_only_major_dots=False, fill=False\n    )\n    chart.add(\n        \"Up Day Bodies\", up_bodies, stroke_width=16, color=UP_COLOR, dots_size=0, show_only_major_dots=False, fill=False\n    )\n\nif down_wicks:\n    chart.add(\n        \"Down Day Wicks\",\n        down_wicks,\n        stroke_width=5,\n        color=DOWN_COLOR,\n        dots_size=0,\n        show_only_major_dots=False,\n        fill=False,\n    )\n    chart.add(\n        \"Down Day Bodies\",\n        down_bodies,\n        stroke_width=16,\n        color=DOWN_COLOR,\n        dots_size=0,\n        show_only_major_dots=False,\n        fill=False,\n    )\n\nvolume_min = low_prices.min() - 4\nvolume_normalized = (volumes - volumes.min()) / (volumes.max() - volumes.min()) * 3 + volume_min\nvolume_series = [(float(date_nums[i]), float(volume_normalized[i])) for i in range(n_periods)]\nchart.add(\n    \"Volume Index\",\n    volume_series,\n    stroke_width=2,\n    color=VOLUME_COLOR,\n    dots_size=6,\n    show_only_major_dots=False,\n    fill=False,\n    opacity=0.7,\n)\n\nx_labels = [dates[i].strftime(\"%m-%d\") if i % 10 == 0 else \"\" for i in range(n_periods)]\nchart.x_labels = x_labels\n\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}