Trading day and sessions
How the chart counts days, how to declare a session and read its range, restart a value daily, react to a time of day, and read the previous day, week or month.
Most trading logic is tied to the clock: the high of the London session, the first hour of the day, yesterday's range, an alert at the close. This page shows the pieces. They all use the same idea of a trading day, so we start there.
The trading day#
A trading day is not the calendar day. Each symbol has its own:
| Feed | A day starts at |
|---|---|
| Forex, metals, indices, energy, futures and the broker's other symbols | 17:00 New York time, which is 21:00 or 22:00 UTC depending on daylight saving |
| Crypto from an exchange feed, and archived data | 00:00 UTC |
A week starts with the first trading day of the week and a month with the first of the month. Weeks and months are counted in whole trading days, so a week that contains a daylight-saving change is 167 or 169 hours long, not exactly 168. Everything on this page, state day, prior(day), a week or month, counts days this way, so a script gives the same answer for the same symbol wherever you run it.
trading_day is the time at which the current bar's trading day opened:
indicator "Bars in the trading day"
edition = 1
state day bars_today = 0
bars_today += 1
plot(bars_today, name = "Bars so far today", style = histogram)
plot(trading_day, name = "Trading day opened at", display = none)Sessions#
A session is a daily window in a named time zone. Declare it on a line of its own at the top level of the file, then read its members.
indicator "London session"
edition = 1
display = price
session london = 08:00 .. 16:30 @ "Europe/London"
plot(london.high, name = "Session high", color = color.green)
plot(london.low, name = "Session low", color = color.red)
chart.background(
if london.start then color.fade(color.blue, 80) else if london.active then color.fade(
color.blue,
94,
) else na,
)| Member | Is |
|---|---|
london.active | true on the bars that open inside the session |
london.start | true on the first bar of a run of the session |
london.end | true on the last bar of a run: the bar that opens inside the window and closes at its end |
london.index | 0, 1, 2 … the number of the latest run; na before the first |
london.high, london.low | the highest high and lowest low of the latest run; they keep their values after the run ends, until the next one starts |
A session may run over midnight when it ends earlier than it starts:
indicator "Tokyo night"
edition = 1
display = price
session night = 22:00 .. 06:00 @ "Asia/Tokyo"
chart.background(if night.active then color.fade(color.purple, 92) else na, name = "Night")A session is declared with the session line, as above. A session is exactly two times and a zone. It has no days of the week and no second window. The zone is a text from the time zone database, such as "Europe/London"; one the chart does not know is an error. The hours follow the zone's daylight saving, so the London session stays at 08:00 London time in summer and winter.
indicator "A zone that does not exist"
edition = 1
session london = 08:00 .. 16:00 @ "Mars/Olympus"
plot(if london.active then 1 else 0)A session name is read through a member, never alone, and a session is declared at the top level only:
indicator "A session by itself"
edition = 1
session london = 08:00 .. 16:00 @ "UTC"
plot(london)Values that start again#
state day starts a value again on the first run of its statement in each trading day, wherever the statement is. state session does the same with each run of a session. When a script has several sessions, the session must be named.
indicator "Opening range of two sessions"
edition = 1
display = price
session london = 08:00 .. 16:30 @ "Europe/London"
session newyork = 09:30 .. 16:00 @ "America/New_York"
state session london london_high = high
state session newyork newyork_high = high
if london.active
london_high = math.max(london_high, high)
if newyork.active
newyork_high = math.max(newyork_high, high)
plot(london_high, name = "London high so far", color = color.green)
plot(newyork_high, name = "New York high so far", color = color.orange)indicator "Which session?"
edition = 1
session london = 08:00 .. 16:30 @ "Europe/London"
session newyork = 09:30 .. 16:00 @ "America/New_York"
state session high_so_far = high
plot(high_so_far)A time of day#
clock.reached(time @ zone) is true on the first bar of each date whose close is at or after that time in that zone. It is true once a day. It works at any timeframe: on 15-minute bars it is the bar that closes 16:45; on hourly bars it is the bar that closes 17:00, the first bar that can know.
indicator "Close of New York"
edition = 1
display = price
if clock.reached(16:45 @ "America/New_York")
alert.send("New York is closing", key = "ny-close", once = "bar_close")
plot(close)The time and its zone are one argument. clock.reached(16:45) is an error (E3271), because a time without a zone means a different moment on every machine.
The example sends an alert so that you can see where one would go. In this release an alert of your own script does not reach you: see Alert keys and options.
The previous day, week and month#
prior(period) is the last complete period and prior(period, n) goes back n periods, where n is a whole number from 1 to 64 written in the script. current(period) is the period that is forming now. The period is day, week or month, and after it you name the field: open, high, low, close, volume or time.
indicator "Yesterday and last week"
edition = 1
display = price
plot(prior(day).high, name = "Yesterday high", color = color.green)
plot(prior(day).low, name = "Yesterday low", color = color.red)
plot(prior(week).high, name = "Last week high", color = color.gray)
plot(current(day).open, name = "Open of today", color = color.orange)These are built from the chart's own bars, with no extra request. A period counts only after its first boundary was seen, so prior(day) is na for the first days of the loaded history. The period must be longer than one bar of the chart: on a daily chart prior(day) is an error. The engine stops the script with E-PERIOD-TF and says which chart it is on. On a daily chart use prior(week) or prior(month).
When a condition turns true#
rises(condition) is true on the bar where a condition becomes true after being false, and falls(condition) on the bar where it becomes false. They remember the condition from bar to bar, so call them on every bar.
indicator "Edges of a trend"
edition = 1
display = price
let uptrend = close > tech.sma(close, 50)
mark.shape(
rises(uptrend),
name = "Trend starts",
shape = triangle_up,
place = below,
color = color.green,
)
mark.shape(
falls(uptrend),
name = "Trend ends",
shape = triangle_down,
place = above,
color = color.red,
)See also: Variables, Units and literals, Other timeframes and symbols.