Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions plots/ohlc-bar/implementations/pygal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
""" pyplots.ai
ohlc-bar: OHLC Bar Chart
Library: pygal 3.1.0 | Python 3.13.11
Quality: 90/100 | Created: 2026-01-08
"""

import numpy as np
import pandas as pd
import pygal
from pygal.style import Style


# Data - Generate realistic stock price data
np.random.seed(42)
n_days = 40

# Starting price and generate daily returns
start_price = 150.0
daily_returns = np.random.normal(0.001, 0.02, n_days)

# Generate OHLC data
dates = pd.date_range("2024-06-01", periods=n_days, freq="B") # Business days
closes = start_price * np.cumprod(1 + daily_returns)
opens = np.roll(closes, 1)
opens[0] = start_price

# Generate highs and lows based on volatility
daily_volatility = np.abs(np.random.normal(0.01, 0.005, n_days))
highs = np.maximum(opens, closes) * (1 + daily_volatility)
lows = np.minimum(opens, closes) * (1 - daily_volatility)

# Custom style for 4800x2700 px canvas
# Use green for up bars, red for down bars
up_color = "#27AE60"
down_color = "#E74C3C"

custom_style = Style(
background="white",
plot_background="white",
foreground="#333333",
foreground_strong="#333333",
foreground_subtle="#999999",
guide_stroke_color="#CCCCCC",
opacity=".95",
opacity_hover=".85",
colors=(up_color, down_color),
title_font_size=60,
label_font_size=32,
major_label_font_size=28,
legend_font_size=32,
value_font_size=28,
tooltip_font_size=24,
stroke_width=6,
)

# Create date labels for x-axis (every 5th date for clarity)
date_labels = {i: dates[i].strftime("%b %d") for i in range(0, n_days, 5)}

# Create XY chart with legend near plot
chart = pygal.XY(
width=4800,
height=2700,
style=custom_style,
title="ohlc-bar · pygal · pyplots.ai",
x_title="Date (Jun-Aug 2024)",
y_title="Price ($)",
show_legend=True,
legend_at_bottom=True,
legend_at_bottom_columns=2,
legend_box_size=24,
show_x_guides=False,
show_y_guides=True,
truncate_label=-1,
truncate_legend=-1,
stroke=True,
show_dots=False,
x_labels=list(date_labels.values()),
x_labels_major_every=1,
)

# Tick width for open/close marks (slightly wider for visibility)
tick_width = 0.4

# Build OHLC bar segments - each bar needs:
# 1. Vertical line from low to high
# 2. Open tick (left horizontal)
# 3. Close tick (right horizontal)

# Separate up and down bars into different series
up_bars = [] # Each bar as list of points with None separators
down_bars = []

for i in range(n_days):
x = float(i)
o, h, lo, c = opens[i], highs[i], lows[i], closes[i]

# Each OHLC bar: vertical line + open tick + close tick
bar_points = [
# Vertical line (low to high)
(x, lo),
(x, h),
# Break
(None, None),
# Open tick (left horizontal)
(x - tick_width, o),
(x, o),
# Break
(None, None),
# Close tick (right horizontal)
(x, c),
(x + tick_width, c),
# Break for next bar
(None, None),
]

if c >= o: # Up bar (bullish)
up_bars.extend(bar_points)
else: # Down bar (bearish)
down_bars.extend(bar_points)

# Add series with descriptive legend labels
chart.add("Bullish (Close ≥ Open)", up_bars)
chart.add("Bearish (Close < Open)", down_bars)

