Report Skill

SkillFiles & storage

Use when tasks need complete HTML research reports, HTML dashboards, PNG chart helpers, or files under the research reports directory.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Report Skill skill

What this skill tells your AI

The instructions your AI receives, as published by quantskills/agent-quantspace in skills/report/SKILL.md and read by ahel’s review.

Render research outputs into self-contained HTML using Jinja2 templates and matplotlib charts. Install with uv sync --extra report. Default output uses QUANTSPACE_REPORTS_ROOT when set, otherwise the workspace reports/ path.

Complete research archives are HTML only. Do not write them as Markdown or PDF. Do not use a database. Other skills must not write HTML; they return objects, and the caller fills ResearchReport then calls write_research_bundle.

Two output paths

PathEntryOutputUse for
Complete research archiveResearchReport + write_research_bundlereports/<namespace>/<slug>/index.htmlTakeaway research document, including public examples
Dashboard previewReportRenderer + factor_report / backtest_report / signal_digestone HTML fileQuick look, not an archive

Charts are inlined as base64 data URIs via the png_data_uri Jinja filter.

Complete research archive

from skills.report import (
    ReportFigure,
    ReportTable,
    ResearchReport,
    charts,
    write_research_bundle,
    write_research_catalog,
)

equity_png = charts.plot_backtest_performance(result_df, title="Performance")
report = ResearchReport(
    namespace="lesson_09",
    slug="if_ma10_atr",
    title="IF MA10 + ATR",
    question="Does this rule beat buy-and-hold under the stated costs?",
    universe=["CFFEX.IF99"],
    frequency="1d",
    sample_start="2024-01-01",
    sample_end="2026-07-01",
    in_sample_end=None,
    out_of_sample_start=None,
    hypothesis="Close below MA10 enters; ATR stop only ratchets up.",
    method_notes=["Rule from strategies.time_series.rules."],
    execution={
        "trade_at": "close",
        "signal_lag": 1,
        "commission": 0.0002,
        "slippage_bp": 2.0,
        "return_mode": "forward",
    },
    metrics=execution.metrics,  # from VectorBacktester, do not invent numbers
    metrics_source="BacktestResult.metrics",
    figures=[ReportFigure(name="equity", caption="Equity and drawdown", png=equity_png)],
    tables=[],
    caveats=["Historical result only; not a live trading recommendation."],
    next_steps=["Add cost sensitivity."],
    reproduce_command="uv run python -m strategies.time_series.workflows.run_demo",
    visibility="private",
    domain="time_series",
)
study_dir = write_research_bundle(report)
write_research_catalog()

Directory contract:

reports/<namespace>/<slug>/index.html
reports/<namespace>/<slug>/params.json
reports/catalog.html
reports/catalog.json

index.html is the human-readable nine-section report. params.json is the catalog sidecar. list_research_studies only accepts folders that have both files. CSV-only experiment folders are ignored. write_research_bundle does not write the catalog; call write_research_catalog after a batch.

Required HTML sections: 研究问题, 数据与样本, 假设与方法, 执行约定, 证据与指标, 图表, 对照与稳健性, 限制与下一步, 复现与产物. If there is no comparison or robustness evidence, section 7 still renders 本报告未做.

Hard rules:

  • Fill metrics from BacktestResult, CSV, JSON, or result_df. Never invent Sharpe.
  • metrics_source is required.
  • namespace and slug are safe path segments only.
  • Default visibility="private". Do not git-add private studies.
  • Public examples use namespace="strategy_examples" and visibility="public_example". They still write reports/<namespace>/<slug>/.
  • Do not import strategies/ from this skill.
  • Do not export PDF.

Public examples from scripts/run_strategy_reports use the same archive contract: namespace="strategy_examples", visibility="public_example", kind="public_example". README gallery PNGs are extra sidecar files written by that script, not by write_research_bundle.

Dashboard preview

from skills.report import ReportRenderer, charts

