Skip to main content

Targets

Target classes in Limen define how the target column is built during the manifest preparation pipeline. A target class is fitted once on the training split, then applied to validation and test without refitting.

Use this page to choose a target class, understand the convention each class must follow, or see what columns each class adds.

How Targets Fit In The Pipeline

The manifest pipeline is split-first:

  1. fetch and prepare raw data
  2. split into train, validation, and test
  3. apply indicators and features
  4. fit the target class on the training split
  5. apply the fitted instance to all splits using the stored state

This ensures that any statistics derived from the training data (such as quantile cutoffs) do not leak into validation or test.

Using A Target Class

from limen.targets import QuantileBinaryTarget

manifest.with_target_label(
'quantile_flag',
QuantileBinaryTarget,
fit_params={'source_column': 'roc_{roc_period}', 'quantile': 'q'},
transform_params={'shift': 'shift'},
)

fit_params are forwarded to __init__ on the training split. transform_params are forwarded to transform() on every split. Both support round-param references such that 'q' resolves to the current round's q value.

Choosing A Target Class

ClassOutput typeFitted on trainShift defaultNotes
QuantileBinaryTargetbinary UInt8yes — quantile cutoffshift=0Positive label above the top-N quantile of the source column.
ThresholdBinaryTargetbinary UInt8noshift=-1Positive label above a fixed numeric threshold.
TradelineLongBinaryTargetbinary UInt8yes — line-height percentile thresholdn/aConfirmed long breakout: forward max over lookahead_hours reaches the train-fitted threshold and a point return confirms it.
ForwardBreakoutTargetbinary UInt8noshift=-1Positive label if price rises at least threshold over the next forward_periods bars.
EmaBreakoutTargetbinary UInt8non/aPositive label if price breakout_horizon bars ahead exceeds EMA by breakout_delta.
NextBarUpTargetbinary UInt8nononeCanonical up decoder outcome: positive label if the next close is higher.
NextBarDownTargetbinary UInt8nononeCanonical down decoder outcome: positive label if the next close is lower.
NextReturnTargetcontinuous Float64non/aPercentage return over the next N bars. Use with regression architectures.
VolNormalizedReturnTargetcontinuous Float64yes — train sanity gatenoneCanonical return decoder outcome: log return divided by prior Parkinson volatility.
ForwardVolNormalizedReturnTargetcontinuous Float64yes — train sanity gatenonePredictive label: forward log return divided by current Parkinson volatility.
TripleBarrierTargetternary Int8non/aFirst barrier touched over the next max_horizon bars: 1 upper, -1 lower, 0 vertical. Horizontal barriers are volatility multiples.
RiskRewardRatioTargetcontinuous Float64non/aRatio of capturable_breakout to absolute max_drawdown per row.
ExitQualityTargetcontinuous Float64non/aCategorical score for closed trades based on exit_reason and exit_net_return.
RandomBinaryTargetbinary UInt8nononeUniformly random labels. Use as a noise benchmark.
IdentityTargetexisting columnnononeTarget column already present in the data. Validates the column exists on every split.

Reference

QuantileBinaryTarget

QuantileBinaryTarget(train_data, target_name, source_column, quantile)

Fits a quantile cutoff on the training split. Labels a bar as positive if the source column exceeds the (1 - quantile) quantile of the training distribution.

.with_target_label(
'quantile_flag',
QuantileBinaryTarget,
fit_params={'source_column': 'roc_4', 'quantile': 0.35},
transform_params={'shift': -1},
)
ParameterTypeDescription
source_columnstrColumn used to compute the quantile threshold
quantilefloatTop-N fraction for positive label (0.3 = top 30%)
shiftintPeriods to shift the label; shift=0 means no shift

TradelineLongBinaryTarget

TradelineLongBinaryTarget(train_data, target_name, max_duration_hours, min_height_pct, long_threshold_percentile)

Fits the breakout threshold on the training split: detects price lines on close (limen.utils.find_price_lines) and stores the long_threshold_percentile percentile of long-line heights. Raises when the training split yields no long lines. The transform labels a bar 1 when the maximum close over [t, t + lookahead_hours] reaches the threshold AND the point return at +confirmation_hours or at +lookahead_hours also exceeds it — the move must stick, not just spike. Rows without a full forward window become null and fall to the pipeline's drop_nulls.

.with_target_label(
'tradeline_long',
TradelineLongBinaryTarget,
fit_params={'max_duration_hours': 48, 'min_height_pct': 0.003, 'long_threshold_percentile': 75},
transform_params={'lookahead_hours': 48, 'confirmation_hours': 24},
)
ParameterTypeDescription
max_duration_hoursintExclusive upper bound on line duration in bars (fit)
min_height_pctfloatMinimum absolute line height as a fraction of start price (fit)
long_threshold_percentilefloatPercentile of long-line heights used as the threshold, in [0, 100] (fit)
lookahead_hoursintForward window for the breakout check; default 48
confirmation_hoursintForward offset for the point-confirmation check; default 24