# Save outputs
chart.render_to_png("plot.png")
chart.render_to_file("plot.html")
211 changes: 211 additions & 0 deletions plots/ohlc-bar/metadata/pygal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
library: pygal
specification_id: ohlc-bar
created: '2026-01-08T16:12:54Z'
updated: '2026-01-08T16:24:48Z'
generated_by: claude-opus-4-5-20251101
workflow_run: 20823200220
issue: 3293
python_version: 3.13.11
library_version: 3.1.0
preview_url: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/pygal/plot.png
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/pygal/plot_thumb.png
preview_html: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/pygal/plot.html
quality_score: 90
review:
strengths:
- Correct OHLC bar representation using pygal XY chart with creative line segment
approach
- Good color differentiation between bullish (green) and bearish (red) bars
- Clean code structure following KISS principles
- Appropriate font sizes for the 4800x2700 canvas
- Realistic stock price data generation with proper OHLC relationships
weaknesses:
- X-axis shows numeric indices instead of actual date labels despite dates being
generated
- Missing horizontal grid lines which spec mentions for reading price levels
- Legend labels in output differ from code comments (Up/Down vs Bullish/Bearish)
image_description: 'The plot displays an OHLC (Open-High-Low-Close) bar chart with
40 trading days of stock price data on a white background. Green bars indicate
bullish days (close ≥ open) and red bars indicate bearish days (close < open).
Each OHLC bar correctly shows: a vertical line for the high-low range, a left
horizontal tick for the opening price, and a right horizontal tick for the closing
price. The chart shows a general downtrend from approximately $150 to $130 over
the period. The title "ohlc-bar · pygal · pyplots.ai" appears at top center. Y-axis
is labeled "Price ($)" ranging from ~130 to ~166. X-axis is labeled "Trading Day"
with numeric indices 0-36. A legend at the bottom shows "Up (Close ≥ Open)" in
green and "Down (Close < Open)" in red. Light gray dashed vertical grid lines
are visible.'
criteria_checklist:
visual_quality:
score: 35
max: 40
items:
- id: VQ-01
name: Text Legibility
score: 10
max: 10
passed: true
comment: Title, axis labels, and tick labels are all clearly readable at the
canvas size
- id: VQ-02
name: No Overlap
score: 8
max: 8
passed: true
comment: No overlapping text elements; bars are well-spaced
- id: VQ-03
name: Element Visibility
score: 7
max: 8
passed: true
comment: OHLC bars are visible and distinguishable, though some bars overlap
slightly due to data density
- id: VQ-04
name: Color Accessibility
score: 5
max: 5
passed: true
comment: Green/red color scheme is standard for financial charts and has good
contrast on white background
- id: VQ-05
name: Layout Balance
score: 3
max: 5
passed: true
comment: Good use of canvas space, but x-axis shows numeric indices instead
of date labels which reduces context
- id: VQ-06
name: Axis Labels
score: 2
max: 2
passed: true
comment: Y-axis has Price ($) with units, X-axis has Trading Day which is
descriptive
- id: VQ-07
name: Grid & Legend
score: 0
max: 2
passed: false
comment: Only vertical grid lines visible; missing horizontal guides for price
levels; legend is well-placed but uses different label format than code
spec_compliance:
score: 23
max: 25
items:
- id: SC-01
name: Plot Type
score: 8
max: 8
passed: true
comment: Correct OHLC bar chart with vertical lines and horizontal ticks
- id: SC-02
name: Data Mapping
score: 5
max: 5
passed: true
comment: Date on X-axis, price on Y-axis, OHLC values correctly mapped
- id: SC-03
name: Required Features
score: 4
max: 5
passed: true
comment: Has open/close ticks, high-low lines, color coding; but spec mentions
horizontal grid lines for reading price levels which are missing
- id: SC-04
name: Data Range
score: 3
max: 3
passed: true
comment: All data points visible within axes range
- id: SC-05
name: Legend Accuracy
score: 1
max: 2
passed: true
comment: Legend labels differ from code (shows Up/Down not Bullish/Bearish)
- id: SC-06
name: Title Format
score: 2
max: 2
passed: true
comment: 'Correct format: ohlc-bar · pygal · pyplots.ai'
data_quality:
score: 19
max: 20
items:
- id: DQ-01
name: Feature Coverage
score: 8
max: 8
passed: true
comment: Shows both bullish and bearish bars, various price ranges, realistic
price movements
- id: DQ-02
name: Realistic Context
score: 6
max: 7
passed: true
comment: Plausible stock price scenario with business days, but no specific
stock context
- id: DQ-03
name: Appropriate Scale
score: 5
max: 5
passed: true
comment: Price values ($130-$166) are realistic for stock prices
code_quality:
score: 10
max: 10
items:
- id: CQ-01
name: KISS Structure
score: 3
max: 3
passed: true
comment: Clean imports → data → plot → save structure, no unnecessary functions/classes
- id: CQ-02
name: Reproducibility
score: 3
max: 3
passed: true
comment: Uses np.random.seed(42) for reproducibility
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
comment: All imports are used (numpy, pandas, pygal, Style)
- id: CQ-04
name: No Deprecated API
score: 1
max: 1
passed: true
comment: Uses current pygal API
- id: CQ-05
name: Output Correct
score: 1
max: 1
passed: true
comment: Saves as plot.png and plot.html
library_features:
score: 3
max: 5
items:
- id: LF-01
name: Distinctive Features
score: 3
max: 5
passed: true
comment: Uses pygal Style customization and XY chart with line segments to
create OHLC bars; creative use of None separators for discontinuous lines
verdict: APPROVED
impl_tags:
dependencies: []
techniques:
- html-export
patterns:
- data-generation
- iteration-over-groups
dataprep:
- time-series
styling: []