Skip to content

feat(ggplot2): implement indicator-ema#7348

Merged
MarkusNeusinger merged 5 commits into
mainfrom
implementation/indicator-ema/ggplot2
May 19, 2026
Merged

feat(ggplot2): implement indicator-ema#7348
MarkusNeusinger merged 5 commits into
mainfrom
implementation/indicator-ema/ggplot2

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Implementation: indicator-ema - r/ggplot2

Implements the r/ggplot2 version of indicator-ema.

File: plots/indicator-ema/implementations/r/ggplot2.R

Parent Issue: #3652


🤖 impl-generate workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 19, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): The plot displays three lines on a warm off-white (#FAF8F1) background: a green (#009E73) price line (slightly thicker, alpha 0.80), an orange (#D55E00) EMA(12) line, and a blue (#0072B2) EMA(26) line. The X-axis spans Jan–Jul 2024 with bi-monthly date labels; the Y-axis shows dollar values from ~$135 to ~$180 with dollar-sign formatting. The bold title "Tech Stock EMA · indicator-ema · r · ggplot2 · anyplot.ai" sits clearly at the top in dark ink. A legend box (off-white fill) sits at the bottom center labeling all three series. Subtle horizontal grid lines are visible. All text — title, axis labels ("Date", "Closing Price (USD)"), tick labels, and legend text — is clearly readable against the light background. Legibility verdict: PASS.

Dark render (plot-dark.png): The same plot rendered on a warm near-black (#1A1A17) background. Data colors are identical: green price line, orange EMA(12), blue EMA(26) — unchanged from the light render. Title text and axis labels render in near-white (#F0EFE8), and tick/legend labels appear in a lighter gray (#B8B7B0), both clearly readable against the dark surface. The legend box uses the elevated dark surface (#242420) with a subtle border. No dark-on-dark failures observed — all text is legible. Grid lines remain subtly visible on the dark background. Legibility verdict: PASS.

Both paragraphs are required. A review that only describes one render is invalid.

Score: 84/100

Category Score Max
Visual Quality 29 30
Design Excellence 10 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 6 10
Total 84 100

Visual Quality (29/30)

  • VQ-01: Text Legibility (7/8) — Sizes explicitly set (axis.title=20, axis.text=16, legend.text=16), but plot.title=22pt is below the ≥24pt guideline
  • VQ-02: No Overlap (6/6) — No overlapping text elements
  • VQ-03: Element Visibility (6/6) — Price line (lw=1.5) and EMA lines (lw=1.1) clearly visible and well-differentiated
  • VQ-04: Color Accessibility (2/2) — Okabe-Ito palette is CVD-safe; green/orange/blue are distinguishable under deuteranopia and protanopia
  • VQ-05: Layout & Canvas (4/4) — 16×9 at 300 dpi fills canvas well; legend bottom-placement works
  • VQ-06: Axis Labels & Title (2/2) — Y-axis "Closing Price (USD)" includes units; X-axis "Date" is descriptive
  • VQ-07: Palette Compliance (2/2) — Price=#009E73 (position 1), EMA12=#D55E00 (position 2), EMA26=#0072B2 (position 3); backgrounds #FAF8F1/#1A1A17; chrome theme-correct in both renders

Design Excellence (10/20)

  • DE-01: Aesthetic Sophistication (4/8) — Correctly configured with Okabe-Ito palette and good typography, but reads as a well-tuned library default rather than a distinctive design
  • DE-02: Visual Refinement (4/6) — Y-only grid (subtle, lw=0.3), no minor grid, no panel border, theme_minimal removes top/right spines, generous 20pt margins — solid refinement
  • DE-03: Data Storytelling (2/6) — Data is displayed with mild hierarchy (price line thicker/slightly translucent), but no visual emphasis on key events such as trend reversals or EMA crossover points

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — EMA overlay chart with price line and two EMA lines
  • SC-02: Required Features (4/4) — Price line prominent (thicker), two distinct EMA periods (12 and 26), EMA lines thinner than price, legend labels each EMA
  • SC-03: Data Mapping (3/3) — Date on X, closing price (USD) on Y, all 180 data points visible
  • SC-04: Title & Legend (3/3) — "Tech Stock EMA · indicator-ema · r · ggplot2 · anyplot.ai" matches {Descriptive Title} · {spec-id} · {language} · {library} · anyplot.ai; legend labels "Price", "EMA (12)", "EMA (26)" are correct

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Shows both short-term (12-day) and long-term (26-day) EMAs, demonstrates smoothing lag, includes both uptrend and downtrend phases
  • DQ-02: Realistic Context (5/5) — Tech stock starting at $150 with plausible daily drift (0.05%) and volatility (1.8%) — neutral, comprehensible financial scenario
  • DQ-03: Appropriate Scale (4/4) — Price range $135–$180 over 180 trading days is realistic; volatility parameters are factually appropriate for a tech stock

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3) — calc_ema() helper function defined; while the iterative EMA computation makes this pragmatic, it departs from the flat Imports→Data→Plot→Save structure
  • CQ-02: Reproducibility (2/2) — set.seed(42) present
  • CQ-03: Clean Imports (2/2) — ggplot2, scales, ragg all used; no dead imports
  • CQ-04: Code Elegance (2/2) — Compact, readable, no fake functionality
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png via ragg::agg_png with correct dimensions

Library Mastery (6/10)

  • LM-01: Idiomatic Usage (4/5) — Good use of scale_x_date(), dollar_format(), scale_color_manual() with named breaks, and the theme_minimal() + theme() overlay pattern; could be more idiomatic using pivot_longer + a single aesthetic mapping
  • LM-02: Distinctive Features (2/5) — Uses scale_x_date() date formatting and dollar_format() from the scales package (ggplot2-ecosystem features), but the overall chart structure is not deeply ggplot2-distinctive

Score Caps Applied

  • None — no caps triggered (DE-01=4, DE-02=4, so the "both ≤ 2" cap does not apply)

Strengths

  • Perfect spec compliance and data quality: all required EMA features present, realistic financial data, correct axis mapping
  • Solid theme adaptation: both light and dark renders are fully legible with correct Okabe-Ito colors and theme-adaptive chrome
  • Correct palette order: Price=#009E73, EMA12=#D55E00, EMA26=#0072B2 — canonical Okabe-Ito positions 1→3
  • Good visual refinement: Y-only subtle grid, no panel border, proper margins

Weaknesses

  • Plot title size is 22pt — raise to at least 24pt per the style guide (plot.title size = 24)
  • No visual storytelling: spec explicitly mentions "consider highlighting crossover points where short EMA crosses long EMA" — identifying and marking at least one crossover with a point or annotation would raise DE-03
  • Design is clean but generic; adding subtle visual emphasis (e.g., a vertical reference line or shaded region for the trend reversal zone) would elevate DE-01 beyond the "well-configured default" tier
  • calc_ema() function defined at top level; could be inlined or replaced with a one-liner using filter() from the stats package (built-in R) to stay KISS

Issues Found

  1. VQ-01 MINOR: plot.title size is 22pt, below the ≥24pt guideline
    • Fix: Change plot.title = element_text(color = INK, size = 22, ...)size = 24
  2. DE-01/DE-03 LOW: No visual emphasis on crossover signals or trend shifts
    • Fix: Detect and mark EMA crossover points (where ema12 crosses ema26) with geom_point() markers or a vertical geom_vline() — even one or two markers would add meaningful storytelling
  3. CQ-01 MINOR: Helper function calc_ema() breaks KISS flat structure
    • Fix: Replace with the stats::filter() built-in or inline the exponential weighting using Reduce() to avoid a named function

AI Feedback for Next Attempt

Raise plot.title to size=24. Add crossover detection: find indices where (ema12 > ema26) != lag(ema12 > ema26) and mark those dates with small geom_point() markers or geom_vline() in a neutral INK_SOFT color — this directly addresses the spec note about crossover highlighting and raises DE-03 from 2 to 4+. For DE-01, consider adding a subtle shaded region (e.g., geom_ribbon() between price and EMA26 in OKABE_ITO[1] with low alpha) to visually tie the price to its trend. Remove the calc_ema() function definition by using Reduce(function(a, x) x * k + a * (1 - k), prices[-1], accumulate = TRUE) inline or prepend the first value.

Verdict: REJECTED

@github-actions github-actions Bot added quality:84 Quality score 84/100 ai-rejected Quality not OK, triggers update labels May 19, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels May 19, 2026
Attempt 1/3 - fixes based on AI review
@github-actions
Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

Status: Repair completed, re-triggering review...


🤖 impl-repair

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 19, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): The plot is rendered on a warm off-white #FAF8F1 background. Three lines are drawn: Price (teal #009E73, slightly transparent), EMA (12) (orange #D55E00), and EMA (26) (blue #0072B2). Dashed vertical gray lines mark EMA crossover points; small open circles highlight the crossover positions on the EMA (12) line. The y-axis shows dollar-formatted closing prices ($140–$180), the x-axis shows date labels (Feb 2024, Apr 2024, Jun 2024). Title reads "Tech Stock EMA · indicator-ema · r · ggplot2 · anyplot.ai" in bold dark text. A boxed legend sits at the bottom. All text — title, axis labels, tick labels, legend — is clearly readable against the light background.

