---
title: "Typed inputs"
description: "param() turns a value into a setting users can change without editing code. Each param you declare shows up in the chart's settings for the overlay and in om…"
order: 44
section: "functions"
---

<!-- source: docs/indicators/functions/typed-inputs.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Typed inputs

`param()` turns a value into a setting users can change without editing
code. Each param you declare shows up in the chart's settings for the
overlay and in `om metric series --params`, and the value you read in
`init()` drives your logic. This is how you ship a configurable
Indicator: an adjustable period, a threshold, a multiplier, an on/off
toggle. kScript (legacy) had fifteen `input()` types; an Indicator has
one, the number, and this page is what each of the fifteen becomes.

```typescript
param("length", 20, { min: 2, max: 200, description: "Length" });
let n: i32 = 20;
export function init(): void { n = i32(p_length()); }
```

Every param takes a `name`, a `default`, and usually a `description` (the
text shown beside the field). `min` and `max` bound the setting, and
`required: true` refuses a use that does not set it. There is no `step`,
no `group`, and no `label`: the description is the label.

## The types

| kScript `type` | Surfaced as | Indicator |
| --- | --- | --- |
| `number`, `float` | number field | `param(name, default, { min, max })`, read as `f64` |
| `int` | integer field | the same param, cast with `i32(p_name())`; the chart rounds nothing, so cast where you use it |
| `slider` | slider | the same param with `min` and `max`; the chart chooses the control |
| `boolean` | toggle | `param(name, 1, { min: 0, max: 1 })`, tested as `p_name() > 0.5` |
| `select`, `multiSelect` | dropdown | **not in Indicators yet**; a numeric mode param (`0`, `1`, `2`) switched on with `switch` is the nearest |
| `string`, `text` | text field | **not in Indicators yet**; strings leave the module through string slots, never enter it |
| `color`, `color[]` | color picker | **not in Indicators yet** as a param; `color` and `colors` are declared per output, box, or segment, and a hand-written sheet can bind a style knob ([Styling](styling.md)) |
| `source` | source dropdown | **not in Indicators yet**; the source is the declaration `input(name, source.field)` |
| `timeframe` | timeframe field | **not in Indicators yet**; an `interval` pin on the input, honored on your machine |
| `session` | session field | **not in Indicators yet**; session math is integer math on the `time` source ([Time and sessions](../core-concepts/time-and-sessions.md)) |
| `symbol` | symbol field | **not in Indicators yet**; a `symbol` + `exchange` pin on the input, honored on your machine |

Every param is an `f64` today; non-numeric params are product work, and
the rows above name the declaration that carries the same intent. What a
kScript `select` chose at runtime, an Indicator chooses at declaration
time: two moving-average kinds are two classes, and a `mode` param picks
which one writes the output.

## Examples by type

**Number with min and max.** An adjustable period clamped to a sensible
range; the cast makes it a bar count:

```typescript
param("length", 14, { min: 2, max: 100, description: "Length" });
```

**Select, as a mode number.** A dropdown that switched behavior becomes a
numeric mode and a `switch` in `init()` or `state()`:

```typescript
param("ma_type", 1, { min: 0, max: 1, description: "0 = simple, 1 = exponential" });
```

**Color.** The user recolors the plot in the chart's style settings; the
file declares the default:

```typescript
output("ma", line, overlay, { color: "#2563eb", width: 2 });
```

**Boolean.** An on/off toggle is a 0/1 param; to hide a line, write `NaN`
to it while the toggle is off, and to switch a mark off, feed the toggle
into its gate output:

```typescript
param("show_fast", 1, { min: 0, max: 1, description: "Show the fast line" });
```

**Source.** Which data feeds the module is the declaration, and a second
feed is a second input; the chart user picks the market, not the source:

```typescript
input("close", ohlcv.close);
input("buy", trades.volume, { side: "BUY" });
```

**Symbol and timeframe.** Pins on the input, both halves together for a
market, `interval` on its own; the chart reads its own market for every
input, your machine honors the pins:

```typescript
input("btc_4h", ohlcv.close, { symbol: "BTCUSDT", exchange: "BINANCE_FUTURES", interval: "FOUR_HOURS" });
```

## Style-only inputs

kScript treated a plot color, line width, or show/hide toggle as
style-only when it could prove the setting was presentation-only, and
patched the rendered output without recomputing. An Indicator makes the
split explicit: `color`, `width`, `opacity`, and `line_style` on an output
are declared style the chart edits without a recompute, and a
hand-written sheet can expose one as a settings knob with `style: {
"output", "property" }` (no accessor, no recompute). Every `param(...)`
is compute-affecting by construction: it reaches `init()` and the module
re-runs when it changes.

