Skip to content

Quantitative Finance API Reference

Return Analysis

financelib.quant.returns

Return calculation and analysis functions.

Provides simple, logarithmic, cumulative, and annualized return calculations used in quantitative finance.

Functions

annualized_return(returns: pd.Series, periods_per_year: int = 252) -> float

Calculate the annualized return.

Uses geometric mean for compounding.

Parameters:

Name Type Description Default
returns Series

Simple returns series.

required
periods_per_year int

Trading periods per year (252 for daily, 52 for weekly).

252

Returns:

Type Description
float

Annualized return as a decimal.

Source code in financelib/quant/returns.py
def annualized_return(returns: pd.Series, periods_per_year: int = 252) -> float:
    """Calculate the annualized return.

    Uses geometric mean for compounding.

    Args:
        returns: Simple returns series.
        periods_per_year: Trading periods per year (252 for daily, 52 for weekly).

    Returns:
        Annualized return as a decimal.
    """
    n = len(returns)
    if n == 0:
        return 0.0
    total_return = (1 + returns).prod()
    return float(total_return ** (periods_per_year / n) - 1)

annualized_volatility(returns: pd.Series, periods_per_year: int = 252) -> float

Calculate annualized volatility (standard deviation of returns).

sigma_annual = sigma_daily * sqrt(periods_per_year)

Parameters:

Name Type Description Default
returns Series

Returns series.

required
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
float

Annualized volatility as a decimal.

Source code in financelib/quant/returns.py
def annualized_volatility(returns: pd.Series, periods_per_year: int = 252) -> float:
    """Calculate annualized volatility (standard deviation of returns).

    sigma_annual = sigma_daily * sqrt(periods_per_year)

    Args:
        returns: Returns series.
        periods_per_year: Trading periods per year.

    Returns:
        Annualized volatility as a decimal.
    """
    return float(returns.std() * np.sqrt(periods_per_year))

cumulative_returns(returns: pd.Series) -> pd.Series

Calculate cumulative returns from a returns series.

Parameters:

Name Type Description Default
returns Series

Simple returns series.

required

Returns:

Type Description
Series

Cumulative returns series (starting from 0).

Source code in financelib/quant/returns.py
def cumulative_returns(returns: pd.Series) -> pd.Series:
    """Calculate cumulative returns from a returns series.

    Args:
        returns: Simple returns series.

    Returns:
        Cumulative returns series (starting from 0).
    """
    return (1 + returns).cumprod() - 1

log_returns(prices: pd.Series) -> pd.Series

Calculate logarithmic (continuously compounded) returns.

r_t = ln(P_t / P_{t-1})

Log returns are additive across time, making them preferable for multi-period analysis.

Parameters:

Name Type Description Default
prices Series

Price series.

required

Returns:

Type Description
Series

Log returns series.

Source code in financelib/quant/returns.py
def log_returns(prices: pd.Series) -> pd.Series:
    """Calculate logarithmic (continuously compounded) returns.

    r_t = ln(P_t / P_{t-1})

    Log returns are additive across time, making them preferable
    for multi-period analysis.

    Args:
        prices: Price series.

    Returns:
        Log returns series.
    """
    return np.log(prices / prices.shift(1)).dropna()

rolling_beta(asset_returns: pd.Series, market_returns: pd.Series, window: int = 63) -> pd.Series

Calculate rolling beta against a market benchmark.

Parameters:

Name Type Description Default
asset_returns Series

Asset returns series.

required
market_returns Series

Market/benchmark returns series.

required
window int

Rolling window size.

63

Returns:

Type Description
Series

Rolling beta series.

Source code in financelib/quant/returns.py
def rolling_beta(
    asset_returns: pd.Series,
    market_returns: pd.Series,
    window: int = 63,
) -> pd.Series:
    """Calculate rolling beta against a market benchmark.

    Args:
        asset_returns: Asset returns series.
        market_returns: Market/benchmark returns series.
        window: Rolling window size.

    Returns:
        Rolling beta series.
    """
    cov = asset_returns.rolling(window=window).cov(market_returns)
    var = market_returns.rolling(window=window).var()
    return cov / var

