Skip to content

Commit 657c149

Browse files
feat(highcharts): implement ohlc-bar (#3335)
## Implementation: `ohlc-bar` - highcharts Implements the **highcharts** version of `ohlc-bar`. **File:** `plots/ohlc-bar/implementations/highcharts.py` **Parent Issue:** #3293 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20823200816)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 6d20a56 commit 657c149

2 files changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"""pyplots.ai
2+
ohlc-bar: OHLC Bar Chart
3+
Library: highcharts | Python 3.13
4+
Quality: pending | Created: 2026-01-08
5+
"""
6+
7+
import json
8+
import tempfile
9+
import time
10+
import urllib.request
11+
from datetime import datetime, timedelta
12+
from pathlib import Path
13+
14+
import numpy as np
15+
from selenium import webdriver
16+
from selenium.webdriver.chrome.options import Options
17+
18+
19+
# Data - 45 trading days of simulated stock prices
20+
np.random.seed(42)
21+
22+
# Start price and generate OHLC data
23+
start_price = 125.0
24+
n_days = 45
25+
26+
# Generate realistic stock movements
27+
opens = [start_price]
28+
highs = []
29+
lows = []
30+
closes = []
31+
32+
for i in range(n_days):
33+
open_price = opens[i] if i == 0 else closes[i - 1] + np.random.randn() * 0.3
34+
if i > 0:
35+
opens.append(open_price)
36+
37+
# Daily volatility
38+
daily_range = abs(np.random.randn() * 1.5) + 0.5
39+
direction = np.random.choice([-1, 1], p=[0.48, 0.52]) # Slight bullish bias
40+
41+
close_price = open_price + direction * np.random.rand() * daily_range
42+
high_price = max(open_price, close_price) + abs(np.random.randn() * 0.4)
43+
low_price = min(open_price, close_price) - abs(np.random.randn() * 0.4)
44+
45+
highs.append(round(high_price, 2))
46+
lows.append(round(low_price, 2))
47+
closes.append(round(close_price, 2))
48+
49+
opens = [round(o, 2) for o in opens]
50+
51+
# Generate dates (trading days, skip weekends)
52+
start_date = datetime(2024, 9, 1)
53+
dates = []
54+
current_date = start_date
55+
while len(dates) < n_days:
56+
if current_date.weekday() < 5: # Monday to Friday
57+
dates.append(current_date)
58+
current_date += timedelta(days=1)
59+
60+
# Format data for Highcharts: [timestamp, open, high, low, close]
61+
ohlc_data = []
62+
for i in range(n_days):
63+
timestamp = int(dates[i].timestamp() * 1000) # JavaScript timestamp in ms
64+
ohlc_data.append([timestamp, opens[i], highs[i], lows[i], closes[i]])
65+
66+
# Chart options for Highcharts Stock OHLC chart
67+
# Using colorblind-safe palette: Python Blue for bullish, warm amber for bearish
68+
chart_options = {
69+
"chart": {
70+
"type": "ohlc",
71+
"width": 4800,
72+
"height": 2700,
73+
"backgroundColor": "#ffffff",
74+
"marginBottom": 220,
75+
"marginLeft": 250,
76+
"marginRight": 80,
77+
"marginTop": 150,
78+
"style": {"fontFamily": "Arial, sans-serif"},
79+
},
80+
"title": {
81+
"text": "Stock Price Analysis · ohlc-bar · highcharts · pyplots.ai",
82+
"style": {"fontSize": "72px", "fontWeight": "bold", "color": "#333333"},
83+
"y": 60,
84+
},
85+
"xAxis": {
86+
"type": "datetime",
87+
"title": {"text": "Date", "style": {"fontSize": "52px", "color": "#333333"}, "margin": 30},
88+
"labels": {
89+
"style": {"fontSize": "36px", "color": "#333333"},
90+
"format": "{value:%b %d}",
91+
"y": 45,
92+
"step": 3, # Show every 3rd label to prevent overlap with more data points
93+
},
94+
"gridLineWidth": 1,
95+
"gridLineColor": "rgba(0, 0, 0, 0.15)",
96+
"gridLineDashStyle": "Dash",
97+
"lineWidth": 3,
98+
"lineColor": "#333333",
99+
"tickWidth": 3,
100+
"tickColor": "#333333",
101+
"tickLength": 15,
102+
},
103+
"yAxis": {
104+
"title": {"text": "Price (USD)", "style": {"fontSize": "52px", "color": "#333333"}, "margin": 30},
105+
"labels": {"style": {"fontSize": "36px", "color": "#333333"}, "format": "${value:.0f}", "x": -15},
106+
"gridLineWidth": 1,
107+
"gridLineColor": "rgba(0, 0, 0, 0.15)",
108+
"gridLineDashStyle": "Dash",
109+
"lineWidth": 3,
110+
"lineColor": "#333333",
111+
"opposite": False, # Keep Y-axis on left side only
112+
"tickInterval": 2, # Show labels at $2 intervals to avoid duplicates
113+
},
114+
"legend": {"enabled": False},
115+
"tooltip": {
116+
"split": False,
117+
"style": {"fontSize": "28px"},
118+
"headerFormat": "<b>{point.x:%b %d, %Y}</b><br/>",
119+
"pointFormat": "Open: ${point.open:.2f}<br/>"
120+
+ "High: ${point.high:.2f}<br/>"
121+
+ "Low: ${point.low:.2f}<br/>"
122+
+ "Close: ${point.close:.2f}",
123+
},
124+
"plotOptions": {
125+
"ohlc": {
126+
# Colorblind-safe: Python Blue for bullish, warm amber for bearish
127+
"color": "#E67E22", # Warm amber for bearish (close < open)
128+
"upColor": "#306998", # Python Blue for bullish (close > open)
129+
"lineWidth": 5, # Bar line width - visible at large size
130+
}
131+
},
132+
"rangeSelector": {"enabled": False},
133+
"navigator": {"enabled": False},
134+
"scrollbar": {"enabled": False},
135+
"credits": {"enabled": False},
136+
"series": [{"type": "ohlc", "name": "Stock Price", "data": ohlc_data}],
137+
}
138+
139+
# Download Highstock JS (includes OHLC support)
140+
highstock_url = "https://code.highcharts.com/stock/highstock.js"
141+
with urllib.request.urlopen(highstock_url, timeout=30) as response:
142+
highstock_js = response.read().decode("utf-8")
143+
144+
# Generate chart options JSON
145+
chart_options_json = json.dumps(chart_options)
146+
147+
# Generate HTML with inline scripts
148+
html_content = f"""<!DOCTYPE html>
149+
<html>
150+
<head>
151+
<meta charset="utf-8">
152+
<script>{highstock_js}</script>
153+
</head>
154+
<body style="margin:0; background-color: #ffffff;">
155+
<div id="container" style="width: 4800px; height: 2700px;"></div>
156+
<script>
157+
document.addEventListener('DOMContentLoaded', function() {{
158+
Highcharts.stockChart('container', {chart_options_json});
159+
}});
160+
</script>
161+
</body>
162+
</html>"""
163+
164+
# Write temp HTML file
165+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
166+
f.write(html_content)
167+
temp_path = f.name
168+
169+
# Also save the HTML for interactive viewing
170+
with open("plot.html", "w", encoding="utf-8") as f:
171+
f.write(html_content)
172+
173+
# Take screenshot with headless Chrome
174+
chrome_options = Options()
175+
chrome_options.add_argument("--headless")
176+
chrome_options.add_argument("--no-sandbox")
177+
chrome_options.add_argument("--disable-dev-shm-usage")
178+
chrome_options.add_argument("--disable-gpu")
179+
chrome_options.add_argument("--window-size=4800,2700")
180+
181+
driver = webdriver.Chrome(options=chrome_options)
182+
driver.get(f"file://{temp_path}")
183+
time.sleep(5)
184+
driver.save_screenshot("plot.png")
185+
driver.quit()
186+
187+
# Clean up temp file
188+
Path(temp_path).unlink()
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Per-library metadata for highcharts implementation of ohlc-bar
2+
# Auto-generated by impl-generate.yml
3+
4+
library: highcharts
5+
specification_id: ohlc-bar
6+
created: '2026-01-08T16:13:44Z'
7+
updated: '2026-01-08T16:13:44Z'
8+
generated_by: claude-opus-4-5-20251101
9+
workflow_run: 20823200816
10+
issue: 3293
11+
python_version: 3.13.11
12+
library_version: unknown
13+
preview_url: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/highcharts/plot.png
14+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/highcharts/plot_thumb.png
15+
preview_html: https://storage.googleapis.com/pyplots-images/plots/ohlc-bar/highcharts/plot.html
16+
quality_score: null
17+
review:
18+
strengths: []
19+
weaknesses: []

0 commit comments

Comments
 (0)