> ## Documentation Index
> Fetch the complete documentation index at: https://systematica.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Range Breakout

> systematica.api.models.range_breakout

Range-based models in Systematica implement trading strategies that identify price ranges within specific time periods and generate signals based on price behavior relative to these ranges.
These models use time-based grouping rather than rolling windows, making them fundamentally different from other model types in the framework.

## Overview and Architecture

Range-based models operate by dividing time series data into discrete temporal groups (e.g., daily, weekly periods) and defining trading ranges using a subset of each period. The framework provides two complementary strategies:

* **Range Breakout**: Trend-following strategy that trades breakouts above/below established ranges
* **Range Mean Reversion**: Counter-trend strategy that trades reversals at range boundaries

## Core Concepts: Time-Based Grouping vs Rolling Windows

Range-based models use `vbt.Splitter.from_grouper` to create discrete time periods, contrasting with rolling window approaches used by other models:

| Aspect            | Range-Based Models                 | Rolling Window Models         |
| ----------------- | ---------------------------------- | ----------------------------- |
| Grouping Method   | `splitter.from_grouper`            | `splitter.from_rolling`       |
| Period Definition | Fixed time periods (daily, weekly) | Sliding windows (N bars)      |
| Range Calculation | First portion of each period       | Continuous moving calculation |
| Signal Reset      | Resets each period                 | Continuous evolution          |

## `RangeBreakout`

```python theme={null}
RangeBreakout(
    splitter: str = 'from_grouper',
    custom_splitter: str = None,
    custom_splitter_kwargs: Dict[str, Any] = None,
    training_window: str | float = '2h',
    by: str = 'D',
    freq: str = 'auto',
    start_time: str = None,
    end_time: str = '23:00',
    tol: float = 0.5,
)
```

The RangeBreakout class implements trend-following breakout strategies by detecting
when prices break above or below established ranges.

Range breakout model implementation using `splitter.from_grouper`.

This model identifies trading ranges during specific time periods and generates
signals when price breaks above or below these ranges. Unlike rolling window
models, this uses time-based grouping (e.g., daily, weekly) to define ranges.

**Notes**:

**Parameter Requirements:**

* `training_window`: Must be a string in time format (e.g., "2h", "4h") or a float
  representing a fraction of the `by` period. Integer values are NOT supported and
  will cause errors during cross-validation.

* When using with Optuna optimization, define `training_window` as:

```python theme={null}
>>> training_window = sma.Categorical(
...     "training_window", 
...     choices=["1h", "2h", "4h", "6h", "8h", "12h"]
... )
```

**Signal Model Compatibility:**

This model outputs discrete position values (-1, 0, 1) and works with the "crossover"
signal model. For successful signal generation:

* Set `long_entries < 1.0` (e.g., 0.9) so signals cross the threshold
* Set `short_entries > -1.0` (e.g., -0.9) so signals cross the threshold
* Exit thresholds should be within (-0.5, 0.5) for optimal results

**Common Issues and Solutions:**

1. **Integer training\_window causes "Must provide at least one range" error**

   ```python theme={null}
    >>> # WRONG - Integer hours not supported
    >>> model = RangeBreakout(training_window=24)  
    >>> # CORRECT - Use string format
    >>> model = RangeBreakout(training_window="24h")
    >>> # CORRECT - Use fraction of period
    >>> model = RangeBreakout(training_window=0.5)  # 50% of the day
   ```

2. **All Optuna trials pruned with NaN metrics**

   ```python theme={null}
    >>> # WRONG - Thresholds at extremes prevent crossovers
    >>> signal_params = {
    ...     "long_entries": 1.0,   # Model output reaches but never crosses 1.0
    ...     "short_entries": -1.0  # Model output reaches but never crosses -1.0
    ... }
    >>> # CORRECT - Thresholds within output range allow crossovers
    >>> signal_params = {
    ...     "long_entries": 0.9,   # Crossover happens when output goes from <0.9 to >0.9
    ...     "short_entries": -0.9, # Crossover happens when output goes from >-0.9 to <-0.9
    ...     "long_exits": 0.1,
    ...     "short_exits": -0.1
    ... }
   ```

**Examples**:

Basic usage with string training window:

```python theme={null}
>>> model = RangeBreakout(
...     by="1d",
...     training_window="2h",  # First 2 hours of each day
...     start_time="1:00",
...     end_time="21:00",
...     tol=0.1
... )
>>> output = model(data, "BTCUSDT", "ETHUSDT")
```

Optuna optimization setup:

```python theme={null}
>>> study = RangeBreakout.run_optuna_study(
...     data, "BTCUSDT", "ETHUSDT",
...     by="D",
...     training_window=sma.Categorical("training_window", choices=["2h", "4h", "6h"]),
...     signal_model='crossover',
...     long_entries=sma.Float("long_entries", low=0.5, high=0.95),
...     short_entries=sma.Float("short_entries", low=-0.95, high=-0.5),
...     metrics="sharpe_ratio",
...     n_trials=100
... )
```

**See Also**:

* `systematica.signals.Crossover` : Signal model used with this indicator
* `examples/scripts/models/range_breakout/example_range_breakout.py` : Complete example

Method generated by attrs for class RangeBreakout.

### Ancestors