## Every type in one module

Number, integer, float, slider, and boolean params driving one average,
with the select-as-mode and the color-as-declaration in the same file.

```typescript
import { input, line, none, ohlcv, output, overlay, param, shape } from "./sdk/declare";
import { in_close, in_low } from "./gen/inputs";
import { emitRow, out_above, out_average, out_band, out_mark } from "./gen/outputs";
import { p_average, p_float_multiplier, p_int_length, p_number_length, p_show_marks, p_slider_width } from "./gen/params";
import { Ema, Sma } from "./sdk/ta";

// number: a period, cast to a bar count where it is used.
param("number_length", 12, { min: 2, max: 50, description: "Number Length" });
// int: the same declaration; the cast is what makes it an integer.
param("int_length", 14, { min: 2, max: 50, description: "Int Length" });
// float: a fractional multiplier, used as f64.
param("float_multiplier", 1.5, { min: 0.5, max: 5, description: "Float Multiplier" });
// boolean: 0 or 1.
param("show_marks", 1, { min: 0, max: 1, description: "Show marks on bars above the band" });
// select: a mode number, 0 = simple, 1 = exponential.
param("average", 1, { min: 0, max: 1, description: "Average: 0 simple, 1 exponential" });
// slider: a bounded number; the band width in percent.
param("slider_width", 2, { min: 1, max: 5, description: "Band width, percent" });
input("close", ohlcv.close);
input("low", ohlcv.low);
// color: declared on the output, editable in the chart's style settings.
output("average", line, overlay, { color: "#2563eb", width: 2, description: "The chosen average" });
output("band", line, overlay, { color: "#94a3b8", width: 1, line_style: "dotted", description: "Average plus the band width" });
output("mark", shape, overlay, { color: "#16a34a", shape_where: "above", description: "The low of each bar closing above the band" });
output("above", none, overlay, { description: "1 when the close is above the band and marks are on" });

let sma = new Sma(12);
let ema = new Ema(14);
let mode: i32 = 1;
let multiplier: f64 = 1.5;
let showMarks: bool = true;
let bandPct: f64 = 2.0;
let average: f64 = NaN;
let close: f64 = NaN;
let low: f64 = NaN;

export function init(): void {
  sma = new Sma(i32(p_number_length()));
  ema = new Ema(i32(p_int_length()));
  mode = i32(p_average());
  multiplier = p_float_multiplier();
  showMarks = p_show_marks() > 0.5;
  bandPct = p_slider_width();
}

export function state(): i32 {
  close = in_close();
  low = in_low();
  const simple = sma.update(close);
  const exponential = ema.update(close);
  switch (mode) {
    case 0:
      average = simple;
      break;
    default:
      average = exponential;
  }
  return isNaN(average) ? 0 : 1;
}

export function finalize(): void {
  const band = average * (1.0 + (bandPct * multiplier) / 100.0);
  out_average(average);
  out_band(band);
  out_mark(low);
  out_above(showMarks && close > band ? 1.0 : 0.0);
  emitRow();
}

export function reset(): void {
  sma.reset();
  ema.reset();
  average = NaN;
  close = NaN;
  low = NaN;
}
```

Both averages update on every bar whatever the mode, so switching the
setting never restarts a warm-up; only the line written changes.

## Defaults are checked

The `default` must sit inside `min..max`, and the build checks it before
the compiler runs, naming the path:

| Mistake | Error |
| --- | --- |
| a default above `max` | `params.0.default: default above max` |
| `min` above `max` | `params.0.min: min must be <= max` |
| a `step` option | `param options accept only { required, min, max, description }, not 'step'` |
| a computed default (`param("n", base * 2)`) | `expected a numeric literal` |
| a name built from a variable | `expected a string literal` |
| a string default (`param("mode", "ema")`) | `expected a numeric literal` |

Declarations are literals: the extractor reads them from the text
without running it. A kScript `defaultValue` type mismatch has no
counterpart because there is one type.

## Reading a param on your machine

An installed package's params are arguments to every surface that
evaluates it, so the same setting the chart user drags is the one an
alert or a series query pins:

```bash
om metric series --metric wrun/@you/typed-inputs/average --params number_length=20,average=0 --symbol BTCUSDT --exchange BINANCE_FUTURES --bars 60
```

`om metric get --metric wrun/@you/name/output:length=20` is the inline
form. A param the caller does not pass takes its default; one declared
`required: true` refuses the call by name instead.
