Skip to content

Commit 41b24dd

Browse files
feat(pygal): implement ohlc-bar (#3334)
## Implementation: `ohlc-bar` - pygal Implements the **pygal** version of `ohlc-bar`. **File:** `plots/ohlc-bar/implementations/pygal.py` **Parent Issue:** #3293 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20823200220)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent d1ef570 commit 41b24dd

2 files changed

Lines changed: 338 additions & 0 deletions

File tree

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
""" pyplots.ai
2+
ohlc-bar: OHLC Bar Chart
3+
Library: pygal 3.1.0 | Python 3.13.11
4+
Quality: 90/100 | Created: 2026-01-08
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
import pygal
10+
from pygal.style import Style
11+
12+
13+
# Data - Generate realistic stock price data
14+
np.random.seed(42)
15+
n_days = 40
16+
17+
# Starting price and generate daily returns
18+
start_price = 150.0
19+
daily_returns = np.random.normal(0.001, 0.02, n_days)
20+
21+
# Generate OHLC data
22+
dates = pd.date_range("2024-06-01", periods=n_days, freq="B") # Business days
23+
closes = start_price * np.cumprod(1 + daily_returns)
24+
opens = np.roll(closes, 1)
25+
opens[0] = start_price
26+
27+
# Generate highs and lows based on volatility
28+
daily_volatility = np.abs(np.random.normal(0.01, 0.005, n_days))
29+
highs = np.maximum(opens, closes) * (1 + daily_volatility)
30+
lows = np.minimum(opens, closes) * (1 - daily_volatility)
31+
32+
# Custom style for 4800x2700 px canvas
33+
# Use green for up bars, red for down bars
34+
up_color = "#27AE60"
35+
down_color = "#E74C3C"
36+
37+
custom_style = Style(
38+
background="white",
39+
plot_background="white",
40+
foreground="#333333",
41+
foreground_strong="#333333",
42+
foreground_subtle="#999999",
43+
guide_stroke_color="#CCCCCC",
44+
opacity=".95",
45+
opacity_hover=".85",
46+
colors=(up_color, down_color),
47+
title_font_size=60,
48+
label_font_size=32,
49+
major_label_font_size=28,
50+
legend_font_size=32,
51+
value_font_size=28,
52+
tooltip_font_size=24,
53+
stroke_width=6,
54+
)
55+
56+
# Create date labels for x-axis (every 5th date for clarity)
57+
date_labels = {i: dates[i].strftime("%b %d") for i in range(0, n_days, 5)}
58+
59+
# Create XY chart with legend near plot
60+
chart = pygal.XY(
61+
width=4800,
62+
height=2700,
63+
style=custom_style,
64+
title="ohlc-bar · pygal · pyplots.ai",
65+
x_title="Date (Jun-Aug 2024)",
66+
y_title="Price ($)",
67+
show_legend=True,
68+
legend_at_bottom=True,
69+
legend_at_bottom_columns=2,
70+
legend_box_size=24,
71+
show_x_guides=False,
72+
show_y_guides=True,
73+
truncate_label=-1,
74+
truncate_legend=-1,
75+
stroke=True,
76+
show_dots=False,
77+
x_labels=list(date_labels.values()),
78+
x_labels_major_every=1,
79+
)
80+
81+
# Tick width for open/close marks (slightly wider for visibility)
82+
tick_width = 0.4
83+
84+
# Build OHLC bar segments - each bar needs:
85+
# 1. Vertical line from low to high
86+
# 2. Open tick (left horizontal)
87+
# 3. Close tick (right horizontal)
88+
89+
# Separate up and down bars into different series
90+
up_bars = [] # Each bar as list of points with None separators
91+
down_bars = []
92+
93+
for i in range(n_days):
94+
x = float(i)
95+
o, h, lo, c = opens[i], highs[i], lows[i], closes[i]
96+
97+
# Each OHLC bar: vertical line + open tick + close tick
98+
bar_points = [
99+
# Vertical line (low to high)
100+
(x, lo),
101+
(x, h),
102+
# Break
103+
(None, None),
104+
# Open tick (left horizontal)
105+
(x - tick_width, o),
106+
(x, o),
107+
# Break
108+
(None, None),
109+
# Close tick (right horizontal)
110+
(x, c),
111+
(x + tick_width, c),
112+
# Break for next bar
113+
(None, None),
114+
]
115+
116+
if c >= o: # Up bar (bullish)
117+
up_bars.extend(bar_points)
118+
else: # Down bar (bearish)
119+
down_bars.extend(bar_points)
120+
121+
# Add series with descriptive legend labels
122+
chart.add("Bullish (Close ≥ Open)", up_bars)
123+
chart.add("Bearish (Close < Open)", down_bars)
124+
125+
# Save outputs
126+
chart.render_to_png("plot.png")
127+
chart.render_to_file("plot.html")

plots/ohlc-bar/metadata/pygal.yaml

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
library: pygal
2+
specification_id: ohlc-bar
3+
created: '2026-01-08T16:12:54Z'
4+
updated: '2026-01-08T16:24:48Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20823200220
7+
issue: 3293
8+
python_version: 3.13.11
9+
library_version: 3.1.0
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/pygal/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/pygal/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/pygal/plot.html
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Correct OHLC bar representation using pygal XY chart with creative line segment
17+
approach
18+
- Good color differentiation between bullish (green) and bearish (red) bars
19+
- Clean code structure following KISS principles
20+
- Appropriate font sizes for the 4800x2700 canvas
21+
- Realistic stock price data generation with proper OHLC relationships
22+
weaknesses:
23+
- X-axis shows numeric indices instead of actual date labels despite dates being
24+
generated
25+
- Missing horizontal grid lines which spec mentions for reading price levels
26+
- Legend labels in output differ from code comments (Up/Down vs Bullish/Bearish)
27+
image_description: 'The plot displays an OHLC (Open-High-Low-Close) bar chart with
28+
40 trading days of stock price data on a white background. Green bars indicate
29+
bullish days (close ≥ open) and red bars indicate bearish days (close < open).
30+
Each OHLC bar correctly shows: a vertical line for the high-low range, a left
31+
horizontal tick for the opening price, and a right horizontal tick for the closing
32+
price. The chart shows a general downtrend from approximately $150 to $130 over
33+
the period. The title "ohlc-bar · pygal · pyplots.ai" appears at top center. Y-axis
34+
is labeled "Price ($)" ranging from ~130 to ~166. X-axis is labeled "Trading Day"
35+
with numeric indices 0-36. A legend at the bottom shows "Up (Close ≥ Open)" in
36+
green and "Down (Close < Open)" in red. Light gray dashed vertical grid lines
37+
are visible.'
38+
criteria_checklist:
39+
visual_quality:
40+
score: 35
41+
max: 40
42+
items:
43+
- id: VQ-01
44+
name: Text Legibility
45+
score: 10
46+
max: 10
47+
passed: true
48+
comment: Title, axis labels, and tick labels are all clearly readable at the
49+
canvas size
50+
- id: VQ-02
51+
name: No Overlap
52+
score: 8
53+
max: 8
54+
passed: true
55+
comment: No overlapping text elements; bars are well-spaced
56+
- id: VQ-03
57+
name: Element Visibility
58+
score: 7
59+
max: 8
60+
passed: true
61+
comment: OHLC bars are visible and distinguishable, though some bars overlap
62+
slightly due to data density
63+
- id: VQ-04
64+
name: Color Accessibility
65+
score: 5
66+
max: 5
67+
passed: true
68+
comment: Green/red color scheme is standard for financial charts and has good
69+
contrast on white background
70+
- id: VQ-05
71+
name: Layout Balance
72+
score: 3
73+
max: 5
74+
passed: true
75+
comment: Good use of canvas space, but x-axis shows numeric indices instead
76+
of date labels which reduces context
77+
- id: VQ-06
78+
name: Axis Labels
79+
score: 2
80+
max: 2
81+
passed: true
82+
comment: Y-axis has Price ($) with units, X-axis has Trading Day which is
83+
descriptive
84+
- id: VQ-07
85+
name: Grid & Legend
86+
score: 0
87+
max: 2
88+
passed: false
89+
comment: Only vertical grid lines visible; missing horizontal guides for price
90+
levels; legend is well-placed but uses different label format than code
91+
spec_compliance:
92+
score: 23
93+
max: 25
94+
items:
95+
- id: SC-01
96+
name: Plot Type
97+
score: 8
98+
max: 8
99+
passed: true
100+
comment: Correct OHLC bar chart with vertical lines and horizontal ticks
101+
- id: SC-02
102+
name: Data Mapping
103+
score: 5
104+
max: 5
105+
passed: true
106+
comment: Date on X-axis, price on Y-axis, OHLC values correctly mapped
107+
- id: SC-03
108+
name: Required Features
109+
score: 4
110+
max: 5
111+
passed: true
112+
comment: Has open/close ticks, high-low lines, color coding; but spec mentions
113+
horizontal grid lines for reading price levels which are missing
114+
- id: SC-04
115+
name: Data Range
116+
score: 3
117+
max: 3
118+
passed: true
119+
comment: All data points visible within axes range
120+
- id: SC-05
121+
name: Legend Accuracy
122+
score: 1
123+
max: 2
124+
passed: true
125+
comment: Legend labels differ from code (shows Up/Down not Bullish/Bearish)
126+
- id: SC-06
127+
name: Title Format
128+
score: 2
129+
max: 2
130+
passed: true
131+
comment: 'Correct format: ohlc-bar · pygal · pyplots.ai'
132+
data_quality:
133+
score: 19
134+
max: 20
135+
items:
136+
- id: DQ-01
137+
name: Feature Coverage
138+
score: 8
139+
max: 8
140+
passed: true
141+
comment: Shows both bullish and bearish bars, various price ranges, realistic
142+
price movements
143+
- id: DQ-02
144+
name: Realistic Context
145+
score: 6
146+
max: 7
147+
passed: true
148+
comment: Plausible stock price scenario with business days, but no specific
149+
stock context
150+
- id: DQ-03
151+
name: Appropriate Scale
152+
score: 5
153+
max: 5
154+
passed: true
155+
comment: Price values ($130-$166) are realistic for stock prices
156+
code_quality:
157+
score: 10
158+
max: 10
159+
items:
160+
- id: CQ-01
161+
name: KISS Structure
162+
score: 3
163+
max: 3
164+
passed: true
165+
comment: Clean imports → data → plot → save structure, no unnecessary functions/classes
166+
- id: CQ-02
167+
name: Reproducibility
168+
score: 3
169+
max: 3
170+
passed: true
171+
comment: Uses np.random.seed(42) for reproducibility
172+
- id: CQ-03
173+
name: Clean Imports
174+
score: 2
175+
max: 2
176+
passed: true
177+
comment: All imports are used (numpy, pandas, pygal, Style)
178+
- id: CQ-04
179+
name: No Deprecated API
180+
score: 1
181+
max: 1
182+
passed: true
183+
comment: Uses current pygal API
184+
- id: CQ-05
185+
name: Output Correct
186+
score: 1
187+
max: 1
188+
passed: true
189+
comment: Saves as plot.png and plot.html
190+
library_features:
191+
score: 3
192+
max: 5
193+
items:
194+
- id: LF-01
195+
name: Distinctive Features
196+
score: 3
197+
max: 5
198+
passed: true
199+
comment: Uses pygal Style customization and XY chart with line segments to
200+
create OHLC bars; creative use of None separators for discontinuous lines
201+
verdict: APPROVED
202+
impl_tags:
203+
dependencies: []
204+
techniques:
205+
- html-export
206+
patterns:
207+
- data-generation
208+
- iteration-over-groups
209+
dataprep:
210+
- time-series
211+
styling: []

0 commit comments

Comments
 (0)