# Indicators: calculate, compose, and read values



Use `self.ta` inside `Strategy.init()` for built-in indicators, `self.ind` for a custom function, and `indicators.compute` for a calculation outside a backtest. Browse the [indicator catalog](https://docs.openbacktesting.com/reference/indicators/index.md) for every formula, its parameters, inputs, and outputs.

[compute](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-compute) · [catalog](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-catalog) · [describe](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-describe) · [IndicatorHandle](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-types-indicatorhandle) · [IndicatorResult](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-api-indicatorresult) · [is_cross](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-methods-technicalanalysis-is-cross) · [is_crossover](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-methods-technicalanalysis-is-crossover) · [is_crossunder](https://docs.openbacktesting.com/reference/indicator-api/#openbacktesting-indicators-methods-technicalanalysis-is-crossunder)

## Single and multiple outputs

```python
from openbacktesting import Backtest, Strategy
from openbacktesting.test import GOOG

class IndicatorExample(Strategy):
    def init(self):
        self.ema = self.ta.ema(self.close, length=20)
        self.macd = self.ta.macd(fast=12, slow=26, signal=9)
        self.smooth = self.ta.sma(self.macd.histogram, length=3)

    def next(self):
        if self.ema.valid[-1] and self.smooth.valid[-1]:
            if self.close[-1] > self.ema[-1] and self.smooth[-1] > 0:
                if not self.position:
                    self.buy(qty=1)
            elif self.position:
                self.position.close()

result = Backtest(GOOG, IndicatorExample).run()
print(result["Return [%]"])
```

An EMA returns an `IndicatorHandle`. A multi-output formula such as MACD returns an `IndicatorResult`; select `.macd`, `.signal`, or `.histogram` before passing a channel to another indicator. Bracket access such as `result["histogram"]` also works. Built-in calculations are registered only in `init()`.

## Bind inputs and read outputs

| Argument or operation | Meaning |
| --- | --- |
| `self.ta.ema(length=20)` | Use that indicator's default market column (Close here). |
| `self.ta.ema(self.hl2, length=20)` | Use a same-run single-channel handle. A column name such as `"Close"` is also accepted. |
| `inputs={"high": "High", "low": "Low", "close": "Close"}` | Explicit mapping of formula inputs to data columns or same-run channels; do not combine with positional source arguments. |
| Formula keywords such as `length`, `fast`, `slow` | See each indicator's parameter table for defaults, allowed values, and meaning. |
| `backend=None` | Use the formula's default implementation. Only documented alternatives are accepted. |
| `required_outputs=None` | Use default outputs for readiness diagnostics; an explicit channel list changes readiness, not whether `next()` runs. |
| `handle[-1]`, `handle[-2]` | Current and previous visible values; check history length. |
| `handle.valid[-1]` | Whether this channel has a consumable current value. Check it before trading. |
| `handle.ready[-1]` | Minimum-history/readiness diagnostic. It does not skip callbacks. |
| `handle.s`, `handle.df`, `numpy.asarray(handle)` | Independent snapshots of the visible prefix. |

During `next()`, saved handles and their slices follow the current bar. A saved array/Series or arithmetic result does not. `False` and zero may be valid signals; NaN is not a usable signal. Do not use `bool(NaN)` as a validity check. For a custom calculation, see [Strategy.ind](https://docs.openbacktesting.com/reference/strategy/index.md#openbacktesting-strategy-ind).

## Crosses and confirmed events

`self.ta.is_crossover(a, b)` returns a boolean at the current bar; `is_crossunder` reverses direction and `is_cross` accepts either direction. Both operands need two valid visible observations. These predicates can be called in `next()`.

`self.ta.crossover(a, b)` instead registers a whole signal history in `init()`. Use it when composing signals with `valuewhen` or `barssince`. A pivot becomes available when its right-hand confirmation window finishes; it does not backfill a tradable signal at the earlier extreme. Check the [pivot](https://docs.openbacktesting.com/reference/indicators/pivothigh/index.md) and [session-level](https://docs.openbacktesting.com/reference/indicators/pivot_points/index.md) pages before using those results.

## Calculate outside a strategy

```python
from openbacktesting import indicators
from openbacktesting.test import GOOG

ema = indicators.compute("ta.ema", GOOG, parameters={"length": 20})
print(ema.s.tail())
description = indicators.describe("ta.ema")
catalog = indicators.catalog()
```

`compute` returns an `IndicatorHandle` covering the full input. Multi-output offline results select channels by name, for example `macd["histogram"]`. `parameters` supplies formula options; `inputs` maps input names to frame columns. Ordinary offline calls can omit `cache` and `cursor`. `describe` returns one formula's specification and `catalog` lists the available definitions.

<a id="openbacktesting-indicators-compute"></a>



## compute

```python
indicators.compute(
    indicator,
    frame,
    *,
    parameters=None,
    backend=None,
    inputs=None,
    cache=None,
    cursor=None,
    required_outputs=None,
)
```

Compute reviewed indicators in bulk on explicit market data and return read-only result handles.

<a id="openbacktesting-indicators-catalog"></a>



## catalog

```python
indicators.catalog()
```

Return a tuple of all reviewed definitions, including deferred entries that cannot be called.

<a id="openbacktesting-indicators-describe"></a>



## describe

```python
indicators.describe(indicator)
```

Look up an indicator definition by stable ID without computing it.

<a id="openbacktesting-indicators-types-indicatorhandle"></a>



## IndicatorHandle

Time is the final dimension; all derived views apply cursors and selectors only when read.

### Attributes

| Attribute | Meaning |
| --- | --- |
| <a id="openbacktesting-indicators-types-indicatorhandle-df"></a>`df` | pandas DataFrame copy of all channels' current prefixes. |
| <a id="openbacktesting-indicators-types-indicatorhandle-dtype"></a>`dtype` | Data type of the underlying read-only numeric buffer. |
| <a id="openbacktesting-indicators-types-indicatorhandle-metadata"></a>`metadata` | Return a deep copy of provenance metadata; modifications do not affect the registered result. |
| <a id="openbacktesting-indicators-types-indicatorhandle-ndim"></a>`ndim` | Output dimensions: 1 for a single channel, 2 for multiple channels. |
| <a id="openbacktesting-indicators-types-indicatorhandle-ready"></a>`ready` | Read-only mask indicating whether the current prefix meets minimum-history conditions. |
| <a id="openbacktesting-indicators-types-indicatorhandle-s"></a>`s` | pandas Series copy of the first channel's current prefix; saved copies do not grow with the cursor. |
| <a id="openbacktesting-indicators-types-indicatorhandle-shape"></a>`shape` | Shape of the currently visible prefix; time is the last axis for multiple outputs. |
| <a id="openbacktesting-indicators-types-indicatorhandle-valid"></a>`valid` | Read-only validity masks for each channel's current prefix; False does not mean numeric zero. |

<a id="openbacktesting-indicators-types-indicatorhandle-astype"></a>



### IndicatorHandle.astype

```python
IndicatorHandle.astype(dtype)
```

Convert the current prefix to a NumPy array snapshot with dtype; do not retain a dynamic cursor.

<a id="openbacktesting-indicators-types-indicatorhandle-copy"></a>



### IndicatorHandle.copy

```python
IndicatorHandle.copy()
```

Return a writable NumPy copy of the current prefix that no longer follows the cursor.

<a id="openbacktesting-indicators-types-indicatorhandle-first-ready"></a>



### IndicatorHandle.first_ready

```python
IndicatorHandle.first_ready()
```

Return the earliest ready position for required outputs, or total length if never ready; does not control callbacks.

<a id="openbacktesting-indicators-types-indicatorhandle-ready-at"></a>



### IndicatorHandle.ready_at

```python
IndicatorHandle.ready_at(bar)
```

Query readiness of required outputs and minimum-history conditions; does not control strategy callbacks.

<a id="openbacktesting-indicators-api-indicatorresult"></a>



## IndicatorResult

Read-only attribute container for multiple outputs; channel views are created only during initialization.

### Attributes

| Attribute | Meaning |
| --- | --- |
| <a id="openbacktesting-indicators-api-indicatorresult-df"></a>`df` | Return a pandas DataFrame copy of the currently visible prefix of all channels. |
| <a id="openbacktesting-indicators-api-indicatorresult-metadata"></a>`metadata` | Return an independent copy of the underlying handle's provenance metadata. |
| <a id="openbacktesting-indicators-api-indicatorresult-ready"></a>`ready` | Minimum-history mask shared by all outputs. |
| <a id="openbacktesting-indicators-api-indicatorresult-valid"></a>`valid` | Underlying validity masks for each channel; check the selected channel before trading. |

<a id="openbacktesting-indicators-methods-technicalanalysis-is-cross"></a>



## is_cross

```python
indicators.methods.TechnicalAnalysis.is_cross(left, right)
```

Check whether either-direction crossing occurs on the last currently visible bar.

<a id="openbacktesting-indicators-methods-technicalanalysis-is-crossover"></a>



## is_crossover

```python
indicators.methods.TechnicalAnalysis.is_crossover(left, right)
```

Check whether the current left value is strictly above the right, with the preceding left value at or below the right.

<a id="openbacktesting-indicators-methods-technicalanalysis-is-crossunder"></a>



## is_crossunder

```python
indicators.methods.TechnicalAnalysis.is_crossunder(left, right)
```

Check whether the current left value is strictly below the right, with the preceding left value at or above the right.

[Indicator catalog](https://docs.openbacktesting.com/reference/indicators/index.md) · [Strategy](https://docs.openbacktesting.com/reference/strategy/index.md) · [User manual](https://docs.openbacktesting.com/index.md)