renderer = ReportRenderer()
ranking_png = charts.plot_factor_ranking(ranking_df, value_col="IC_IR")
html = renderer.render(
    "factor_report",
    {
        "title": "Macro universe — weekly factor screen",
        "namespace": "macro_weekly",
        "n": 5,
        "as_of": "2026-05-08",
        "ranking_chart": ranking_png,
        "ranking_html": ranking_df.to_html(),
    },
)
path = renderer.save(html, "macro_weekly_2026-05-08.html")

Pass a template name with or without .html. Relative output paths resolve against reports/; absolute paths are respected as-is.

Available charts

FunctionReturns
plot_equity_curve(returns, title)Cumulative (1+r).cumprod() equity curve
plot_backtest_performance(result_df, title)Backtest equity curve plus drawdown
plot_factor_diagnostics(ic_series, ic_stats, group_returns, turnover, title, rolling_ir_window)IC, rolling IR, layered NAV, and turnover dashboard
plot_ic_heatmap(ic_df, title)RdBu_r symmetric heatmap (rows=factors, cols=namespaces/periods)
plot_rolling_pair_correlation(history, title)Small-multiple histories from Analyze's tidy rolling factor correlations
plot_horizon_ic(summary, factors, segment)Multi-factor Horizon IC term structure
plot_lagged_ic(summary, factors, horizons, segment)Four-panel signal-delay decay curves
plot_rebalance_comparison(comparison, segment, selected_days)Net Sharpe and turnover by rebalance interval
plot_factor_weight_history(factor_weights, method, start)Stacked dynamic factor weights
plot_equity_comparison(equities, start, title)Rebased equity curves for multiple combination methods
plot_factor_ranking(ranking_df, value_col, label_col, title, top_n)Horizontal bar chart of top factors, colored by sign
plot_regime_states(prices, states, title)Price line with colored bands per regime

All helpers return bytes (PNG). The headless Agg backend is pinned at import time so reports render without a display.

Importing skills.report.charts (or calling charts.configure_cjk_matplotlib()) selects a system Han font so Chinese titles in PNG files render on macOS (PingFang / Hiragino Sans GB), Windows (Microsoft YaHei / SimHei from %WINDIR%\\Fonts), and Linux (Noto / Source Han / WenQuanYi). On Windows the file is registered with fontManager.addfont and applied by file path, not by font name alone — that is what makes msyh.ttc work with matplotlib. Custom figures should go through charts.fig_to_png(fig) instead of savefig. If no CJK font is installed (for example an English Windows image without the Chinese language pack), charts still save; glyphs may fall back to boxes.

Windows and paths

  • Matplotlib Chinese: load msyh.ttc / simhei.ttf from %WINDIR%\\Fonts (case-insensitive), register the file, set Microsoft YaHei / 微软雅黑 aliases, and disable axes.unicode_minus.
  • Write HTML/JSON/Markdown as UTF-8 with \n newlines. Catalog reads tolerate a UTF-8 BOM (utf-8-sig).
  • Study directories use pathlib.Path (reports_root / namespace / slug).
  • Catalog href values are POSIX (namespace/slug/index.html) so they work in browsers on Windows.

Template conventions

  • Inline CSS only — reports must render standalone without external assets.
  • Header band uses #4f81bd; table header background #f4f4f4.
  • Body font stack includes PingFang / YaHei / Noto so HTML Chinese is visible on macOS and Windows.
  • Every HTML report includes a relative link back to catalog.html (../../catalog.html from namespace/slug/index.html).
  • Optional chart blocks are wrapped in {% if chart %} … {% endif %}.
  • Safe-render pre-built tables via {{ table.html | safe }}.

Signals

GitHub stars
57
Forks
11
Last commit
Sep 2026

ahel review

  • K1binfo
    installs-packages (in renderer.py)

Automated review, not a security audit. Ruleset v1+k2.

Advanced
Catalog kind
skill
Gateway key
report-quantskills
Source
github.com/quantskills/agent-quantspace