{"record":{"id":"b6d282774a289446","repo":"HKUDS/Vibe-Trading","slug":"winsorise-must-be-in-0-0-5-got-winsorise","errorCode":null,"errorMessage":"winsorise must be in [0, 0.5), got {winsorise}","messagePattern":"winsorise must be in \\[0, 0\\.5\\), got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/factormodel.py","lineNumber":268,"sourceCode":"\n    Args:\n        values: Raw characteristic, indexed by asset. NaN entries survive as NaN\n            and are the caller's to fill (:func:`build_style_exposures` fills\n            them with zero and counts them).\n        market_caps: Market capitalisation on the same index, used for the mean.\n            When None, the mean is equal-weighted.\n        winsorise: Fraction trimmed from each tail, in ``[0, 0.5)``.\n\n    Returns:\n        Standardised exposures on the input index.\n\n    Raises:\n        ValueError: If ``winsorise`` is outside ``[0, 0.5)``, if fewer than\n            :data:`MIN_CROSS_SECTION` finite values are present, or if\n            ``market_caps`` does not cover the same index.\n    \"\"\"\n    if not 0.0 <= winsorise < 0.5:\n        raise ValueError(f\"winsorise must be in [0, 0.5), got {winsorise}\")\n\n    series = pd.Series(values, dtype=float)\n    finite = series.dropna()\n    if finite.size < MIN_CROSS_SECTION:\n        raise ValueError(\n            f\"a cross-section needs at least {MIN_CROSS_SECTION} finite values to \"\n            f\"standardise, got {finite.size}\"\n        )\n\n    if winsorise > 0:\n        lower, upper = finite.quantile(winsorise), finite.quantile(1.0 - winsorise)\n        clipped = series.clip(lower=lower, upper=upper)\n    else:\n        clipped = series\n\n    if market_caps is None:\n        centre = float(clipped.dropna().mean())\n    else:","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/factormodel.py#L250-L286","documentation":"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).","triggerScenarios":"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).","commonSituations":"Porting winsorisation settings from another library with a different convention (per-tail vs both-tails), YAML config typos, or parameters derived from percentile tuples.","solutions":["Use a per-tail fraction strictly below 0.5, e.g. winsorise=0.01 clips 1% from each tail.","If your config stores a two-tailed total, halve it: winsorise=cfg_value / 2."],"exampleFix":"# before\nexpo = standardise_exposures(values, winsorise=0.02)  # meant 2% total\n# after\nexpo = standardise_exposures(values, winsorise=0.01)  # 1% per tail","handlingStrategy":"validation","validationCode":"assert 0.0 <= winsorise < 0.5","typeGuard":"def is_valid_winsorise(w: float) -> bool:\n    return isinstance(w, (int, float)) and 0.0 <= w < 0.5","tryCatchPattern":null,"preventionTips":["Document per-tail semantics in config comments.","Halve two-tailed fractions coming from other tools."],"tags":["factormodel","winsorisation","argument-validation"],"backgroundTag":"argument-range-validation","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}