{"record":{"id":"1f1b3317c30623f0","repo":"HKUDS/Vibe-Trading","slug":"n-folds-must-be-at-least-min-folds-got-n-folds","errorCode":null,"errorMessage":"n_folds must be at least {MIN_FOLDS}, got {n_folds}","messagePattern":"n_folds must be at least (.+?), got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/quantlib/crossvalidation.py","lineNumber":243,"sourceCode":"\n    Args:\n        n_samples: Number of observations.\n        label_end_times: Where each label's outcome window ends. When None, each\n            label is assumed to resolve within its own bar, so purging removes\n            only the boundary and the embargo does the remaining work.\n        n_folds: Number of folds, at least :data:`MIN_FOLDS`.\n        embargo_fraction: Fraction of the sample embargoed after each test block.\n\n    Yields:\n        One :class:`Split` per fold, in chronological order of the test block.\n\n    Raises:\n        ValueError: If ``n_folds`` is below :data:`MIN_FOLDS`, exceeds the\n            sample size, if ``embargo_fraction`` is negative or at least 1, or\n            if ``label_end_times`` does not match the sample.\n    \"\"\"\n    if n_folds < MIN_FOLDS:\n        raise ValueError(f\"n_folds must be at least {MIN_FOLDS}, got {n_folds}\")\n    if n_samples < n_folds:\n        raise ValueError(f\"{n_samples} samples cannot make {n_folds} folds\")\n    if not 0.0 <= embargo_fraction < 1.0:\n        raise ValueError(\n            f\"embargo_fraction must be in [0, 1), got {embargo_fraction}\"\n        )\n\n    if label_end_times is None:\n        label_ends = np.arange(n_samples)\n    else:\n        label_ends = _as_label_spans(label_end_times, n_samples)\n\n    embargo_size = int(round(n_samples * embargo_fraction))\n    boundaries = np.linspace(0, n_samples, n_folds + 1).astype(int)\n\n    for fold in range(n_folds):\n        start, stop = int(boundaries[fold]), int(boundaries[fold + 1])\n        if stop <= start:","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/quantlib/crossvalidation.py#L225-L261","documentation":"purged_kfold_splits requires at least MIN_FOLDS folds for cross-validation to be meaningful; passing a smaller n_folds (e.g. 1) is rejected immediately. This mirrors sklearn's KFold minimum-fold constraint but for the purged variant.","triggerScenarios":"Calling purged_kfold_splits(n_folds=1) or n_folds=0 (e.g. n_folds computed from a config or len of an empty list).","commonSituations":"Config-driven n_folds (YAML/CLI override typo), computing n_folds dynamically as len(something) that can be 0 or 1, or copying a walk-forward parameter into a k-fold call.","solutions":["Set n_folds >= MIN_FOLDS (typically 2); read the MIN_FOLDS constant from the module","Guard dynamic computation: n_folds = max(MIN_FOLDS, computed_value)","Validate config before the run starts"],"exampleFix":"// before\nsplits = list(purged_kfold_splits(X, n_folds=1, label_end_times=le))\n// after\nfrom agent.src.quantlib.crossvalidation import MIN_FOLDS\nsplits = list(purged_kfold_splits(X, n_folds=max(MIN_FOLDS, cfg.n_folds), label_end_times=le))","handlingStrategy":"validation","validationCode":"from agent.src.quantlib.crossvalidation import MIN_FOLDS\nassert n_folds >= MIN_FOLDS, f'n_folds must be >= {MIN_FOLDS}'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp config values: n_folds = max(MIN_FOLDS, cfg.n_folds)","Validate CV config once at startup"],"tags":["crossvalidation","argument-validation","quantlib"],"backgroundTag":"invalid-cv-fold-count","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}