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
// 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/ | 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/ | UTC+10 | first Sunday of October to first Sunday of April |
| 9 | Asia/Kolkata | UTC+5:30 | none |
| 10 | America/ | 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/ | UTC-6 | none |
| 13 | America/ | UTC-3 | none |
| 14 | America/ | UTC-3 | none |
| 15 | Europe/Paris | UTC+1 | last Sunday of March to last Sunday of October |
| 16 | Europe/ | 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/ | 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/ | 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/ | UTC+2 | none |
| 32 | Asia/Karachi | UTC+5 | none |
| 33 | Asia/Bangkok | UTC+7 | none |
| 34 | Asia/Jakarta | UTC+7 | none |
| 35 | Asia/ | UTC+7 | none |
| 36 | Asia/ | 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/ | 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 says who fills what).
What the dialog draws
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).
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().
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.
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). 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.
// 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. | p_ | 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. | p_ | 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. | p_ | 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. | i32( | the background colour, packed | the chart's number | 0 |
chart. | i32( | the text colour, packed | the chart's number | 0 |
market. | i32( | what the chart's market trades | the chart's number | what the market directory says the market trades |
market. | p_ | 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. | i32( | 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. | i32( | whether prices are in US dollars | the chart's number | from the market's quote currency |
- Read 0 as "unknown".
unitToPriceleaves a ticks value as it is when the tick is 0, and aBucketrefuses 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
// 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 throughp_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, andchart.fg_color()is the chart's text colour, each as the packed colour the colours kit uses ((r << 24) | (g << 16) | (b << 8) | aas a signed 32-bit integer, so white reads-1). Read them withi32(p_chart_bg_color())andi32(p_chart_fg_color()); 0 means unavailable.
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
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
// 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( | 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. | p_ | 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( | 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. | i32( | 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(...)ornew Clock(SESSION_TZ_IDS[zone])and each bar gets its own daylight-saving offset.
// 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
unitToPricethen 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
unitlist as one more toward the 128-setting cap (Picks, lanes, the cap). - 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). - A window fixed in the file needs no setting: it is a
Session(Clock and sessions kit).