* `systematica.models.base.BaseStatArb`
* `abc.ABC`

### Static methods

#### `apply_between_time`

```python theme={null}
apply_between_time(
    data: vectorbtpro.data.base.Data,
    start_time: str,
    end_time: str,
    **kwargs,
) ‑> vectorbtpro.data.base.Data
```

Helper function to clean data using pandas `between_time`.

More information in the [Pandas Documentation](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.between_time.html)

**Parameters**:

| Name         | Type            | Default | Description                                      |
| ------------ | --------------- | ------- | ------------------------------------------------ |
| `data`       | `vbt.Data`      | `--`    | Input data containing time series data.          |
| `start_time` | `datetime.time` | `--`    | Initial time as a time filter limit.             |
| `end_time`   | `datetime.time` | `--`    | End time as a time filter limit.                 |
| `kwargs`     | `tp.Kwargs`     | `--`    | Additional keyword arguments for `between_time`. |

**Returns**:

| Type       | Description        |
| ---------- | ------------------ |
| `vbt.Data` | Slice data object. |

### Instance variables

* `by: str`: Split data temporarily. Defaults to `D` for daily.

* `custom_splitter: str`: Custom splitter function, if provided. Defaults to `None`.

* `custom_splitter_kwargs: Dict[str, Any]`: Additional arguments for the custom splitter. Defaults to `None`.

* `end_time: str`: End time as a time filter limit. If `None`, no processing is done.  Defaults to `00:00`.

* `freq: str`: Frequency of the index (e.g., `daily`, `15 min`, `index_mean`). Infer or convert the frequency for a datetime index. If freq is None, the frequency is inferred using `parse_index_freq`. If a  string is provided:  If `auto`, the frequency is detected with `auto_detect_freq`. If the string starts with `index_`, the corresponding method  (obtained after stripping the prefix) is applied to the differences between  consecutive index values. If `freq_from_n` is an integer (positive or negative), the index is  limited to the first or last `N` elements respectively. Defaults to `auto`.

* `splitter: str`: Splitter method for cross-validation, by default `from_grouper`.

* `start_time: str`: Initial time as a time filter limit. If `None`, no processing is done.  Defaults to `None`.

* `tol: float`: Range tolerance to trigger a long/short signal such as `trigger = tol * range_values`.  Defaults to `0.5`.

* `training_window: str | float`: Specification for further splitting of each range. The split size of the  training window used in `splitter.from_grouper`. It could be a float  (e.i. `0.5`, which split each range in half) or a string (e.i. `4h` or `4 hours`. which  takes the first 4 hours of the range). Defaults to `2h` (2 hours).

### Methods

#### `check_training_window`

```python theme={null}
check_training_window(
    self,
    attribute: Type,
    value: Any,
)
```

Validate training\_window.

**Parameters**:

| Name        | Type      | Default | Description                    |
| ----------- | --------- | ------- | ------------------------------ |
| `attribute` | `tp.Type` | `--`    | The attribute being validated. |
| `value`     | `tp.Any`  | `--`    | The model instance.            |

**Raises**:

| Type         | Description                                                              |
| ------------ | ------------------------------------------------------------------------ |
| `ValueError` | If the training window size is greater than the temporal parameter `by`. |

#### `plot_model_output`

```python theme={null}
plot_model_output(
    self,
    data: vectorbtpro.data.base.Data,
    symbol: str,
    use_close: bool = True,
    **layout_kwargs,
) ‑> vectorbtpro.utils.figure.FigureWidget
```

Plot range breakout.

**Parameters**:

| Name            | Type        | Default | Description                                                           |
| --------------- | ----------- | ------- | --------------------------------------------------------------------- |
| `data`          | `vbt.Data`  | `--`    | A data object containing asset prices and other relevant information. |
| `symbol`        | `str`       | `--`    | The asset symbol to plot.                                             |
| `use_close`     | `bool`      | `True`  | Use close price if set to `True`. Otherwise, use open price.          |
| `layout_kwargs` | `tp.Kwargs` | `--`    | Additional layout arguments for the figure.                           |

**Returns**:

| Type               | Description                                            |
| ------------------ | ------------------------------------------------------ |
| `vbt.FigureWidget` | A Plotly FigureWidget containing range breakout model. |

#### `plot_signals`

```python theme={null}
plot_signals(
    self,
    data: vectorbtpro.data.base.Data,
    symbol: str,
    use_close: bool = True,
    **layout_kwargs,
) ‑> vectorbtpro.utils.figure.FigureWidget
```

Plot range breakout.

**Parameters**:

| Name            | Type        | Default | Description                                                           |
| --------------- | ----------- | ------- | --------------------------------------------------------------------- |
| `data`          | `vbt.Data`  | `--`    | A data object containing asset prices and other relevant information. |
| `symbol`        | `str`       | `--`    | The asset symbol to plot.                                             |
| `use_close`     | `bool`      | `True`  | Use close price if set to `True`. Otherwise, use open price.          |
| `layout_kwargs` | `tp.Kwargs` | `--`    | Additional layout arguments for the figure.                           |

**Returns**:

| Type               | Description                                            |
| ------------------ | ------------------------------------------------------ |
| `vbt.FigureWidget` | A Plotly FigureWidget containing range breakout model. |
