Publication-Ready Figures
SkillCommerce & financePublication-ready figure conventions for empirical finance and economics. Covers matplotlib styling, color palettes, export settings, and common figure types (time series, decile bars, coefficient plots, event studies). Auto-apply when creating any figure, plot, or visualization.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Publication-Ready Figures skill
What this skill tells your AI
The instructions your AI receives, as published by alexander-m-dickerson/ai-asset-pricing in .claude/skills/publication-figures/SKILL.md and read by ahel’s review.
For this repo, the production plotting toolkit is fintools.figures. Read
docs/ai/figures.md first, then choose the plotting path deliberately:
- Use native
fintools.figureshelpers for repo work, Word proof packs, validation checks, and dataframe-to-figure suites. - Use
style="fins"for the house publication style andstyle="ft"for FT-style output. - Use the legacy skill-local
finance.mplstyle/figutils.pyassets only when the user explicitly wants the older standalone helper style or needs a portable snippet outside the package workflow.
To recreate the FT validation gallery:
python tools/figure_examples.py --style ft --docx --output results/figures
To recreate the house-style gallery:
python tools/figure_examples.py --style fins --docx --output results/figures
Generated PNG/PDF/DOCX/caption files belong under ignored results/figures/
paths. Do not commit maintainer proof packs or local gallery outputs.
Apply these conventions whenever creating figures. The goal: every figure Claude produces is publication-ready by default — no manual cleanup needed.
Legacy Helper Quick Start
Prefer fintools.figures for this repo. The helper assets below remain
available for standalone or explicitly requested legacy publication-style
plots.
Copy finance.mplstyle from this skill directory into the project, then:
import matplotlib.pyplot as plt
plt.style.use('path/to/finance.mplstyle')
Or apply inline (no file needed):
import matplotlib.pyplot as plt
plt.rcParams.update({
'font.family': 'serif',
'font.serif': ['Times New Roman', 'STIXGeneral', 'DejaVu Serif'],
'mathtext.fontset': 'stix',
'font.size': 9,
'axes.labelsize': 9,
'xtick.labelsize': 8,
'ytick.labelsize': 8,
'legend.fontsize': 8,
'axes.linewidth': 0.6,
'axes.spines.top': False,
'axes.spines.right': False,
'lines.linewidth': 1.2,
'xtick.direction': 'out',
'ytick.direction': 'out',
'legend.frameon': False,
'figure.dpi': 150,
'savefig.dpi': 600,
'savefig.format': 'pdf',
'pdf.fonttype': 42,
})
Default Aesthetic
- Fonts: Times New Roman / STIX (serif, no LaTeX dependency)
- Spines: Bottom + left only (no top/right)
- Grid: Off by default
- Ticks: Outward, 8pt labels
- Colors: Okabe-Ito colorblind-safe palette (blue first)
- Export: PDF vector, 600 DPI, fonts embedded (type 42)
Color Palettes
Default cycle (Okabe-Ito, colorblind-safe):
PALETTE = ['#377EB8', '#E41A1C', '#4DAF4A', '#984EA3',
'#FF7F00', '#A65628', '#F781BF', '#999999']
Two-series (long vs short, treatment vs control):
BLUE_RED = ['#377EB8', '#E41A1C']
Grayscale-safe (for guaranteed print clarity):
GRAYSCALE = ['#000000', '#555555', '#999999', '#CCCCCC']
# Combine with linestyles: '-', '--', ':', '-.'
Sequential/diverging colormaps: Use viridis or cividis (colorblind-safe) for heatmaps. Use RdBu_r for diverging (correlation matrices).
Figure Sizing
| Context | Width (inches) | Use |
|---|---|---|
| Single column | 3.5 | Most journal figures |
| 1.5 column | 5.25 | Medium panels |
| Double column / full width | 7.0 | Wide multi-panel figures |
| Slide / presentation | 10.0 | Beamer, PowerPoint |
Aspect ratio: Default to golden ratio (width / 1.618). Use square for heatmaps, wide (width / 2.0) for time series.
def set_size(width='single', ratio='golden'):
widths = {'single': 3.5, 'onehalf': 5.25, 'double': 7.0, 'slide': 10.0}
ratios = {'golden': 1.618, 'square': 1.0, 'wide': 2.0}
w = widths.get(width, width)
r = ratios.get(ratio, ratio)
return (w, w / r)
Common Figure Types in Empirical Finance
Time Series
fig, ax = plt.subplots(figsize=set_size('double', 'wide'))
ax.plot(dates, values)
ax.set_xlabel(''); ax.set_ylabel('Return (%)')
- Use
ax.axhline(0, color='grey', linewidth=0.5, zorder=0)for zero reference - Add NBER recession bands with
ax.axvspan(start, end, alpha=0.1, color='grey')
Cumulative Return / Wealth Paths
cumret = (1 + returns).cumprod()
ax.plot(cumret.index, cumret.values)
ax.set_ylabel('Growth of $1')
- Start at 1.0 (or 100 for percentage scale)
- Log scale optional for long horizons:
ax.set_yscale('log')
Decile Portfolio Bar Chart with Newey-West CIs
# returns_df: DataFrame with columns 0..9 (portfolio return time series)
plot_portfolio_bars(ax, returns_df, show_ls=True, ls_label='10-1')
# Computes means, Newey-West SEs (lag = floor(T^0.25)), 95% CI error bars
# Includes long-short bar with t-stat annotation
- Use
plot_portfolio_barsfor any portfolio sort figure — it handles NW SEs automatically - Short leg (decile 1) colored red, long leg (decile 10) green, L-S bar purple
- t-stat annotated on the L-S bar
For simple bars without CIs (pre-computed means):
plot_decile_bars(ax, means, highlight_extremes=True, spread_label=True)
Coefficient Plot (Forest Plot)
ax.errorbar(coefs, range(len(coefs)), xerr=[coefs-ci_lo, ci_hi-coefs],
fmt='o', color='#377EB8', capsize=3, markersize=4)
ax.axvline(0, color='grey', linewidth=0.5, linestyle='--')
ax.set_yticks(range(len(names))); ax.set_yticklabels(names)
Event Study (CAR Plot)
days = range(event_window[0], event_window[1] + 1)
ax.plot(days, car, color='#377EB8')
ax.fill_between(days, ci_lo, ci_hi, alpha=0.2, color='#377EB8')
ax.axvline(0, color='grey', linewidth=0.5, linestyle='--')
ax.axhline(0, color='grey', linewidth=0.5)
ax.set_xlabel('Days Relative to Event'); ax.set_ylabel('CAR (%)')
Correlation Heatmap
import seaborn as sns
mask = np.triu(np.ones_like(corr, dtype=bool), k=1)
sns.heatmap(corr, mask=mask, cmap='RdBu_r', center=0, vmin=-1, vmax=1,
annot=True, fmt='.2f', linewidths=0.5, ax=ax,
cbar_kws={'shrink': 0.8})
Multi-Panel Figures
fig, axes = plt.subplots(1, 3, figsize=set_size('double', 'wide'))
# Label panels
for i, ax in enumerate(axes):
ax.text(-0.1, 1.05, f'({chr(97+i)})', transform=ax.transAxes,
fontsize=10, fontweight='bold', va='top')
Export Checklist
Before saving any figure:
- Format: PDF (vector) for papers; PNG (300+ DPI) for slides/web
- Font embedding:
pdf.fonttype = 42(already in style) - Bbox:
bbox_inches='tight'to avoid clipped labels - DPI: 600 for publication, 150 for screen preview
- Size: Match target journal column width — don't rescale in LaTeX/Word
fig.savefig('figure.pdf', bbox_inches='tight', dpi=600)
# Also save PNG for quick preview:
fig.savefig('figure.png', bbox_inches='tight', dpi=150)
Journal-Specific Overrides
| Journal | Override |
|---|---|
| RFS | Export as TIF at 300 DPI (photos) or 600 DPI (line art). Fonts: Arial, Courier, Times, Helvetica, Symbol only. |
| AER | No shading, no gridlines, no background color. Vector PDF/EPS preferred. Max 9 columns wide including row headings for tables. |
| JF | Color figures OK online (free). Color in print costs $500/page. Design for grayscale print compatibility. |
| Nature | Sans-serif fonts required (Helvetica/Arial). Override: plt.rcParams['font.family'] = 'sans-serif' |
Do NOT
- Use rainbow colormaps (
jet,hsv) — not perceptually uniform, not colorblind-safe - Use 3D plots unless the data genuinely requires a third dimension
- Add chartjunk: unnecessary gridlines, borders, background colors
- Scale figures in LaTeX/Word — set correct size in matplotlib, include at 100%
- Use different fonts/sizes across figures in the same paper
- Use LaTeX escapes (
\&,\%,\_) in matplotlib text (titles, labels, annotations) — matplotlib's default text engine renders backslashes literally. Write plainS&P 500, notS\&P 500. LaTeX escapes only work whenplt.rcParams['text.usetex'] = True, which requires a full LaTeX installation and is NOT enabled by default in our style.
Signals
- GitHub stars
- 59
- Forks
- 10
- Last commit
- Apr 2026
Advanced
- Catalog kind
- skill
- Gateway key
publication-figures- Source
- github.com/alexander-m-dickerson/ai-asset-pricing