rolling_sharpe(returns: pd.Series, window: int = 63, risk_free_rate: float = 0.0, periods_per_year: int = 252) -> pd.Series

Calculate rolling Sharpe ratio.

Parameters:

Name Type Description Default
returns Series

Returns series.

required
window int

Rolling window size (default: 63 = ~3 months).

63
risk_free_rate float

Annualized risk-free rate.

0.0
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
Series

Rolling Sharpe ratio series.

Source code in financelib/quant/returns.py
def rolling_sharpe(
    returns: pd.Series,
    window: int = 63,
    risk_free_rate: float = 0.0,
    periods_per_year: int = 252,
) -> pd.Series:
    """Calculate rolling Sharpe ratio.

    Args:
        returns: Returns series.
        window: Rolling window size (default: 63 = ~3 months).
        risk_free_rate: Annualized risk-free rate.
        periods_per_year: Trading periods per year.

    Returns:
        Rolling Sharpe ratio series.
    """
    rf_per_period = risk_free_rate / periods_per_year
    excess = returns - rf_per_period
    rolling_mean = excess.rolling(window=window).mean()
    rolling_std = returns.rolling(window=window).std()
    return (rolling_mean / rolling_std) * np.sqrt(periods_per_year)

rolling_volatility(returns: pd.Series, window: int = 21, periods_per_year: int = 252) -> pd.Series

Calculate rolling annualized volatility.

Parameters:

Name Type Description Default
returns Series

Returns series.

required
window int

Rolling window size.

21
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
Series

Rolling annualized volatility series.

Source code in financelib/quant/returns.py
def rolling_volatility(
    returns: pd.Series, window: int = 21, periods_per_year: int = 252
) -> pd.Series:
    """Calculate rolling annualized volatility.

    Args:
        returns: Returns series.
        window: Rolling window size.
        periods_per_year: Trading periods per year.

    Returns:
        Rolling annualized volatility series.
    """
    return returns.rolling(window=window).std() * np.sqrt(periods_per_year)

simple_returns(prices: pd.Series) -> pd.Series

Calculate simple (arithmetic) returns.

R_t = (P_t - P_{t-1}) / P_{t-1}

Parameters:

Name Type Description Default
prices Series

Price series.

required

Returns:

Type Description
Series

Simple returns series.

Source code in financelib/quant/returns.py
def simple_returns(prices: pd.Series) -> pd.Series:
    """Calculate simple (arithmetic) returns.

    R_t = (P_t - P_{t-1}) / P_{t-1}

    Args:
        prices: Price series.

    Returns:
        Simple returns series.
    """
    return prices.pct_change().dropna()

Risk Metrics

financelib.quant.risk_metrics

Risk metrics for quantitative finance.

Implements industry-standard risk-adjusted return measures and risk quantification tools used in portfolio management and performance attribution.

Functions

alpha(asset_returns: pd.Series, market_returns: pd.Series, risk_free_rate: float = 0.0, periods_per_year: int = 252) -> float

Calculate Jensen's alpha.

alpha = R_a - [R_f + beta * (R_m - R_f)]

Parameters:

Name Type Description Default
asset_returns Series

Asset returns series.

required
market_returns Series

Market/benchmark returns series.

required
risk_free_rate float

Annualized risk-free rate.

0.0
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
float

Annualized alpha.

Source code in financelib/quant/risk_metrics.py
def alpha(
    asset_returns: pd.Series,
    market_returns: pd.Series,
    risk_free_rate: float = 0.0,
    periods_per_year: int = 252,
) -> float:
    """Calculate Jensen's alpha.

    alpha = R_a - [R_f + beta * (R_m - R_f)]

    Args:
        asset_returns: Asset returns series.
        market_returns: Market/benchmark returns series.
        risk_free_rate: Annualized risk-free rate.
        periods_per_year: Trading periods per year.

    Returns:
        Annualized alpha.
    """
    from .returns import annualized_return
    b = beta(asset_returns, market_returns)
    ann_asset = annualized_return(asset_returns, periods_per_year)
    ann_market = annualized_return(market_returns, periods_per_year)
    return float(ann_asset - (risk_free_rate + b * (ann_market - risk_free_rate)))

