Skip to content

Commit a83d66b

Browse files
Merge remote-tracking branch 'origin/main'
2 parents f54daae + 40f913f commit a83d66b

File tree

10 files changed

+1267
-0
lines changed

10 files changed

+1267
-0
lines changed
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
""" pyplots.ai
2+
candlestick-volume: Stock Candlestick Chart with Volume
3+
Library: highcharts unknown | Python 3.13.11
4+
Quality: 90/100 | Created: 2026-01-01
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 - Generate 60 trading days of OHLC data
20+
np.random.seed(42)
21+
22+
# Start date and generate trading days (skip weekends)
23+
start_date = datetime(2024, 9, 2) # A Monday
24+
dates = []
25+
current_date = start_date
26+
while len(dates) < 60:
27+
if current_date.weekday() < 5: # Monday to Friday
28+
dates.append(current_date)
29+
current_date += timedelta(days=1)
30+
31+
# Generate realistic stock price movements
32+
n_days = 60
33+
initial_price = 150.0
34+
returns = np.random.normal(0.001, 0.02, n_days) # Daily returns with slight upward bias
35+
close_prices = initial_price * np.cumprod(1 + returns)
36+
37+
# Generate OHLC from close prices
38+
open_prices = np.zeros(n_days)
39+
high_prices = np.zeros(n_days)
40+
low_prices = np.zeros(n_days)
41+
volumes = np.zeros(n_days)
42+
43+
open_prices[0] = initial_price
44+
for i in range(n_days):
45+
if i > 0:
46+
# Open is close of previous day with small gap
47+
gap = np.random.normal(0, close_prices[i - 1] * 0.005)
48+
open_prices[i] = close_prices[i - 1] + gap
49+
50+
# High and low based on volatility
51+
volatility = abs(close_prices[i] - open_prices[i]) + np.random.uniform(0.5, 2.0)
52+
if close_prices[i] >= open_prices[i]: # Bullish candle
53+
high_prices[i] = max(open_prices[i], close_prices[i]) + np.random.uniform(0.3, volatility)
54+
low_prices[i] = min(open_prices[i], close_prices[i]) - np.random.uniform(0.2, volatility * 0.7)
55+
else: # Bearish candle
56+
high_prices[i] = max(open_prices[i], close_prices[i]) + np.random.uniform(0.2, volatility * 0.7)
57+
low_prices[i] = min(open_prices[i], close_prices[i]) - np.random.uniform(0.3, volatility)
58+
59+
# Ensure high >= max(open, close) and low <= min(open, close)
60+
high_prices[i] = max(high_prices[i], open_prices[i], close_prices[i])
61+
low_prices[i] = min(low_prices[i], open_prices[i], close_prices[i])
62+
63+
# Volume: higher on days with larger price moves
64+
base_volume = 5_000_000
65+
move_factor = 1 + abs(close_prices[i] - open_prices[i]) / open_prices[i] * 20
66+
volumes[i] = int(base_volume * move_factor * np.random.uniform(0.6, 1.4))
67+
68+
# Convert dates to JavaScript timestamps (milliseconds since epoch)
69+
timestamps = [int(d.timestamp() * 1000) for d in dates]
70+
71+
# Prepare data for Highcharts
72+
ohlc_data = []
73+
volume_data = []
74+
75+
for i in range(n_days):
76+
ohlc_data.append(
77+
[
78+
timestamps[i],
79+
round(open_prices[i], 2),
80+
round(high_prices[i], 2),
81+
round(low_prices[i], 2),
82+
round(close_prices[i], 2),
83+
]
84+
)
85+
# Volume color matches candle direction
86+
color = "#306998" if close_prices[i] >= open_prices[i] else "#E74C3C"
87+
volume_data.append({"x": timestamps[i], "y": int(volumes[i]), "color": color})
88+
89+
# Convert to JSON for JavaScript
90+
ohlc_json = json.dumps(ohlc_data)
91+
volume_json = json.dumps(volume_data)
92+
93+
# Chart configuration using Highstock (for synchronized charts)
94+
chart_js = """
95+
Highcharts.stockChart('container', {
96+
chart: {
97+
width: 4800,
98+
height: 2700,
99+
backgroundColor: '#ffffff',
100+
spacingBottom: 100,
101+
style: {
102+
fontFamily: 'Arial, sans-serif'
103+
}
104+
},
105+
106+
title: {
107+
text: 'candlestick-volume \\u00b7 highcharts \\u00b7 pyplots.ai',
108+
style: {
109+
fontSize: '48px',
110+
fontWeight: 'bold'
111+
}
112+
},
113+
114+
rangeSelector: {
115+
enabled: false
116+
},
117+
118+
navigator: {
119+
enabled: false
120+
},
121+
122+
scrollbar: {
123+
enabled: false
124+
},
125+
126+
credits: {
127+
enabled: false
128+
},
129+
130+
yAxis: [{
131+
labels: {
132+
align: 'right',
133+
x: -10,
134+
style: {
135+
fontSize: '24px'
136+
},
137+
formatter: function() {
138+
return '$' + this.value.toFixed(0);
139+
}
140+
},
141+
title: {
142+
text: 'Price (USD)',
143+
style: {
144+
fontSize: '28px'
145+
}
146+
},
147+
height: '70%',
148+
lineWidth: 2,
149+
resize: {
150+
enabled: false
151+
},
152+
gridLineWidth: 1,
153+
gridLineColor: '#E0E0E0'
154+
}, {
155+
labels: {
156+
align: 'right',
157+
x: -10,
158+
style: {
159+
fontSize: '24px'
160+
},
161+
formatter: function() {
162+
return (this.value / 1000000).toFixed(1) + 'M';
163+
}
164+
},
165+
title: {
166+
text: 'Volume',
167+
style: {
168+
fontSize: '28px'
169+
}
170+
},
171+
top: '72%',
172+
height: '22%',
173+
offset: 0,
174+
lineWidth: 2,
175+
gridLineWidth: 1,
176+
gridLineColor: '#E0E0E0'
177+
}],
178+
179+
xAxis: {
180+
type: 'datetime',
181+
labels: {
182+
style: {
183+
fontSize: '28px'
184+
},
185+
format: '{value:%b %d}',
186+
y: 40
187+
},
188+
crosshair: {
189+
width: 2,
190+
color: '#888888',
191+
snap: false
192+
},
193+
gridLineWidth: 1,
194+
gridLineColor: '#E0E0E0',
195+
lineWidth: 2
196+
},
197+
198+
tooltip: {
199+
split: true,
200+
style: {
201+
fontSize: '20px'
202+
}
203+
},
204+
205+
plotOptions: {
206+
candlestick: {
207+
color: '#E74C3C',
208+
upColor: '#306998',
209+
lineColor: '#E74C3C',
210+
upLineColor: '#306998',
211+
lineWidth: 2
212+
},
213+
column: {
214+
borderWidth: 0
215+
}
216+
},
217+
218+
series: [{
219+
type: 'candlestick',
220+
name: 'Stock Price',
221+
data: OHLC_DATA_PLACEHOLDER,
222+
yAxis: 0
223+
}, {
224+
type: 'column',
225+
name: 'Volume',
226+
data: VOLUME_DATA_PLACEHOLDER,
227+
yAxis: 1
228+
}]
229+
});
230+
"""
231+
232+
# Replace data placeholders
233+
chart_js = chart_js.replace("OHLC_DATA_PLACEHOLDER", ohlc_json)
234+
chart_js = chart_js.replace("VOLUME_DATA_PLACEHOLDER", volume_json)
235+
236+
# Download Highcharts and Highstock JS
237+
highcharts_url = "https://code.highcharts.com/stock/highstock.js"
238+
with urllib.request.urlopen(highcharts_url, timeout=30) as response:
239+
highcharts_js = response.read().decode("utf-8")
240+
241+
# Generate HTML with inline scripts
242+
html_content = f"""<!DOCTYPE html>
243+
<html>
244+
<head>
245+
<meta charset="utf-8">
246+
<script>{highcharts_js}</script>
247+
</head>
248+
<body style="margin:0; padding:0;">
249+
<div id="container" style="width: 4800px; height: 2700px;"></div>
250+
<script>
251+
{chart_js}
252+
</script>
253+
</body>
254+
</html>"""
255+
256+
# Write temp HTML file
257+
with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f:
258+
f.write(html_content)
259+
temp_path = f.name
260+
261+
# Also save HTML for interactive version
262+
with open("plot.html", "w", encoding="utf-8") as f:
263+
f.write(html_content)
264+
265+
# Take screenshot using Selenium
266+
chrome_options = Options()
267+
chrome_options.add_argument("--headless")
268+
chrome_options.add_argument("--no-sandbox")
269+
chrome_options.add_argument("--disable-dev-shm-usage")
270+
chrome_options.add_argument("--disable-gpu")
271+
chrome_options.add_argument("--window-size=4800,2700")
272+
273+
driver = webdriver.Chrome(options=chrome_options)
274+
driver.get(f"file://{temp_path}")
275+
time.sleep(8) # Wait for chart to render
276+
driver.save_screenshot("plot.png")
277+
driver.quit()
278+
279+
# Clean up temp file
280+
Path(temp_path).unlink()
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
library: highcharts
2+
specification_id: candlestick-volume
3+
created: '2026-01-01T21:31:03Z'
4+
updated: '2026-01-01T21:33:14Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20645827229
7+
issue: 3068
8+
python_version: 3.13.11
9+
library_version: unknown
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/candlestick-volume/highcharts/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/candlestick-volume/highcharts/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/candlestick-volume/highcharts/plot.html
13+
quality_score: 90
14+
review:
15+
strengths:
16+
- Excellent use of Highstock for synchronized dual-pane layout
17+
- Proper 70%/25% vertical split between price and volume panes as specified
18+
- Volume bars correctly color-matched to candlestick direction
19+
- Crosshair configured for precise price/volume reading across both panes
20+
- Realistic stock data generation with proper gap handling and volatility modeling
21+
- Clean title format following pyplots.ai convention
22+
- Well-configured axis formatting ($ prefix for prices, M suffix for volumes)
23+
weaknesses:
24+
- Blue/red color scheme could be improved for colorblind accessibility (consider
25+
blue/orange instead)
26+
- X-axis date labels appear clipped/truncated at bottom of image
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
""" pyplots.ai
2+
contour-decision-boundary: Decision Boundary Classifier Visualization
3+
Library: letsplot 4.8.2 | Python 3.13.11
4+
Quality: 91/100 | Created: 2026-01-01
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from lets_plot import (
10+
LetsPlot,
11+
aes,
12+
element_text,
13+
geom_point,
14+
geom_tile,
15+
ggplot,
16+
ggsave,
17+
ggsize,
18+
labs,
19+
scale_color_manual,
20+
scale_fill_manual,
21+
scale_shape_manual,
22+
theme,
23+
theme_minimal,
24+
)
25+
from sklearn.datasets import make_moons
26+
from sklearn.neighbors import KNeighborsClassifier
27+
28+
29+
LetsPlot.setup_html()
30+
31+
# Data - Create synthetic classification data
32+
np.random.seed(42)
33+
X, y = make_moons(n_samples=200, noise=0.25, random_state=42)
34+
35+
# Train a KNN classifier
36+
classifier = KNeighborsClassifier(n_neighbors=5)
37+
classifier.fit(X, y)
38+
39+
# Create mesh grid for decision boundary
40+
h = 0.02 # Step size
41+
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
42+
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
43+
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
44+
45+
# Predict class for each point in mesh
46+
Z = classifier.predict(np.c_[xx.ravel(), yy.ravel()])
47+
Z = Z.reshape(xx.shape)
48+
49+
# Create DataFrame for mesh grid
50+
mesh_df = pd.DataFrame({"X1": xx.ravel(), "X2": yy.ravel(), "Predicted": Z.ravel().astype(str)})
51+
52+
# Create DataFrame for training points
53+
train_df = pd.DataFrame({"X1": X[:, 0], "X2": X[:, 1], "Class": y.astype(str)})
54+
55+
# Add classification result (correct/incorrect) for training points
56+
predictions = classifier.predict(X)
57+
train_df["Correct"] = np.where(predictions == y, "Correct", "Incorrect")
58+
59+
# Plot - Decision boundary with training points
60+
plot = (
61+
ggplot()
62+
# Decision regions as filled contour using tiles
63+
+ geom_tile(aes(x="X1", y="X2", fill="Predicted"), data=mesh_df, alpha=0.4)
64+
# Training points
65+
+ geom_point(aes(x="X1", y="X2", color="Class", shape="Correct"), data=train_df, size=5, stroke=1.5)
66+
# Color scales
67+
+ scale_fill_manual(values=["#306998", "#FFD43B"], name="Predicted Class")
68+
+ scale_color_manual(values=["#306998", "#FFD43B"], name="True Class")
69+
+ scale_shape_manual(values=[16, 4], name="Classification") # Circle for correct, X for incorrect
70+
# Labels
71+
+ labs(title="contour-decision-boundary · letsplot · pyplots.ai", x="Feature X1", y="Feature X2")
72+
# Theme
73+
+ theme_minimal()
74+
+ theme(
75+
plot_title=element_text(size=24),
76+
axis_title=element_text(size=20),
77+
axis_text=element_text(size=16),
78+
legend_title=element_text(size=18),
79+
legend_text=element_text(size=14),
80+
legend_position="right",
81+
)
82+
+ ggsize(1600, 900)
83+
)
84+
85+
# Save - use path parameter to specify exact location
86+
ggsave(plot, filename="plot.png", path=".", scale=3)
87+
88+
# Save HTML for interactive version
89+
ggsave(plot, filename="plot.html", path=".")

0 commit comments

Comments
 (0)