uniqueness_weights

Defined in fynance.features.labels

uniqueness_weights(t_in, t_out, T)[source]

Average-uniqueness sample weights for overlapping labels (AFML ch. 4).

Warning

This produces sample weights derived from labels, not a feature — see the module warning.

Overlapping triple_barrier labels are drawn from overlapping price paths, so they are not i.i.d.: a bar covered by many concurrent labels gets diluted “credit” in each of them. For each label, the uniqueness at a bar is 1 / concurrency there (see label_concurrency); the weight is the label’s average uniqueness over its own [t_in, t_out] span, rescaled so the weights sum to n (the number of labels) — the AFML convention, so the mean weight is 1 regardless of how much overlap there is.

\[\bar u_i = \frac{1}{t_{out,i} - t_{in,i} + 1} \sum_{t=t_{in,i}}^{t_{out,i}} \frac{1}{c_t}, \qquad w_i = \bar u_i \cdot \frac{n}{\sum_j \bar u_j}\]

where \(c_t\) is label_concurrency.

This composes multiplicatively with a recency scheme such as fynance.models.training.exp_sample_weights: multiply the two weight vectors together (both already convey “importance”, one for overlap and one for recency) rather than choosing one over the other.

Parameters:
t_in, t_outnp.ndarray[int, ndim=1]

Label start / end indices, as in label_concurrency.

Tint

Number of bars in the underlying series.

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

Shape (n,) where n = len(t_in), non-negative, summing to n (up to floating-point error). Disjoint labels all get weight 1.0; more overlap means a smaller weight.

Raises:
ValueError

Propagated from label_concurrency for malformed spans.

See also

label_concurrency, triple_barrier
fynance.models.training.exp_sample_weights

References

[1]

Lopez de Prado, M. (2018). Advances in Financial Machine Learning. Wiley. Chapter 4, “Sample Weights”.

Examples

Disjoint labels share no overlap, so every weight is 1:

>>> import numpy as np
>>> uniqueness_weights(np.array([0, 2]), np.array([1, 3]), T=4)
array([1., 1.])

Two identical, fully overlapping labels split their combined uniqueness evenly, and a third, disjoint label keeps its full weight – rescaled so all three sum to 3:

>>> uniqueness_weights(np.array([0, 0, 2]), np.array([1, 1, 3]), T=4)
array([0.75, 0.75, 1.5 ])