Dark render (plot-dark.png): The same chart on a warm near-black #1A1A17 background. All three data line colors are identical to the light render (teal, orange, blue — only chrome flips). Title, axis labels, tick labels, and legend text are rendered in light off-white tones, fully readable against the dark background. The dashed crossover verticals and open-circle crossover markers adapt to lighter chrome. Grid lines are subtle but visible. No dark-on-dark failures detected — all text is clearly legible. The legend box uses the elevated background #242420, providing subtle contrast from the main surface.

Both paragraphs are required. A review that only describes one render is invalid.

Score: 92/100

Category Score Max
Visual Quality 30 30
Design Excellence 13 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 10 10
Library Mastery 9 10
Total 92 100

Visual Quality (30/30)

  • VQ-01: Text Legibility (8/8) — Title 24pt, axis labels 20pt, tick labels 16pt, legend 16pt all explicitly set; fully readable in both themes
  • VQ-02: No Overlap (6/6) — No text collisions; lines overlap as expected for EMA overlay charts
  • VQ-03: Element Visibility (6/6) — Price and EMA lines well-sized at linewidth 1.1–1.5; crossover markers visible at size=4
  • VQ-04: Color Accessibility (2/2) — Okabe-Ito palette (teal/orange/blue) is CVD-safe; adequate contrast in both themes
  • VQ-05: Layout & Canvas (4/4) — Plot fills canvas well; generous 20pt margins; legend well-positioned at bottom
  • VQ-06: Axis Labels & Title (2/2) — "Closing Price (USD)" with units, "Date" appropriate for x-axis
  • VQ-07: Palette Compliance (2/2) — First series Price = #009E73 ✓; EMA (12) = #D55E00 (pos 2) ✓; EMA (26) = #0072B2 (pos 3) ✓; backgrounds #FAF8F1/#1A1A17 ✓; data colors identical across themes ✓

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) — Above generic defaults: alpha differentiation (price 0.80 vs EMAs 1.0) creates meaningful visual hierarchy; hollow crossover markers (filled with PAGE_BG) are elegant; intentional design choices elevate beyond defaults, though not quite publication-grade
  • DE-02: Visual Refinement (4/6) — L-shaped frame via axis.line + panel.border = element_blank(); y-only major grid; no minor grid; x-grid replaced by semantic crossover verticals; clean margins
  • DE-03: Data Storytelling (4/6) — Crossover highlights (dashed verticals + circle markers) create clear focal points and guide the viewer to the key trading signal; alpha/linewidth hierarchy keeps EMAs visually prominent over the price line

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct EMA overlay chart; price + two EMA periods displayed
  • SC-02: Required Features (4/4) — Price line prominent; EMA lines overlaid; distinct colors per EMA; EMA lines thinner than price; legend labels per EMA; crossover highlighting implemented
  • SC-03: Data Mapping (3/3) — Date on x, closing price on y; all series visible across 180 trading days
  • SC-04: Title & Legend (3/3) — "Tech Stock EMA · indicator-ema · r · ggplot2 · anyplot.ai" matches {Descriptive} · {spec-id} · {language} · {library} · anyplot.ai; legend labels "Price", "EMA (12)", "EMA (26)" correct

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Shows price volatility, EMA smoothing behavior, crossovers, both uptrend and downtrend phases, 180-day timespan
  • DQ-02: Realistic Context (5/5) — "Tech Stock EMA" is a neutral, real-world trading scenario; uses standard EMA periods (12, 26); plausible price action
  • DQ-03: Appropriate Scale (4/4) — Starting price $150, range ~$135–$180; daily drift/volatility (mean=0.0005, sd=0.018) realistic for tech stocks

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Clean linear flow: imports → tokens → data → EMA calc → crossovers → reshape → plot → save; no functions or classes
  • CQ-02: Reproducibility (2/2) — set.seed(42)
  • CQ-03: Clean Imports (2/2) — ggplot2, scales, tidyr, ragg all actively used; no dead imports
  • CQ-04: Code Elegance (2/2) — Idiomatic Reduce for inline EMA; pivot_longer for wide-to-long; named color vector for scale_color_manual; clean and non-redundant
  • CQ-05: Output & API (1/1) — sprintf("plot-%s.png", THEME)plot-light.png/plot-dark.png ✓; ragg::agg_png device ✓

