{"spec_id":"ohlc-bar","library":"letsplot","language":"python","code":"\"\"\" anyplot.ai\nohlc-bar: OHLC Bar Chart\nLibrary: letsplot 4.9.0 | 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 lets_plot import (\n    LetsPlot,\n    aes,\n    element_line,\n    element_rect,\n    element_text,\n    geom_segment,\n    ggplot,\n    ggsave,\n    ggsize,\n    labs,\n    scale_color_manual,\n    scale_x_datetime,\n    theme,\n    theme_minimal,\n)\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\"\nRULE = \"rgba(26,26,23,0.10)\" if THEME == \"light\" else \"rgba(240,239,232,0.10)\"\n\n# Okabe-Ito palette for up/down\nUP_COLOR = \"#009E73\"  # Brand green (position 1)\nDOWN_COLOR = \"#AE3030\"  # imprint red — down bars\n\n# Data: Generate 50 trading days of OHLC data\nnp.random.seed(42)\nn_days = 50\ndates = pd.date_range(\"2024-06-01\", periods=n_days, freq=\"B\")\n\n# Simulate price movements with random walk\nprice = 150.0\nopens, highs, lows, closes = [], [], [], []\n\nfor _ in range(n_days):\n    daily_return = np.random.normal(0, 0.02)\n    daily_volatility = np.random.uniform(0.01, 0.03)\n\n    open_price = price\n    close_price = price * (1 + daily_return)\n    high_price = max(open_price, close_price) * (1 + daily_volatility)\n    low_price = min(open_price, close_price) * (1 - daily_volatility)\n\n    opens.append(open_price)\n    highs.append(high_price)\n    lows.append(low_price)\n    closes.append(close_price)\n\n    price = close_price\n\ndf = pd.DataFrame({\"date\": dates, \"open\": opens, \"high\": highs, \"low\": lows, \"close\": closes})\n\n# Add direction for coloring\ndf[\"direction\"] = np.where(df[\"close\"] >= df[\"open\"], \"up\", \"down\")\n\n# Calculate tick offsets (in milliseconds for datetime axis)\ntick_offset = pd.Timedelta(hours=8)\ndf[\"date_left\"] = df[\"date\"] - tick_offset\ndf[\"date_right\"] = df[\"date\"] + tick_offset\n\n# Build OHLC bar chart using segments\n# 1. Vertical line from low to high\n# 2. Left tick at open price\n# 3. Right tick at close price\n\nanyplot_theme = theme(\n    plot_background=element_rect(fill=PAGE_BG, color=PAGE_BG),\n    panel_background=element_rect(fill=PAGE_BG),\n    panel_grid_major_y=element_line(color=RULE, size=0.3),\n    panel_grid_major_x=element_line(color=RULE, size=0.3),\n    axis_title=element_text(size=20, color=INK),\n    axis_text=element_text(size=16, color=INK_SOFT),\n    axis_line=element_line(color=INK_SOFT, size=0.5),\n    plot_title=element_text(size=24, color=INK),\n    legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),\n    legend_text=element_text(size=16, color=INK_SOFT),\n    legend_title=element_text(size=18, color=INK),\n)\n\nplot = (\n    ggplot()\n    # High-Low vertical line\n    + geom_segment(aes(x=\"date\", y=\"low\", xend=\"date\", yend=\"high\", color=\"direction\"), data=df, size=1.2)\n    # Open tick (left)\n    + geom_segment(aes(x=\"date_left\", y=\"open\", xend=\"date\", yend=\"open\", color=\"direction\"), data=df, size=1.5)\n    # Close tick (right)\n    + geom_segment(aes(x=\"date\", y=\"close\", xend=\"date_right\", yend=\"close\", color=\"direction\"), data=df, size=1.5)\n    + scale_color_manual(values={\"up\": UP_COLOR, \"down\": DOWN_COLOR}, name=\"Direction\")\n    + scale_x_datetime(format=\"%b %d\")\n    + labs(title=\"ohlc-bar · letsplot · anyplot.ai\", x=\"Date\", y=\"Price (USD)\")\n    + theme_minimal()\n    + anyplot_theme\n    + ggsize(1600, 900)\n)\n\n# Save as PNG (scale 3x for 4800 × 2700 px) and HTML\nggsave(plot, f\"plot-{THEME}.png\", path=\".\", scale=3)\nggsave(plot, f\"plot-{THEME}.html\", path=\".\")\n"}