Gold Structure Lite: a Free TradingView Indicator for Gold
Gold Structure Lite puts the structure layer of the XAUUSD live chart on a TradingView chart: the 50 and 200 EMA for context, an ATR band for stop distance, confirmed swing highs and lows, and BOS and CHoCH labels that are only drawn on a closed candle. It is free, it never repaints, and it gives no signals.
What it draws
How to install it
- Open any chart on TradingView and click Pine Editor at the bottom of the screen.
- Delete the sample code, press Copy code below and paste the script into the editor.
- Click Add to chart. The EMAs, the band and the labels appear on the current symbol and timeframe.
- Optional: click Save to keep it in your indicators list, and set alerts on "Bullish structure break" or "Bearish structure break".
The same code can be opened from the TradingView indicator library under the name Gold Structure Lite once the public listing is live. Either route gives you the same script.
// This Pine Script code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Gold Structure Lite - free structure indicator for XAUUSD and other markets
// Published by XAUUSD Live Chart - https://xauusdlivechart.com
// How to read it: https://xauusdlivechart.com/free-gold-indicator
//
// What it draws
// 1. EMA 50 and EMA 200 (trend filter: 50 above 200 = bullish context, below = bearish)
// 2. An ATR stop band around the EMA 50 (a volatility-sized distance for stops, not a signal)
// 3. Swing highs and lows found with confirmed pivots, and the structure labels built from them:
// BOS = Break of Structure : close beyond the last swing in the direction of the current trend
// CHoCH = Change of Character : close beyond the last swing AGAINST the current trend
//
// No repaint rules
// - Pivots only exist after `pivotRight` bars have closed to the right of them, so a swing is never
// redrawn once it is printed. The cost is a delay of `pivotRight` bars, which is deliberate.
// - Every BOS / CHoCH label is created on a CONFIRMED bar close (barstate.isconfirmed), never on a
// forming candle. A wick through a level does not count; only a close does.
// - Nothing uses future data (no lookahead, no security() calls).
//
// It is a reading aid. It does not give buy or sell signals and it is not financial advice.
//@version=5
indicator("Gold Structure Lite", shorttitle="GS Lite", overlay=true, max_labels_count=200, max_lines_count=200)
// ---------- inputs ----------
grpT = "Trend"
emaFastLen = input.int(50, "Fast EMA", minval=2, group=grpT)
emaSlowLen = input.int(200, "Slow EMA", minval=2, group=grpT)
showEma = input.bool(true, "Show EMAs", group=grpT)
grpA = "ATR stop band"
atrLen = input.int(14, "ATR length", minval=1, group=grpA)
atrMult = input.float(1.5, "ATR multiplier", minval=0.1, step=0.1, group=grpA)
showBand = input.bool(true, "Show ATR band around the fast EMA", group=grpA)
grpS = "Structure"
pivotLeft = input.int(5, "Pivot bars (left)", minval=1, group=grpS)
pivotRight = input.int(5, "Pivot bars (right, confirmation delay)", minval=1, group=grpS)
showSwings = input.bool(true, "Mark swing highs and lows", group=grpS)
showLevels = input.bool(true, "Draw the live swing levels", group=grpS)
showLabels = input.bool(true, "BOS / CHoCH labels", group=grpS)
// ---------- trend + volatility ----------
emaFast = ta.ema(close, emaFastLen)
emaSlow = ta.ema(close, emaSlowLen)
atrVal = ta.atr(atrLen)
bandUp = emaFast + atrVal * atrMult
bandDn = emaFast - atrVal * atrMult
bullCtx = emaFast > emaSlow
pFast = plot(showEma ? emaFast : na, "EMA fast", color=color.new(#f5c93a, 0), linewidth=2)
pSlow = plot(showEma ? emaSlow : na, "EMA slow", color=color.new(#8aa0b8, 0), linewidth=2)
pUp = plot(showBand ? bandUp : na, "ATR band upper", color=color.new(#2ee6a0, 70), linewidth=1)
pDn = plot(showBand ? bandDn : na, "ATR band lower", color=color.new(#ff5c7a, 70), linewidth=1)
fill(pUp, pDn, color=color.new(bullCtx ? #2ee6a0 : #ff5c7a, 92), title="ATR band fill")
// ---------- confirmed pivots (no repaint: a pivot appears only after pivotRight closed bars) ----------
ph = ta.pivothigh(high, pivotLeft, pivotRight) // value of the swing high, printed pivotRight bars late
pl = ta.pivotlow(low, pivotLeft, pivotRight)
var float lastHigh = na // most recent confirmed swing high (price)
var float lastLow = na // most recent confirmed swing low (price)
var int lastHighBar = na
var int lastLowBar = na
var int trendDir = 0 // +1 after a bullish break, -1 after a bearish break, 0 until the first break
var bool highTaken = false // the current swing high has already been broken by a close
var bool lowTaken = false
if not na(ph)
lastHigh := ph
lastHighBar := bar_index - pivotRight
highTaken := false
if showSwings
label.new(lastHighBar, lastHigh, "H", style=label.style_label_down, color=color.new(#ff5c7a, 60), textcolor=color.white, size=size.tiny)
if not na(pl)
lastLow := pl
lastLowBar := bar_index - pivotRight
lowTaken := false
if showSwings
label.new(lastLowBar, lastLow, "L", style=label.style_label_up, color=color.new(#2ee6a0, 60), textcolor=color.white, size=size.tiny)
// ---------- live swing levels (one line per side, extended to the current bar) ----------
var line hiLine = na
var line loLine = na
if showLevels and not na(lastHigh)
if na(hiLine)
hiLine := line.new(lastHighBar, lastHigh, bar_index, lastHigh, color=color.new(#ff5c7a, 30), style=line.style_dotted)
else
line.set_xy1(hiLine, lastHighBar, lastHigh)
line.set_xy2(hiLine, bar_index, lastHigh)
if showLevels and not na(lastLow)
if na(loLine)
loLine := line.new(lastLowBar, lastLow, bar_index, lastLow, color=color.new(#2ee6a0, 30), style=line.style_dotted)
else
line.set_xy1(loLine, lastLowBar, lastLow)
line.set_xy2(loLine, bar_index, lastLow)
// ---------- structure breaks, decided on CONFIRMED closes only ----------
// Bullish break: a close above the last swing high that has not been taken yet.
// Bearish break: a close below the last swing low that has not been taken yet.
// BOS when the break continues the current trend, CHoCH when it reverses it (or when there is no trend yet).
bullBreak = barstate.isconfirmed and not na(lastHigh) and not highTaken and close > lastHigh
bearBreak = barstate.isconfirmed and not na(lastLow) and not lowTaken and close < lastLow
if bullBreak
isChoch = trendDir <= 0
if showLabels
label.new(bar_index, lastHigh, isChoch ? "CHoCH" : "BOS", style=label.style_label_down, color=color.new(#2ee6a0, isChoch ? 0 : 35), textcolor=#04100b, size=size.small, tooltip=(isChoch ? "Change of character: " : "Break of structure: ") + "close above the swing high " + str.tostring(lastHigh, format.mintick))
highTaken := true
trendDir := 1
if bearBreak
isChoch = trendDir >= 0
if showLabels
label.new(bar_index, lastLow, isChoch ? "CHoCH" : "BOS", style=label.style_label_up, color=color.new(#ff5c7a, isChoch ? 0 : 35), textcolor=color.white, size=size.small, tooltip=(isChoch ? "Change of character: " : "Break of structure: ") + "close below the swing low " + str.tostring(lastLow, format.mintick))
lowTaken := true
trendDir := -1
// ---------- alerts (fire once per confirmed bar) ----------
alertcondition(bullBreak, "Bullish structure break", "Gold Structure Lite: close above the last swing high")
alertcondition(bearBreak, "Bearish structure break", "Gold Structure Lite: close below the last swing low")
// ---------- small status box ----------
var table info = table.new(position.top_right, 1, 3, border_width=0)
if barstate.islast
table.cell(info, 0, 0, "Gold Structure Lite", text_color=#8aa0b8, text_size=size.tiny)
table.cell(info, 0, 1, (bullCtx ? "EMA context: bullish" : "EMA context: bearish"), text_color=(bullCtx ? #2ee6a0 : #ff5c7a), text_size=size.small)
table.cell(info, 0, 2, trendDir > 0 ? "Structure: bullish (last break up)" : trendDir < 0 ? "Structure: bearish (last break down)" : "Structure: no break yet", text_color=#e6edf3, text_size=size.small)
Licence: Mozilla Public License 2.0. You may use, modify and republish the code; keep the licence header.
How to read it on gold
Start with the status box. If the EMA context says bullish and the last structure break was up, you are looking for pullbacks into demand, not for shorts. If the two disagree, the market is transitioning and the honest reading is to wait for the next label.
A BOS label confirms that the trend has made a new leg. The swing it broke becomes the level to watch on a pullback. A CHoCH label is the first warning that the sequence has flipped. One CHoCH does not make a new trend, and gold produces false flips around news, so treat it as a reason to stop pressing the old direction rather than a reason to reverse.
The ATR band answers one question: how much normal movement should a stop survive. A stop inside the band is likely to be hit by noise. The methodology behind this, and why gold needs volatility-sized stops, is covered in the ATR stop-loss guide and the best indicator for gold page.
Settings worth changing
- Pivot bars. Five left and five right is a reasonable default on M15 to H4. Use three on M1 and M5 for faster structure, seven or more on D1 for cleaner swings. More bars means fewer, later, more reliable labels.
- ATR multiplier. One and a half is a gold default. Scalpers on M1 often prefer one, swing traders on D1 two.
- EMA lengths. The 50 and 200 are the most watched pair on gold. Changing them changes what other traders are not looking at, which defeats part of the purpose.
Three ways a label can mislead you
First, a CHoCH printed in the middle of a range is not a reversal; it is the range doing what ranges do. Check the EMA context and the higher timeframe before reading anything into it. Second, a BOS that forms on a news candle can be undone within the hour, because the close that confirmed it was a spike, not a decision. Wait for the next candle. Third, a swing label that appears five bars late can tempt you to act on the level as if it were fresh. It is not fresh; price has already moved on, and the useful question is whether it returns to that level, not whether it broke it. Reading the labels with those three cautions in mind is the difference between a structure map and a signal generator.
What it does not do
It does not draw supply and demand zones or order blocks; the live chart on this site does that with scoring and lifecycle rules that are documented on the methodology page. It does not use volume, because spot gold on most TradingView feeds shows tick volume. It does not predict. A label describes a closed candle, nothing more. Learn the concepts behind the labels in the free SMC course, then decide with your own plan and risk rules. This page is educational and is not investment advice.
FAQ
Is Gold Structure Lite free?
Yes. The code is published under the Mozilla Public License 2.0. Copy it into the TradingView Pine Editor, or open it from the TradingView indicator library once it is listed there. There is no paid version.
Does the indicator repaint?
No. Swing points are confirmed pivots, so they only appear after the chosen number of bars has closed to the right, and BOS and CHoCH labels are created on a confirmed candle close. The price for that is a delay of a few bars, which is deliberate.
Does it work on markets other than gold?
Yes. The logic is generic market structure with EMA and ATR context, so it runs on any symbol and timeframe. The defaults were chosen with gold in mind, which is why the ATR band is wide.
Is there an MT5 version?
Not yet. The structure layer is available on the live chart on this site without any installation, and the Pine Script version covers TradingView. An MT5 port is on the list but has no date.
Is this a trading signal?
No. The labels describe what price has done on closed candles. They do not tell you to buy or sell, and the site does not provide investment advice.
Prefer zones and order blocks drawn for you? Open the live gold chart, no installation needed.