ConformalWrapper

Defined in fynance.models.conformal

class ConformalWrapper(model, alpha=0.1, window=252)[source]

Bases: object

Causal split-conformal prediction interval around a point model.

Wraps a model exposing fit(X, y) / predict(X) (the SignalModel contract). fit trains model on the leading T - window bars only and calibrates the interval half-width q_hat on the absolute residuals of the trailing window bars – bars the wrapped model never saw during its own fit, so the calibration is honest (no in-sample residuals).

Parameters:
modelSignalModel

Any regressor exposing fit(X, y) (returns anything, self is conventional) and predict(X) -> array-like.

alphafloat, optional

Miscoverage level in (0, 1); the interval targets marginal coverage 1 - alpha. Default 0.1 (90% interval).

windowint, optional

Number of trailing bars held out for calibration. Must be strictly less than the number of observations passed to fit. Default 252 (one trading year of daily bars).

Attributes:
q_hat_float or None

Calibrated half-width, set by fit; None before fitting.

See also

rolling_conformal, fynance.core.protocols.SignalModel

Examples

A closed-form linear model (OLS), fit/calibrated on noiseless-ish synthetic data – the interval should be tight and cover the held-out calibration residuals by construction:

>>> import numpy as np
>>> from fynance.models.conformal import ConformalWrapper
>>> class LinearModel:
...     ''' Closed-form ordinary least squares. '''
...     def fit(self, X, y):
...         self.coef_ = np.linalg.lstsq(X, y, rcond=None)[0]
...         return self
...     def predict(self, X):
...         return X @ self.coef_
>>> rng = np.random.default_rng(0)
>>> T, F = 500, 2
>>> X = rng.standard_normal((T, F))
>>> w_true = np.array([1.5, -0.5])
>>> y = X @ w_true + 0.1 * rng.standard_normal(T)
>>> wrapper = ConformalWrapper(LinearModel(), alpha=0.1, window=100)
>>> _ = wrapper.fit(X, y)
>>> wrapper.q_hat_ > 0
True
>>> interval = wrapper.predict_interval(X[-5:])
>>> interval.shape
(5, 2)
>>> bool((interval[:, 1] > interval[:, 0]).all())
True
fit(X, y)[source]

Fit the wrapped model and calibrate the interval half-width.

Splits (X, y) at T - window: the wrapped model is fit on the leading slice only; the trailing window bars are held out and never used for fitting, only to compute calibration residuals |y - model.predict(X)| and their split-conformal quantile (q_hat_).

Parameters:
Xarray-like

Feature matrix, shape (T, N), time-ordered.

yarray-like

Target, shape (T,) or (T, 1), aligned with X.

Returns:
ConformalWrapper

self, to allow chaining (the SignalModel contract).

Raises:
ValueError

If window is not strictly less than the number of observations in X/y (there would be no leading slice left to fit the wrapped model on).

predict(X)[source]

Point predictions of the wrapped model.

Parameters:
Xarray-like

Feature matrix, shape (T, N).

Returns:
numpy.ndarray

Point predictions, shape (T,) (the SignalModel contract).

predict_interval(X)[source]

Constant-width prediction interval around the point prediction.

Parameters:
Xarray-like

Feature matrix, shape (T, N).

Returns:
numpy.ndarray

Shape (T, 2); column 0 is the lower bound (predict(X) - q_hat_), column 1 the upper bound (predict(X) + q_hat_). The width 2 * q_hat_ is the same for every row – see the module docstring’s note on the constant-width limitation under heteroskedasticity.

Raises:
RuntimeError

If called before fit.