# finance calcs
Standard financial calculations
[](https://github.com/prettygoodcapital/finance-calcs/actions/workflows/build.yaml)
[](https://codecov.io/gh/prettygoodcapital/finance-calcs)
[](https://github.com/prettygoodcapital/finance-calcs)
[](https://pypi.python.org/pypi/finance-calcs)
## Overview
`finance-calcs` provides financial calculations as composable Polars
expressions. It is designed for lazy execution, namespace-style ergonomics, and
direct interoperability with the rest of the `finance-*` stack.
The public API follows a few rules:
- every expression metric accepts and returns `pl.Expr`
- metrics are exposed once, with optional `window=` and `period=` controls
rather than separate rolling, monthly, and annual variants
- functions are also available through the `.fcalcs` namespace on both
`pl.Expr` and `pl.Series`
- examples use synthetic but realistic fixtures from `finance-datagen`
## Implemented coverage
| Returns and periods | `period_bucket`, `simple_returns`, `log_returns`, `cum_returns`, `cum_returns_final`, `returns`, `aggregate_returns`, `annualized_return`, `annualized_volatility` |
| Risk and drawdown | `volatility`, `sharpe`, `sortino`, `calmar`, `downside_deviation`, `downside_risk`, `drawdown_series`, `underwater_series`, `max_drawdown`, `value_at_risk`, `conditional_value_at_risk`, `parametric_var` |
| Technical indicators | Moving averages, Bollinger/Donchian channels, momentum oscillators, range volatility, and volume indicators |
| Alpha and quantiles | Forward returns, conditional/horizon IC, IC decay, IC summaries, quantile assignment, signal normalization, quantile returns, turnover, and long/short spreads |
| Factor and benchmark metrics | Alpha, beta, up/down capture, batting average, tracking error, and information ratio |
| Distribution and tail risk | Higher moments, Sharpe significance helpers, tail ratio, ulcer index, omega ratio, GPD VaR, and GPD CVaR |
| Portfolio and post-trade | Exposure, concentration, active share, transaction costs/volume/attribution, slippage, turnover, round trips, MAE/MFE, and trade-quality metrics |
See the [Examples](docs/src/EXAMPLES.md) page for workflows with generated data
and the [API](docs/src/API.md) page for a complete grouped reference for every
public function.
## Quick start
Generate a deterministic daily equity path with `finance-datagen`, then compute
return and risk metrics as Polars expressions.
```python
import polars as pl
from finance_datagen import generate_prices
import finance_calcs as fc
prices = generate_prices(symbol="ACME", seed=7)
out = prices.with_columns(
pl.col("price").fcalcs.simple_returns().alias("ret"),
).select(
fc.returns(pl.col("ret")).alias("total_return"),
pl.col("ret").fcalcs.annualized_return().alias("ann_return"),
pl.col("ret").fcalcs.volatility().alias("ann_vol"),
pl.col("ret").fcalcs.sharpe().alias("sharpe"),
pl.col("ret").fcalcs.max_drawdown().alias("max_drawdown"),
)
```
Use `finance-datagen.ohlc_from_close` when calculations need OHLCV bars:
```python
from finance_datagen import ohlc_from_close
bars = ohlc_from_close(prices["price"], symbol="ACME", seed=7)
features = bars.with_columns(
pl.col("close").fcalcs.sma(20).alias("sma_20"),
pl.col("close").fcalcs.rsi(14).alias("rsi_14"),
fc.atr(pl.col("high"), pl.col("low"), pl.col("close")).alias("atr_14"),
fc.obv(pl.col("close"), pl.col("volume")).alias("obv"),
)
```
## Period and frequency slices
Use `period=` for calendar-style slices and keep `window=` for rolling row-count
windows. A `period` can be a `finance_enums.Frequency`, any alias accepted by
`finance_enums.to_frequency()`, any Polars `dt.truncate()` duration string, or a
precomputed bucket expression.
```python
import polars as pl
from finance_enums import Frequency
monthly = prices.with_columns(
pl.col("price").fcalcs.simple_returns().alias("ret"),
).with_columns(
fc.period_bucket(pl.col("timestamp"), Frequency.Month).alias("month"),
pl.col("ret").fcalcs.returns(period="month", date=pl.col("timestamp")).alias("month_return"),
pl.col("ret").fcalcs.sharpe(period="1q", date=pl.col("timestamp")).alias("quarter_sharpe"),
)
```
For fiscal periods, strategy regimes, or exchange-calendar grids built upstream,
pass the bucket expression directly:
```python
bucketed = prices.with_columns(
pl.col("price").fcalcs.simple_returns().alias("ret"),
pl.col("timestamp").dt.year().alias("fiscal_year"),
).with_columns(
fc.returns(pl.col("ret"), period=pl.col("fiscal_year")).alias("fiscal_return"),
)
```
## Stack integration
`finance-calcs` is intended to pair with:
- `finance-datagen` for synthetic fixtures and test inputs
- `finance-dates` for calendar-aware date handling upstream
- `finance-enums` for shared enum-backed trading semantics upstream
That keeps calculations focused on typed expressions instead of schema cleanup,
string parsing, or calendar repair.