ThresholdBinaryTarget

ThresholdBinaryTarget(train_data, target_name, source_column, threshold)

Labels a bar as positive if the source column exceeds a fixed threshold. No fitting step — the threshold is specified directly.

.with_target_label(
'above_zero',
ThresholdBinaryTarget,
fit_params={'source_column': 'roc_4', 'threshold': 0.0},
transform_params={'shift': -1},
)
ParameterTypeDescription
source_columnstrColumn to compare against the threshold
thresholdfloatFixed value; bars above this are labeled 1
shiftintPeriods to shift the label; default -1

ForwardBreakoutTarget

ForwardBreakoutTarget(train_data, target_name)

Labels a bar as positive if the close price rises by at least threshold over the next forward_periods bars. No fitting step.

.with_target_label(
'breakout',
ForwardBreakoutTarget,
transform_params={'forward_periods': 24, 'threshold': 0.02, 'shift': -1},
)
ParameterTypeDescription
forward_periodsintLook-ahead window in bars; default 24
thresholdfloatMinimum return for positive label; default 0.02 (2%)
shiftintAdditional shift applied after labeling; default -1

EmaBreakoutTarget

EmaBreakoutTarget(train_data, target_name)

Labels a bar as positive if the price breakout_horizon bars ahead exceeds the EMA of target_col by at least breakout_delta. No fitting step.

.with_target_label(
'breakout_ema',
EmaBreakoutTarget,
transform_params={'target_col': 'close', 'ema_span': 30, 'breakout_delta': 0.2, 'breakout_horizon': 3},
)
ParameterTypeDescription
target_colstrColumn name to analyze for breakouts; default 'close'
ema_spanintPeriod for EMA calculation; default 30
breakout_deltafloatMinimum EMA-relative displacement for positive label; default 0.2
breakout_horizonintForward window in bars; default 3

NextReturnTarget

NextReturnTarget(train_data, target_name)

Produces a continuous target as the percentage return over the next N bars. Use with regression architectures such as xgboost_regressor. No fitting step.

.with_target_label(
'next_return',
NextReturnTarget,
transform_params={'periods': 1, 'scale': 100.0},
)
ParameterTypeDescription
periodsintLook-ahead window in bars; default 1
scalefloatMultiplier applied to the raw return; default 100.0

NextBarUpTarget

NextBarUpTarget(train_data, target_name)

Produces a binary target named next_bar_up: 1 when the next close is higher than the current close, 0 otherwise. The final row in each split has no next bar, so it is null and is dropped by manifest preparation.

.with_target_label('next_bar_up', NextBarUpTarget)

No parameters.

NextBarDownTarget

NextBarDownTarget(train_data, target_name)

Produces a binary target named next_bar_down: 1 when the next close is lower than the current close, 0 otherwise. The final row in each split has no next bar, so it is null and is dropped by manifest preparation.

.with_target_label('next_bar_down', NextBarDownTarget)

No parameters.

VolNormalizedReturnTarget

VolNormalizedReturnTarget(train_data, target_name, high_col='high', low_col='low', open_col='open', close_col='close', halflife=50, min_periods=150)

Produces a continuous target named vol_normalized_return. The target is log(close / close.shift(1)) / sigma.shift(1), where sigma is the EWMA Parkinson volatility from (log(high / low) ** 2) / (4 * ln(2)). The one-bar sigma shift prevents the current bar range from entering the current return label.

Dirty OHLC rows are dropped before target construction when high < max(open, close) or low > min(open, close). Zero volatility is converted to null, not epsilon.

.with_target_label(
'vol_normalized_return',
VolNormalizedReturnTarget,
fit_params={'halflife': 50, 'min_periods': 150},
)
ParameterTypeDescription
high_colstrHigh price column; default 'high'
low_colstrLow price column; default 'low'
open_colstrOpen price column used for dirty-bar filtering; default 'open'
close_colstrClose price column; default 'close'
halflifeintEWMA half-life in bars; default 50
min_periodsintWarmup before volatility is emitted; default 150

The training split must pass the Parkinson-to-close volatility sanity gate: median sigma_P / sigma_C2C must be within [0.9, 1.1]. Bucketing is deliberately downstream: fit q33/q66 of abs(vol_normalized_return) on train, persist the boundaries, and load them at inference instead of refitting online.

ForwardVolNormalizedReturnTarget

ForwardVolNormalizedReturnTarget(train_data, target_name, periods=1, absolute=False, halflife=50, min_periods=150, high_col='high', low_col='low', open_col='open', close_col='close')

Produces a continuous predictive label as log(close.shift(-periods) / close) / sigma, where sigma is the current EWMA Parkinson volatility. Set absolute=True to normalize absolute forward movement instead of signed direction.