beta(asset_returns: pd.Series, market_returns: pd.Series) -> float

Calculate beta (systematic risk) relative to a market benchmark.

beta = Cov(R_a, R_m) / Var(R_m)

Parameters:

Name Type Description Default
asset_returns Series

Asset returns series.

required
market_returns Series

Market/benchmark returns series.

required

Returns:

Type Description
float

Beta coefficient.

Source code in financelib/quant/risk_metrics.py
def beta(
    asset_returns: pd.Series,
    market_returns: pd.Series,
) -> float:
    """Calculate beta (systematic risk) relative to a market benchmark.

    beta = Cov(R_a, R_m) / Var(R_m)

    Args:
        asset_returns: Asset returns series.
        market_returns: Market/benchmark returns series.

    Returns:
        Beta coefficient.
    """
    aligned = pd.concat([asset_returns, market_returns], axis=1).dropna()
    if len(aligned) < 2:
        return 0.0
    cov_matrix = aligned.cov()
    market_var = aligned.iloc[:, 1].var()
    if market_var == 0:
        return 0.0
    return float(cov_matrix.iloc[0, 1] / market_var)

calmar_ratio(returns: pd.Series, prices: pd.Series, periods_per_year: int = 252) -> float

Calculate the Calmar ratio.

Calmar = Annualized Return / |Max Drawdown|

Parameters:

Name Type Description Default
returns Series

Returns series.

required
prices Series

Price series.

required
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
float

Calmar ratio.

Source code in financelib/quant/risk_metrics.py
def calmar_ratio(
    returns: pd.Series,
    prices: pd.Series,
    periods_per_year: int = 252,
) -> float:
    """Calculate the Calmar ratio.

    Calmar = Annualized Return / |Max Drawdown|

    Args:
        returns: Returns series.
        prices: Price series.
        periods_per_year: Trading periods per year.

    Returns:
        Calmar ratio.
    """
    from .returns import annualized_return
    ann_ret = annualized_return(returns, periods_per_year)
    mdd = abs(max_drawdown(prices))
    if mdd == 0:
        return 0.0
    return float(ann_ret / mdd)

conditional_var(returns: pd.Series, confidence: float = 0.95) -> float

Calculate Conditional Value at Risk (CVaR / Expected Shortfall).

The expected loss given that the loss exceeds VaR. More conservative than VaR.

Parameters:

Name Type Description Default
returns Series

Returns series.

required
confidence float

Confidence level.

0.95

Returns:

Type Description
float

CVaR as a negative decimal.

Source code in financelib/quant/risk_metrics.py
def conditional_var(
    returns: pd.Series,
    confidence: float = 0.95,
) -> float:
    """Calculate Conditional Value at Risk (CVaR / Expected Shortfall).

    The expected loss given that the loss exceeds VaR.
    More conservative than VaR.

    Args:
        returns: Returns series.
        confidence: Confidence level.

    Returns:
        CVaR as a negative decimal.
    """
    var = value_at_risk(returns, confidence)
    tail_losses = returns[returns <= var]
    if len(tail_losses) == 0:
        return var
    return float(tail_losses.mean())

information_ratio(asset_returns: pd.Series, benchmark_returns: pd.Series, periods_per_year: int = 252) -> float

Calculate the Information Ratio.

IR = (R_a - R_b) / TE, where TE is tracking error.

Parameters:

Name Type Description Default
asset_returns Series

Asset returns series.

required
benchmark_returns Series

Benchmark returns series.

required
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
float

Annualized Information Ratio.

Source code in financelib/quant/risk_metrics.py
def information_ratio(
    asset_returns: pd.Series,
    benchmark_returns: pd.Series,
    periods_per_year: int = 252,
) -> float:
    """Calculate the Information Ratio.

    IR = (R_a - R_b) / TE, where TE is tracking error.

    Args:
        asset_returns: Asset returns series.
        benchmark_returns: Benchmark returns series.
        periods_per_year: Trading periods per year.

    Returns:
        Annualized Information Ratio.
    """
    active_returns = asset_returns - benchmark_returns
    tracking_error = active_returns.std()
    if tracking_error == 0:
        return 0.0
    return float(active_returns.mean() / tracking_error * np.sqrt(periods_per_year))

