OHLC Bar Chart — plotnine

An OHLC (Open-High-Low-Close) bar chart displays financial price data using vertical bars with horizontal tick marks. Each bar shows the price range from high to low as a thin vertical line, with a left tick indicating the opening price and a right tick indicating the closing price. Unlike candlestick charts that use colored bodies, OHLC bars provide a cleaner, less cluttered view favored by technical analysts who prefer to focus on price levels rather than visual patterns.

OHLC Bar Chart rendered with plotnine

Python source (plotnine)

""" anyplot.ai
ohlc-bar: OHLC Bar Chart
Library: plotnine 0.15.4 | Python 3.13.13
Quality: 91/100 | Updated: 2026-05-17
"""

import os

import numpy as np
import pandas as pd
from plotnine import (
    aes,
    element_blank,
    element_line,
    element_rect,
    element_text,
    geom_segment,
    ggplot,
    labs,
    scale_color_manual,
    scale_x_continuous,
    theme,
    theme_minimal,
)


# Theme tokens
THEME = os.getenv("ANYPLOT_THEME", "light")
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"

# imprint semantic anchors for up/down distinction
UP_COLOR = "#009E73"  # green - up bars
DOWN_COLOR = "#AE3030"  # red - down bars

# Data - Generate 50 days of realistic stock OHLC data
np.random.seed(42)
n_days = 50
start_price = 150.0

# Generate price movements using random walk
returns = np.random.normal(0.001, 0.02, n_days)
close_prices = start_price * np.cumprod(1 + returns)

# Generate open, high, low based on close
open_prices = np.roll(close_prices, 1)
open_prices[0] = start_price

# High and low are generated around the open-close range
daily_volatility = np.abs(np.random.normal(0, 0.015, n_days))
high_prices = np.maximum(open_prices, close_prices) * (1 + daily_volatility)
low_prices = np.minimum(open_prices, close_prices) * (1 - daily_volatility)

# Create date range (business days)
dates = pd.bdate_range(start="2024-06-01", periods=n_days)

# Create DataFrame
df = pd.DataFrame(
    {
        "date": dates,
        "day_num": range(n_days),
        "open": open_prices,
        "high": high_prices,
        "low": low_prices,
        "close": close_prices,
    }
)

# Determine bar direction for coloring
df["direction"] = np.where(df["close"] >= df["open"], "up", "down")

# Define tick width for open/close marks
tick_width = 0.3

# Create data for open ticks (extend left from bar)
df["open_x_start"] = df["day_num"] - tick_width
df["open_x_end"] = df["day_num"]

# Create data for close ticks (extend right from bar)
df["close_x_start"] = df["day_num"]
df["close_x_end"] = df["day_num"] + tick_width

# Create the OHLC bar chart
plot = (
    ggplot(df)
    # High-Low vertical line
    + geom_segment(aes(x="day_num", xend="day_num", y="low", yend="high", color="direction"), size=1.2)
    # Open tick (left horizontal line)
    + geom_segment(aes(x="open_x_start", xend="open_x_end", y="open", yend="open", color="direction"), size=1.2)
    # Close tick (right horizontal line)
    + geom_segment(aes(x="close_x_start", xend="close_x_end", y="close", yend="close", color="direction"), size=1.2)
    # Colors: up (Okabe-Ito position 1) and down (Okabe-Ito position 2)
    + scale_color_manual(
        values={"up": UP_COLOR, "down": DOWN_COLOR}, labels={"up": "Up (Close > Open)", "down": "Down (Close < Open)"}
    )
    # X-axis labels - show every 10th date
    + scale_x_continuous(
        breaks=list(range(0, n_days, 10)), labels=[dates[i].strftime("%b %d") for i in range(0, n_days, 10)]
    )
    # Labels
    + labs(title="ohlc-bar · plotnine · anyplot.ai", x="Date", y="Price (USD)", color="Direction")
    # Theme
    + theme_minimal()
    + theme(
        figure_size=(16, 9),
        plot_title=element_text(size=24, weight="bold", color=INK),
        axis_title=element_text(size=20, color=INK),
        axis_text=element_text(size=16, color=INK_SOFT),
        axis_text_x=element_text(size=16, color=INK_SOFT),
        legend_title=element_text(size=18, color=INK),
        legend_text=element_text(size=16, color=INK_SOFT),
        legend_position="right",
        legend_background=element_rect(fill=ELEVATED_BG, color=INK_SOFT),
        panel_grid_major=element_line(color=INK, alpha=0.10, size=0.3),
        panel_grid_minor=element_blank(),
        panel_background=element_rect(fill=PAGE_BG, color=None),
        plot_background=element_rect(fill=PAGE_BG, color=None),
        axis_line=element_line(color=INK_SOFT, size=0.5),
    )
)

# Save the plot
plot.save(f"plot-{THEME}.png", dpi=300, verbose=False)

Part of OHLC Bar Chart on anyplot.ai.

Other implementations