- layout: _PAD_BOTTOM 100→160, _PAD_LEFT 120→160 to prevent label clipping - axes: fix _format_tick float-comparison bug (1.1→"1.1" not "1.10"); add auto-rotation for categorical x-axis labels when they overflow slot width - engine: tick guard (>20 ticks falls back to nice_ticks); stacked_bar falls back to bar instead of crashing; extend _find_date_column keywords - series: bar width uses 25th-percentile gap instead of min to avoid invisible bars - legend: line series use line swatch with dash pattern; proportional square size - export: @font-face injected in HTML <head> for PDF to prevent T3Font in InDesign - prompts: add categorical bar chart few-shot example; ban stacked_bar; improve tick_interval guidance; add bar chart refine examples Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .claude | ||
| app | ||
| .env.example | ||
| .gitignore | ||
| deploy.sh | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.md | ||
| requirements.txt | ||
| test_render.py | ||
| test_render_all.py | ||
PIMCO Chart Generator
Automated publication-quality chart generation matching PIMCO's InDesign visual style. Upload data, describe what you want in plain English, and get a pixel-perfect SVG ready for any platform. Then iterate with natural language — "make lines thicker", "change the title", "remove Japan" — until it's exactly right.
How It Works
PIMCO CHART GENERATOR
=====================
┌─────────────┐ ┌─────────────┐
│ Excel/CSV │ │ Plain-text │
│ Data File │ │ Brief │
└──────┬──────┘ └──────┬──────┘
│ │
▼ │
┌─────────────┐ │
│ Loader │ │
│ (pandas + │ │
│ openpyxl) │ │
└──────┬──────┘ │
│ │
▼ │
┌─────────────┐ │
│ Analyzer │ │
│ Summarize │ │
│ columns, │ │
│ ranges, │ │
│ types │ │
└──────┬──────┘ │
│ │
▼ ▼
┌──────────────────────────────────┐
│ Claude Opus 4.6 │
│ ┌────────────────────────────┐ │
│ │ System Prompt: │ │
│ │ - PIMCO style guide │ │
│ │ - ChartSpec JSON schema │ │
│ │ - Few-shot examples │ │
│ └────────────────────────────┘ │
│ │
│ Input: data summary + brief │
│ Output: structured ChartSpec │
│ (via tool use) │
└───────────────┬──────────────────┘
│
▼
┌──────────────────────────────────┐
│ Spec Validator │
│ - Fuzzy column name matching │
│ - Range validation │
└───────────────┬──────────────────┘
│
▼
┌──────────────────────────────────┐
│ SVG Renderer │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Layout │ │ Scale │ │
│ │ (single/ │ │ (data → │ │
│ │ dual) │ │ pixels) │ │
│ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Axes │ │ Series │ │
│ │ (grid, │ │ (lines, │ │
│ │ ticks) │ │ bars, fill) │ │
│ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Legend │ │ Annotations │ │
│ │ (top- │ │ (ellipses, │ │
│ │ right) │ │ callouts) │ │
│ └──────────┘ └──────────────┘ │
│ ┌─────────────────────────────┐ │
│ │ Typography + Font Embed │ │
│ │ (Roboto base64 in SVG) │ │
│ └─────────────────────────────┘ │
└───────────────┬──────────────────┘
│
▼
┌──────────────┐
│ SVG Output │
│ 2560x1440 │
└──────┬───────┘
│
▼
┌──────────────────────────────────┐
│ Iterative Refinement │
│ │
│ "Make lines thicker" │
│ "Change title to ..." │
│ "Remove the Japan line" │
│ "Add a subtitle" │
│ "Use dashed lines for trend" │
│ │
│ ┌────────┐ ┌──────────────┐ │
│ │Current │───▶│ Claude edits │ │
│ │ Spec │ │ only what │ │
│ │ JSON │◀───│ you asked │ │
│ └────────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ Re-render ──▶ Updated SVG │
└──────────────────────────────────┘
│
┌────────┼────────┐
▼ ▼ ▼
InDesign Web Figma
Illustr. Pages Sketch
Architecture
The AI does NOT generate code or SVG markup. It outputs a structured JSON specification (ChartSpec) which is then rendered deterministically by the Python engine. This means:
- No visual hallucinations — the renderer is pure code, not generative AI
- Fully debuggable — inspect the ChartSpec JSON to see exactly what the AI decided
- Consistent style — every chart follows the same PIMCO style rules regardless of the brief
- Iterative — refine with natural language, each edit builds on the last
Iterative Refinement
After generating a chart, a refinement bar appears below the preview. Type natural language edits and the chart updates in place, keeping full conversation history.
What you can say
| Category | Example edits |
|---|---|
| Lines | "Make lines thicker" / "Use dashed lines for the trend" |
| Colors | "Change the U.S. line to the purple color" / "Swap U.K. and Australia colors" |
| Series | "Remove the Japan line" / "Add the Germany data" |
| Titles | "Change title to 'Global Yields'" / "Add subtitle: Source: Bloomberg" |
| Axes | "Y-axis from 0 to 8%" / "Show years only on X-axis" |
| Annotations | "Add an ellipse around 2023-2024" / "Remove the annotation" |
| Layout | "Make it a dual-panel chart" / "Switch to single panel" |
Each edit sends the current spec + your full conversation history to Claude, which modifies only what you asked and returns the updated chart. You can chain as many edits as you like.
PIMCO Visual Style
| Element | Specification |
|---|---|
| Canvas | 2560 x 1440 px (configurable per chart) |
| Title font | Roboto Condensed, 32px |
| Axis font | Roboto Regular, 22px |
| Legend font | Roboto Regular, 20px |
| Line weight | 3.0px (solid), dashed 14,8, dotted 5,7 |
| Gridlines | Horizontal only, #D4D4D4, 1.0px |
| Background | White (#FFFFFF) |
Color Palette
██ #003D5C Dark teal-blue (primary / U.S.)
██ #5A6B28 Olive (secondary / Australia / trends)
██ #891A6A Purple-magenta (tertiary / U.K.)
██ #1FBFAA Cyan-teal (Germany)
██ #8B7D32 Dark gold (Japan)
Shaded Fills
- Blue (#003D5C at 20% opacity) — data above reference line
- Pink (#891A6A at 20% opacity) — data below reference line
Supported Chart Types
- Multi-line time series — e.g., bond yields across countries
- Annotated line charts — with grey ellipse highlights on regions of interest
- Dual-panel side-by-side — with trend lines, reference levels, and shaded deviation fills
- Bar charts — vertical bars for categorical or time-based data
- Stacked bar charts — multiple categories stacked
Quick Start
1. Setup
cd PIMCO-CHARTS
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
2. Configure API Key
cp .env.example .env
# Edit .env and add your Anthropic API key:
# ANTHROPIC_API_KEY=sk-ant-xxxxx
3. Run
source venv/bin/activate
python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8080 --reload
Open http://localhost:8080 in your browser.
4. Generate a Chart
- Upload an Excel (.xlsx) or CSV file
- Optionally specify a sheet name
- Write a brief describing the chart you want
- Set width/height (default 2560x1440)
- Click Generate Chart
- Preview the SVG inline, then Download SVG
5. Refine
After generating, type edits in the refinement bar below the chart:
- "Make lines thicker"
- "Change title to 'Bond Yields 2020-2025'"
- "Remove the Japan line"
- Each edit re-renders instantly
Example Briefs
Simple line chart:
Create a line chart showing 10-year government bond yields for the US, UK, Australia, Germany, and Japan. Y-axis from -1% to 6%.
Annotated chart:
Show quarterly tech and non-tech investment contribution to GDP growth from 1994 to 2024. Highlight the recent convergence area with an ellipse.
Dual panel with fills:
Create a dual-panel chart. Left panel: PIMCO global industrial production indexed to Oct 2024 = 100, with a dashed two-year pre-election trend and dotted election level. Shade blue above trend, pink below. Right panel: same treatment for world exports to U.S.
Project Structure
PIMCO-CHARTS/
├── app/
│ ├── main.py # FastAPI web app (upload, generate, refine, download)
│ ├── config.py # Settings, API keys, paths
│ ├── models/
│ │ ├── chart_spec.py # ChartSpec Pydantic schema
│ │ └── style.py # PIMCO colors, fonts, layout constants
│ ├── data/
│ │ ├── loader.py # Excel/CSV parsing
│ │ ├── analyzer.py # Data summarization for AI prompt
│ │ └── transformer.py # Date detection, column cleaning
│ ├── ai/
│ │ ├── brief_interpreter.py # Claude Opus 4.6: brief → ChartSpec + refine
│ │ ├── prompts.py # System + refinement prompts, few-shot examples
│ │ └── spec_validator.py # Fuzzy column matching
│ ├── renderer/
│ │ ├── engine.py # Main orchestrator: spec + data → SVG
│ │ ├── layout.py # Single/dual-panel positioning
│ │ ├── scale.py # Data domain → pixel coordinates
│ │ ├── axes.py # Gridlines, tick labels
│ │ ├── series.py # Lines, bars, shaded fills
│ │ ├── legend.py # Horizontal top-right legend
│ │ ├── typography.py # Title, subtitle text
│ │ └── annotations.py # Ellipses, callouts
│ ├── templates/ # HTMX HTML templates
│ └── static/
│ ├── style.css
│ └── fonts/ # Roboto & Roboto Condensed TTFs
├── output/ # Generated SVGs
├── requirements.txt
└── .env # ANTHROPIC_API_KEY (not committed)
Technical Details
Font Embedding
SVGs are self-contained — Roboto and Roboto Condensed fonts are base64-encoded directly into the SVG's <defs><style> block as @font-face declarations. This means the SVG renders correctly in any viewer without requiring the fonts to be installed.
ChartSpec Schema
The ChartSpec is the contract between the AI interpreter and the deterministic renderer:
ChartSpec
layout: "single" | "dual_panel"
panels: [PanelSpec]
title, subtitle
x_axis: AxisSpec (label, suffix, date_format, min/max, tick_interval)
y_axis: AxisSpec
series: [SeriesSpec]
label, data_column, chart_type, color_index, line_style, line_weight
shaded_fill: {reference_series, above_color, below_color}
annotations: [AnnotationSpec]
type: "ellipse" | "callout" | "label", x_range, y_range, text
Shaded Fill Algorithm
For charts that show deviation from a trend line (blue above, pink below), the renderer:
- Walks both polylines point-by-point
- Detects sign changes (crossings) via linear interpolation
- Splits fill paths at intersection points
- Renders separate SVG
<path>elements for above/below regions
Iterative Refinement Architecture
The refinement system works by:
- Keeping an in-memory session with the current ChartSpec, loaded data, and conversation history
- When you type an edit, sending Claude the current spec JSON + full history + your edit
- Claude returns a complete updated ChartSpec (modifying only what was asked)
- The renderer re-renders from the new spec against the same data
- The cycle repeats — chain as many edits as needed
Dependencies
| Package | Purpose |
|---|---|
| anthropic | Claude Opus 4.6 API (brief → spec) |
| drawsvg | Direct SVG element construction |
| pandas | Data manipulation and date parsing |
| openpyxl | Excel file reading |
| fastapi | Web framework |
| uvicorn | ASGI server |
| jinja2 | HTML templating |
| pydantic | Schema validation |
| python-dotenv | Environment variable loading |
| python-multipart | File upload handling |