---
title: "Sessions and units"
description: "param.session declares a window on the 24-hour clock in its zone, and unit on a number puts a unit picker in the field."
order: 28
section: "settings"
---

<!-- source: docs/indicators/settings/sessions-and-units.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Sessions and units

`param.session` declares a window on the 24-hour clock in its zone, and `unit` on a number puts a unit picker in the field.

## What it is

`param.session(name, "HH:MM-HH:MM", { tz })` declares a window on the
24-hour clock and the zone it is written in. A window may wrap midnight
(`"22:00-04:00"`). Per bar, `inSession(...)` says whether the bar's open
sits inside the window, with daylight saving applied per bar from the
zone's own rule.

`unit: ["price", "ticks", "%", "atr"]` on a number puts a unit picker in
the field, listing the units in your order; `unit_default` names the one
picked at first (the first listed when absent). `unitToPrice(...)` then
turns the number into a price distance per bar.

## Declare it

```typescript
// session: a window in its zone.
param.session("rth", "09:30-16:00", { tz: "America/New_York", label: "Active hours" });
// number with a unit picker: the value and the unit's code.
param.number("offset", 0.0, { min: -100, max: 100, unit: ["price", "ticks", "%", "atr"], unit_default: "%" });
// A bounded int drawn as a slider: the ATR an atr-unit offset scales by.
param.int("atr_length", 14, { min: 2, max: 50, slider: true, label: "ATR length" });
// The tick size, written by the chart into a hidden setting.
market.tick_size();
```

`tz` is `param.session` only: the zone the window is written in, one of
these, each with its own daylight-saving rule:

| Index | `tz` | Standard offset | Daylight saving (one hour ahead) |
| --- | --- | --- | --- |
| 0 | `UTC` (the default) | UTC+0 | none |
| 1 | `America/New_York` | UTC-5 | second Sunday of March to first Sunday of November |
| 2 | `America/Chicago` | UTC-6 | second Sunday of March to first Sunday of November |
| 3 | `Europe/London` | UTC+0 | last Sunday of March to last Sunday of October |
| 4 | `Europe/Berlin` | UTC+1 | last Sunday of March to last Sunday of October |
| 5 | `Asia/Tokyo` | UTC+9 | none |
| 6 | `Asia/Hong_Kong` | UTC+8 | none |
| 7 | `Asia/Singapore` | UTC+8 | none |
| 8 | `Australia/Sydney` | UTC+10 | first Sunday of October to first Sunday of April |
| 9 | `Asia/Kolkata` | UTC+5:30 | none |
| 10 | `America/Los_Angeles` | UTC-8 | second Sunday of March to first Sunday of November |
| 11 | `America/Toronto` | UTC-5 | second Sunday of March to first Sunday of November |
| 12 | `America/Mexico_City` | UTC-6 | none |
| 13 | `America/Sao_Paulo` | UTC-3 | none |
| 14 | `America/Argentina/Buenos_Aires` | UTC-3 | none |
| 15 | `Europe/Paris` | UTC+1 | last Sunday of March to last Sunday of October |
| 16 | `Europe/Amsterdam` | UTC+1 | last Sunday of March to last Sunday of October |
| 17 | `Europe/Zurich` | UTC+1 | last Sunday of March to last Sunday of October |
| 18 | `Europe/Madrid` | UTC+1 | last Sunday of March to last Sunday of October |
| 19 | `Europe/Rome` | UTC+1 | last Sunday of March to last Sunday of October |
| 20 | `Europe/Stockholm` | UTC+1 | last Sunday of March to last Sunday of October |
| 21 | `Europe/Oslo` | UTC+1 | last Sunday of March to last Sunday of October |
| 22 | `Europe/Copenhagen` | UTC+1 | last Sunday of March to last Sunday of October |
| 23 | `Europe/Warsaw` | UTC+1 | last Sunday of March to last Sunday of October |
| 24 | `Europe/Helsinki` | UTC+2 | last Sunday of March to last Sunday of October |
| 25 | `Europe/Athens` | UTC+2 | last Sunday of March to last Sunday of October |
| 26 | `Europe/Istanbul` | UTC+3 | none |
| 27 | `Europe/Moscow` | UTC+3 | none |
| 28 | `Asia/Jerusalem` | UTC+2 | Friday before the last Sunday of March to last Sunday of October |
| 29 | `Asia/Riyadh` | UTC+3 | none |
| 30 | `Asia/Dubai` | UTC+4 | none |
| 31 | `Africa/Johannesburg` | UTC+2 | none |
| 32 | `Asia/Karachi` | UTC+5 | none |
| 33 | `Asia/Bangkok` | UTC+7 | none |
| 34 | `Asia/Jakarta` | UTC+7 | none |
| 35 | `Asia/Ho_Chi_Minh` | UTC+7 | none |
| 36 | `Asia/Kuala_Lumpur` | UTC+8 | none |
| 37 | `Asia/Shanghai` | UTC+8 | none |
| 38 | `Asia/Taipei` | UTC+8 | none |
| 39 | `Asia/Manila` | UTC+8 | none |
| 40 | `Asia/Seoul` | UTC+9 | none |
| 41 | `Pacific/Auckland` | UTC+12 | last Sunday of September to first Sunday of April |

