Metrics¶
All single-series metrics (sharpe, sortino, calmar, omega, max_drawdown, ulcer_index, value_at_risk, cvar) take a returns Series and a small number of keyword arguments (periods_per_year, risk_free, alpha, target) with sensible defaults for daily data. The batch_* variants accept a dict of named series or a wide DataFrame and dispatch to the Rust-accelerated kernel when available — see Rust kernels. returns_stats / batch_summary produce the same Series / DataFrame shape used by the tear sheet, so custom reporting code can share the formatting layer.
fundcloud.metrics
¶
Portfolio analytics.
Free functions on returns. Every function accepts a pd.Series (single
strategy) or a pd.DataFrame (panel of strategies as columns) and
returns the same shape: a scalar stays a scalar for Series input, a
Series indexed by column name for DataFrame input.
Organised into six concerns:
- :mod:
fundcloud.metrics.core— scalar metrics independent of a benchmark (return, risk, risk-adjusted, higher moments). - :mod:
fundcloud.metrics.benchmark— benchmark-relative metrics (alpha, beta, capture ratios, Treynor, information ratio). - :mod:
fundcloud.metrics.periods— calendar-period aggregates (monthly / yearly tables, best/worst, positive/negative counts). - :mod:
fundcloud.metrics.rolling— rolling-window metric series (Sharpe / Sortino / volatility / beta / drawdown). - :mod:
fundcloud.metrics.summary— :func:metricsone-shot bundle and :func:drawdown_detailsepisode table. - :mod:
fundcloud.metrics.batch— GIL-released batch variants over large panels via the Rust kernels.
sharpe
¶
sharpe(
returns: Series | DataFrame,
*,
risk_free: float | None = None,
periods_per_year: int | None = None,
) -> float | pd.Series
Annualised Sharpe ratio.
Uses the sample standard deviation (ddof=1). Returns are
assumed to be simple per-period returns; for log returns the
formula is the same numerator and denominator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
risk_free
|
float | None
|
Annualised risk-free rate to subtract from returns. |
None
|
periods_per_year
|
int | None
|
Annualisation factor. |
None
|
Returns:
| Type | Description |
|---|---|
float or Series
|
Scalar when |
See Also
smart_sharpe : Sharpe scaled by an autocorrelation penalty. sortino : Downside-only analogue. probabilistic_sharpe_ratio : Probability the true Sharpe exceeds a target.
Examples:
>>> import pandas as pd, numpy as np
>>> rng = np.random.default_rng(0)
>>> r = pd.Series(rng.normal(0.0005, 0.01, 252))
>>> round(sharpe(r, periods_per_year=252), 2)
0.7
>>> # Works on a DataFrame too — returns a Series indexed by column:
>>> panel = pd.DataFrame({"a": r, "b": -r})
>>> isinstance(sharpe(panel), pd.Series)
True
Source code in python/fundcloud/metrics/core.py
sortino
¶
sortino(
returns: Series | DataFrame,
*,
target: float = 0.0,
periods_per_year: int | None = None,
) -> float | pd.Series
Annualised Sortino ratio.
Like Sharpe but penalises only the downside: the denominator is
the root-mean-square of negative deviations from target,
divided by the full sample count (ddof=0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
target
|
float
|
Minimum acceptable return per period. Returns at or above
|
0.0
|
periods_per_year
|
int | None
|
Annualisation factor. |
None
|
Returns:
| Type | Description |
|---|---|
float or Series
|
Scalar for a Series input, Series for a DataFrame input. |
See Also
sharpe : Symmetric counterpart.
adjusted_sortino : Sortino / sqrt(2) for scale-comparison with Sharpe.
smart_sortino : Sortino scaled by Lo's autocorrelation penalty.
Source code in python/fundcloud/metrics/core.py
calmar
¶
Calmar ratio: annualised return divided by |max_drawdown|.
A drawdown-aware return-on-risk score. Often more informative than Sharpe in volatile markets because it cares about the worst realised loss rather than full-distribution variance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
periods_per_year
|
int | None
|
Annualisation factor for the CAGR numerator. |
None
|
Returns:
| Type | Description |
|---|---|
float or Series
|
Scalar for a Series input, Series for a DataFrame input.
|
See Also
sharpe : Volatility-based risk-adjusted return.
pain_ratio : Like Calmar but uses pain_index instead of max drawdown.
Source code in python/fundcloud/metrics/core.py
omega
¶
Omega ratio at target threshold.
Ratio of cumulative gains above target to cumulative losses
below it: sum(max(r - target, 0)) / sum(max(target - r, 0)).
Captures the full distribution rather than just its first two
moments — values above 1 mean the upside outweighs the downside.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
target
|
float
|
Threshold separating gains from losses. Default |
0.0
|
Returns:
| Type | Description |
|---|---|
float or Series
|
Non-negative. Scalar for a Series input, Series for a
DataFrame input. |
See Also
profit_factor : Same shape with target=0, dollar-weighted.
sortino : Mean / downside-vol form of the same intuition.
Source code in python/fundcloud/metrics/core.py
drawdown_series
¶
Drawdown at each timestamp: wealth / running_max - 1.
Always ≤ 0. The running maximum is taken on cumulative wealth
((1 + r).cumprod()), so the series tracks how far below the
most recent peak the strategy is on each bar.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
Returns:
| Type | Description |
|---|---|
Series or DataFrame
|
Same shape as |
See Also
max_drawdown : Scalar minimum of this series. ulcer_index : RMS of drawdown magnitudes. pain_index : Mean of drawdown magnitudes.
Source code in python/fundcloud/metrics/core.py
max_drawdown
¶
Largest peak-to-trough loss over the sample (negative number).
Equivalent to drawdown_series(returns).min().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
Returns:
| Type | Description |
|---|---|
float or Series
|
Negative number in |
See Also
drawdown_series : Per-bar drawdown timeseries.
calmar : CAGR / |max_drawdown|.
Source code in python/fundcloud/metrics/core.py
ulcer_index
¶
Ulcer Index: RMS of drawdowns, in percent.
Captures both the depth and duration of drawdowns — a fast brief drawdown scores lower than a shallow but persistent one. Scale is percent (multiplied by 100) to match Peter Martin's original convention.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
Returns:
| Type | Description |
|---|---|
float or Series
|
Non-negative. Scalar for a Series input, Series for a DataFrame input. |
See Also
pain_index : Mean of drawdown magnitudes (linear, not RMS). ulcer_performance_index : Martin ratio — return per unit of ulcer.
Source code in python/fundcloud/metrics/core.py
cvar
¶
Conditional Value-at-Risk (Expected Shortfall) at confidence alpha.
Average return in the left tail — i.e. the mean of all returns at
or below the (1 - alpha) quantile. More conservative than
:func:value_at_risk: where VaR asks "how bad is the tail
threshold?", CVaR asks "how bad is the average outcome inside
the tail?".
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
alpha
|
float
|
Confidence level in |
0.95
|
Returns:
| Type | Description |
|---|---|
float or Series
|
Negative number. Scalar for a Series input, Series for a
DataFrame input. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
See Also
value_at_risk : Tail-quantile threshold (less conservative).
Source code in python/fundcloud/metrics/core.py
value_at_risk
¶
Historical Value-at-Risk at confidence alpha.
The empirical (1 - alpha) quantile of the returns
distribution. Sign convention: a loss is a negative number,
so VaR is typically negative. Higher alpha means deeper into
the tail.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
alpha
|
float
|
Confidence level in |
0.95
|
Returns:
| Type | Description |
|---|---|
float or Series
|
Negative number (or zero for non-negative-return samples). Scalar for a Series input, Series for a DataFrame input. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
See Also
cvar : Mean of returns below this quantile (Expected Shortfall).
Source code in python/fundcloud/metrics/core.py
returns_stats
¶
returns_stats(
returns: Series | DataFrame,
*,
risk_free: float | None = None,
periods_per_year: int | None = None,
cvar_alpha: float = 0.95,
) -> pd.DataFrame
Bundle of the common metrics into a single, scannable summary table.
Rows are metrics (periods, total_return, cagr,
ann_volatility, sharpe, sortino, calmar,
max_drawdown, ulcer_index, cvar, omega); columns
are strategies. Backs the :meth:fundcloud.accessors.DataFrameAccessor.summary
accessor and :meth:Portfolio.summary.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
returns
|
Series | DataFrame
|
Per-period returns. |
required |
risk_free
|
float | None
|
Annualised risk-free rate; forwarded to :func: |
None
|
periods_per_year
|
int | None
|
Annualisation factor; forwarded to the annualised metrics.
|
None
|
cvar_alpha
|
float
|
Confidence level for the CVaR row. Default |
0.95
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
Metric-by-strategy table; always a DataFrame, even for a
single-strategy |
See Also
fundcloud.metrics.metrics : Full ~55-row analogue with optional benchmark columns.
Source code in python/fundcloud/metrics/core.py
batch_sharpe
¶
batch_sharpe(
strategies: Mapping[str, Series | DataFrame],
*,
risk_free: float | None = None,
periods_per_year: int | None = None,
) -> pd.Series
Source code in python/fundcloud/metrics/batch.py
batch_sortino
¶
batch_sortino(
strategies: Mapping[str, Series | DataFrame],
*,
target: float = 0.0,
periods_per_year: int | None = None,
) -> pd.Series
Source code in python/fundcloud/metrics/batch.py
batch_max_drawdown
¶
batch_cvar
¶
Source code in python/fundcloud/metrics/batch.py
batch_summary
¶
batch_summary(
strategies: Mapping[str, Series | DataFrame],
*,
risk_free: float | None = None,
periods_per_year: int | None = None,
cvar_alpha: float = 0.95,
) -> pd.DataFrame
One row per strategy, standard metrics as columns.