HKUDS/Vibe-Trading · error · ValueError

winsorise must be in [0, 0.5), got {winsorise}

Error message

winsorise must be in [0, 0.5), got {winsorise}

What it means

standardise_exposures winsorises the cross-section at the given tail fraction before z-scoring; a fraction of 0.5 or more would clip away the entire distribution (and negative values are meaningless), so winsorise must lie in [0, 0.5).

Source

Thrown at agent/src/quantlib/factormodel.py:268

    Args:
        values: Raw characteristic, indexed by asset. NaN entries survive as NaN
            and are the caller's to fill (:func:`build_style_exposures` fills
            them with zero and counts them).
        market_caps: Market capitalisation on the same index, used for the mean.
            When None, the mean is equal-weighted.
        winsorise: Fraction trimmed from each tail, in ``[0, 0.5)``.

    Returns:
        Standardised exposures on the input index.

    Raises:
        ValueError: If ``winsorise`` is outside ``[0, 0.5)``, if fewer than
            :data:`MIN_CROSS_SECTION` finite values are present, or if
            ``market_caps`` does not cover the same index.
    """
    if not 0.0 <= winsorise < 0.5:
        raise ValueError(f"winsorise must be in [0, 0.5), got {winsorise}")

    series = pd.Series(values, dtype=float)
    finite = series.dropna()
    if finite.size < MIN_CROSS_SECTION:
        raise ValueError(
            f"a cross-section needs at least {MIN_CROSS_SECTION} finite values to "
            f"standardise, got {finite.size}"
        )

    if winsorise > 0:
        lower, upper = finite.quantile(winsorise), finite.quantile(1.0 - winsorise)
        clipped = series.clip(lower=lower, upper=upper)
    else:
        clipped = series

    if market_caps is None:
        centre = float(clipped.dropna().mean())
    else:

View on GitHub (pinned to 80ffdda44c)

Solutions

  1. Use a per-tail fraction strictly below 0.5, e.g. winsorise=0.01 clips 1% from each tail.
  2. If your config stores a two-tailed total, halve it: winsorise=cfg_value / 2.

Example fix

# before
expo = standardise_exposures(values, winsorise=0.02)  # meant 2% total
# after
expo = standardise_exposures(values, winsorise=0.01)  # 1% per tail
Defensive patterns

Strategy: validation

Validate before calling

assert 0.0 <= winsorise < 0.5

Type guard

def is_valid_winsorise(w: float) -> bool:
    return isinstance(w, (int, float)) and 0.0 <= w < 0.5

Prevention

When it happens

Trigger: Passing winsorise=0.5, 1.0, or a negative number; commonly from a config that expresses the winsorisation as total two-tailed fraction (e.g. 0.02 meaning 1% per tail gets doubled).

Common situations: Porting winsorisation settings from another library with a different convention (per-tail vs both-tails), YAML config typos, or parameters derived from percentile tuples.

Related errors


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