Library Mastery (9/10)

  • LM-01: Idiomatic Usage (5/5) — Exemplary ggplot2 grammar: long-format data for multi-series mapping, named aesthetic vectors, scale_linewidth_manual/scale_alpha_manual with guide="none", multi-dataset geoms with inherit.aes=FALSE
  • LM-02: Distinctive Features (4/5) — Distinctively ggplot2: grammar-driven multi-aesthetic scaling (color+linewidth+alpha from a single series aesthetic), multi-dataset geom layering for crossover annotations, factor levels for legend ordering — could not be trivially reproduced in another library

Score Caps Applied

  • None — no caps triggered

Strengths

  • Complete theme-adaptive chrome: all text, grid, legend box, and backgrounds correctly flip between light and dark; zero dark-on-dark failures
  • Idiomatic ggplot2 long-format pattern enables clean multi-series aesthetic mapping in a single geom_line() call
  • Crossover detection and visualization (dashed verticals + hollow circle markers) is technically correct and adds real chart value
  • Alpha + linewidth differentiation creates meaningful visual hierarchy between the price line and EMA indicators
  • Correct EMA formula using Reduce with exponential smoothing factor k = 2/(n+1)

Weaknesses

  • DE-01 could be pushed toward 6–7 with more typography polish (e.g., bold EMA legend items, subtitle with EMA description) or a focal annotation at the most recent/significant crossover
  • DE-02 minor: the vertical dashed crossover lines add some visual noise when crossovers are frequent; could reduce alpha or limit to the most significant crossover events

Issues Found

  1. DE-01 MODERATE: Design is above defaults but could reach near-publication quality with one more refinement — e.g., a subtitle noting the trend signal or a callout annotation at the peak crossover

AI Feedback for Next Attempt

Implementation is high quality and approved. If regenerated: consider adding a subtitle (e.g., "12-day vs 26-day EMA crossover signals") and reducing crossover line count to only show the most significant crossovers to reduce noise and improve storytelling (DE-03). These are refinements, not blockers.

Verdict: APPROVED

@github-actions github-actions Bot added quality:92 Quality score 92/100 ai-approved Quality OK, ready for merge and removed quality:84 Quality score 84/100 labels May 19, 2026
@MarkusNeusinger MarkusNeusinger merged commit ae53344 into main May 19, 2026
6 checks passed
@MarkusNeusinger MarkusNeusinger deleted the implementation/indicator-ema/ggplot2 branch May 19, 2026 05:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-approved Quality OK, ready for merge ai-attempt-1 First repair attempt quality:92 Quality score 92/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant