Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions plots/indicator-sma/implementations/plotnine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
""" pyplots.ai
indicator-sma: Simple Moving Average (SMA) Indicator Chart
Library: plotnine 0.15.2 | Python 3.13.11
Quality: 91/100 | Created: 2026-01-11
"""

import numpy as np
import pandas as pd
from plotnine import (
aes,
element_line,
element_text,
geom_line,
ggplot,
labs,
scale_color_manual,
scale_x_datetime,
theme,
theme_minimal,
)


# Data - Generate realistic stock price data with trend and volatility
np.random.seed(42)
n_days = 300
dates = pd.date_range("2024-01-01", periods=n_days, freq="B") # Business days

# Create a price series with trends and mean reversion
base_price = 150
returns = np.random.normal(0.0003, 0.015, n_days) # Daily returns
# Add some trending behavior
trend = np.sin(np.linspace(0, 3 * np.pi, n_days)) * 0.001
returns = returns + trend
close = base_price * np.cumprod(1 + returns)

# Calculate SMAs
df = pd.DataFrame({"date": dates, "close": close})
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()

# Reshape data for plotnine (long format for multiple lines with legend)
df_long = pd.melt(
df, id_vars=["date"], value_vars=["close", "sma_20", "sma_50", "sma_200"], var_name="series", value_name="price"
)

# Rename series for legend
series_labels = {"close": "Price", "sma_20": "SMA 20", "sma_50": "SMA 50", "sma_200": "SMA 200"}
df_long["series"] = df_long["series"].map(series_labels)

# Set order for legend
series_order = ["Price", "SMA 20", "SMA 50", "SMA 200"]
df_long["series"] = pd.Categorical(df_long["series"], categories=series_order, ordered=True)

# Define colors matching the other implementations
colors = {"Price": "#306998", "SMA 20": "#FFD43B", "SMA 50": "#E74C3C", "SMA 200": "#2ECC71"}

# Plot
plot = (
ggplot(df_long, aes(x="date", y="price", color="series"))
+ geom_line(size=1.5, alpha=0.9)
+ scale_color_manual(values=colors)
+ scale_x_datetime(date_breaks="2 months", date_labels="%b %Y")
+ labs(x="Date", y="Price ($)", title="indicator-sma · plotnine · pyplots.ai", color="")
+ theme_minimal()
+ theme(
figure_size=(16, 9),
plot_title=element_text(size=24, weight="bold"),
axis_title=element_text(size=20),
axis_text=element_text(size=16),
axis_text_x=element_text(rotation=30, ha="right"),
legend_text=element_text(size=16),
legend_position=(0.02, 0.98),
legend_direction="vertical",
panel_grid_major_y=element_line(color="#cccccc", size=0.5, alpha=0.3),
panel_grid_major_x=element_line(alpha=0),
panel_grid_minor=element_line(alpha=0),
)
)

