jquantstats¶
Portfolio analytics for quants — built on Polars, powered by Plotly.
Quick Links: 📚 Repository • 📦 PyPI • 🐛 Issues • 💬 Discussions
📋 Overview¶
jQuantStats is a Python library for portfolio analytics that helps quants and portfolio managers understand their strategy performance in depth. It provides two complementary entry points: a Portfolio route that works directly from price and position data, and a Data route for arbitrary return streams. All analytics, visualisations, and HTML reports are available from either entry point.
🚀 Installation¶
Python 3.11+ is required. Add optional extras as needed:
pip install jquantstats[plot] # static chart export (kaleido)
pip install jquantstats[web] # FastAPI web server
💻 Quick Start¶
Use Portfolio when you have prices and positions.
This is the recommended entry point — it exposes the full analytics suite.
import polars as pl
from jquantstats import Portfolio
pf = Portfolio.from_cash_position(
prices=prices_df, # pl.DataFrame: date + asset columns
cash_position=pos_df, # pl.DataFrame: date + asset columns (£ amounts)
aum=1_000_000,
)
pf.stats.sharpe() # {"AAPL": 1.34, "MSFT": 0.91, "portfolio": 1.21}
pf.plots.snapshot() # interactive NAV + drawdown dashboard
pf.report.to_html() # full HTML tearsheet
Use Data when you only have a return series (or prices without positions).
import polars as pl
from jquantstats import Data
data = Data.from_returns(
returns=returns_df, # pl.DataFrame: date + return columns
benchmark="Benchmark", # optional column name
rf=0.0,
)
data.stats.sharpe()
data.stats.sortino()
data.stats.annual_breakdown()
data.plots.monthly_heatmap()
data.reports.to_html()
🔬 Portfolio Route — Why It Matters¶
Working from positions rather than returns unlocks analysis that is impossible from a return series alone:
pf.lag(n)— execution-delay analysis: simulate T+1, T+2 fills and see the Sharpe impactpf.tilt/pf.timing— decompose performance into allocation skill vs. timing skillpf.turnover/pf.turnover_summary()— daily and weekly turnover analyticspf.trading_cost_impact(max_bps=20)— sweep transaction-cost sensitivity in one call
pf_t0 = pf # ideal T+0 execution
pf_t1 = pf.lag(1) # signal fires today, fills tomorrow
print(pf_t0.stats.sharpe()) # {"portfolio": 1.34}
print(pf_t1.stats.sharpe()) # {"portfolio": 1.28}
fig = pf.plots.lead_lag_ir_plot(start=-5, end=10)
See Getting Started for a complete walkthrough.
📊 jQuantStats vs QuantStats¶
| Feature | QuantStats | jquantstats |
|---|---|---|
| DataFrame engine | pandas | Polars |
| Charts | matplotlib (static) | Plotly (interactive) |
| Multi-asset in one call | no | yes |
| Portfolio route | no | yes |
| Execution-delay analysis | no | pf.lag(n) |
| Tilt / timing decomp | no | yes |
| Turnover analytics | no | yes |
| Cost modelling | no | yes |
| Type annotations | partial | full |
See the Migration Guide for a complete API mapping.
📄 Next Steps¶
-
Step-by-step guide for both entry points.
-
Model transaction costs and sweep sensitivity.
-
Complete API mapping and behavioural differences.
-
Full method signatures and docstrings.