Appendix A: 27 Standard Metrics (21 Core + 6 Generic Aliases)
All functions are in simudyne.engine.metrics. Each takes a NumPy array and returns a Python float.
Central Tendency & Dispersion
| # | Function | Signature | Description |
|---|---|---|---|
| 1 | mean | (values: ndarray) -> float | Arithmetic mean: $\bar{x} = \frac{1}{n}\sum x_i$ |
| 2 | variance | (values: ndarray) -> float | Sample variance (Bessel-corrected, ddof=1): $s^2 = \frac{1}{n-1}\sum(x_i - \bar{x})^2$ |
| 3 | std | (values: ndarray) -> float | Sample standard deviation (ddof=1): $s = \sqrt{s^2}$ |
| 4 | coefficient_of_variation | (values: ndarray) -> float | Dimensionless relative variability: $CV = s / \lvert\bar{x}\rvert$. Returns 0 if mean is zero. |
Serial Dependence & Memory
| # | Function | Signature | Description |
|---|---|---|---|
| 5 | autocorrelation | (values: ndarray, lag: int = 1) -> float | Autocorrelation at lag $k$: $\rho(k) = C(k)/C(0)$ where $C(k)$ is autocovariance. Returns 0 if series too short or zero variance. |
| 6 | hurst_exponent | (values: ndarray, max_lag: int = 20) -> float | R/S (rescaled range) analysis. $H > 0.5$: trending (persistent). $H < 0.5$: mean-reverting. $H \approx 0.5$: random walk. Returns 0.5 if series < 20 values. |
| 7 | ljung_box_statistic | (values: ndarray, max_lag: int = 20) -> float | Ljung-Box portmanteau test statistic: $Q = n(n+2)\sum_{k=1}^{h}\frac{\rho_k^2}{n-k}$. High values indicate significant serial correlation. |
| 8 | cross_correlation | (x: ndarray, y: ndarray, lag: int = 0) -> float | Cross-correlation between two series at given lag: $\rho_{xy}(k) = \text{Cov}(x_t, y_{t+k}) / (\sigma_x \sigma_y)$. Returns 0 if either has zero variance. |
| 9 | sample_entropy | (values: ndarray, m: int = 2, r_fraction: float = 0.2) -> float | Sample entropy: $-\ln(A/B)$ where $A$ and $B$ are template match counts at embedding dimensions $m+1$ and $m$. Tolerance $r = r_fraction \times \text{std}$. Lower values indicate more regularity. |
Distribution Shape
| # | Function | Signature | Description |
|---|---|---|---|
| 10 | kurtosis | (values: ndarray) -> float | Excess kurtosis (Fisher, 0 for normal): $\kappa = E[((x-\mu)/\sigma)^4] - 3$. Positive = heavy tails (leptokurtic). Needs $\geq 4$ values. |
| 11 | skewness | (values: ndarray) -> float | Sample skewness: $\gamma_1 = E[((x-\mu)/\sigma)^3]$. Positive = right tail, negative = left tail. Needs $\geq 3$ values. |
| 12 | hill_tail_index | (values: ndarray, quantile: float = 0.05) -> float | Hill estimator of power-law tail exponent: $\alpha = k / \sum \ln(x_i/x_{\min})$ where $k$ is the number of order statistics above the quantile threshold. Typical financial returns: $\alpha \in [2, 5]$. |
| 13 | entropy | (values: ndarray, n_bins: int = 50) -> float | Shannon entropy of the histogram: $H = -\sum p_i \log_2 p_i$. Higher values indicate more uniform (less predictable) distributions. |
| 14 | gini_coefficient | (values: ndarray) -> float | Gini coefficient from Lorenz curve: 0 = perfect equality, 1 = maximum inequality. Computed as $G = \frac{2\sum i \cdot x_{(i)}}{n \sum x_{(i)}} - \frac{n+1}{n}$ on sorted values. |
| 15 | tail_ratio | (values: ndarray, percentile: float = 5.0) -> float | Ratio of upper to lower tail: $\lvert P_{100-p}\rvert / \lvert P_p\rvert$. Values > 1 indicate heavier upper tail; < 1 indicates heavier lower tail. |
Risk Metrics
| # | Function | Signature | Description |
|---|---|---|---|
| 16 | max_drawdown | (values: ndarray) -> float | Maximum peak-to-trough decline as a fraction in $[0, 1]$. Tracks running peak and reports largest relative decline. Needs $\geq 2$ values. |
| 17 | realized_volatility | (values: ndarray, window: int = 20, annualize_factor: float = 1.0) -> float | Rolling standard deviation over the trailing window values, scaled by $\sqrt{\text{annualize_factor}}$. Uses the final window of the series. |
| 18 | value_at_risk | (values: ndarray, alpha: float = 0.05) -> float | Value at Risk: the $\alpha$-quantile of the distribution. At $\alpha=0.05$, this is the value below which 5% of observations fall. |
| 19 | conditional_value_at_risk | (values: ndarray, alpha: float = 0.05) -> float | Expected Shortfall (CVaR): $E[X \mid X \leq \text{VaR}]$. The mean of all values below the VaR threshold. More sensitive to tail shape than VaR. |
Risk-Adjusted Performance
| # | Function | Signature | Description |
|---|---|---|---|
| 20 | sharpe_ratio | (returns: ndarray, risk_free_rate: float = 0.0, annualize_factor: float = 1.0) -> float | Sharpe ratio: $SR = \frac{\bar{r} - r_f}{\sigma_r} \times \sqrt{F}$ where $F$ is the annualisation factor (e.g., 252 for daily returns). Returns 0 if std is zero. |
| 21 | sortino_ratio | (returns: ndarray, risk_free_rate: float = 0.0, annualize_factor: float = 1.0) -> float | Sortino ratio: $\frac{\bar{r} - r_f}{\sigma_{\text{down}}} \times \sqrt{F}$ where $\sigma_{\text{down}}$ is the standard deviation of negative excess returns only. Penalises downside risk but not upside. |
Generic Aliases (6)
These are domain-neutral aliases that delegate to the core metrics above with appropriate defaults. They provide more intuitive names for common use cases without introducing new computation.
| # | Alias | Delegates To | Description |
|---|---|---|---|
| 22 | peak_to_trough | max_drawdown(values) | Identical to max_drawdown. Provides a domain-neutral name for the maximum peak-to-trough decline metric. |
| 23 | rolling_std | realized_volatility(values, window, annualize_factor=1.0) | Identical to realized_volatility with annualize_factor=1.0. Provides a generic name when the series is not financial returns. |
| 24 | lower_quantile | value_at_risk(values, alpha) | Identical to value_at_risk. Provides a domain-neutral name for the lower $\alpha$-quantile (not specific to financial risk). |
| 25 | lower_tail_mean | conditional_value_at_risk(values, alpha) | Identical to conditional_value_at_risk. Provides a domain-neutral name for the mean of values below the lower quantile. |
| 26 | risk_adjusted_mean | sharpe_ratio(returns, risk_free_rate, annualize_factor) | Identical to sharpe_ratio. Provides a generic name for the mean-to-standard-deviation ratio of any series. |
| 27 | downside_adjusted_mean | sortino_ratio(returns, risk_free_rate, annualize_factor) | Identical to sortino_ratio. Provides a generic name for the mean-to-downside-deviation ratio. |
These aliases exist so that non-financial models (epidemiology, supply chain, ecology) can reference metrics by semantically appropriate names without implying a financial context. For example, a supply chain model can use peak_to_trough to measure maximum inventory decline without calling it a "drawdown", and an epidemiology model can use lower_tail_mean to measure expected tail severity without referencing "CVaR".
Usage with MCRunner
Metrics are referenced by name in the mc_spec:
\{
"seeds": 100,
"steps": 200,
"burn_in": 10,
"metrics_to_compute": [
\{"name": "kurtosis", "field": "price"},
\{"name": "hurst_exponent", "field": "price", "params": \{"max_lag": 50}},
\{"name": "sharpe_ratio", "field": "returns", "params": \{"annualize_factor": 252}},
\{"name": "value_at_risk", "field": "returns", "params": \{"alpha": 0.01}}
]
}
The interpret_metrics_spec() function in simudyne.engine.metrics converts these declarations into callable functions that are applied to the specified trace fields after each MC seed completes. Results are aggregated into MCResult.aggregate_metrics as per-metric dictionaries with keys mean, std, min, max, median, and values (the full array across seeds).