From 70572340be7a97cd0085d6d24ddbc2c22721d15b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 00:54:52 +0000 Subject: [PATCH 1/3] feat(highcharts): implement indicator-sma --- .../implementations/highcharts.py | 200 ++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 plots/indicator-sma/implementations/highcharts.py diff --git a/plots/indicator-sma/implementations/highcharts.py b/plots/indicator-sma/implementations/highcharts.py new file mode 100644 index 0000000000..10cda94f30 --- /dev/null +++ b/plots/indicator-sma/implementations/highcharts.py @@ -0,0 +1,200 @@ +"""pyplots.ai +indicator-sma: Simple Moving Average (SMA) Indicator Chart +Library: highcharts | Python 3.13 +Quality: pending | Created: 2026-01-11 +""" + +import json +import tempfile +import time +import urllib.request +from pathlib import Path + +import numpy as np +import pandas as pd +from selenium import webdriver +from selenium.webdriver.chrome.options import Options + + +# Generate sample stock data (365 trading days) +np.random.seed(42) +n_days = 365 +dates = pd.date_range(start="2024-01-02", periods=n_days, freq="B") # Business days + +# Generate realistic stock price movement using random walk +returns = np.random.normal(0.0005, 0.015, n_days) # Daily returns +price_start = 150.0 +close_prices = price_start * np.cumprod(1 + returns) + +# Add some trend and volatility patterns +trend = np.linspace(0, 20, n_days) +close_prices = close_prices + trend + +# Create DataFrame +df = pd.DataFrame({"date": dates, "close": close_prices}) + +# Calculate SMAs +df["sma_20"] = df["close"].rolling(window=20).mean() +df["sma_50"] = df["close"].rolling(window=50).mean() +df["sma_200"] = df["close"].rolling(window=200).mean() + +# Convert dates to timestamps for Highcharts (milliseconds since epoch) +timestamps = [int(d.timestamp() * 1000) for d in df["date"]] + +# Prepare data series (as [timestamp, value] pairs, handling NaN for initial periods) +close_data = [[t, round(v, 2)] for t, v in zip(timestamps, df["close"], strict=True)] +sma20_data = [[t, round(v, 2)] for t, v in zip(timestamps, df["sma_20"], strict=True) if not np.isnan(v)] +sma50_data = [[t, round(v, 2)] for t, v in zip(timestamps, df["sma_50"], strict=True) if not np.isnan(v)] +sma200_data = [[t, round(v, 2)] for t, v in zip(timestamps, df["sma_200"], strict=True) if not np.isnan(v)] + +# Colors (colorblind-safe palette) +colors = { + "close": "#306998", # Python Blue - price line + "sma20": "#FFD43B", # Python Yellow - short-term + "sma50": "#17BECF", # Cyan - medium-term + "sma200": "#9467BD", # Purple - long-term +} + +# Chart options for Highcharts +chart_options = { + "chart": { + "type": "line", + "width": 4800, + "height": 2700, + "backgroundColor": "#ffffff", + "marginBottom": 200, + "marginLeft": 220, + "marginRight": 100, + "marginTop": 180, + "style": {"fontFamily": "Arial, sans-serif"}, + }, + "title": { + "text": "indicator-sma · highcharts · pyplots.ai", + "style": {"fontSize": "64px", "fontWeight": "bold", "color": "#333333"}, + "y": 70, + }, + "subtitle": { + "text": "Stock Price with 20, 50, and 200-day Simple Moving Averages", + "style": {"fontSize": "36px", "color": "#666666"}, + "y": 130, + }, + "xAxis": { + "type": "datetime", + "title": {"text": "Date", "style": {"fontSize": "40px", "color": "#333333"}, "margin": 25}, + "labels": {"style": {"fontSize": "28px", "color": "#333333"}, "format": "{value:%b %Y}", "y": 35}, + "gridLineWidth": 1, + "gridLineColor": "rgba(0, 0, 0, 0.1)", + "lineWidth": 2, + "lineColor": "#333333", + "tickWidth": 2, + "tickColor": "#333333", + "tickLength": 12, + }, + "yAxis": { + "title": {"text": "Price (USD)", "style": {"fontSize": "40px", "color": "#333333"}, "margin": 25}, + "labels": {"style": {"fontSize": "28px", "color": "#333333"}, "format": "${value:.0f}", "x": -15}, + "gridLineWidth": 1, + "gridLineColor": "rgba(0, 0, 0, 0.1)", + "lineWidth": 2, + "lineColor": "#333333", + }, + "legend": { + "enabled": True, + "align": "right", + "verticalAlign": "top", + "layout": "vertical", + "x": -60, + "y": 120, + "itemStyle": {"fontSize": "28px", "color": "#333333"}, + "itemMarginBottom": 15, + "symbolWidth": 40, + "symbolHeight": 18, + }, + "tooltip": { + "shared": True, + "valueDecimals": 2, + "valuePrefix": "$", + "headerFormat": '{point.key:%b %d, %Y}
', + "style": {"fontSize": "22px"}, + }, + "plotOptions": {"line": {"lineWidth": 4, "marker": {"enabled": False}}, "series": {"animation": False}}, + "credits": {"enabled": False}, + "series": [ + {"name": "Close Price", "data": close_data, "color": colors["close"], "lineWidth": 5, "zIndex": 4}, + { + "name": "SMA 20", + "data": sma20_data, + "color": colors["sma20"], + "lineWidth": 3, + "dashStyle": "Solid", + "zIndex": 3, + }, + { + "name": "SMA 50", + "data": sma50_data, + "color": colors["sma50"], + "lineWidth": 3, + "dashStyle": "ShortDash", + "zIndex": 2, + }, + { + "name": "SMA 200", + "data": sma200_data, + "color": colors["sma200"], + "lineWidth": 3, + "dashStyle": "LongDash", + "zIndex": 1, + }, + ], +} + +# Download Highcharts JS +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") + +# Generate chart options JSON +chart_options_json = json.dumps(chart_options) + +# Generate HTML with inline scripts +html_content = f""" + + + + + + +
+ + +""" + +# Write temp HTML file +with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False, encoding="utf-8") as f: + f.write(html_content) + temp_path = f.name + +# Also save the HTML for interactive viewing +with open("plot.html", "w", encoding="utf-8") as f: + f.write(html_content) + +# Take screenshot with headless Chrome +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) # Wait for chart to render +driver.save_screenshot("plot.png") +driver.quit() + +# Clean up temp file +Path(temp_path).unlink() From 5e5c917d46c0b00cccfbd3b10b2d83510fa72f73 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 00:55:05 +0000 Subject: [PATCH 2/3] chore(highcharts): add metadata for indicator-sma --- plots/indicator-sma/metadata/highcharts.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 plots/indicator-sma/metadata/highcharts.yaml diff --git a/plots/indicator-sma/metadata/highcharts.yaml b/plots/indicator-sma/metadata/highcharts.yaml new file mode 100644 index 0000000000..baf329ab64 --- /dev/null +++ b/plots/indicator-sma/metadata/highcharts.yaml @@ -0,0 +1,19 @@ +# Per-library metadata for highcharts implementation of indicator-sma +# Auto-generated by impl-generate.yml + +library: highcharts +specification_id: indicator-sma +created: '2026-01-11T00:55:05Z' +updated: '2026-01-11T00:55:05Z' +generated_by: claude-opus-4-5-20251101 +workflow_run: 20886980688 +issue: 3651 +python_version: 3.13.11 +library_version: unknown +preview_url: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/highcharts/plot.png +preview_thumb: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/highcharts/plot_thumb.png +preview_html: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/highcharts/plot.html +quality_score: null +review: + strengths: [] + weaknesses: [] From 7e380f2f8d69b16255b2f5928ddfd1afac4730de Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 Jan 2026 00:57:47 +0000 Subject: [PATCH 3/3] chore(highcharts): update quality score 91 and review feedback for indicator-sma --- .../implementations/highcharts.py | 6 +- plots/indicator-sma/metadata/highcharts.yaml | 214 +++++++++++++++++- 2 files changed, 210 insertions(+), 10 deletions(-) diff --git a/plots/indicator-sma/implementations/highcharts.py b/plots/indicator-sma/implementations/highcharts.py index 10cda94f30..cf5cfe5df0 100644 --- a/plots/indicator-sma/implementations/highcharts.py +++ b/plots/indicator-sma/implementations/highcharts.py @@ -1,7 +1,7 @@ -"""pyplots.ai +""" pyplots.ai indicator-sma: Simple Moving Average (SMA) Indicator Chart -Library: highcharts | Python 3.13 -Quality: pending | Created: 2026-01-11 +Library: highcharts unknown | Python 3.13.11 +Quality: 91/100 | Created: 2026-01-11 """ import json diff --git a/plots/indicator-sma/metadata/highcharts.yaml b/plots/indicator-sma/metadata/highcharts.yaml index baf329ab64..a4d48adfea 100644 --- a/plots/indicator-sma/metadata/highcharts.yaml +++ b/plots/indicator-sma/metadata/highcharts.yaml @@ -1,10 +1,7 @@ -# Per-library metadata for highcharts implementation of indicator-sma -# Auto-generated by impl-generate.yml - library: highcharts specification_id: indicator-sma created: '2026-01-11T00:55:05Z' -updated: '2026-01-11T00:55:05Z' +updated: '2026-01-11T00:57:47Z' generated_by: claude-opus-4-5-20251101 workflow_run: 20886980688 issue: 3651 @@ -13,7 +10,210 @@ library_version: unknown preview_url: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/highcharts/plot.png preview_thumb: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/highcharts/plot_thumb.png preview_html: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/highcharts/plot.html -quality_score: null +quality_score: 91 review: - strengths: [] - weaknesses: [] + strengths: + - Excellent visual clarity with well-differentiated lines using color and dash styles + - Proper handling of NaN values for initial SMA periods (filtered out correctly) + - Clean, well-structured code with good separation of concerns + - Colorblind-safe palette following project guidelines + - Appropriate use of Highcharts tooltip sharing for multi-series comparison + - Realistic stock data generation with trend and volatility + weaknesses: + - Legend could be better integrated within the plot area rather than floating outside + - Does not use Highcharts Stock module which would provide native financial chart + features like range selector + image_description: |- + The plot displays a stock price chart with Simple Moving Average (SMA) overlays over approximately 365 business days from January 2024 to May 2025. The chart shows: + + - Close Price (blue solid line, #306998) - the main price series showing typical stock price volatility with an overall upward trend from ~$150 to ~$195 + - SMA 20 (yellow solid line, #FFD43B) - short-term moving average closely following the price + - SMA 50 (cyan dashed line, #17BECF) - medium-term moving average showing smoother trend + - SMA 200 (purple long-dashed line, #9467BD) - long-term moving average starting later due to the 200-day calculation window + + The title "indicator-sma · highcharts · pyplots.ai" appears at the top with a subtitle describing the chart. The Y-axis shows "Price (USD)" with dollar values ranging from $126 to $212. The X-axis shows dates formatted as "Month Year". A legend is positioned in the upper right corner identifying all four series. Grid lines are visible but subtle. + criteria_checklist: + visual_quality: + score: 36 + max: 40 + items: + - id: VQ-01 + name: Text Legibility + score: 9 + max: 10 + passed: true + comment: Title, axis labels, and tick marks are all clearly readable. Font + sizes are appropriate for the 4800x2700 canvas, though tick labels could + be slightly larger. + - id: VQ-02 + name: No Overlap + score: 8 + max: 8 + passed: true + comment: No overlapping text elements. X-axis labels are well-spaced, legend + does not overlap data. + - id: VQ-03 + name: Element Visibility + score: 7 + max: 8 + passed: true + comment: Line widths are appropriate. The price line is prominently thicker + (5px) while SMA lines are thinner (3px). Lines are clearly distinguishable. + - id: VQ-04 + name: Color Accessibility + score: 5 + max: 5 + passed: true + comment: Colorblind-safe palette using blue, yellow, cyan, and purple. No + red-green conflicts. + - id: VQ-05 + name: Layout Balance + score: 5 + max: 5 + passed: true + comment: Plot fills the canvas well with balanced margins. Good use of space. + - id: VQ-06 + name: Axis Labels + score: 2 + max: 2 + passed: true + comment: Y-axis has Price (USD) with currency formatting. X-axis shows Date + with proper date formatting. + - id: VQ-07 + name: Grid & Legend + score: 0 + max: 2 + passed: false + comment: Grid is subtle and appropriate. However, the legend is positioned + outside the plot area on the upper right, which is functional but could + be better integrated. + spec_compliance: + score: 25 + max: 25 + items: + - id: SC-01 + name: Plot Type + score: 8 + max: 8 + passed: true + comment: Correct line chart type for time series with SMA overlays + - id: SC-02 + name: Data Mapping + score: 5 + max: 5 + passed: true + comment: X-axis is datetime, Y-axis is price values + - id: SC-03 + name: Required Features + score: 5 + max: 5 + passed: true + comment: 'All required features present: price line, 20/50/200-day SMAs, legend + showing periods' + - id: SC-04 + name: Data Range + score: 3 + max: 3 + passed: true + comment: All data visible, appropriate axis scaling + - id: SC-05 + name: Legend Accuracy + score: 2 + max: 2 + passed: true + comment: Legend correctly labels Close Price, SMA 20, SMA 50, SMA 200 + - id: SC-06 + name: Title Format + score: 2 + max: 2 + passed: true + comment: Title follows format {spec-id} · {library} · pyplots.ai + data_quality: + score: 18 + max: 20 + items: + - id: DQ-01 + name: Feature Coverage + score: 7 + max: 8 + passed: true + comment: 'Shows all aspects: price volatility, trend following by SMAs, SMA + crossovers visible around August/September, different smoothing levels. + Missing explicit golden/death cross demonstration.' + - id: DQ-02 + name: Realistic Context + score: 7 + max: 7 + passed: true + comment: Realistic stock price movement using random walk with trend, plausible + price range and volatility + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 5 + passed: true + comment: Reasonable starting price ($150), realistic daily returns (~0.05% + mean, 1.5% std), appropriate 365-day period + code_quality: + score: 9 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: 'Clean linear structure: imports, data generation, SMA calculation, + chart config, export' + - id: CQ-02 + name: Reproducibility + score: 3 + max: 3 + passed: true + comment: np.random.seed(42) ensures reproducibility + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: All imports are used (json, tempfile, time, urllib, Path, numpy, + pandas, selenium) + - id: CQ-04 + name: No Deprecated API + score: 1 + max: 1 + passed: true + comment: Using current Highcharts API patterns + - id: CQ-05 + name: Output Correct + score: 0 + max: 1 + passed: false + comment: Outputs both plot.png and plot.html, which is correct for Highcharts + library_features: + score: 3 + max: 5 + items: + - id: LF-01 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: Uses Highcharts datetime axis, tooltip formatting with shared tooltip + and value formatting. Uses dashStyle for line differentiation. Could leverage + more Highcharts-specific features like stock chart module or interactive + zooming. + verdict: APPROVED +impl_tags: + dependencies: + - selenium + techniques: + - html-export + - hover-tooltips + patterns: + - data-generation + - time-series + dataprep: + - rolling-window + styling: + - grid-styling