triple_barrier

Defined in fynance.features.labels

triple_barrier(prices, events=None, pt=1.0, sl=1.0, max_holding=21, vol=None)[source]

Path-dependent triple-barrier labels (AFML ch. 3).

Warning

This is a label, not a feature — it is only known at t_out, strictly after t_in. Never use it as a model input; see the module warning and route it through fynance.data.split.

From each event start i (t_in), two horizontal barriers are set on the simple return of prices relative to prices[i]: an upper barrier at +pt * scale_i and a lower barrier at -sl * scale_i. The path is then walked forward one bar at a time over (i, i + max_holding] (clipped to the last index): the first bar that touches a barrier sets the label (+1 upper, -1 lower); if neither is touched before the vertical barrier, the label is 0 at that vertical bar.

\[\tau_i = \min\{t > i : r_{i,t} \geq pt \cdot scale_i \text{ or } r_{i,t} \leq -sl \cdot scale_i\} \wedge (i + max\_holding)\]

with \(r_{i,t} = prices_t / prices_i - 1\).

Parameters:
pricesnp.ndarray[dtype, ndim=1]

One-dimensional series of price levels, length T.

eventsnp.ndarray[int], optional

Integer indices of label start times (t_in). Must lie in [0, T - 2] so that at least one future bar exists. Default is np.arange(T - 1) (one label per bar, except the last).

pt, slfloat, optional

Profit-take / stop-loss barrier width, as a multiple of scale_i. Both must be strictly positive. Default 1.0 for both.

max_holdingint, optional

Maximum holding period (vertical barrier), in bars. Must be >= 1. Default 21.

volnp.ndarray[dtype, ndim=1], optional

Per-bar, return-scale volatility, shape (T,) (e.g. a causal trailing realized volatility of simple returns — see realized_volatility or smstd on returns). scale_i is vol[i]. If None (default), scale_i is the constant standard deviation of the 1-bar simple returns computed over the whole series — this is an in-sample quantity (it peeks at the entire history, future included) kept only as a convenience default; pass a causal vol for a genuinely leakage-free label.

Returns:
np.ndarray[LABEL_DTYPE, ndim=1]

Structured array of length len(events) with fields:

  • t_in (int64) — label start index (== events).

  • t_out (int64) — index where the label was resolved.

  • label (int8) — +1 upper barrier, -1 lower barrier, 0 vertical barrier (timeout).

  • ret (float64) — simple return from t_in to t_out.

Raises:
ValueError

If prices has fewer than 2 observations; if any events index is outside [0, T - 2]; if pt <= 0 or sl <= 0; if max_holding < 1; or if vol is given with a shape other than (T,).

See also

meta_labels, label_concurrency, uniqueness_weights
fynance.data.split.train_test_split, fynance.data.split.walk_forward

Notes

Barrier checks favor the upper barrier on a tie (both touched on the same bar, which can only happen with scale_i = 0).

References

[1]

Lopez de Prado, M. (2018). Advances in Financial Machine Learning. Wiley. Chapter 3, “Labeling”.

Examples

>>> import numpy as np
>>> prices = np.array([100., 101., 105., 101., 98., 97.])
>>> vol = np.full(6, 0.02)
>>> out = triple_barrier(prices, events=np.array([0]), max_holding=4, vol=vol)
>>> out['t_in'], out['t_out'], out['label'], out['ret']
(array([0]), array([2]), array([1], dtype=int8), array([0.05]))

A path that never touches either barrier resolves at the vertical bar:

>>> flat = np.array([100., 100.5, 100.2, 100.8, 100.1])
>>> out = triple_barrier(flat, events=np.array([0]), max_holding=3, vol=np.full(5, 1.0))
>>> out['t_out'], out['label']
(array([3]), array([0], dtype=int8))