max_drawdown(prices: pd.Series) -> float

Calculate maximum drawdown from peak.

MDD = (Trough - Peak) / Peak

Parameters:

Name Type Description Default
prices Series

Price series.

required

Returns:

Type Description
float

Maximum drawdown as a negative decimal (e.g., -0.25 for -25%).

Source code in financelib/quant/risk_metrics.py
def max_drawdown(prices: pd.Series) -> float:
    """Calculate maximum drawdown from peak.

    MDD = (Trough - Peak) / Peak

    Args:
        prices: Price series.

    Returns:
        Maximum drawdown as a negative decimal (e.g., -0.25 for -25%).
    """
    cumulative_max = prices.cummax()
    drawdowns = (prices - cumulative_max) / cumulative_max
    return float(drawdowns.min())

sharpe_ratio(returns: pd.Series, risk_free_rate: float = 0.0, periods_per_year: int = 252) -> float

Calculate the annualized Sharpe ratio.

SR = (R_p - R_f) / sigma_p * sqrt(N)

Parameters:

Name Type Description Default
returns Series

Asset returns series.

required
risk_free_rate float

Annualized risk-free rate (e.g., 0.05 for 5%).

0.0
periods_per_year int

Trading periods per year (252 daily, 12 monthly).

252

Returns:

Type Description
float

Annualized Sharpe ratio.

Source code in financelib/quant/risk_metrics.py
def sharpe_ratio(
    returns: pd.Series,
    risk_free_rate: float = 0.0,
    periods_per_year: int = 252,
) -> float:
    """Calculate the annualized Sharpe ratio.

    SR = (R_p - R_f) / sigma_p * sqrt(N)

    Args:
        returns: Asset returns series.
        risk_free_rate: Annualized risk-free rate (e.g., 0.05 for 5%).
        periods_per_year: Trading periods per year (252 daily, 12 monthly).

    Returns:
        Annualized Sharpe ratio.
    """
    rf_per_period = risk_free_rate / periods_per_year
    excess_returns = returns - rf_per_period
    if returns.std() == 0:
        return 0.0
    return float(excess_returns.mean() / returns.std() * np.sqrt(periods_per_year))

sortino_ratio(returns: pd.Series, risk_free_rate: float = 0.0, periods_per_year: int = 252) -> float

Calculate the annualized Sortino ratio.

Uses downside deviation instead of total standard deviation, penalizing only negative volatility.

Parameters:

Name Type Description Default
returns Series

Asset returns series.

required
risk_free_rate float

Annualized risk-free rate.

0.0
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
float

Annualized Sortino ratio.

Source code in financelib/quant/risk_metrics.py
def sortino_ratio(
    returns: pd.Series,
    risk_free_rate: float = 0.0,
    periods_per_year: int = 252,
) -> float:
    """Calculate the annualized Sortino ratio.

    Uses downside deviation instead of total standard deviation,
    penalizing only negative volatility.

    Args:
        returns: Asset returns series.
        risk_free_rate: Annualized risk-free rate.
        periods_per_year: Trading periods per year.

    Returns:
        Annualized Sortino ratio.
    """
    rf_per_period = risk_free_rate / periods_per_year
    excess_returns = returns - rf_per_period
    downside = returns[returns < 0]
    downside_std = downside.std() if len(downside) > 0 else 0.0
    if downside_std == 0:
        return 0.0
    return float(excess_returns.mean() / downside_std * np.sqrt(periods_per_year))

treynor_ratio(asset_returns: pd.Series, market_returns: pd.Series, risk_free_rate: float = 0.0, periods_per_year: int = 252) -> float

Calculate the Treynor ratio.

Treynor = (R_a - R_f) / beta

Parameters:

Name Type Description Default
asset_returns Series

Asset returns series.

required
market_returns Series

Market/benchmark returns series.

required
risk_free_rate float

Annualized risk-free rate.

0.0
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
float

Treynor ratio.

