HKUDS/Vibe-Trading · error · ValueError
{n_samples} samples cannot make {n_folds} folds
Error message
{n_samples} samples cannot make {n_folds} folds What it means
purged_kfold_splits cannot produce n_folds non-empty test blocks from fewer than n_folds samples, so n_samples < n_folds is rejected. Each fold needs at least one observation for the test set.
Source
Thrown at agent/src/quantlib/crossvalidation.py:245
n_samples: Number of observations.
label_end_times: Where each label's outcome window ends. When None, each
label is assumed to resolve within its own bar, so purging removes
only the boundary and the embargo does the remaining work.
n_folds: Number of folds, at least :data:`MIN_FOLDS`.
embargo_fraction: Fraction of the sample embargoed after each test block.
Yields:
One :class:`Split` per fold, in chronological order of the test block.
Raises:
ValueError: If ``n_folds`` is below :data:`MIN_FOLDS`, exceeds the
sample size, if ``embargo_fraction`` is negative or at least 1, or
if ``label_end_times`` does not match the sample.
"""
if n_folds < MIN_FOLDS:
raise ValueError(f"n_folds must be at least {MIN_FOLDS}, got {n_folds}")
if n_samples < n_folds:
raise ValueError(f"{n_samples} samples cannot make {n_folds} folds")
if not 0.0 <= embargo_fraction < 1.0:
raise ValueError(
f"embargo_fraction must be in [0, 1), got {embargo_fraction}"
)
if label_end_times is None:
label_ends = np.arange(n_samples)
else:
label_ends = _as_label_spans(label_end_times, n_samples)
embargo_size = int(round(n_samples * embargo_fraction))
boundaries = np.linspace(0, n_samples, n_folds + 1).astype(int)
for fold in range(n_folds):
start, stop = int(boundaries[fold]), int(boundaries[fold + 1])
if stop <= start:
continue
test_mask = np.zeros(n_samples, dtype=bool)View on GitHub (pinned to 80ffdda44c)
Solutions
- Lower n_folds to at most n_samples (ideally much smaller)
- Increase the sample size / reduce filtering so n_samples >= n_folds
- Add an upfront check: if len(X) < n_folds: skip or reduce folds
Example fix
// before splits = list(purged_kfold_splits(X_small, n_folds=10, label_end_times=le)) // after n_folds = min(10, len(X_small)) splits = list(purged_kfold_splits(X_small, n_folds=n_folds, label_end_times=le))
Defensive patterns
Strategy: validation
Validate before calling
assert len(X) >= n_folds, f'{len(X)} samples cannot make {n_folds} folds' Prevention
- Scale n_folds to sample size: n_folds = min(cfg.n_folds, len(X))
- Skip CV for tiny samples
When it happens
Trigger: Calling purged_kfold_splits on a tiny sample (e.g. 3 rows with n_folds=5), or after heavy filtering reduced the dataframe below the configured fold count.
Common situations: Unit-test fixtures with a handful of rows, per-symbol or per-month slicing that leaves tiny samples, or a large n_folds (e.g. 20) inherited from a big dataset config.
Related errors
- n_folds must be at least {MIN_FOLDS}, got {n_folds}
- embargo_fraction must be in [0, 1), got {embargo_fraction}
- a label cannot end before the observation it belongs to star
- label_end_times has {span_ends.size} entries but the sample
- Purge and embargo removed all training samples for fold {fol
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/c12280d918b0c66c.
Report an issue: GitHub.