The dialog shows each zone by its city (`SESSION_TZ_LABELS`: `New York`,
`Kolkata`, `Sao Paulo`, ...). The list only grows at its end: a saved
setting stores the index `p_<name>_tz()` reads, so the first nine never
move and a new zone is appended. A `tz` outside the list refuses the build
and names every id.

`unit` and `unit_default` belong to one number field (`int`, `number`,
`price`): a range's two ends have no unit picker. `market.tick_size()`
declares the tick size as a hidden setting the host writes before
`onStart()`; `market.price_precision()` does the same for the price
decimals, and `market.zone()` for the market's own time zone as an index
into this same list ([Chart context](#chart-context) says who fills what).

## What the dialog draws

![a day strip with the window shaded, a start and an end on the 24-hour clock, and a zone menu](/wrun/images/wrun-settings-session.svg)

![a number field with a unit picker beside it](/wrun/images/wrun-settings-number-unit.svg)

## Read it in onStart()

A session is three readers: `p_<name>_start()` and `p_<name>_end()`
(minutes from midnight) and `p_<name>_tz()` (the zone's index). The index
is what `p_<name>_tz()` reads and what `inSession` takes;
`SESSION_TZ_IDS` and `SESSION_TZ_LABELS` hold the ids and the dialog's
words in the same order. The rules are compiled into the module, so a
session reads the same in your browser, on OpenMarket's servers and in
an alert. The bar's open is `bar.time()`
([Time and sessions](../core-concepts/time-and-sessions.md)).

A unit pick reaches the module as a code that never changes with the
order you list: `price` 0, `ticks` 1, `%` 2, `atr` 3, read through
`p_<name>_unit()` beside `p_<name>()`. The tick size is read through
`p_market_tick_size()`, the price decimals through
`p_market_price_precision()`.

```typescript
function onStart(): void {
  offset = p_offset();
  offsetUnit = i32(p_offset_unit());
  atr = new Atr(i32(p_atr_length()));
  tick = p_market_tick_size();
  sessionStart = p_rth_start();
  sessionEnd = p_rth_end();
  zone = i32(p_rth_tz());
}
```

Per bar, `inSession(barOpenSec, startMin, endMin, zone)` answers whether
the bar's open sits inside the window, and `unitToPrice(value, unit,
close, tick, atr)` turns the number into a price distance: `ticks`
multiplies by the tick size, `%` takes that percent of `close`, `atr`
multiplies by the ATR you pass, and `price` is the value as it is.

```typescript
const atrValue = atr.update(bar.high(), bar.low(), close);
// The offset in the unit the dialog picked, as a price distance.
if (isFinite(basis)) basis += unitToPrice(offset, offsetUnit, close, tick, atrValue);
active = inSession(t, sessionStart, sessionEnd, zone) && multiHas(dayMask, weekday);
```

## Chart context

Numbers about the chart and its market reach your file before the first
bar: the bar interval, the price decimals, the tick size, the chart's
colours, and the market's kind, point value, time zone and quote currency
([The market's facts](#the-markets-facts)). Declare the ones you need at
the top level and read them in `onStart()`. Wherever the indicator runs, it
gets the numbers that place can know, and 0 for the rest.

```typescript sample=fn-chart-facts
// A close line with a corner readout of the three facts the host writes before the first bar.
chart.interval_sec();
market.price_precision();
market.tick_size();

output("close_line", line, overlay, { color: "#94a3b8" });
string("facts", { max_bytes: 96 });
render.table("corner", { rows: 1, cols: 1, cells: ["facts"], position: "top_left" });

let intervalSec: f64 = 0;
let decimals: i32 = 0;
let tick: f64 = 0;

function onStart(): void {
  intervalSec = p_chart_interval_sec();
  decimals = i32(p_market_price_precision());
  tick = p_market_tick_size();
}

function onBar(): void {
  const close = bar.close();
  if (isNaN(close)) return;
  out_close_line(close);
  sb_clear();
  sb_text("bar ");
  // 0 means the host did not fill the fact.
  if (intervalSec > 0) sb_duration(intervalSec); else sb_text("unknown");
  sb_text(", close ");
  sb_f64(close, decimals);
  sb_text(tick > 0 ? ", tick known" : ", tick unknown");
  str_facts_sb();
}
```

On a 15-minute BTC chart the readout says `bar 15m`, the close at the
chart's own decimals, and `tick unknown`.

### Who fills what

| Declare | Read with | On the chart | On OpenMarket's servers and in alerts | Where there is no chart |
| --- | --- | --- | --- | --- |
| `chart.interval_sec()` | `p_chart_interval_sec()` | the bar interval in seconds (a 15-minute chart reads 900) | the chart's number, carried with the indicator | the interval of the bars it runs on |
| `market.price_precision()` | `p_market_price_precision()` | the chart's price decimals | the chart's number | the most decimals the market's candle prices carry, at most 10; 0 when it reads no candles of that market |
| `market.tick_size()` | `p_market_tick_size()` | the market's tick where the market publishes one (CME markets), else 0 | the chart's number | 0, until market data carries tick sizes |
| `chart.bg_color()` | `i32(p_chart_bg_color())` | the background colour, packed | the chart's number | 0 |
| `chart.fg_color()` | `i32(p_chart_fg_color())` | the text colour, packed | the chart's number | 0 |
| `market.kind()` | `i32(p_market_kind())` | what the chart's market trades | the chart's number | what the market directory says the market trades |
| `market.point_value()` | `p_market_point_value()` | the money one point of price is worth on one contract | the chart's number | 1 for a market priced per unit (a coin, a perpetual, a stock), 0 on a CME futures market, whose multiplier is not known there |
| `market.zone()` | `i32(p_market_zone())` | the market's exchange time zone | the chart's number | New York for US stocks and indices, Chicago for CME, London for the London metals, else 0 (UTC) |
| `market.quote_is_usd()` | `i32(p_market_quote_is_usd())` | whether prices are in US dollars | the chart's number | from the market's quote currency |

- Read 0 as "unknown". `unitToPrice` leaves a ticks value as it is when
  the tick is 0, and a `Bucket` refuses its span when the interval is 0.
  The zone is the one exception: its 0 is UTC, also where the zone is
  unknown.
- These are hidden settings: the settings dialog never shows them, and the
  number the host writes always wins over anything passed under the same
  name.
- Each one counts toward the 128 settings.

### The interval and the colours

```typescript
// The bar interval and the chart's two colours, written by the chart into hidden settings.
chart.interval_sec();
chart.bg_color();
chart.fg_color();
```

- `chart.interval_sec()` is the chart's bar interval in seconds, read
  through `p_chart_interval_sec()` and available from the first bar (a
  15-minute chart reads 900); 0 only when the interval is unknown.
- `chart.bg_color()` is the chart's background, a custom background
  included, and `chart.fg_color()` is the chart's text colour, each as the
  packed colour the colours kit uses (`(r << 24) | (g << 16) | (b << 8) |
  a` as a signed 32-bit integer, so white reads `-1`). Read them with
  `i32(p_chart_bg_color())` and `i32(p_chart_fg_color())`; 0 means
  unavailable.

```typescript
function onStart(): void {
  intervalSec = p_chart_interval_sec();
  bg = i32(p_chart_bg_color());
  fg = i32(p_chart_fg_color());
}
```

The text colour is the theme's, not the background's: on a custom
background it may not contrast with the background. Choose ink from the
background's brightness instead: `red(bg)`, `green(bg)` and `blue(bg)`
read its channels, and [Read a color back](../functions/colors-kit.md#read-a-color-back)
draws dark words on a light chart and light words on a dark one. Treat
`fg_color` as the theme's hint.

### The market's facts

```typescript
// What the market trades, its point value, its time zone and its quote, written by the host into hidden settings.
market.kind();
market.point_value();
market.zone();
market.quote_is_usd();
```

Each is a number, and 0 means the host does not know:

| Declare | Reads | Values |
| --- | --- | --- |
| `market.kind()` | `i32(p_market_kind())` | 1 crypto, 2 a stock or an ETF, 3 forex, 4 a metal, 5 an index, 6 an economic series; 0 unknown, or none of these (a prediction market, an oil contract) |
| `market.point_value()` | `p_market_point_value()` | the money one whole point of price is worth on one contract: a futures multiplier (50 on the E-mini S&P), 1 on a market priced per unit (a coin, a perpetual, a stock); 0 unknown |
| `market.zone()` | `i32(p_market_zone())` | the market's exchange time zone as its index in the zone list above (1 New York, 2 Chicago, 3 London); 0 is UTC, also where the zone is unknown |
| `market.quote_is_usd()` | `i32(p_market_quote_is_usd())` | 1 when prices are in US dollars, 2 in another currency or coin (USDT and USDC included: BTCUSDT is priced in USDT), 0 unknown |

- A futures market reports what it trades: an index future reads 5, a gold
  future 4.
- A stock that trades around the clock on a crypto venue reads 2 and its
  venue's zone, UTC.
- The codes never change: a new kind is added at the end of the list.
- The zone is an index, not an offset: pass it to `inSession(...)` or
  `new Clock(SESSION_TZ_IDS[zone])` and each bar gets its own
  daylight-saving offset.

```typescript sample=settings-market-facts
// A listed stock's regular hours on its own exchange clock: the close drawn only between 09:30 and 16:00 there, every bar on a market that trades around the clock.
market.kind();
market.zone();
market.point_value();
market.quote_is_usd();

output("session_close", line, overlay, { color: "#2962ff" });
output("point_value_usd", line, lower, { color: "#94a3b8" });

let kind: i32 = 0;
let zone: i32 = 0;
let pointValue: f64 = 0;
let inDollars = false;

function onStart(): void {
  kind = i32(p_market_kind());
  // 0 is UTC, also where the host does not know the zone.
  zone = i32(p_market_zone());
  pointValue = p_market_point_value();
  inDollars = i32(p_market_quote_is_usd()) == 1;
}

function onBar(): void {
  const close = bar.close();
  // A stock (kind 2) on an exchange clock keeps its hours, 09:30 (minute 570) to 16:00 (minute 960);
  // zone 0 is a venue that trades around the clock (a stock perpetual), or an unknown zone.
  const open = kind != 2 || zone == 0 || inSession(bar.time(), 570, 960, zone);
  out_session_close(open ? close : NaN);
  // A one-point move on one contract, in dollars where the market is priced in them.
  out_point_value_usd(inDollars && pointValue > 0 ? pointValue : NaN);
}
```

On a US stock the line breaks outside New York's regular hours, daylight
saving included, and the lower pane reads 1. On BTCUSDT, and on a stock
perpetual that trades around the clock, the line runs through every bar;
on BTCUSDT the lower pane stays empty, since the price is in USDT.

## Gotchas

- A tick size or an ATR of 0 means unavailable, and `unitToPrice` then
  leaves the value as it is.
- A unit picker on a range is refused: `param.range 'r' takes no unit
  option (a unit picker sits beside one number field, not a range)`.
- A session counts as three settings and a `unit` list as one more toward
  the 128-setting cap ([Picks, lanes, the cap](picks-and-lanes.md)).
- A preset sets a session or a unit through its parts (`rth_start`,
  `offset_unit`, by the dialog's words for a session zone or a unit)
  ([Presets](presets.md)).
- A window fixed in the file needs no setting: it is a `Session`
  ([Clock and sessions kit](../functions/time-and-sessions-kit.md)).

## Related

- [Setting kinds](kinds.md): every kind and its readers
- [Options on a setting](options.md): `unit`, `unit_default` and `tz` among the other keys
- [Time and sessions](../core-concepts/time-and-sessions.md): the integer math underneath a session
- [Clock and sessions kit](../functions/time-and-sessions-kit.md): a window fixed in the file
