Skip to content
GetProfitable
Search

Bar states, sessions and time

Lesson 9 · about 11 min

Trading logic is full of clauses about time: only during the regular session, only on the first bar of the day, not in the last ten minutes, only once the bar has closed. Pine exposes all of this through three groups of built-ins. Bar states tell you what kind of bar the script is on; session functions tell you whether a bar falls in a time window; time functions give you the clock.

Bar states

Variable True when
barstate.isfirst the first bar of the chart
barstate.islast the last bar on the chart, whether historical or live
barstate.ishistory a historical bar
barstate.isrealtime the live bar (any update of it)
barstate.isnew the first update of a bar (every historical bar, first tick live)
barstate.isconfirmed the final update of a bar (every historical bar, closing tick live)
barstate.islastconfirmedhistory the last historical bar before the live one

Two are used constantly. barstate.islast is where you draw things that should only exist once, like a table or a label at the current price, because drawing on every bar wastes the object budget. barstate.isconfirmed is the guard that makes a condition fire only when the live bar has actually closed, which is the foundation of the repainting discussion in module 5.

//@version=6
indicator("Bar states", overlay=true)
var label info = na
if barstate.islast
    label.delete(info)
    txt = barstate.isrealtime ? "live bar, updating" : "last historical bar"
    info := label.new(bar_index, high, txt, style=label.style_label_down, color=color.new(color.blue, 20), textcolor=color.white)

On historical bars isconfirmed is always true, because history is made of closed bars. That is why a script can look identical on history and behave differently live if it does not guard with it.

Time variables

time is the bar's open time as a Unix timestamp in milliseconds; time_close is the close time. Calendar pieces are available directly: year, month, dayofmonth, dayofweek, hour, minute, second. These use the exchange's time zone by default. To read them in a specific zone, use the function forms: hour(time, "America/New_York").

timestamp() builds a time from parts: timestamp("America/New_York", 2024, 3, 15, 9, 30), or from a string timestamp("2024-03-15T09:30:00-04:00"). Comparing time against a timestamp is how you restrict a backtest to a date range, and input.time() lets the user pick that date from a calendar.

dayofweek compares against constants: dayofweek == dayofweek.friday.

Sessions

A session string is "HHMM-HHMM", optionally with days: "0930-1600:23456" means weekdays only (1 is Sunday). The time() function with a session returns the bar's time if the bar is inside the session and na otherwise, which makes not na(time(...)) the standard "in session" test.

//@version=6
indicator("Sessions and time", overlay=true)
sess = input.session("0930-1600", "Session")
tz = input.string("America/New_York", "Time zone")
inSession = not na(time(timeframe.period, sess, tz))
newDay = timeframe.change("D")
lastTenMin = timeframe.isintraday and not na(time(timeframe.period, "1550-1600", tz))
bgcolor(inSession ? color.new(color.blue, 93) : na)
bgcolor(lastTenMin ? color.new(color.orange, 85) : na)
plotchar(newDay, "New day", char="|", location=location.top, color=color.gray, size=size.tiny)

timeframe.change("D") is true on the first bar of each new day and works for any period string, so timeframe.change("W") marks Mondays (or Sundays for forex and crypto, which is worth checking on your symbol). It replaces the older idiom of comparing dayofmonth to its previous value.

The session.* group covers the symbol's own hours: session.ismarket is true during regular hours, session.ispremarket and session.ispostmarket for extended hours on symbols that have them, and session.isfirstbar on the first bar of a session.

Timeframe information

timeframe.period is the chart's period as a string ("15", "D"). timeframe.isintraday, timeframe.isdaily, timeframe.isweekly are booleans. timeframe.multiplier is the number part. timeframe.in_seconds() converts a period to seconds, which lets you compare timeframes: timeframe.in_seconds("60") > timeframe.in_seconds(timeframe.period) checks that an hour is higher than the chart.

Scripts that only make sense intraday should say so rather than draw nonsense:

if not timeframe.isintraday
    runtime.error("This script needs an intraday chart.")

runtime.error stops the script and shows the message on the chart.

Key idea: barstate.isconfirmed means the bar is finished, not na(time(timeframe.period, session, tz)) means the bar is inside a time window, and timeframe.change("D") means a new day started. Most time logic is a combination of those three.

Try it: Colour the background of the first 30 minutes of the regular session on a 5-minute chart, add a label on barstate.islast showing the current hour and minute in New York time, and stop the script with runtime.error if it is added to a daily chart.

Recap

  • barstate.islast is for one-off drawings; barstate.isconfirmed is for "bar has closed" logic and is always true on history.
  • time and time_close are millisecond timestamps; hour, dayofweek and friends read the exchange zone unless you pass a time zone.
  • Sessions are "HHMM-HHMM:days" strings; not na(time(timeframe.period, sess, tz)) tests membership.
  • timeframe.change("D") marks a new period; session.ismarket marks regular hours.
  • timeframe.isintraday and timeframe.in_seconds() let a script check it is on a suitable chart; runtime.error stops it otherwise.

See it drawn

Original diagrams for the ideas on this page. Illustrative, not real market data.

One daily candle broken into four six-hour candlesA tall daily candle on the left and the four six-hour candles that make it up on the right, with dashed lines linking the day's open to the first candle and the day's close to the last.ONE DAILY CANDLEFOUR 6-HOUR CANDLEScloseopenhighlow=00:0006:0012:0018:00one dayThe same trading, summed up in one bar or spelled out in four.
How timeframes stack up. A daily candle is not different data, only coarser data: it opens where the first six-hour candle opened, closes where the last one closed, and its wicks reach the highest and lowest prices any of the four touched.

Finished this module? Take the module quiz.