HKUDS/Vibe-Trading · error · ValueError
a label cannot end before the observation it belongs to star
Error message
a label cannot end before the observation it belongs to starts
What it means
Raised by _as_label_spans in the purged cross-validation module when a label's end time is smaller than the index of the observation it belongs to, i.e. a label span ends before the observation's own start time. Since observation i is assumed to start at time i, label_end_times[i] < i is temporally impossible for forward-looking labels. The library rejects it because purging/embargo logic assumes labels extend at least to their observation start.
Source
Thrown at agent/src/quantlib/crossvalidation.py:152
starts = label_end_times.index
ends = label_end_times.to_numpy()
# searchsorted on the start index converts label end *times* into label
# end *positions*; the right insertion point minus one keeps a label
# that ends between two observations attached to the earlier one.
positions = np.searchsorted(starts, ends, side="right") - 1
positions = np.clip(positions, np.arange(len(starts)), len(starts) - 1)
span_ends = positions.astype(int)
else:
span_ends = np.asarray(label_end_times, dtype=float)
if span_ends.ndim != 1:
raise ValueError(f"label_end_times must be 1-D, got shape {span_ends.shape}")
if span_ends.size == 0:
raise ValueError("label_end_times is empty")
if not np.isfinite(span_ends).all():
raise ValueError("label_end_times holds a non-finite value")
span_ends = span_ends.astype(int)
if (span_ends < np.arange(span_ends.size)).any():
raise ValueError(
"a label cannot end before the observation it belongs to starts"
)
if n_samples is not None and span_ends.size != n_samples:
raise ValueError(
f"label_end_times has {span_ends.size} entries but the sample has {n_samples}"
)
return span_ends
def _apply_purge_and_embargo(
label_ends: np.ndarray,
test_mask: np.ndarray,
embargo_size: int,
) -> tuple[np.ndarray, int, int]:
"""Build a training mask that is purged of overlap and embargoed after.
Args:View on GitHub (pinned to 80ffdda44c)
Solutions
- Verify label_end_times[i] >= i for every row; regenerate labels from the actual horizon end dates
- If labels are stored as timestamps, convert them with the same time-origin/index mapping used for the observations
- Check for row drops/reordering: recompute label_end_times on the same dataframe you pass as the sample
- Pass label_end_times=None to fall back to the identity span np.arange(n_samples) while debugging
Example fix
// before label_ends = df['label_start'].to_numpy() # starts, not ends splits = list(purged_kfold_splits(X, n_folds=5, label_end_times=label_ends)) // after label_ends = df['label_end'].to_numpy() assert (label_ends >= np.arange(len(label_ends))).all() splits = list(purged_kfold_splits(X, n_folds=5, label_end_times=label_ends))
Defensive patterns
Strategy: validation
Validate before calling
import numpy as np label_ends = np.asarray(label_end_times) assert label_ends.size == len(X) assert np.isfinite(label_ends.astype(float)).all() assert (label_ends >= np.arange(label_ends.size)).all(), 'label ends before observation start'
Prevention
- Always derive label_end_times on the same dataframe you pass as the sample
- Use label end timestamps, not starts
- Assert monotone-consistent spans before splitting
When it happens
Trigger: Calling purged_kfold_splits, purged_walk_forward_splits, combinatorial_purged_splits, or detect_boundary_leakage with a label_end_times array (integer or datetime-converted-to-int) where some entry is less than its positional index, e.g. label_end_times=[5, 0, 7].
Common situations: Misaligned label arrays after slicing/dropping rows without reindexing, using label start times instead of end times, off-by-one when converting timestamps to integer indices, or sorting the sample without sorting label_end_times alongside.
Related errors
- label_end_times has {span_ends.size} entries but the sample
- n_folds must be at least {MIN_FOLDS}, got {n_folds}
- {n_samples} samples cannot make {n_folds} folds
- embargo_fraction must be in [0, 1), got {embargo_fraction}
- 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/552114b68de73ab1.
Report an issue: GitHub.