What it does
The indicator boxes the trading range of the three main forex sessions: Asia, London and New York. Each box grows with the session, showing its high and low as they form, and prints the range size. Where sessions overlap, the boxes overlap too, so you can see at a glance where the day's moves happened.
How it works
- Each session has its own hours and time zone. The defaults are local business hours in each financial center: 09:00 to 18:00 in Tokyo, 08:00 to 17:00 in London and 08:00 to 17:00 in New York. Because the hours are written in local time, daylight-saving changes are handled automatically by the time zone.
- On the first candle inside a session, a new box starts at that candle's high and low.
- On every later candle in the session the box's top becomes the highest high so far, its bottom the lowest low so far, and its right edge moves to the current candle.
- The range is shown in pips for forex symbols (one pip = 10 minimum ticks on 5-digit and 3-digit quotes) and in ticks for other symbols.
- Each session keeps the chosen number of past boxes; older ones are deleted.
You can switch any session to another time zone, including the chart's exchange time zone, if you prefer to type hours in a different clock.
How to use it
- The Asia range is often narrower. Many intraday traders mark its high and low and watch how London trades around them.
- The London and New York overlap is usually the most active part of the day for EUR/USD, GBP/USD and gold. See forex market hours and the lesson on forex trading sessions for background.
- Compare today's session range with recent ones. An unusually small range can come before expansion, and an unusually large one may already include the day's main move.
- Use it on M1 to H1 charts. On daily and higher charts the script stays blank.
Alerts are available for the open and close of each session.
Settings
| Input | Default | What it changes |
|---|---|---|
| Asia: show, color, hours, time zone | On, amber, 0900-1800, Asia/Tokyo | Asia box |
| London: show, color, hours, time zone | On, blue, 0800-1700, Europe/London | London box |
| New York: show, color, hours, time zone | On, pink, 0800-1700, America/New_York | New York box |
| Sessions kept per market | 20 | Number of past boxes |
| Box transparency | 88 | Fill strength |
| Show range size in the box | On | Range in pips or ticks |
Limitations
Session hours are conventions, not official opening times, and brokers and traders define them differently, so adjust them to match your own view. Boxes are built from the candles on your chart, so on H1 a session that starts at half past the hour will begin on the candle that contains that time. The pip calculation assumes standard forex quotes; for metals, indices and crypto the range is shown in ticks. Holidays and thin markets can produce small, unrepresentative ranges.
Pine Script source
In TradingView open Pine Editor, paste the code, click Save and then Add to chart.
FXP_ForexSessionBoxes.pine
78 lines
// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/forex-session-boxes/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
// Draws a box around the high and low of the Asia, London and New York sessions.
// Each session has its own hours and time zone, so daylight-saving changes are handled
// by the time zone itself (defaults use each financial center's local business hours).
indicator("FXP Forex Session Boxes", shorttitle = "FXP Sessions", overlay = true, max_boxes_count = 500)
// ── Inputs ─────────────────────────────────────────────────────────────
grpA = "Asia"
showA = input.bool(true, "Show", inline = "a1", group = grpA)
colA = input.color(#f9a825, "", inline = "a1", group = grpA)
sessA = input.session("0900-1800", "Hours", inline = "a2", group = grpA)
tzA = input.string("Asia/Tokyo", "Time zone", options = ["UTC", "Asia/Tokyo", "Asia/Singapore", "Asia/Kolkata", "Asia/Dubai", "Australia/Sydney", "Europe/London", "Europe/Berlin", "America/New_York", "America/Chicago", "Exchange"], inline = "a2", group = grpA)
grpL = "London"
showL = input.bool(true, "Show", inline = "l1", group = grpL)
colL = input.color(#2962ff, "", inline = "l1", group = grpL)
sessL = input.session("0800-1700", "Hours", inline = "l2", group = grpL)
tzL = input.string("Europe/London", "Time zone", options = ["UTC", "Asia/Tokyo", "Asia/Singapore", "Asia/Kolkata", "Asia/Dubai", "Australia/Sydney", "Europe/London", "Europe/Berlin", "America/New_York", "America/Chicago", "Exchange"], inline = "l2", group = grpL)
grpN = "New York"
showN = input.bool(true, "Show", inline = "n1", group = grpN)
colN = input.color(#e91e63, "", inline = "n1", group = grpN)
sessN = input.session("0800-1700", "Hours", inline = "n2", group = grpN)
tzN = input.string("America/New_York", "Time zone", options = ["UTC", "Asia/Tokyo", "Asia/Singapore", "Asia/Kolkata", "Asia/Dubai", "Australia/Sydney", "Europe/London", "Europe/Berlin", "America/New_York", "America/Chicago", "Exchange"], inline = "n2", group = grpN)
grpD = "Display"
keepCount = input.int(20, "Sessions kept per market", minval = 1, maxval = 150, group = grpD)
fillTransp = input.int(88, "Box transparency", minval = 0, maxval = 100, group = grpD)
showRange = input.bool(true, "Show range size in the box", group = grpD)
// ── Helpers ────────────────────────────────────────────────────────────
bool isFx = syminfo.type == "forex"
float pipSize = isFx ? syminfo.mintick * 10 : syminfo.mintick
string unit = isFx ? " pips" : " ticks"
resolveTz(string tz) =>
tz == "Exchange" ? syminfo.timezone : tz
sessionBox(string sess, string tz, string name, color col, bool show) =>
bool inSess = show and timeframe.isintraday and not na(time(timeframe.period, sess, resolveTz(tz)))
bool started = inSess and not inSess[1]
bool ended = not inSess and inSess[1]
var float hi = na
var float lo = na
var box bx = na
var array<box> hist = array.new<box>()
if started
hi := high
lo := low
bx := box.new(bar_index, high, bar_index, low, border_color = col, border_width = 1, bgcolor = color.new(col, fillTransp), text = name, text_color = col, text_size = size.small, text_halign = text.align_left, text_valign = text.align_top)
hist.push(bx)
while hist.size() > keepCount
box.delete(hist.shift())
else if inSess
hi := math.max(hi, high)
lo := math.min(lo, low)
if not na(bx)
bx.set_top(hi)
bx.set_bottom(lo)
bx.set_right(bar_index)
if inSess and showRange and not na(bx)
bx.set_text(name + " " + str.tostring((hi - lo) / pipSize, "#.0") + unit)
[started, ended]
[asiaOpen, asiaClose] = sessionBox(sessA, tzA, "Asia", colA, showA)
[londonOpen, londonClose] = sessionBox(sessL, tzL, "London", colL, showL)
[nyOpen, nyClose] = sessionBox(sessN, tzN, "New York", colN, showN)
// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(asiaOpen, "Asia session open", "{{ticker}}: Asia session opened")
alertcondition(asiaClose, "Asia session close", "{{ticker}}: Asia session closed")
alertcondition(londonOpen, "London session open", "{{ticker}}: London session opened")
alertcondition(londonClose, "London session close", "{{ticker}}: London session closed")
alertcondition(nyOpen, "New York session open", "{{ticker}}: New York session opened")
alertcondition(nyClose, "New York session close", "{{ticker}}: New York session closed")

