Skip to content

Commit 67266dc

Browse files
feat(highcharts): implement renko-basic (#3338)
## Implementation: `renko-basic` - highcharts Implements the **highcharts** version of `renko-basic`. **File:** `plots/renko-basic/implementations/highcharts.py` **Parent Issue:** #3294 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20823171414)* --------- 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 3cb77e1 commit 67266dc

2 files changed

Lines changed: 432 additions & 0 deletions

File tree

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
""" pyplots.ai
2+
renko-basic: Basic Renko Chart
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 92/100 | Created: 2026-01-08
5+
"""
6+
7+
import tempfile
8+
import time
9+
import urllib.request
10+
from pathlib import Path
11+
12+
import numpy as np
13+
from highcharts_core.chart import Chart
14+
from highcharts_core.options import HighchartsOptions
15+
from selenium import webdriver
16+
from selenium.webdriver.chrome.options import Options
17+
18+
19+
# Data - Generate synthetic stock price data
20+
np.random.seed(42)
21+
22+
# Simulate 300 days of stock prices starting at $100
23+
n_days = 300
24+
returns = np.random.normal(0.0005, 0.015, n_days) # Daily returns
25+
prices = 100 * np.cumprod(1 + returns)
26+
27+
# Calculate Renko bricks
28+
brick_size = 2.0 # $2 per brick
29+
30+
bricks = []
31+
base_price = np.floor(prices[0] / brick_size) * brick_size
32+
33+
for price in prices[1:]:
34+
diff = price - base_price
35+
if diff >= brick_size:
36+
num_bricks = int(diff // brick_size)
37+
for _ in range(num_bricks):
38+
bricks.append({"open": base_price, "close": base_price + brick_size, "direction": "up"})
39+
base_price += brick_size
40+
elif diff <= -brick_size:
41+
num_bricks = int(abs(diff) // brick_size)
42+
for _ in range(num_bricks):
43+
bricks.append({"open": base_price, "close": base_price - brick_size, "direction": "down"})
44+
base_price -= brick_size
45+
46+
# Prepare data for Highcharts columnrange
47+
bullish_series_data = []
48+
bearish_series_data = []
49+
50+
for i, brick in enumerate(bricks):
51+
if brick["direction"] == "up":
52+
bullish_series_data.append([i, brick["open"], brick["close"]])
53+
else:
54+
bearish_series_data.append([i, brick["close"], brick["open"]])
55+
56+
# Create chart
57+
chart = Chart(container="container")
58+
chart.options = HighchartsOptions()
59+
60+
# Chart configuration
61+
chart.options.chart = {
62+
"type": "columnrange",
63+
"width": 4800,
64+
"height": 2700,
65+
"backgroundColor": "#ffffff",
66+
"marginBottom": 280,
67+
"marginLeft": 250,
68+
"marginTop": 200,
69+
"marginRight": 100,
70+
}
71+
72+
# Title
73+
chart.options.title = {
74+
"text": "renko-basic · highcharts · pyplots.ai",
75+
"style": {"fontSize": "56px", "fontWeight": "bold"},
76+
"y": 50,
77+
}
78+
79+
# Subtitle
80+
chart.options.subtitle = {
81+
"text": f"Stock Price Movement | Brick Size: ${brick_size:.0f}",
82+
"style": {"fontSize": "36px", "color": "#666666"},
83+
"y": 100,
84+
}
85+
86+
# X-axis
87+
chart.options.x_axis = {
88+
"title": {"text": "Brick Index", "style": {"fontSize": "40px"}, "margin": 25},
89+
"labels": {"style": {"fontSize": "32px"}},
90+
"gridLineWidth": 1,
91+
"gridLineColor": "rgba(0, 0, 0, 0.1)",
92+
"tickInterval": 5,
93+
}
94+
95+
# Y-axis
96+
chart.options.y_axis = {
97+
"title": {"text": "Price ($)", "style": {"fontSize": "40px"}, "margin": 25},
98+
"labels": {"style": {"fontSize": "32px"}, "format": "${value}"},
99+
"gridLineWidth": 1,
100+
"gridLineColor": "rgba(0, 0, 0, 0.15)",
101+
}
102+
103+
# Legend - positioned at top right for visibility
104+
chart.options.legend = {
105+
"enabled": True,
106+
"align": "right",
107+
"verticalAlign": "top",
108+
"layout": "vertical",
109+
"x": -50,
110+
"y": 150,
111+
"itemStyle": {"fontSize": "32px"},
112+
"symbolHeight": 24,
113+
"symbolWidth": 50,
114+
"itemMarginBottom": 15,
115+
"backgroundColor": "rgba(255, 255, 255, 0.9)",
116+
"borderWidth": 1,
117+
"borderColor": "#cccccc",
118+
"padding": 15,
119+
}
120+
121+
# Plot options for column range
122+
chart.options.plot_options = {
123+
"columnrange": {"borderWidth": 2, "borderColor": "#333333", "pointPadding": 0.05, "groupPadding": 0}
124+
}
125+
126+
# Series data - using colorblind-safe blue/orange palette
127+
chart.options.series = [
128+
{
129+
"type": "columnrange",
130+
"name": "Bullish (Up)",
131+
"data": bullish_series_data,
132+
"color": "#2563eb", # Blue - colorblind safe
133+
"borderColor": "#1e40af",
134+
"borderWidth": 2,
135+
},
136+
{
137+
"type": "columnrange",
138+
"name": "Bearish (Down)",
139+
"data": bearish_series_data,
140+
"color": "#ea580c", # Orange - colorblind safe
141+
"borderColor": "#c2410c",
142+
"borderWidth": 2,
143+
},
144+
]
145+
146+
# Tooltip configuration - Highcharts distinctive feature
147+
chart.options.tooltip = {
148+
"enabled": True,
149+
"headerFormat": '<span style="font-size: 28px">Brick {point.x}</span><br/>',
150+
"pointFormat": '<span style="font-size: 24px; color:{point.color}">●</span> {series.name}: <b>${point.low:.2f} - ${point.high:.2f}</b><br/>',
151+
"style": {"fontSize": "24px"},
152+
"backgroundColor": "rgba(255, 255, 255, 0.95)",
153+
"borderWidth": 2,
154+
}
155+
156+
# Credits - Highcharts distinctive feature
157+
chart.options.credits = {"enabled": False}
158+
159+
# Exporting options - Highcharts distinctive feature
160+
chart.options.exporting = {"enabled": False}
161+
162+
# Note: Animation is enabled by default in Highcharts
163+
164+
# Responsive rules - Highcharts distinctive feature
165+
chart.options.responsive = {
166+
"rules": [
167+
{
168+
"condition": {"maxWidth": 2400},
169+
"chartOptions": {"legend": {"itemStyle": {"fontSize": "18px"}}, "title": {"style": {"fontSize": "32px"}}},
170+
}
171+
]
172+
}
173+
174+
# Download Highcharts JS (required for headless Chrome)
175+
highcharts_url = "https://code.highcharts.com/highcharts.js"
176+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
177+
highcharts_js = response.read().decode("utf-8")
178+
179+
# Download highcharts-more.js for columnrange
180+
highcharts_more_url = "https://code.highcharts.com/highcharts-more.js"
181+
with urllib.request.urlopen(highcharts_more_url, timeout=30) as response:
182+
highcharts_more_js = response.read().decode("utf-8")
183+
184+
# Generate HTML with INLINE scripts
185+
html_str = chart.to_js_literal()
186+
html_content = f"""<!DOCTYPE html>
187+
<html>
188+
<head>
189+
<meta charset="utf-8">
190+
<script>{highcharts_js}</script>
191+
<script>{highcharts_more_js}</script>
192+
</head>
193+
<body style="margin:0;">
194+
<div id="container" style="width: 4800px; height: 2700px;"></div>
195+
<script>{html_str}</script>
196+
</body>
197+
</html>"""
198+
199+
# Write temp HTML and take screenshot
200+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
201+
f.write(html_content)
202+
temp_path = f.name
203+
204+
chrome_options = Options()
205+
chrome_options.add_argument("--headless")
206+
chrome_options.add_argument("--no-sandbox")
207+
chrome_options.add_argument("--disable-dev-shm-usage")
208+
chrome_options.add_argument("--disable-gpu")
209+
chrome_options.add_argument("--window-size=4800,2700")
210+
211+
driver = webdriver.Chrome(options=chrome_options)
212+
driver.get(f"file://{temp_path}")
213+
time.sleep(5) # Wait for chart to render
214+
driver.save_screenshot("plot.png")
215+
driver.quit()
216+
217+
Path(temp_path).unlink() # Clean up temp file
218+
219+
# Also save HTML for interactive version
220+
with open("plot.html", "w", encoding="utf-8") as f:
221+
f.write(html_content)

0 commit comments

Comments
 (0)