Source code in financelib/quant/risk_metrics.py
def treynor_ratio(
    asset_returns: pd.Series,
    market_returns: pd.Series,
    risk_free_rate: float = 0.0,
    periods_per_year: int = 252,
) -> float:
    """Calculate the Treynor ratio.

    Treynor = (R_a - R_f) / beta

    Args:
        asset_returns: Asset returns series.
        market_returns: Market/benchmark returns series.
        risk_free_rate: Annualized risk-free rate.
        periods_per_year: Trading periods per year.

    Returns:
        Treynor ratio.
    """
    from .returns import annualized_return
    b = beta(asset_returns, market_returns)
    if b == 0:
        return 0.0
    ann_ret = annualized_return(asset_returns, periods_per_year)
    return float((ann_ret - risk_free_rate) / b)

value_at_risk(returns: pd.Series, confidence: float = 0.95, method: str = 'historical') -> float

Calculate Value at Risk (VaR).

Estimates the maximum expected loss at a given confidence level.

Parameters:

Name Type Description Default
returns Series

Returns series.

required
confidence float

Confidence level (e.g., 0.95 for 95%).

0.95
method str

Calculation method ('historical' or 'parametric').

'historical'

Returns:

Type Description
float

VaR as a negative decimal representing potential loss.

Source code in financelib/quant/risk_metrics.py
def value_at_risk(
    returns: pd.Series,
    confidence: float = 0.95,
    method: str = "historical",
) -> float:
    """Calculate Value at Risk (VaR).

    Estimates the maximum expected loss at a given confidence level.

    Args:
        returns: Returns series.
        confidence: Confidence level (e.g., 0.95 for 95%).
        method: Calculation method ('historical' or 'parametric').

    Returns:
        VaR as a negative decimal representing potential loss.
    """
    if method == "parametric":
        from scipy import stats
        z_score = stats.norm.ppf(1 - confidence)
        return float(returns.mean() + z_score * returns.std())
    else:
        return float(returns.quantile(1 - confidence))

Portfolio Analysis

financelib.quant.portfolio

Portfolio analysis and optimization tools.

Provides mean-variance optimization, portfolio performance metrics, and covariance/correlation analysis for multi-asset portfolios.

Functions

correlation_matrix(returns: pd.DataFrame) -> pd.DataFrame

Calculate the correlation matrix of asset returns.

Parameters:

Name Type Description Default
returns DataFrame

DataFrame of asset returns (columns = assets).

required

Returns:

Type Description
DataFrame

Correlation matrix as DataFrame.

Source code in financelib/quant/portfolio.py
def correlation_matrix(returns: pd.DataFrame) -> pd.DataFrame:
    """Calculate the correlation matrix of asset returns.

    Args:
        returns: DataFrame of asset returns (columns = assets).

    Returns:
        Correlation matrix as DataFrame.
    """
    return returns.corr()

covariance_matrix(returns: pd.DataFrame) -> pd.DataFrame

Calculate the covariance matrix of asset returns.

Parameters:

Name Type Description Default
returns DataFrame

DataFrame of asset returns (columns = assets).

required

Returns:

Type Description
DataFrame

Covariance matrix as DataFrame.

Source code in financelib/quant/portfolio.py
def covariance_matrix(returns: pd.DataFrame) -> pd.DataFrame:
    """Calculate the covariance matrix of asset returns.

    Args:
        returns: DataFrame of asset returns (columns = assets).

    Returns:
        Covariance matrix as DataFrame.
    """
    return returns.cov()

equal_weight_portfolio(n_assets: int) -> np.ndarray

Generate equal-weight portfolio allocation.

Parameters:

Name Type Description Default
n_assets int

Number of assets.

required

Returns:

Type Description
ndarray

Array of equal weights (1/n for each asset).

Source code in financelib/quant/portfolio.py
def equal_weight_portfolio(n_assets: int) -> np.ndarray:
    """Generate equal-weight portfolio allocation.

    Args:
        n_assets: Number of assets.

    Returns:
        Array of equal weights (1/n for each asset).
    """
    return np.ones(n_assets) / n_assets

minimum_variance_weights(returns: pd.DataFrame) -> np.ndarray

Calculate minimum variance portfolio weights.

Uses analytical solution: w = (Sigma^-1 * 1) / (1^T * Sigma^-1 * 1)

Parameters:

Name Type Description Default
returns DataFrame

DataFrame of asset returns (columns = assets).

required

Returns:

Type Description
ndarray

Array of optimal weights for minimum variance portfolio.

Source code in financelib/quant/portfolio.py
def minimum_variance_weights(returns: pd.DataFrame) -> np.ndarray:
    """Calculate minimum variance portfolio weights.

    Uses analytical solution: w = (Sigma^-1 * 1) / (1^T * Sigma^-1 * 1)

    Args:
        returns: DataFrame of asset returns (columns = assets).

    Returns:
        Array of optimal weights for minimum variance portfolio.
    """
    cov = returns.cov().values
    n = len(cov)

    try:
        inv_cov = np.linalg.inv(cov)
    except np.linalg.LinAlgError:
        # Fallback to pseudo-inverse for singular matrices
        inv_cov = np.linalg.pinv(cov)

    ones = np.ones(n)
    weights = inv_cov @ ones / (ones @ inv_cov @ ones)
    return weights

portfolio_return(weights: np.ndarray, returns: pd.DataFrame) -> float

Calculate expected portfolio return.

R_p = w^T * mu

Parameters:

Name Type Description Default
weights ndarray

Array of portfolio weights (must sum to 1).

required
returns DataFrame

DataFrame of asset returns (columns = assets).

required

Returns:

Type Description
float

Expected portfolio return.

Source code in financelib/quant/portfolio.py
def portfolio_return(weights: np.ndarray, returns: pd.DataFrame) -> float:
    """Calculate expected portfolio return.

    R_p = w^T * mu

    Args:
        weights: Array of portfolio weights (must sum to 1).
        returns: DataFrame of asset returns (columns = assets).

    Returns:
        Expected portfolio return.
    """
    mean_returns = returns.mean()
    return float(np.dot(weights, mean_returns))

portfolio_sharpe(weights: np.ndarray, returns: pd.DataFrame, risk_free_rate: float = 0.0, periods_per_year: int = 252) -> float

Calculate portfolio Sharpe ratio.

Parameters:

Name Type Description Default
weights ndarray

Portfolio weights.

required
returns DataFrame

DataFrame of asset returns.

required
risk_free_rate float

Annualized risk-free rate.

0.0
periods_per_year int

Trading periods per year.

252

Returns:

Type Description
float

Annualized portfolio Sharpe ratio.

Source code in financelib/quant/portfolio.py
def portfolio_sharpe(
    weights: np.ndarray,
    returns: pd.DataFrame,
    risk_free_rate: float = 0.0,
    periods_per_year: int = 252,
) -> float:
    """Calculate portfolio Sharpe ratio.

    Args:
        weights: Portfolio weights.
        returns: DataFrame of asset returns.
        risk_free_rate: Annualized risk-free rate.
        periods_per_year: Trading periods per year.

    Returns:
        Annualized portfolio Sharpe ratio.
    """
    ret = portfolio_return(weights, returns) * periods_per_year
    vol = portfolio_volatility(weights, returns) * np.sqrt(periods_per_year)
    if vol == 0:
        return 0.0
    return float((ret - risk_free_rate) / vol)

portfolio_volatility(weights: np.ndarray, returns: pd.DataFrame) -> float

Calculate portfolio volatility (standard deviation).

sigma_p = sqrt(w^T * Sigma * w)

Parameters:

Name Type Description Default
weights ndarray

Array of portfolio weights.

required
returns DataFrame

DataFrame of asset returns.

required

Returns:

Type Description
float

Portfolio standard deviation.

Source code in financelib/quant/portfolio.py
def portfolio_volatility(weights: np.ndarray, returns: pd.DataFrame) -> float:
    """Calculate portfolio volatility (standard deviation).

    sigma_p = sqrt(w^T * Sigma * w)

    Args:
        weights: Array of portfolio weights.
        returns: DataFrame of asset returns.

    Returns:
        Portfolio standard deviation.
    """
    cov = returns.cov()
    port_var = np.dot(weights.T, np.dot(cov, weights))
    return float(np.sqrt(port_var))

Statistical Analysis

financelib.quant.statistics

Statistical analysis functions for quantitative finance.

Provides distribution tests, mean-reversion analysis, and time-series statistics commonly used in academic finance research.

Functions

autocorrelation(returns: pd.Series, lag: int = 1) -> float

Calculate autocorrelation at a given lag.

Measures how correlated returns are with their own past values. Significant autocorrelation may indicate momentum or mean reversion.

Parameters:

Name Type Description Default
returns Series

Returns series.

required
lag int

Number of periods to lag.

1

Returns:

Type Description
float

Autocorrelation coefficient at the specified lag.

Source code in financelib/quant/statistics.py
def autocorrelation(returns: pd.Series, lag: int = 1) -> float:
    """Calculate autocorrelation at a given lag.

    Measures how correlated returns are with their own past values.
    Significant autocorrelation may indicate momentum or mean reversion.

    Args:
        returns: Returns series.
        lag: Number of periods to lag.

    Returns:
        Autocorrelation coefficient at the specified lag.
    """
    return float(returns.autocorr(lag=lag))

half_life(prices: pd.Series) -> float

Calculate the half-life of mean reversion using OLS.

Fits: delta_y = alpha + beta * y_{t-1} + epsilon Half-life = -ln(2) / beta

Parameters:

Name Type Description Default
prices Series

Price series (should be a spread or log-price series).

required

Returns:

Type Description
float

Half-life in periods. Lower values indicate faster mean reversion.

Source code in financelib/quant/statistics.py
def half_life(prices: pd.Series) -> float:
    """Calculate the half-life of mean reversion using OLS.

    Fits: delta_y = alpha + beta * y_{t-1} + epsilon
    Half-life = -ln(2) / beta

    Args:
        prices: Price series (should be a spread or log-price series).

    Returns:
        Half-life in periods. Lower values indicate faster mean reversion.
    """
    y = prices.values
    y_lag = y[:-1]
    delta_y = np.diff(y)

    # OLS regression
    X = np.column_stack([np.ones(len(y_lag)), y_lag])
    beta = np.linalg.lstsq(X, delta_y, rcond=None)[0]

    if beta[1] >= 0:
        return float("inf")  # Not mean-reverting

    return float(-np.log(2) / beta[1])

hurst_exponent(prices: pd.Series, max_lag: int = 100) -> float

Estimate the Hurst exponent using R/S analysis.

H < 0.5: Mean-reverting (anti-persistent) H = 0.5: Random walk H > 0.5: Trending (persistent)

Parameters:

Name Type Description Default
prices Series

Price series.

required
max_lag int

Maximum lag for R/S calculation.

100

Returns:

Type Description
float

Estimated Hurst exponent.

Source code in financelib/quant/statistics.py
def hurst_exponent(prices: pd.Series, max_lag: int = 100) -> float:
    """Estimate the Hurst exponent using R/S analysis.

    H < 0.5: Mean-reverting (anti-persistent)
    H = 0.5: Random walk
    H > 0.5: Trending (persistent)

    Args:
        prices: Price series.
        max_lag: Maximum lag for R/S calculation.

    Returns:
        Estimated Hurst exponent.
    """
    returns = np.log(prices / prices.shift(1)).dropna().values
    n = len(returns)

    if n < max_lag:
        max_lag = n // 2

    lags = range(2, max_lag)
    rs_values = []

    for lag in lags:
        subseries = [returns[i:i + lag] for i in range(0, n - lag, lag)]
        rs_list = []
        for s in subseries:
            if len(s) < 2:
                continue
            mean = np.mean(s)
            deviations = np.cumsum(s - mean)
            r = np.max(deviations) - np.min(deviations)
            std = np.std(s, ddof=1)
            if std > 0:
                rs_list.append(r / std)
        if rs_list:
            rs_values.append(np.mean(rs_list))

    if len(rs_values) < 2:
        return 0.5

    log_lags = np.log(list(lags)[:len(rs_values)])
    log_rs = np.log(rs_values)

    # Linear regression: log(R/S) = H * log(n) + c
    coeffs = np.polyfit(log_lags, log_rs, 1)
    return float(coeffs[0])

jarque_bera_test(returns: pd.Series) -> Tuple[float, float]

Perform the Jarque-Bera test for normality.

Tests whether the sample data has skewness and kurtosis matching a normal distribution.

JB = (n/6) * (S^2 + (K-3)^2/4)

Parameters:

Name Type Description Default
returns Series

Returns series.

required

Returns:

Type Description
float

Tuple of (JB statistic, p-value).

float

p-value < 0.05 rejects normality at 95% confidence.

Source code in financelib/quant/statistics.py
def jarque_bera_test(returns: pd.Series) -> Tuple[float, float]:
    """Perform the Jarque-Bera test for normality.

    Tests whether the sample data has skewness and kurtosis
    matching a normal distribution.

    JB = (n/6) * (S^2 + (K-3)^2/4)

    Args:
        returns: Returns series.

    Returns:
        Tuple of (JB statistic, p-value).
        p-value < 0.05 rejects normality at 95% confidence.
    """
    n = len(returns)
    s = skewness(returns)
    k = kurtosis(returns, excess=True)
    jb_stat = (n / 6) * (s**2 + (k**2) / 4)

    # Chi-squared approximation with 2 degrees of freedom
    from math import exp
    p_value = exp(-jb_stat / 2)  # Simplified; use scipy for exact p-value
    return float(jb_stat), float(min(p_value, 1.0))

kurtosis(returns: pd.Series, excess: bool = True) -> float

Calculate the kurtosis of a returns distribution.

Excess kurtosis > 0 indicates fat tails (leptokurtic), common in financial returns.

Parameters:

Name Type Description Default
returns Series

Returns series.

required
excess bool

If True, return excess kurtosis (Fisher's definition).

True

Returns:

Type Description
float

Kurtosis value.

Source code in financelib/quant/statistics.py
def kurtosis(returns: pd.Series, excess: bool = True) -> float:
    """Calculate the kurtosis of a returns distribution.

    Excess kurtosis > 0 indicates fat tails (leptokurtic),
    common in financial returns.

    Args:
        returns: Returns series.
        excess: If True, return excess kurtosis (Fisher's definition).

    Returns:
        Kurtosis value.
    """
    if excess:
        return float(returns.kurtosis())
    return float(returns.kurtosis() + 3)

rolling_z_score(series: pd.Series, window: int = 20) -> pd.Series

Alias for z_score with rolling window.

Parameters:

Name Type Description Default
series Series

Input series.

required
window int

Rolling window.

20

Returns:

Type Description
Series

Rolling z-score series.

Source code in financelib/quant/statistics.py
def rolling_z_score(series: pd.Series, window: int = 20) -> pd.Series:
    """Alias for z_score with rolling window.

    Args:
        series: Input series.
        window: Rolling window.

    Returns:
        Rolling z-score series.
    """
    return z_score(series, window)

skewness(returns: pd.Series) -> float

Calculate the skewness of a returns distribution.

Positive skew: more extreme positive returns. Negative skew: more extreme negative returns (typical for equities).

Parameters:

Name Type Description Default
returns Series

Returns series.

required

Returns:

Type Description
float

Skewness value.

Source code in financelib/quant/statistics.py
def skewness(returns: pd.Series) -> float:
    """Calculate the skewness of a returns distribution.

    Positive skew: more extreme positive returns.
    Negative skew: more extreme negative returns (typical for equities).

    Args:
        returns: Returns series.

    Returns:
        Skewness value.
    """
    return float(returns.skew())

z_score(series: pd.Series, window: int = 20) -> pd.Series

Calculate rolling z-score.

z = (x - mean) / std

Parameters:

Name Type Description Default
series Series

Input series.

required
window int

Rolling window for mean and std.

20

Returns:

Type Description
Series

Z-score series.

Source code in financelib/quant/statistics.py
def z_score(series: pd.Series, window: int = 20) -> pd.Series:
    """Calculate rolling z-score.

    z = (x - mean) / std

    Args:
        series: Input series.
        window: Rolling window for mean and std.

    Returns:
        Z-score series.
    """
    rolling_mean = series.rolling(window=window).mean()
    rolling_std = series.rolling(window=window).std()
    return (series - rolling_mean) / rolling_std