Report Skill
SkillFiles & storageUse 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.
No other account needed.
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
| Path | Entry | Output | Use for |
|---|---|---|---|
| Complete research archive | ResearchReport + write_research_bundle | reports/<namespace>/<slug>/index.html | Takeaway research document, including public examples |
| Dashboard preview | ReportRenderer + factor_report / backtest_report / signal_digest | one HTML file | Quick 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
metricsfromBacktestResult, CSV, JSON, orresult_df. Never invent Sharpe. metrics_sourceis required.namespaceandslugare safe path segments only.- Default
visibility="private". Do not git-add private studies. - Public examples use
namespace="strategy_examples"andvisibility="public_example". They still writereports/<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
| Function | Returns |
|---|---|
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.ttffrom%WINDIR%\\Fonts(case-insensitive), register the file, setMicrosoft YaHei/微软雅黑aliases, and disableaxes.unicode_minus. - Write HTML/JSON/Markdown as UTF-8 with
\nnewlines. Catalog reads tolerate a UTF-8 BOM (utf-8-sig). - Study directories use
pathlib.Path(reports_root / namespace / slug). - Catalog
hrefvalues 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.htmlfromnamespace/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