Skip to content

How Purging and Embargo Work for Overlapping Financial Labels

Original question

Can you explain how purged cross-validation and embargo periods help prevent information leakage in financial machine learning?

Purging removes training rows whose outcome intervals overlap a held-out outcome interval. An embargo removes some additional training decisions shortly after a held-out block when nearby inputs may still depend on the same observations. Both depend on the information window for each row. If your goal is to simulate live retraining, also enforce that training uses only past information at each fit date.

Compare the intervals behind the labels

Store start and end timestamps (si,ei)(s_i,e_i) for every realised outcome. With closed intervals, remove a training row ii when it overlaps any test row jj:

si≤ejandei≥sj.s_i\le e_j \quad\text{and}\quad e_i\ge s_j.

Use a strict inequality where a shared endpoint carries no shared outcome under your convention. After purging, define exactly which timestamp anchors the embargo and how long it lasts. One conservative rule removes later training starts in a buffer after the last test outcome ends. In a strictly forward split there are no post-test training rows to embargo. The equivalent boundary check is that every included training label is resolved by the fit cutoff.

Use closed intervals and suppose a test block contains labels covering sessions 10–13 and 12–15. Training labels covering 6–9 or 19–22 are disjoint. A row spanning 8–11 overlaps the first outcome, and a row spanning 15–18 touches the end of the second. Purge both. With a two-session embargo after session 15, also remove later training decisions on sessions 16 and 17. The boundary conditions change if your labels measure intervals between closes and you choose a different endpoint convention.

Purging and embargo protect different boundaries. Purging tests the actual time span used to realise an outcome. Embargo removes an additional buffer after the test block when the chosen validation design also trains on later rows. If features use trailing windows, examine their reach too. A five-day embargo does not repair a feature accidentally constructed from tomorrow's close. For a strict simulation of a production forecast, train only on earlier observations with labels already known by the cutoff.

Choose an interval convention based on the data used to realise each return. For example, if labels represent half-open time intervals, two labels touching at an endpoint may share no return interval. Their strict overlap condition is si<ejs_i<e_j and ei>sje_i>s_j. If both labels explicitly include the same endpoint observation and that creates information overlap in your design, a closed interval rule is safer. Document the convention and test a touching pair, an entirely contained pair and a fully disjoint pair. Those cases catch different boundary errors.

Filter overlapping intervals in Python

def eligible_rows(intervals, test_indices, embargo_sessions): test_set = set(test_indices) test_windows = [intervals[j] for j in test_indices] test_end = max(end for _, end in test_windows) retained = [] for i, (start, end) in enumerate(intervals): overlaps = any(start <= b and end >= a for a, b in test_windows) embargoed = test_end < start <= test_end + embargo_sessions if i not in test_set and not overlaps and not embargoed: retained.append(i) return retained windows = [(6, 9), (8, 11), (10, 13), (12, 15), (15, 18), (17, 20), (19, 22)] print(eligible_rows(windows, [2, 3], 2)) # [0, 6]

References and next steps