Skip to content

katsu1110/katsustats

Repository files navigation

Katsustats

License Python CI Ruff Sponsor

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, and reports

Preview

Cumulative returns Daily returns
Cumulative returns preview Daily returns preview
Drawdowns Monthly returns
Drawdown preview Monthly returns preview
Yearly returns Rolling Sharpe
Yearly returns preview Rolling Sharpe preview
Rolling volatility Day-of-week returns
Rolling volatility preview Day-of-week preview

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.

Monte Carlo simulation

Enable Monte Carlo analysis by passing monte_carlo=True to reports.html() or reports.full(). The simulation shuffles your historical returns thousands of times to show the range of outcomes that luck alone could have produced — keeping the same return distribution while breaking the original time order.

katsustats.reports.html(
    returns,
    output="report.html",
    monte_carlo=True,
    mc_sims=1000,       # number of shuffled paths (default 1000)
    mc_bust=-0.20,      # optional: probability of hitting this drawdown
    mc_goal=0.50,       # optional: probability of reaching this return
    mc_seed=42,         # optional: reproducibility seed
)

The HTML report adds two panels to the Monte Carlo section:

Simulated paths Max drawdown distribution
Monte Carlo paths Max drawdown distribution
  • Simulated paths — fan of shuffled cumulative return paths with the original path highlighted. Shows how differently the same returns could have played out under alternative orderings.
  • Max drawdown distribution — histogram of the worst drawdown across each simulated path. Because drawdown is path-dependent (a run of losses early hurts far more than the same losses late), this distribution has genuine spread and tells you how lucky or unlucky your drawdown sequence was.

You can also call the underlying stats directly:

# Raw simulation paths as a wide Polars DataFrame
paths = katsustats.stats.monte_carlo_paths(returns, sims=1000, seed=42)

# Probabilistic summary: terminal return, max drawdown, Sharpe, CAGR distributions
summary = katsustats.stats.monte_carlo_summary(
    returns,
    sims=1000,
    bust=-0.20,   # drawdown threshold for bust probability
    goal=0.50,    # return threshold for goal probability
    seed=42,
)

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.

About

Simple and fast backtest module for return series

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages