A modernized backtest report module powered by Polars
Project description
Katsustats
katsustats is a Polars-powered analytics and reporting library for daily return series, inspired by quantstats.
Pass a DataFrame with date and returns, and get summary metrics, drawdown analysis, key metrics with visualizations, and a self-contained HTML report.
Highlights:
- Polars-first API with pandas input support
- Benchmark-aware performance comparison
- Self-contained offline HTML reports
- AI-friendly structured JSON reports
- Readable Markdown summaries for humans and agents
- Functional modules for
stats,plots, andreports
Preview
| Cumulative returns | Daily returns |
|---|---|
| Drawdowns | Monthly returns |
|---|---|
| Yearly returns | Rolling Sharpe |
|---|---|
| Rolling volatility | Day-of-week returns |
|---|---|
Those figures are also available in an HTML report generated by katsustats.reports.html().
How to use
Installation
As a Python library:
pip install katsustats
# or
uv add katsustats
As a standalone CLI (no script needed — just install and run):
pipx install katsustats # recommended for CLI-only use
# or
uv tool install katsustats
Standalone binary (no Python needed at all):
Download a pre-built binary for your platform from the GitHub Releases page, make it executable, and run it directly:
# macOS / Linux
chmod +x katsustats-linux-x86_64
./katsustats-linux-x86_64 report trades.csv -o report.html
Try it online
Data format
katsustats accepts either a Polars or pandas DataFrame
with two required columns:
| column | type | description |
|---|---|---|
date |
date-like | Trading date |
returns |
float-like | Daily return (e.g. 0.01 = +1%) |
When a pandas DataFrame or Series is passed, katsustats converts it to
Polars at the start of processing.
If date is datetime-like, it is normalized to pl.Date before analysis.
If multiple rows share the same date, katsustats compounds those same-day
returns values into one daily return, emits a warning, and continues.
Quantstats-style inputs (pd.Series with a DatetimeIndex, or a
pd.DataFrame with a DatetimeIndex and a returns column) are
accepted automatically — the index is promoted to the date column.
Basic usage
import polars as pl
import katsustats
# Build your return series
returns = pl.DataFrame({
"date": pl.date_range(pl.date(2020, 1, 1), pl.date(2023, 12, 31), "1d", eager=True),
"returns": your_daily_returns, # list / numpy array of floats
})
# Generate the full report (prints metrics + shows all plots)
results = katsustats.reports.full(returns)
Pandas inputs work too:
import pandas as pd
returns = pd.DataFrame({
"date": dates,
"returns": your_daily_returns,
})
results = katsustats.reports.full(returns)
Migrating from quantstats
If your existing code passes a pd.Series or a pd.DataFrame with a
DatetimeIndex (the quantstats convention), both work without modification:
import pandas as pd
# pd.Series with DatetimeIndex
returns = pd.Series(your_daily_returns, index=date_index, name="returns")
results = katsustats.reports.full(returns)
# pd.DataFrame with DatetimeIndex
returns = pd.DataFrame({"returns": your_daily_returns}, index=date_index)
results = katsustats.reports.full(returns)
See also the runnable examples in examples/quickstart.py, examples/with_benchmark.py, and examples/html_report.py.
results is a dict with the following keys:
| key | type | description |
|---|---|---|
summary |
dict[str, float] |
Raw numeric summary values |
metrics |
pl.DataFrame |
Summary metrics table |
drawdowns |
pl.DataFrame |
Top-5 drawdown periods |
dow_stats |
pl.DataFrame |
Day-of-week statistics |
figures |
dict[str, Figure] |
All 8 matplotlib figures |
With a benchmark
benchmark = pl.DataFrame({
"date": pl.date_range(pl.date(2020, 1, 1), pl.date(2023, 12, 31), "1d", eager=True),
"returns": benchmark_daily_returns,
})
results = katsustats.reports.full(returns, benchmark=benchmark)
When a benchmark is provided, the metrics table also includes Alpha, Beta, Correlation, Information Ratio, and Excess Return.
Advanced options
results = katsustats.reports.full(
returns,
benchmark=benchmark,
rf=0.04, # annualized risk-free rate (default 0.0)
periods=252, # trading days per year (default 252)
show=False, # suppress inline plot display
)
CLI
Generate an HTML tearsheet directly from a CSV or Parquet file — no script needed:
# From a CSV file (date and returns columns)
katsustats report trades.csv -o report.html
# Structured JSON for AI agents / downstream tooling
katsustats report trades.csv --format json -o report.json
# Markdown summary for humans and agents
katsustats report trades.csv --format markdown -o report.md
# Custom column names
katsustats report trades.csv --date-col day --returns-col pnl -o report.html
# With a benchmark and a custom title
katsustats report trades.csv --benchmark benchmark.csv --title "My Strategy" -o report.html
# From a Parquet file with a custom risk-free rate
katsustats report trades.parquet --rf 0.04 -o report.html
If -o is omitted the report is written alongside the input file (for example trades.html, trades.json, or trades.md).
HTML report
Generate a self-contained HTML report (similar to qs.reports.html()):
# Save to file
katsustats.reports.html(returns, benchmark=benchmark, title="My Strategy", output="report.html")
# Or get HTML string
html_str = katsustats.reports.html(returns, title="My Strategy")
The report includes headline metric cards, performance tables, period performance, drawdown analysis, day-of-week statistics, and all 8 charts embedded as images — all in a single .html file that works offline.
When a benchmark is provided, the HTML report also includes regime analysis.
View a BTC vs ETH backtest report.
JSON report
Generate an AI-friendly structured JSON report (see example):
# Save to file
katsustats.reports.json(returns, benchmark=benchmark, title="My Strategy", output="report.json")
# Or get JSON string
json_str = katsustats.reports.json(returns, title="My Strategy")
The JSON output is optimized for LLMs, agents, and other automation tools. It includes raw numeric metrics, structured period performance, top drawdowns, day-of-week statistics, and regime analysis when a benchmark is provided.
Markdown report
Generate a Markdown backtest summary (see example):
# Save to file
katsustats.reports.markdown(returns, benchmark=benchmark, title="My Strategy", output="report.md")
# Or get Markdown string
md_str = katsustats.reports.markdown(returns, title="My Strategy")
The Markdown output is designed to be readable in editors, GitHub, chat tools, and agent workflows. It includes an overview, headline metrics, performance tables, period performance, top drawdowns, day-of-week statistics, and optional regime analysis.
Using individual modules
You can also call the lower-level APIs directly:
import katsustats
# --- Stats ---
katsustats.stats.total_return(returns)
katsustats.stats.cagr(returns)
katsustats.stats.sharpe(returns, rf=0.0)
katsustats.stats.sortino(returns)
katsustats.stats.max_drawdown(returns)
katsustats.stats.calmar(returns)
katsustats.stats.volatility(returns)
katsustats.stats.win_rate(returns)
katsustats.stats.profit_factor(returns)
katsustats.stats.value_at_risk(returns, alpha=0.05)
katsustats.stats.drawdown_details(returns, top_n=5) # pl.DataFrame
katsustats.stats.day_of_week_stats(returns) # pl.DataFrame
katsustats.stats.summary_metrics(returns, benchmark) # pl.DataFrame
# --- Plots ---
katsustats.plots.plot_cumulative_returns(returns, benchmark)
katsustats.plots.plot_drawdown(returns)
katsustats.plots.plot_monthly_heatmap(returns)
katsustats.plots.plot_yearly_returns(returns, benchmark)
katsustats.plots.plot_return_distribution(returns, benchmark)
katsustats.plots.plot_rolling_sharpe(returns, benchmark)
katsustats.plots.plot_rolling_volatility(returns, benchmark)
katsustats.plots.plot_dow_returns(returns)
Metrics produced
| metric | description |
|---|---|
| Total Return | Compounded return over the full period |
| CAGR | Compound Annual Growth Rate |
| Sharpe Ratio | Annualized risk-adjusted return |
| Sortino Ratio | Sharpe using only downside deviation |
| Max Drawdown | Largest peak-to-trough decline |
| Calmar Ratio | CAGR / |Max Drawdown| |
| Volatility (ann.) | Annualized standard deviation |
| Win Rate | % of days with positive returns |
| Profit Factor | Gross profit / gross loss |
| Best / Worst Day | Largest single-day gain / loss |
| Avg Win / Avg Loss | Mean return on winning / losing days |
| Daily VaR (95%) | 5th-percentile daily return |
| CVaR (95%) | Mean return in the worst 5% tail |
| Recovery Factor | Total return / |Max Drawdown| |
| Skewness / Kurtosis | Distribution shape statistics |
| Best / Worst Month | Largest / smallest monthly return |
| Best / Worst Year | Largest / smallest yearly return |
| Positive Months / Years | Share of profitable months / years |
When a benchmark is provided, katsustats also reports Alpha, Beta, Correlation, Information Ratio, and Excess Return.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file katsustats-0.6.0.tar.gz.
File metadata
- Download URL: katsustats-0.6.0.tar.gz
- Upload date:
- Size: 851.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
431f9a4441756311d02542136b00c721f2ef38533a0e849909b848ad5f0d5ec7
|
|
| MD5 |
f247e8a4b0ab01240711efd412e513eb
|
|
| BLAKE2b-256 |
935e8ee822eeca99567bb9efa6ea69388318bb90222400b414fa6af6028767c1
|
Provenance
The following attestation bundles were made for katsustats-0.6.0.tar.gz:
Publisher:
publish.yml on katsu1110/katsustats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
katsustats-0.6.0.tar.gz -
Subject digest:
431f9a4441756311d02542136b00c721f2ef38533a0e849909b848ad5f0d5ec7 - Sigstore transparency entry: 1457029971
- Sigstore integration time:
-
Permalink:
katsu1110/katsustats@649a5fecae73543d454ec4a723d209394fdae623 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/katsu1110
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@649a5fecae73543d454ec4a723d209394fdae623 -
Trigger Event:
release
-
Statement type:
File details
Details for the file katsustats-0.6.0-py3-none-any.whl.
File metadata
- Download URL: katsustats-0.6.0-py3-none-any.whl
- Upload date:
- Size: 39.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1222d6ef7a8c9d9eadaf6f5e876daefc92e83660f96a426c8eab8c70ea8b7eae
|
|
| MD5 |
abcee7264779c0aefefcaba0ebc7a60c
|
|
| BLAKE2b-256 |
634b0f5a3ccc0bc6a24a787e365c8d993386a86e7beb20ce69468b2eb048603c
|
Provenance
The following attestation bundles were made for katsustats-0.6.0-py3-none-any.whl:
Publisher:
publish.yml on katsu1110/katsustats
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
katsustats-0.6.0-py3-none-any.whl -
Subject digest:
1222d6ef7a8c9d9eadaf6f5e876daefc92e83660f96a426c8eab8c70ea8b7eae - Sigstore transparency entry: 1457030068
- Sigstore integration time:
-
Permalink:
katsu1110/katsustats@649a5fecae73543d454ec4a723d209394fdae623 -
Branch / Tag:
refs/tags/v0.6.0 - Owner: https://github.com/katsu1110
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@649a5fecae73543d454ec4a723d209394fdae623 -
Trigger Event:
release
-
Statement type: