Skip to content

Stacking

Bayesian model stacking and ensemble prediction.

trade_study.stack_bayesian(compare_dict, *, method='stacking')

Bayesian stacking via arviz.compare.

Parameters:

Name Type Description Default
compare_dict dict[str, Any]

Dictionary mapping model names to arviz DataTree or ELPDData objects (must contain log_likelihood group).

required
method str

Weighting method. One of "stacking", "BB-pseudo-BMA", "pseudo-BMA".

'stacking'

Returns:

Type Description
dict[str, float]

Dictionary mapping model names to stacking weights.

Source code in src/trade_study/stacking.py
def stack_bayesian(
    compare_dict: dict[str, Any],
    *,
    method: str = "stacking",
) -> dict[str, float]:
    """Bayesian stacking via arviz.compare.

    Args:
        compare_dict: Dictionary mapping model names to arviz DataTree
            or ELPDData objects (must contain log_likelihood group).
        method: Weighting method. One of "stacking", "BB-pseudo-BMA",
            "pseudo-BMA".

    Returns:
        Dictionary mapping model names to stacking weights.
    """
    import arviz as az  # type: ignore[import-untyped]

    result = az.compare(compare_dict, method=method)
    return dict(zip(result.index, result["weight"], strict=True))

trade_study.stack_scores(score_matrix, *, maximize=False)

Optimize stacking weights from a score matrix.

For non-Bayesian models where log-likelihoods aren't available. Finds weights w on the simplex that optimize the weighted composite score.

Parameters:

Name Type Description Default
score_matrix NDArray[floating[Any]]

Array of shape (n_models, n_test_points) where each entry is the score of model i on test point j.

required
maximize bool

If True, maximize the weighted score; if False, minimize.

False

Returns:

Type Description
NDArray[floating[Any]]

Array of weights, shape (n_models,), summing to 1.

Source code in src/trade_study/stacking.py
def stack_scores(
    score_matrix: NDArray[np.floating[Any]],
    *,
    maximize: bool = False,
) -> NDArray[np.floating[Any]]:
    """Optimize stacking weights from a score matrix.

    For non-Bayesian models where log-likelihoods aren't available.
    Finds weights w on the simplex that optimize the weighted composite score.

    Args:
        score_matrix: Array of shape (n_models, n_test_points) where each
            entry is the score of model i on test point j.
        maximize: If True, maximize the weighted score; if False, minimize.

    Returns:
        Array of weights, shape (n_models,), summing to 1.
    """
    from scipy.optimize import minimize  # type: ignore[import-untyped]

    n_models = score_matrix.shape[0]

    def objective(w: NDArray[np.floating[Any]]) -> float:
        composite = w @ score_matrix
        val = float(np.mean(composite))
        return -val if maximize else val

    # Simplex constraint: weights >= 0 and sum to 1
    constraints = {"type": "eq", "fun": lambda w: np.sum(w) - 1.0}
    bounds = [(0.0, 1.0)] * n_models
    w0 = np.ones(n_models) / n_models

    result = minimize(
        objective,
        w0,
        method="SLSQP",
        bounds=bounds,
        constraints=constraints,
    )
    return np.asarray(result.x, dtype=np.float64)

trade_study.ensemble_predict(predictions, weights)

Weighted ensemble of model predictions.

Parameters:

Name Type Description Default
predictions list[NDArray[floating[Any]]]

List of prediction arrays, one per model. Each should have the same shape.

required
weights NDArray[floating[Any]]

Stacking weights, shape (n_models,).

required

Returns:

Type Description
NDArray[floating[Any]]

Weighted average prediction array.

Source code in src/trade_study/stacking.py
def ensemble_predict(
    predictions: list[NDArray[np.floating[Any]]],
    weights: NDArray[np.floating[Any]],
) -> NDArray[np.floating[Any]]:
    """Weighted ensemble of model predictions.

    Args:
        predictions: List of prediction arrays, one per model.
            Each should have the same shape.
        weights: Stacking weights, shape (n_models,).

    Returns:
        Weighted average prediction array.
    """
    w = np.asarray(weights, dtype=np.float64)
    w /= w.sum()
    result = np.zeros_like(predictions[0], dtype=np.float64)
    for pred, wi in zip(predictions, w, strict=True):
        result += wi * pred
    return result