---
title: "Named streams"
description: "Multi-output indicators like Bollinger bands, MACD, and the stochastic produce several lines at once. In a wrun indicator each stream is an output of its own…"
order: 7
section: "core-concepts"
---

<!-- source: docs/indicators/core-concepts/named-streams.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Named streams

Multi-output indicators like Bollinger bands, MACD, and the stochastic
produce several lines at once. In a wrun indicator each stream is an
`output` of its own, and the computation behind them is a class whose
fields you read by name after one `update()`: `bands.upper`,
`macd.signal`, `stoch.k`. No positional guessing: every stream is read by
name.

## Introduction

Some indicators produce more than one line. Bollinger bands give you an
upper band, a basis, and a lower band. MACD gives a MACD line, a signal
line, and a histogram. A wrun indicator declares one output per line, and a
class computes all of them from one input per bar:

```text
output("bb_upper", line, overlay, { color: "#2563eb" });
output("bb_basis", line, overlay, { color: "#64748b" });
output("bb_lower", line, overlay, { color: "#2563eb" });

const bands = new Bb(20, 2.0);          // in onBar(): bands.update(close)
out_bb_upper(bands.upper);              // then read the fields by name
out_bb_basis(bands.basis);
out_bb_lower(bands.lower);
```

`bands.update(close)` folds one bar; `bands.upper`, `bands.basis`, and
`bands.lower` are its three lines afterwards. You name what you want. There
is no "is the upper band index 0 or index 2?" guesswork, and a typo like
`bands.upperr` is a compile error rather than a silently wrong line.

## How it works

![Named streams: one Bb object, updated once per bar, holds three fields; the file writes each to an output of its own, and the chart draws three lines: upper, basis and lower](/wrun/images/diagrams/named-streams.svg)

A multi-output computation is a class with one `update(x)` method and one
typed field per stream. `update` returns the primary stream (the MACD line,
the basis) so the class also reads like a single-output one, and the other
streams sit on the instance. Each field is an ordinary `f64`, so it flows
straight into math, other classes, conditions, and outputs:

```text
const m = new Macd(12, 26, 9);
m.update(close);

const risingMomentum = m.hist > prevHist;         // compare bars (prevHist is remembered)
const smoothSignal = signalEma.update(m.signal);  // feed a stream into another class
```

The kit ships every class, single-stream and multi-stream alike, and a
file uses them by name with nothing to import. Two rules keep a module
honest: construct in `onStart()` (allocation once), and `update()` once
per bar.

## The named streams

The multi-stream classes, and the fields each one carries:

| Class | Fields |
| --- | --- |
| `Bb` | `.basis`, `.upper`, `.lower` |
| `Keltner` | `.basis`, `.upper`, `.lower` |
| `Donchian` | `.basis`, `.upper`, `.lower` |
| `Macd` | `.macd`, `.signal`, `.hist` |
| `Stoch`, `Stochastic` | `.k`, `.d` |
| `Supertrend` | `.line`, `.direction` |
| `Adx` | `.adx`, `.plusDi`, `.minusDi` |
| `Ichimoku` | `.tenkan`, `.kijun`, `.senkouA`, `.senkouB`, `.chikou` |

Every class and its conventions: [TA library](../functions/ta-library.md).

A few worth calling out:

- **`Stoch`** splits into the fast `%K` (`.k`) and its smoothed `%D`
  (`.d`). The classic crossover is `Cross.update(stoch.k, stoch.d)`.
- **`Supertrend`** carries the trailing stop level as `.line` and the trend
  side as `.direction` (`1` or `-1`). Use `.direction` as a `color_by`
  index or a gate; draw `.line`.
- **`Macd.hist`** is the MACD line minus the signal line, ready for a
  `histogram` output.

## Reading every stream at once

Two shipped classes and all six of their streams as outputs. `Macd` is
the fast, slow and signal EMAs folded into one object; `Bb` is the window
mean and its population standard deviation. Every stream is an output of
its own, so once you publish the indicator an alert set from the chart can
follow any of them ([Alerts](../functions/alerts.md)).

```typescript sample=cc-streams-macd-bb
param("fast", 12, { min: 1, max: 200, description: "MACD fast EMA" });
param("slow", 26, { min: 2, max: 400, description: "MACD slow EMA" });
param("signal", 9, { min: 1, max: 200, description: "MACD signal EMA" });
param("bb_period", 20, { min: 2, max: 400, description: "Bollinger window" });
param("bb_mult", 2, { min: 0.5, max: 4, description: "Bollinger width in standard deviations" });
output("bb_upper", line, overlay, { color: "#2563eb", width: 2, description: "Upper Bollinger band" });
output("bb_basis", line, overlay, { color: "#64748b", width: 2, description: "Bollinger basis (the moving average)" });
output("bb_lower", line, overlay, { color: "#dc2626", width: 2, description: "Lower Bollinger band" });
output("macd", line, lower, { color: "#1d4ed8", width: 2, description: "MACD line" });
output("signal", line, lower, { color: "#ea580c", width: 2, description: "Signal line" });
output("hist", histogram, lower, { color: "#15803d", description: "MACD minus signal" });

// One update() per bar; the streams are fields you read by name afterwards.
let macd = new Macd(12, 26, 9);
let bands = new Bb(20, 2.0);

function onStart(): void {
  macd = new Macd(i32(p_fast()), i32(p_slow()), i32(p_signal()));
  bands = new Bb(i32(p_bb_period()), p_bb_mult());
}

function onBar(): void {
  const close = bar.close();
  macd.update(close);
  bands.update(close);
  // Nothing is written until the slowest stream is warm: the MACD signal line.
  if (isNaN(macd.signal) || isNaN(bands.basis)) return;
  out_bb_upper(bands.upper);
  out_bb_basis(bands.basis);
  out_bb_lower(bands.lower);
  out_macd(macd.macd);
  out_signal(macd.signal);
  out_hist(macd.hist);
}
```

**What to expect:** the three bands start drawing once the 20-bar window
fills and track together. The MACD streams warm later: the MACD line needs
the slow EMA (26 bars), the signal line nine MACD values beyond that, so
nothing is drawn until the 34th bar and all six lines then start in
lockstep.
Bars where one stream is finite and another is not are handled in the
classes (`NaN` stays `NaN`), never in `onBar()`.

## A composite class as one value

The class instance is one value: pass `macd` to a helper that
takes a `Macd`, keep an array of them for several settings, or read a
single field inline. What you cannot do is index a stream's history
(`m.hist[1]`): remember the previous value in a module-level variable,
exactly as for any other number (`core-variables.md`).

Single-output classes like `Rsi`, `Ema`, and `Sma` return their one value
from `update()` and have no stream fields, so you write them straight to an
output: `out_rsi(rsi.update(close))`. Named streams are only for the
multi-line indicators above. The full catalog is the
[TA library](../functions/ta-library.md).
