diff --git a/plots/subplot-grid-custom/implementations/highcharts.py b/plots/subplot-grid-custom/implementations/highcharts.py new file mode 100644 index 0000000000..9f18061646 --- /dev/null +++ b/plots/subplot-grid-custom/implementations/highcharts.py @@ -0,0 +1,433 @@ +""" pyplots.ai +subplot-grid-custom: Custom Subplot Grid Layout +Library: highcharts unknown | Python 3.13.11 +Quality: 91/100 | Created: 2025-12-30 +""" + +import tempfile +import time +import urllib.request +from pathlib import Path + +import numpy as np +from selenium import webdriver +from selenium.webdriver.chrome.options import Options + + +# Data - Generate sample dashboard data +np.random.seed(42) + +# Main time series (stock price - spans 2x2 cells) +days = 90 +base_price = 150 +returns = np.random.randn(days) * 2 +price = base_price + np.cumsum(returns) +price_data = [[i, float(price[i])] for i in range(days)] + +# Volume data (bar chart - 1 cell) +volume = np.random.uniform(1, 5, days) * 1e6 +volume_data = [[i, float(volume[i] / 1e6)] for i in range(days)] + +# Returns distribution (histogram - 1 cell) +daily_returns = np.diff(price) / price[:-1] * 100 +hist_counts, hist_edges = np.histogram(daily_returns, bins=15) +histogram_data = [[float(hist_edges[i]), int(hist_counts[i])] for i in range(len(hist_counts))] + +# Performance metrics (scatter - 1 cell) +sectors = ["Tech", "Health", "Finance", "Energy", "Consumer"] +sector_returns = np.random.uniform(-5, 15, len(sectors)) +sector_volatility = np.random.uniform(5, 20, len(sectors)) +scatter_data = [ + {"x": float(sector_volatility[i]), "y": float(sector_returns[i]), "name": sectors[i]} for i in range(len(sectors)) +] + +# Moving average data (line chart - 1 cell) +ma_20 = np.convolve(price, np.ones(20) / 20, mode="valid") +ma_data = [[i, float(ma_20[i])] for i in range(len(ma_20))] + +# Build custom HTML with multiple Highcharts in a grid layout +# Use CSS grid to create the custom subplot layout +highcharts_url = "https://code.highcharts.com/highcharts.js" +with urllib.request.urlopen(highcharts_url, timeout=30) as response: + highcharts_js = response.read().decode("utf-8") + +# Colors - colorblind-safe palette +python_blue = "#306998" +python_yellow = "#FFD43B" +purple = "#9467BD" +cyan = "#17BECF" +brown = "#8C564B" + +# Chart configurations as JS objects +# Main chart - large, spans 2x2 +main_chart_config = f""" +{{ + chart: {{ + type: 'line', + backgroundColor: '#ffffff' + }}, + title: {{ + text: 'Stock Price (90 Days)', + style: {{ fontSize: '44px', fontWeight: 'bold' }} + }}, + xAxis: {{ + title: {{ text: 'Trading Day', style: {{ fontSize: '32px' }} }}, + labels: {{ style: {{ fontSize: '24px' }}, step: 15 }} + }}, + yAxis: {{ + title: {{ text: 'Price ($)', style: {{ fontSize: '32px' }} }}, + labels: {{ style: {{ fontSize: '24px' }} }}, + gridLineColor: 'rgba(0,0,0,0.1)' + }}, + legend: {{ enabled: true, itemStyle: {{ fontSize: '24px' }} }}, + credits: {{ enabled: false }}, + plotOptions: {{ + line: {{ lineWidth: 5, marker: {{ radius: 8 }} }} + }}, + series: [{{ + name: 'Daily Price', + data: {price_data}, + color: '{python_blue}' + }}] +}} +""" + +# Volume chart - detail view +volume_chart_config = f""" +{{ + chart: {{ + type: 'column', + backgroundColor: '#ffffff' + }}, + title: {{ + text: 'Trading Volume', + style: {{ fontSize: '40px', fontWeight: 'bold' }} + }}, + xAxis: {{ + title: {{ text: 'Day', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }}, step: 20 }} + }}, + yAxis: {{ + title: {{ text: 'Volume (M)', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }} }}, + gridLineColor: 'rgba(0,0,0,0.1)' + }}, + legend: {{ enabled: true, itemStyle: {{ fontSize: '20px' }} }}, + credits: {{ enabled: false }}, + plotOptions: {{ + column: {{ borderWidth: 0 }} + }}, + series: [{{ + name: 'Volume', + data: {volume_data}, + color: '{python_yellow}' + }}] +}} +""" + +# Histogram chart - detail view (returns distribution) +histogram_chart_config = f""" +{{ + chart: {{ + type: 'column', + backgroundColor: '#ffffff' + }}, + title: {{ + text: 'Returns Distribution', + style: {{ fontSize: '40px', fontWeight: 'bold' }} + }}, + xAxis: {{ + title: {{ text: 'Daily Return (%)', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }} }} + }}, + yAxis: {{ + title: {{ text: 'Frequency', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }} }}, + gridLineColor: 'rgba(0,0,0,0.1)' + }}, + legend: {{ enabled: true, itemStyle: {{ fontSize: '20px' }} }}, + credits: {{ enabled: false }}, + plotOptions: {{ + column: {{ borderWidth: 0, pointPadding: 0, groupPadding: 0.1 }} + }}, + series: [{{ + name: 'Frequency', + data: {histogram_data}, + color: '{purple}' + }}] +}} +""" + +# Scatter chart - detail view (risk vs return) +scatter_chart_config = f""" +{{ + chart: {{ + type: 'scatter', + backgroundColor: '#ffffff' + }}, + title: {{ + text: 'Risk vs Return by Sector', + style: {{ fontSize: '40px', fontWeight: 'bold' }} + }}, + xAxis: {{ + title: {{ text: 'Volatility (%)', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }} }}, + gridLineWidth: 1, + gridLineColor: 'rgba(0,0,0,0.1)' + }}, + yAxis: {{ + title: {{ text: 'Return (%)', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }} }}, + gridLineColor: 'rgba(0,0,0,0.1)' + }}, + legend: {{ enabled: true, itemStyle: {{ fontSize: '20px' }} }}, + credits: {{ enabled: false }}, + tooltip: {{ + formatter: function() {{ + return '' + this.point.name + '
Volatility: ' + this.x.toFixed(1) + '%
Return: ' + this.y.toFixed(1) + '%'; + }} + }}, + plotOptions: {{ + scatter: {{ + marker: {{ + radius: 18, + symbol: 'circle' + }}, + dataLabels: {{ + enabled: true, + format: '{{point.name}}', + style: {{ fontSize: '20px', fontWeight: 'normal' }}, + y: -25 + }} + }} + }}, + series: [{{ + name: 'Sectors', + data: {scatter_data}, + color: '{cyan}' + }}] +}} +""" + +# Moving average chart - detail view +ma_chart_config = f""" +{{ + chart: {{ + type: 'line', + backgroundColor: '#ffffff' + }}, + title: {{ + text: '20-Day Moving Average', + style: {{ fontSize: '40px', fontWeight: 'bold' }} + }}, + xAxis: {{ + title: {{ text: 'Day', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }}, step: 15 }} + }}, + yAxis: {{ + title: {{ text: 'MA Price ($)', style: {{ fontSize: '26px' }} }}, + labels: {{ style: {{ fontSize: '20px' }} }}, + gridLineColor: 'rgba(0,0,0,0.1)' + }}, + legend: {{ enabled: true, itemStyle: {{ fontSize: '20px' }} }}, + credits: {{ enabled: false }}, + plotOptions: {{ + line: {{ lineWidth: 4, marker: {{ enabled: false }} }} + }}, + series: [{{ + name: '20-Day MA', + data: {ma_data}, + color: '{brown}' + }}] +}} +""" + +# Main title for the dashboard +main_title = "subplot-grid-custom · highcharts · pyplots.ai" + +# 5-cell layout: main chart spans 2x2, plus 4 detail views +html_content = f""" + + + + + + + +
{main_title}
+
+
+
+
+
+
+
+ + +""" + +# Write temp HTML and take screenshot +with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: + f.write(html_content) + temp_path = f.name + +# Also save as plot.html for interactive version +with open("plot.html", "w", encoding="utf-8") as f: + # For the standalone HTML, include CDN link instead of inline script + html_standalone = f""" + + + + + + + +
{main_title}
+
+
+
+
+
+
+
+ + +""" + f.write(html_standalone) + +# Screenshot with Selenium +chrome_options = Options() +chrome_options.add_argument("--headless") +chrome_options.add_argument("--no-sandbox") +chrome_options.add_argument("--disable-dev-shm-usage") +chrome_options.add_argument("--disable-gpu") +chrome_options.add_argument("--window-size=4800,2700") + +driver = webdriver.Chrome(options=chrome_options) +driver.get(f"file://{temp_path}") +time.sleep(5) +driver.save_screenshot("plot.png") +driver.quit() + +Path(temp_path).unlink() diff --git a/plots/subplot-grid-custom/metadata/highcharts.yaml b/plots/subplot-grid-custom/metadata/highcharts.yaml new file mode 100644 index 0000000000..6d63b9a09f --- /dev/null +++ b/plots/subplot-grid-custom/metadata/highcharts.yaml @@ -0,0 +1,28 @@ +library: highcharts +specification_id: subplot-grid-custom +created: '2025-12-30T23:54:24Z' +updated: '2025-12-31T00:11:24Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 20608472991 +issue: 2856 +python_version: 3.13.11 +library_version: unknown +preview_url: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/highcharts/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/highcharts/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/subplot-grid-custom/highcharts/plot.html +quality_score: 91 +review: + strengths: + - Excellent custom grid layout with CSS Grid demonstrating colspan/rowspan effectively + - Dashboard-style visualization with a large main chart (2x2) and 4 detail views + (1x1 each) + - Perfect colorblind-safe palette with 5 distinct colors + - All text is highly legible with appropriately sized fonts for the 4800x2700 canvas + - Realistic financial data scenario with coherent relationships between charts + - Clean KISS code structure with proper random seed + weaknesses: + - Legend text sizes are disproportionately small compared to the chart titles and + should be increased for better readability + - Does not use the highcharts-core Python library API (Chart, HighchartsOptions + classes) as recommended in the library guidelines; instead manually constructs + JavaScript configurations