.with_target_label(
'forward_vol_normalized_return',
ForwardVolNormalizedReturnTarget,
fit_params={'periods': 1, 'halflife': 50, 'min_periods': 150},
)
ParameterTypeDescription
periodsintForward return horizon in bars; default 1
absoluteboolWhether to use absolute forward log return; default False
halflifeintEWMA half-life in bars; default 50
min_periodsintWarmup before volatility is emitted; default 150
high_colstrHigh price column; default 'high'
low_colstrLow price column; default 'low'
open_colstrOpen price column used for dirty-bar filtering; default 'open'
close_colstrClose price column; default 'close'

The class reuses the same dirty-bar filter and Parkinson-to-close volatility sanity gate as VolNormalizedReturnTarget.

TripleBarrierTarget

TripleBarrierTarget(train_data, target_name, upper_multiple=1.0, lower_multiple=1.0, max_horizon=10, span=100, min_periods=100, close_col='close')

Produces a ternary label from Lopez de Prado's triple-barrier method. For each bar, the forward close path is followed for up to max_horizon bars and labelled by the first barrier it touches: 1 if the upper (profit-taking) barrier is touched first, -1 if the lower (stop-loss) barrier is touched first, and 0 if neither is touched within max_horizon bars (the vertical barrier). No fitting step.

The horizontal barriers are volatility multiples — +upper_multiple * sigma and -lower_multiple * sigma, where sigma is the EWMA standard deviation of close-to-close returns with the given span. The barrier width is taken from the entry-bar volatility, so it adapts to the local regime instead of using a fixed percentage.

A bar is null during the volatility warmup (min_periods), when its volatility is zero, and when the vertical barrier would extend past the available data and no barrier is touched within the truncated window. Manifest preparation drops null target rows. Size min_periods (and span) against the smallest split: a split shorter than the volatility warmup is entirely null and is dropped, leaving no labelled rows for that split.

.with_target_label(
'triple_barrier',
TripleBarrierTarget,
fit_params={'upper_multiple': 2.0, 'lower_multiple': 2.0, 'max_horizon': 24, 'span': 100, 'min_periods': 100},
)
ParameterTypeDescription
upper_multiplefloatProfit-taking barrier width in volatility units; default 1.0
lower_multiplefloatStop-loss barrier width in volatility units; default 1.0
max_horizonintVertical barrier as a forward window in bars; default 10
spanintEWMA span for the close-to-close return volatility; default 100
min_periodsintWarmup before volatility is emitted; default 100
close_colstrClose price column used for the barrier path; default 'close'

RiskRewardRatioTarget

RiskRewardRatioTarget(train_data, target_name)

Computes capturable_breakout / (|max_drawdown| + 0.001) per row. Expects both columns to be available on every split. No fitting step. No Limen indicator or feature produces capturable_breakout or max_drawdown — they must be user-supplied in the input data, and transform raises ValueError when they are absent.

.with_target_label('rr_ratio', RiskRewardRatioTarget)

No parameters.

ExitQualityTarget

ExitQualityTarget(train_data, target_name)

Scores closed trades as high, low, or medium based on exit_reason and exit_net_return. Expects both columns to be available on every split. No fitting step. No Limen indicator or feature produces exit_reason or exit_net_return — they must be user-supplied in the input data, and transform raises ValueError when they are absent.

.with_target_label(
'exit_quality',
ExitQualityTarget,
transform_params={'exit_quality_high': 1.0, 'exit_quality_low': 0.2, 'exit_quality_medium': 0.5},
)
ParameterTypeDescription
exit_quality_highfloatScore for profitable target_hit or trailing_stop exits; default 1.0
exit_quality_lowfloatScore for stop_loss or unprofitable timeout exits; default 0.2
exit_quality_mediumfloatScore for neutral exits; default 0.5

RandomBinaryTarget

RandomBinaryTarget(train_data, target_name, random_state=None)

Produces uniformly random binary labels. No fitting step. Use as a noise benchmark to verify that a trained model outperforms chance.

.with_target_label('outcome', RandomBinaryTarget)

random_state is optional. Pass an integer for reproducible labels across equal split sizes and call order; None uses an unseeded generator.

IdentityTarget

IdentityTarget(train_data, target_name)

For when the target column is already present in the data. Validates that the column exists on the training split and on every subsequent split. The data is returned unchanged; no new column is added.

.with_target_label('label', IdentityTarget)

No parameters.

Writing A Custom Target Class

A target class must follow this convention:

class MyTarget:

def __init__(self, train_data: pl.DataFrame, target_name: str, **fit_params) -> None:
self.target_name = target_name
# fit on train_data here if needed

def transform(self, data: pl.DataFrame, **transform_params) -> pl.DataFrame:
return data.with_columns(
(pl.col('close').shift(-1) > pl.col('close')).cast(pl.Int8).alias(self.target_name)
)

There is no base class to inherit from — the convention is enforced structurally, the same way scalers work. Any class that accepts (train_data, target_name, **fit_params) in __init__ and exposes transform(data, **transform_params) -> pl.DataFrame is a valid target class.

  • Experiment Manifest for how targets plug into the split-first pipeline
  • Scalers for train-fitted preprocessing that runs after target construction
  • Transforms for stateless post-model helpers