plot.save("plot.png", dpi=300)
218 changes: 218 additions & 0 deletions plots/indicator-sma/metadata/plotnine.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
library: plotnine
specification_id: indicator-sma
created: '2026-01-11T00:53:45Z'
updated: '2026-01-11T00:56:27Z'
generated_by: claude-opus-4-5-20251101
workflow_run: 20886981190
issue: 3651
python_version: 3.13.11
library_version: 0.15.2
preview_url: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/plotnine/plot.png
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/indicator-sma/plotnine/plot_thumb.png
preview_html: null
quality_score: 91
review:
strengths:
- Excellent use of plotnine grammar of graphics with proper long-format data transformation
- Clean color scheme that distinguishes all four series effectively
- Proper title format following the spec-id · library · pyplots.ai convention
- Good handling of date axis with appropriate rotation to prevent overlap
- Realistic synthetic stock price data with visible trend behavior
- All three SMA periods (20, 50, 200) are correctly calculated and displayed
weaknesses:
- Legend could benefit from a background/box for better readability against data
- The SMA 200 line only appears in the final third of the chart due to the 200-day
window requirement
image_description: 'The plot displays a Simple Moving Average (SMA) indicator chart
with a dark blue price line showing daily stock prices over approximately one
year (Jan 2024 - Feb 2025). Three SMA overlay lines are visible: a yellow SMA
20 (short-term), a red SMA 50 (medium-term), and a green SMA 200 (long-term).
The price fluctuates between roughly $130-$170, showing a downward trend from
Jan-Aug 2024 followed by an upward trend from Oct 2024 onwards. The legend is
positioned in the upper left corner with entries for Price, SMA 20, SMA 50, and
SMA 200. The title follows the correct format "indicator-sma · plotnine · pyplots.ai"
displayed in bold at the top. X-axis shows dates in "Mon YYYY" format with labels
rotated 30 degrees to prevent overlap. Y-axis shows "Price ($)" with values ranging
from 130 to 170. Grid lines are subtle, appearing only on the y-axis with low
opacity. The minimal theme provides a clean background. All four lines are clearly
distinguishable with consistent 1.5 line width and 0.9 alpha transparency.'
criteria_checklist:
visual_quality:
score: 36
max: 40
items:
- id: VQ-01
name: Text Legibility
score: 10
max: 10
passed: true
comment: Title is bold (24pt), axis labels (20pt) and tick labels (16pt) are
clearly readable at full resolution
- id: VQ-02
name: No Overlap
score: 8
max: 8
passed: true
comment: No overlapping text elements, date labels rotated 30 degrees to prevent
overlap
- id: VQ-03
name: Element Visibility
score: 7
max: 8
passed: true
comment: Line width of 1.5 and alpha of 0.9 provide good visibility, all four
series clearly distinguishable
- id: VQ-04
name: Color Accessibility
score: 5
max: 5
passed: true
comment: Blue, yellow, red, green color scheme is colorblind-friendly and
provides good contrast
- id: VQ-05
name: Layout Balance
score: 4
max: 5
passed: true
comment: Good canvas utilization, legend well positioned in upper-left, minimal
wasted space
- id: VQ-06
name: Axis Labels
score: 2
max: 2
passed: true
comment: Y-axis has units 'Price ($)', X-axis labeled 'Date'
- id: VQ-07
name: Grid & Legend
score: 0
max: 2
passed: false
comment: Grid is appropriately subtle but legend lacks background frame for
readability against data
spec_compliance:
score: 25
max: 25
items:
- id: SC-01
name: Plot Type
score: 8
max: 8
passed: true
comment: Correct line chart with price and three SMA overlays as specified
- id: SC-02
name: Data Mapping
score: 5
max: 5
passed: true
comment: Date on X-axis, price values on Y-axis, all correctly mapped
- 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
with periods'
- id: SC-04
name: Data Range
score: 3
max: 3
passed: true
comment: All data visible, axes accommodate full range from ~130 to ~170
- id: SC-05
name: Legend Accuracy
score: 2
max: 2
passed: true
comment: Legend correctly shows 'Price', 'SMA 20', 'SMA 50', 'SMA 200'
- id: SC-06
name: Title Format
score: 2
max: 2
passed: true
comment: Correct format 'indicator-sma · plotnine · pyplots.ai'
data_quality:
score: 18
max: 20
items:
- id: DQ-01
name: Feature Coverage
score: 7
max: 8
passed: true
comment: Shows uptrend and downtrend, SMAs crossing, different lag behavior
of each SMA period
- id: DQ-02
name: Realistic Context
score: 7
max: 7
passed: true
comment: Realistic stock price scenario with plausible daily volatility and
price range (~$130-$170)
- id: DQ-03
name: Appropriate Scale
score: 4
max: 5
passed: true
comment: 300 business days is appropriate for showing 200-day SMA; price range
realistic
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
→ reshape → plot → save'
- id: CQ-02
name: Reproducibility
score: 3
max: 3
passed: true
comment: np.random.seed(42) set at the beginning for reproducible data
- id: CQ-03
name: Clean Imports
score: 2
max: 2
passed: true
comment: 'All imports are used: numpy, pandas, and plotnine components'
- id: CQ-04
name: No Deprecated API
score: 1
max: 1
passed: true
comment: Uses current plotnine API
- id: CQ-05
name: Output Correct
score: 0
max: 1
passed: false
comment: Saves as plot.png correctly but minor formatting
library_features:
score: 3
max: 5
items:
- id: LF-01
name: Distinctive Features
score: 3
max: 5
passed: true
comment: Uses plotnine's ggplot grammar, theme_minimal, scale_color_manual,
element_text, and proper long-format data transformation with pd.melt which
is idiomatic for plotnine
verdict: APPROVED
impl_tags:
dependencies: []
techniques:
- layer-composition
patterns:
- data-generation
- wide-to-long
dataprep:
- rolling-window
- time-series
styling:
- alpha-blending
- grid-styling