Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

quant-analytics-toolkit

A vectorized pandas/NumPy library for portfolio risk and performance analytics — returns, risk-adjusted performance metrics, portfolio math, factor regression, and Monte Carlo simulation. Everything is written without explicit Python loops over time steps or assets; operations are expressed as vector/matrix math, which is both the idiomatic way to write this in pandas/NumPy and dramatically faster at realistic data sizes.

Why vectorized, specifically

A for-loop over rows in pandas is almost always a sign of fighting the library rather than using it — and at scale, the difference is not cosmetic. Computing rolling beta across 500 assets with a Python loop re-fitting a regression at every window position is orders of magnitude slower than the pandas rolling().cov()/.var() approach used here. Being able to say why a vectorized approach is faster (single compiled numpy/pandas operation over contiguous memory vs. per-element Python interpreter overhead) is itself a common quant dev interview thread.

Project structure

quant-analytics-toolkit/
├── analytics/
│   ├── returns.py         # simple/log returns, cumulative returns, annualized return
│   ├── risk_metrics.py    # volatility, Sharpe, Sortino, Calmar, max drawdown, VaR, CVaR
│   ├── portfolio.py       # portfolio returns, covariance/correlation, vol, diversification, risk contribution
│   ├── regression.py      # CAPM alpha/beta, rolling beta, multi-factor regression
│   └── monte_carlo.py     # vectorized GBM simulation, simulated VaR
├── tests/                  # 44 tests across all 5 modules
└── examples/
    └── run_analysis.py     # end-to-end demo on synthetic multi-asset data

What's in here

Returns — simple vs. log returns (and why log returns are preferred for statistical work: they're additive across time, simple returns aren't), cumulative growth, annualized (geometric) return.

Risk metrics — annualized volatility, Sharpe ratio, Sortino ratio (penalizes only downside volatility), max drawdown, Calmar ratio, historical VaR, parametric (variance-covariance) VaR, and Conditional VaR/Expected Shortfall. The README-worthy nuance: parametric VaR assumes normality and understates tail risk on real (fat-tailed) return distributions — the code says this explicitly rather than hiding the limitation.

Portfolio — combining asset returns into portfolio returns via matrix multiplication, the sqrt(w^T Cov w) portfolio volatility formula, diversification ratio, and per-asset risk contribution (which can differ a lot from position weight — a concentrated, highly-correlated small position can contribute more risk than its weight suggests).

Regression — CAPM alpha/beta both as a full-sample fit (via scipy.stats.linregress, which gives R², p-value, and standard error for free) and as a rolling series computed via vectorized rolling covariance/variance, plus a general multi-factor OLS regression via numpy.linalg.lstsq.

Monte Carlo — vectorized Geometric Brownian Motion path simulation (the entire n_simulations × n_days matrix generated and evolved in one shot, no loop over days or simulations) and a simulation-based VaR estimate that serves as a cross-check against the analytical VaR figures.

Running the tests

pip install -r requirements.txt
python -m pytest tests/ -v

44 tests, all passing, including statistical sanity checks (e.g. beta recovered from a regression with known true beta lands within tolerance; CVaR is always ≥ VaR; diversification ratio is always ≥ 1).

Running the example

python examples/run_analysis.py

Generates 3 correlated synthetic assets plus a market benchmark, then walks through individual and portfolio risk metrics, CAPM regression against the benchmark, rolling beta, and a Monte Carlo VaR cross-check — all the way from raw prices to a full risk report.

A bug the test suite actually caught

Worth stating directly, since it's the honest version of "I wrote tests": the first version of sharpe_ratio checked if std == 0 to guard against division by zero, and sortino_ratio computed downside deviation as the std of only the negative returns. Both broke in practice — the Sharpe check failed on a constant-return series because summing floating-point values left a tiny nonzero epsilon instead of an exact zero, and Sortino returned NaN whenever there was only one negative return in the sample (sample standard deviation of a single point is 0/0). The tests caught both before this ever reached a demo. Fixes: a < 1e-12 tolerance instead of == 0, and the standard downside-deviation formula (sqrt(mean(min(excess, 0)^2)) over all periods, not just the negative ones).

Known limitations / natural next steps

  • Assumes returns are the unit of analysis throughout; a production system would also need consistent handling of dividends, corporate actions, and multiple currencies.
  • Fixed-weight portfolio returns assume rebalancing every period; no transaction-cost-aware rebalancing logic.
  • Monte Carlo uses plain GBM (no jumps, no stochastic volatility) — a natural extension is a jump-diffusion or Heston-model simulator for more realistic tail behavior.
  • No support yet for a full backtesting loop with these metrics wired in (see the separate quant-backtester project for that piece).

License

MIT

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages