millionchart
فارسی

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:

FeedA day starts at
Forex, metals, indices, energy, futures and the broker's other symbols17:00 New York time, which is 21:00 or 22:00 UTC depending on daylight saving
Crypto from an exchange feed, and archived data00: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:

Cuprel
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.

Cuprel
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,
)
MemberIs
london.activetrue on the bars that open inside the session
london.starttrue on the first bar of a run of the session
london.endtrue on the last bar of a run: the bar that opens inside the window and closes at its end
london.index0, 1, 2 … the number of the latest run; na before the first
london.high, london.lowthe 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:

Cuprel
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.

Cuprel · error E1022
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:

Cuprel · error E3260
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.

Cuprel
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)
Cuprel · error E3280
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.

Cuprel
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.

Cuprel
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.

Cuprel
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.