Skip to content

Commit 9c5702c

Browse files
feat(highcharts): implement span-basic (#5618)
## Implementation: `span-basic` - python/highcharts Implements the **python/highcharts** version of `span-basic`. **File:** `plots/span-basic/implementations/python/highcharts.py` **Parent Issue:** #980 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/25161284754)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent b89dbac commit 9c5702c

2 files changed

Lines changed: 278 additions & 215 deletions

File tree

Lines changed: 86 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
span-basic: Basic Span Plot (Highlighted Region)
3-
Library: highcharts unknown | Python 3.13.11
4-
Quality: 93/100 | Created: 2025-12-23
3+
Library: highcharts unknown | Python 3.13.13
4+
Quality: 90/100 | Updated: 2026-04-30
55
"""
66

7+
import os
78
import tempfile
89
import time
910
import urllib.request
@@ -16,133 +17,149 @@
1617
from selenium.webdriver.chrome.options import Options
1718

1819

19-
# Data - Monthly stock price with recession period highlighted
20-
months = [
21-
"Jan 2007",
22-
"Apr 2007",
23-
"Jul 2007",
24-
"Oct 2007",
25-
"Jan 2008",
26-
"Apr 2008",
27-
"Jul 2008",
28-
"Oct 2008",
29-
"Jan 2009",
30-
"Apr 2009",
31-
"Jul 2009",
32-
"Oct 2009",
33-
"Jan 2010",
34-
"Apr 2010",
35-
"Jul 2010",
36-
"Oct 2010",
20+
# Theme tokens
21+
THEME = os.getenv("ANYPLOT_THEME", "light")
22+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
23+
ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420"
24+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
25+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
26+
GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)"
27+
BRAND = "#009E73" # Okabe-Ito position 1 — always first series
28+
29+
# Data - S&P 500 approximate quarterly closing values during the 2007-2010 financial crisis
30+
quarters = [
31+
"Q1 2007",
32+
"Q2 2007",
33+
"Q3 2007",
34+
"Q4 2007",
35+
"Q1 2008",
36+
"Q2 2008",
37+
"Q3 2008",
38+
"Q4 2008",
39+
"Q1 2009",
40+
"Q2 2009",
41+
"Q3 2009",
42+
"Q4 2009",
43+
"Q1 2010",
44+
"Q2 2010",
45+
"Q3 2010",
46+
"Q4 2010",
3747
]
38-
prices = [145, 152, 158, 155, 148, 135, 125, 95, 85, 78, 88, 105, 115, 122, 128, 135]
48+
sp500_values = [1421, 1503, 1527, 1468, 1323, 1280, 1166, 903, 798, 919, 1057, 1115, 1169, 1031, 1141, 1258]
3949

4050
# Create chart
4151
chart = Chart(container="container")
4252
chart.options = HighchartsOptions()
4353

44-
# Chart configuration
4554
chart.options.chart = {
4655
"type": "line",
4756
"width": 4800,
4857
"height": 2700,
49-
"backgroundColor": "#ffffff",
50-
"marginBottom": 200,
51-
"spacingBottom": 20,
58+
"backgroundColor": PAGE_BG,
59+
"marginBottom": 220,
60+
"spacingTop": 40,
5261
}
5362

54-
# Title
5563
chart.options.title = {
56-
"text": "span-basic · highcharts · pyplots.ai",
57-
"style": {"fontSize": "72px", "fontWeight": "bold"},
64+
"text": "span-basic · highcharts · anyplot.ai",
65+
"style": {"fontSize": "72px", "fontWeight": "bold", "color": INK},
5866
}
5967

60-
# Subtitle
61-
chart.options.subtitle = {"text": "Stock Price with Recession Period Highlighted", "style": {"fontSize": "48px"}}
68+
chart.options.subtitle = {
69+
"text": "S&P 500 Index with Financial Crisis Period Highlighted",
70+
"style": {"fontSize": "48px", "color": INK_SOFT},
71+
}
6272

63-
# X-axis with categories
6473
chart.options.x_axis = {
65-
"categories": months,
66-
"title": {"text": "Date", "style": {"fontSize": "48px"}},
67-
"labels": {"style": {"fontSize": "36px"}},
68-
# Vertical span - highlight recession period (indices 4-8, Jan 2008 - Jan 2009)
74+
"categories": quarters,
75+
"title": {"text": "Quarter", "style": {"fontSize": "44px", "color": INK}, "margin": 20},
76+
"labels": {"style": {"fontSize": "32px", "color": INK_SOFT}},
77+
"lineColor": INK_SOFT,
78+
"tickColor": INK_SOFT,
79+
"gridLineColor": GRID,
80+
# Vertical span — highlight financial crisis (Q4 2007 through Q1 2009, indices 3–8)
6981
"plotBands": [
7082
{
71-
"from": 4,
72-
"to": 8,
73-
"color": "rgba(48, 105, 152, 0.25)",
83+
"from": 3.5,
84+
"to": 8.5,
85+
"color": "rgba(213,94,0,0.18)",
7486
"label": {
75-
"text": "Recession Period",
76-
"style": {"fontSize": "42px", "color": "#306998", "fontWeight": "bold"},
87+
"text": "Financial Crisis",
88+
"style": {"fontSize": "40px", "color": "#D55E00", "fontWeight": "bold"},
7789
"verticalAlign": "top",
78-
"y": 60,
90+
"y": 80,
7991
},
8092
}
8193
],
8294
}
8395

84-
# Y-axis with horizontal span for threshold zone
8596
chart.options.y_axis = {
86-
"title": {"text": "Stock Price ($)", "style": {"fontSize": "48px"}},
87-
"labels": {"style": {"fontSize": "36px"}},
88-
"min": 50,
89-
"max": 180,
90-
# Horizontal span - highlight danger zone below $100
97+
"title": {"text": "Index Value (Points)", "style": {"fontSize": "44px", "color": INK}},
98+
"labels": {"style": {"fontSize": "36px", "color": INK_SOFT}},
99+
"lineColor": INK_SOFT,
100+
"tickColor": INK_SOFT,
101+
"gridLineColor": GRID,
102+
"min": 600,
103+
"max": 1700,
104+
# Horizontal span — highlight bear market zone (below 1000 points)
91105
"plotBands": [
92106
{
93-
"from": 50,
94-
"to": 100,
95-
"color": "rgba(255, 212, 59, 0.25)",
107+
"from": 600,
108+
"to": 1000,
109+
"color": "rgba(230,159,0,0.18)",
96110
"label": {
97-
"text": "Below Target Price",
98-
"style": {"fontSize": "36px", "color": "#B8860B"},
111+
"text": "Bear Market Zone",
112+
"style": {"fontSize": "36px", "color": "#E69F00", "fontWeight": "500"},
99113
"align": "left",
100-
"x": 50,
114+
"x": 40,
115+
"y": -15,
101116
},
102117
}
103118
],
104119
}
105120

106-
# Legend
107-
chart.options.legend = {"enabled": True, "itemStyle": {"fontSize": "36px"}}
121+
chart.options.legend = {
122+
"enabled": True,
123+
"itemStyle": {"fontSize": "36px", "color": INK_SOFT, "fontWeight": "normal"},
124+
"backgroundColor": ELEVATED_BG,
125+
"borderColor": INK_SOFT,
126+
"borderWidth": 1,
127+
}
108128

109-
# Plot options
110129
chart.options.plot_options = {"line": {"lineWidth": 6, "marker": {"radius": 12, "enabled": True}}}
111130

112-
# Add line series
113131
series = LineSeries()
114-
series.name = "Stock Price"
115-
series.data = prices
116-
series.color = "#306998"
117-
series.marker = {"fillColor": "#306998", "lineColor": "#306998", "lineWidth": 2}
118-
132+
series.name = "S&P 500"
133+
series.data = sp500_values
134+
series.color = BRAND
135+
series.marker = {"fillColor": BRAND, "lineColor": PAGE_BG, "lineWidth": 2}
119136
chart.add_series(series)
120137

121138
# Download Highcharts JS for inline embedding
122-
highcharts_url = "https://code.highcharts.com/highcharts.js"
139+
highcharts_url = "https://cdn.jsdelivr.net/npm/highcharts@latest/highcharts.js"
123140
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
124141
highcharts_js = response.read().decode("utf-8")
125142

126-
# Generate HTML with inline scripts
127143
html_str = chart.to_js_literal()
128144
html_content = f"""<!DOCTYPE html>
129145
<html>
130146
<head>
131147
<meta charset="utf-8">
132148
<script>{highcharts_js}</script>
133149
</head>
134-
<body style="margin:0;">
150+
<body style="margin:0; background:{PAGE_BG};">
135151
<div id="container" style="width: 4800px; height: 2700px;"></div>
136152
<script>{html_str}</script>
137153
</body>
138154
</html>"""
139155

140-
# Write temp HTML file
156+
with open(f"plot-{THEME}.html", "w", encoding="utf-8") as f:
157+
f.write(html_content)
158+
141159
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
142160
f.write(html_content)
143161
temp_path = f.name
144162

145-
# Take screenshot with headless Chrome
146163
chrome_options = Options()
147164
chrome_options.add_argument("--headless")
148165
chrome_options.add_argument("--no-sandbox")
@@ -153,11 +170,7 @@
153170
driver = webdriver.Chrome(options=chrome_options)
154171
driver.get(f"file://{temp_path}")
155172
time.sleep(5)
156-
driver.save_screenshot("plot.png")
173+
driver.save_screenshot(f"plot-{THEME}.png")
157174
driver.quit()
158175

159-
# Save HTML file for interactive viewing
160-
Path("plot.html").write_text(html_content, encoding="utf-8")
161-
162-
# Clean up temp file
163176
Path(temp_path).unlink()

0 commit comments

Comments
 (0)