{"spec_id":"ohlc-bar","library":"plotnine","language":"python","code":"\"\"\" anyplot.ai\nohlc-bar: OHLC Bar Chart\nLibrary: plotnine 0.15.4 | Python 3.13.13\nQuality: 91/100 | Updated: 2026-05-17\n\"\"\"\n\nimport os\n\nimport numpy as np\nimport pandas as pd\nfrom plotnine import (\n    aes,\n    element_blank,\n    element_line,\n    element_rect,\n    element_text,\n    geom_segment,\n    ggplot,\n    labs,\n    scale_color_manual,\n    scale_x_continuous,\n    theme,\n    theme_minimal,\n)\n\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\"\n\n# imprint semantic anchors for up/down distinction\nUP_COLOR = \"#009E73\"  # green - up bars\nDOWN_COLOR = \"#AE3030\"  # red - down bars\n\n# Data - Generate 50 days of realistic stock OHLC data\nnp.random.seed(42)\nn_days = 50\nstart_price = 150.0\n\n# Generate price movements using random walk\nreturns = np.random.normal(0.001, 0.02, n_days)\nclose_prices = start_price * np.cumprod(1 + returns)\n\n# Generate open, high, low based on close\nopen_prices = np.roll(close_prices, 1)\nopen_prices[0] = start_price\n\n# High and low are generated around the open-close range\ndaily_volatility = np.abs(np.random.normal(0, 0.015, n_days))\nhigh_prices = np.maximum(open_prices, close_prices) * (1 + daily_volatility)\nlow_prices = np.minimum(open_prices, close_prices) * (1 - daily_volatility)\n\n# Create date range (business days)\ndates = pd.bdate_range(start=\"2024-06-01\", periods=n_days)\n\n# Create DataFrame\ndf = pd.DataFrame(\n    {\n        \"date\": dates,\n        \"day_num\": range(n_days),\n        \"open\": open_prices,\n        \"high\": high_prices,\n        \"low\": low_prices,\n        \"close\": close_prices,\n    }\n)\n\n# Determine bar direction for coloring\ndf[\"direction\"] = np.where(df[\"close\"] >= df[\"open\"], \"up\", \"down\")\n\n# Define tick width for open/close marks\ntick_width = 0.3\n\n# Create data for open ticks (extend left from bar)\ndf[\"open_x_start\"] = df[\"day_num\"] - tick_width\ndf[\"open_x_end\"] = df[\"day_num\"]\n\n# Create data for close ticks (extend right from bar)\ndf[\"close_x_start\"] = df[\"day_num\"]\ndf[\"close_x_end\"] = df[\"day_num\"] + tick_width\n\n# Create the OHLC bar chart\nplot = (\n    ggplot(df)\n    # High-Low vertical line\n    + geom_segment(aes(x=\"day_num\", xend=\"day_num\", y=\"low\", yend=\"high\", color=\"direction\"), size=1.2)\n    # Open tick (left horizontal line)\n    + geom_segment(aes(x=\"open_x_start\", xend=\"open_x_end\", y=\"open\", yend=\"open\", color=\"direction\"), size=1.2)\n    # Close tick (right horizontal line)\n    + geom_segment(aes(x=\"close_x_start\", xend=\"close_x_end\", y=\"close\", yend=\"close\", color=\"direction\"), size=1.2)\n    # Colors: up (Okabe-Ito position 1) and down (Okabe-Ito position 2)\n    + scale_color_manual(\n        values={\"up\": UP_COLOR, \"down\": DOWN_COLOR}, labels={\"up\": \"Up (Close > Open)\", \"down\": \"Down (Close < Open)\"}\n    )\n    # X-axis labels - show every 10th date\n    + scale_x_continuous(\n        breaks=list(range(0, n_days, 10)), labels=[dates[i].strftime(\"%b %d\") for i in range(0, n_days, 10)]\n    )\n    # Labels\n    + labs(title=\"ohlc-bar · plotnine · anyplot.ai\", x=\"Date\", y=\"Price (USD)\", color=\"Direction\")\n    # Theme\n    + theme_minimal()\n    + theme(\n        figure_size=(16, 9),\n        plot_title=element_text(size=24, weight=\"bold\", color=INK),\n        axis_title=element_text(size=20, color=INK),\n        axis_text=element_text(size=16, color=INK_SOFT),\n        axis_text_x=element_text(size=16, color=INK_SOFT),\n        legend_title=element_text(size=18, color=INK),\n        legend_text=element_text(size=16, color=INK_SOFT),\n        legend_position=\"right\",\n        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n        panel_grid_major=element_line(color=INK, alpha=0.10, size=0.3),\n        panel_grid_minor=element_blank(),\n        panel_background=element_rect(fill=PAGE_BG, color=None),\n        plot_background=element_rect(fill=PAGE_BG, color=None),\n        axis_line=element_line(color=INK_SOFT, size=0.5),\n    )\n)\n\n# Save the plot\nplot.save(f\"plot-{THEME}.png\", dpi=300, verbose=False)\n"}