Vertical barriers

q.label.vertical_barriers maps each event to its maximum allowed outcome time. Integer horizons count observations; timedelta-like horizons choose the first observed timestamp at or beyond the requested duration.

Events without enough future data receive a missing barrier rather than being silently shortened.

import pandas as pd

import qrt as q

close = q.data.datasets.load("spy")["close"]
close = close.loc[close.index.max() - pd.DateOffset(years=1) :]
events = close.index[::21]
barriers = pd.DataFrame({
    "10 bars": q.label.vertical_barriers(close.index, 10, events=events),
    "30 days": q.label.vertical_barriers(close.index, "30D", events=events),
})
barriers.tail(10)
10 bars 30 days
event_time
2025-09-22 2025-10-06 2025-10-22
2025-10-21 2025-11-04 2025-11-20
2025-11-19 2025-12-04 2025-12-19
2025-12-19 2026-01-06 2026-01-20
2026-01-22 2026-02-05 2026-02-23
2026-02-23 2026-03-09 2026-03-25
2026-03-24 2026-04-08 2026-04-23
2026-04-23 2026-05-07 2026-05-26
2026-05-22 2026-06-08 2026-06-22
2026-06-24 2026-07-09 NaT

Bar time versus wall-clock time

A 10-bar horizon advances over available observations. A 30-day horizon uses elapsed calendar time and then snaps forward to the next observation. The two contracts intentionally differ around weekends, holidays, and missing sessions.

pd.DataFrame({
    "events": [len(barriers)],
    "complete_10_bar": [barriers["10 bars"].notna().sum()],
    "complete_30_day": [barriers["30 days"].notna().sum()],
})
events complete_10_bar complete_30_day
0 12 12 11
Back to top