HKUDS/Vibe-Trading · error · ValueError

no observation has a finite return and a finite var

Error message

no observation has a finite return and a finite var

What it means

After dropping non-finite pairs, _align requires at least one observation where both the return and the var are finite; otherwise it raises ValueError. If all pairs contain NaN/inf on either side, no backtest statistic can be computed.

Source

Thrown at agent/src/quantlib/var_backtest.py:296

    if var_values.ndim == 0:
        var_values = np.full(ret_values.shape, float(var_values))
    else:
        if var_values.ndim > 1:
            raise ValueError(f"var must be 1-D or scalar, got shape {var_values.shape}")
        var_values = var_values.ravel()

    if ret_values.size != var_values.size:
        raise ValueError(
            f"returns and var must be the same length, got {ret_values.size} "
            f"and {var_values.size}"
        )
    if ret_values.size == 0:
        raise ValueError("returns is empty")

    keep = np.isfinite(ret_values) & np.isfinite(var_values)
    dropped = int((~keep).sum())
    if not keep.any():
        raise ValueError("no observation has a finite return and a finite var")

    index = ret_index if ret_index is not None else var_index
    kept_index = index[keep] if index is not None else None
    return ret_values[keep], var_values[keep], kept_index, dropped


def violation_indicator(
    returns: pd.Series | np.ndarray | Sequence[float],
    var: pd.Series | np.ndarray | Sequence[float] | float,
) -> np.ndarray:
    """Flag the days on which the realised loss exceeded the VaR forecast.

    Args:
        returns: Realised returns, signed. A 3% loss is ``-0.03``.
        var: VaR forecasts as positive loss magnitudes, one per return or a
            single scalar. A negative entry is not flipped: it is taken at face
            value, meaning "the model expects a gain even in the tail", which is
            almost always a caller-side sign error and shows up here as an

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Check the dropped count in the returned metadata to see how many pairs were discarded and align the valid windows.
  2. Shorten the VaR warmup or drop the warmup rows from both series before calling.
  3. Fix zero-division/NaN-producing steps upstream (use pct_change with fill_method=None and then dropna both).

Example fix

# before
rets, var = rets, rolling_var  # rolling_var starts with 20 NaNs, sample is 15 rows
# after
valid = rets.dropna().index.intersection(rolling_var.dropna().index)
var_backtest(rets.loc[valid], rolling_var.loc[valid])
Defensive patterns

Strategy: validation

Validate before calling

import numpy as np
keep = np.isfinite(np.asarray(returns, float)) & np.isfinite(np.asarray(var, float))
assert keep.any(), 'no finite (return, var) pairs'

Type guard

def has_finite_pairs(returns, var) -> bool:
    import numpy as np
    return (np.isfinite(np.asarray(returns, float)) & np.isfinite(np.asarray(var, float))).any()

Try / catch

except ValueError as e:
    if 'finite return and a finite var' in str(e): realign_and_dropna()

Prevention

When it happens

Trigger: Passing all-NaN returns (e.g. log returns of a constant/zero price series), a var series that is entirely NaN during its estimation warmup, or inf values from zero-division in either input.

Common situations: Rolling VaR with a warmup longer than the sample; percentage-change on unadjusted prices containing zeros; both series offset so every pair has one NaN.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/02ff1052f8fe27b0. Report an issue: GitHub.