Sessions
ctx.session tells a script when a session opens and closes, so you key logic off session boundaries instead of clock arithmetic. Indicators and definitions share one API; studies have their own helpers for grouping events by time.
ctx.session(opts): sessions you define
Call it with the session you care about. It returns that session's state on the current bar.
export function onBar(ctx) {
const s = ctx.session({
period: "daily",
open: { hour: 8, minute: 0 },
close: { hour: 16, minute: 30 },
tz: "Europe/London",
});
if (s.didOpen) {
ctx.emit("london_open", { key: s.key }, { label: "London open", price: ctx.open });
}
}Option | Meaning |
|---|---|
|
|
|
|
| IANA time zone for |
| Instead of |
Field | Meaning |
|---|---|
| The session's start as an ISO timestamp string. Use it in |
| Unix seconds, end exclusive |
| This bar is inside the session |
| True on the first bar inside the session |
| True on the first bar at or after the session's end, or on the bar that rolls into the next session |
|
|
Each distinct set of options is tracked separately, so call ctx.session with the same options on every bar. Gate on didOpen, isOpen and didClose. Never count bars to decide when a session ends: a missing bar or a different chart timeframe breaks the count.
didClose fires on a bar after the session, often the next session's first bar. On a rollover bar, finish the old session first, then reset, then add the current bar:
const st = ctx.accum("st", () => ({ hi: -Infinity }), (x) => x);
const s = ctx.session({ period: "daily" });
if (s.didOpen && s.prev) {
ctx.emit("day_high", { dayKey: s.prev.key, high: st.hi }, { label: "Day high" }); // finish
}
if (s.didOpen) st.hi = -Infinity; // reset
if (s.isOpen) st.hi = Math.max(st.hi, ctx.high); // accumulateA script that resets on a session boundary declares the longest gap between two resets as a length of time: warmup((w) => w.day()) for a daily session, warmup((w) => w.span("7d")) for a weekly one. See Warmup and memory.
ctx.session.isStart(mode): named boundaries
if (ctx.session.isStart("london")) {
ctx.mark("ln_open", { price: ctx.open, text: "LN", position: "atPriceMiddle" });
}True on the first bar of a new session of that mode. The times are fixed in UTC and do not move with daylight saving:
Mode | Boundary |
|---|---|
| 00:00 UTC |
| Monday 00:00 UTC |
| The 1st, 00:00 UTC |
| 08:00 UTC |
| 13:30 UTC |
| 00:00 UTC |
| 22:00 UTC |
| Never: the whole history is one session |
"newyork" is 13:30 UTC all year, so it is the New York cash open only while the US is on daylight time. For an exchange session that follows daylight saving, use ctx.session({ period: "daily", open, close, tz }).
In a mode: "once" indicator, ctx.at(i).session.isStart works; the callable ctx.session(opts) does not.
Studies: key, boundsOf, bucket and temporal
A study has no bars, so its helpers take a time:
Call | Returns |
|---|---|
| The UTC day, |
|
|
|
|
|
|
| An explicit span |
|
|
Use these in scope(ctx) to group events by session, and in indexedBy for tables and pins split that way. See Study API.
Common mistakes
Treating
"newyork"as the exchange's regular hours all year.Flooring
ctx.timeby 86,400 to find the day instead of callingctx.session.Emitting once per session without
oncePeron the event: see Emit and labels.Running session logic on weekly charts. Limit it with
runOn.
