What it does
Prices ending in 00 or 50 attract attention. Traders place orders, stops and targets near them, and they are easy to remember. This indicator draws those psychological levels around the current price: solid lines for the "00" levels and dashed lines for the "50" half levels, with price labels.
You do not need to set the spacing for each instrument. The script picks a step based on the size of the price, so the same settings work on EUR/USD, USD/JPY, gold and indices.
How it works
- Auto step = 10 raised to (number of digits in the integer part of price − 3), then multiplied by the Auto step multiplier.
- Illustrative examples, assuming these prices: EUR/USD at 1.0850 gives a step of 0.01, so 00 levels at 1.0800, 1.0900 and 50 levels at 1.0850. USD/JPY at 150.25 gives a step of 1.00. A price of 2,400 gives 10, and 18,000 gives 100.
- The major level at or below the close is found by dividing the close by the step and rounding down.
- Major levels are drawn from N steps below to N steps above that level, each with a half level above it.
- Lines are created once and moved on the last bar, so the drawing count never grows.
- Alerts fire when a closed bar closes on the other side of at least one 00 level (or any 00 or 50 level) compared with the previous close.
On EUR/USD the default 00 step equals 100 pips and the half step equals 50 pips. Read more in what is a pip.
How to use it
Round numbers are best treated as zones rather than exact prices. Price often reacts a little before or after the level because orders cluster around it.
Typical uses:
- Placing a take-profit just before a round number instead of exactly on it.
- Avoiding a stop-loss exactly at a round number, where many other stops may sit.
- Watching for rejection or acceptance at a 00 level that lines up with other support and resistance.
If the automatic step is too fine or too coarse for an instrument, change the multiplier (for example 5 on gold for levels every 50) or switch to manual.
Settings
| Input | Default | What it changes |
|---|---|---|
| Step mode | Auto | Automatic or manual spacing. |
| Auto step multiplier | 1.0 | Scales the automatic step. |
| Manual '00' step | 0.01 | Major spacing in manual mode. |
| Show '50' half levels | on | Dashed half levels. |
| Major levels above and below price | 5 | Number of levels each side. |
| Show price labels | on | Prices next to major levels. |
| Colors and line width | gray | Appearance. |
Limitations
- The automatic step is a rule of thumb. Some instruments are traded around different increments, so check that the levels match what you see on the chart.
- When price crosses a power of ten (for example from 99 to 100), the automatic step changes by a factor of ten.
- Lines are drawn across the whole chart from the current price; they are not historical levels.
- Round numbers are only one input. A level that has no other support from price action is usually weaker.
Pine Script source
In TradingView open Pine Editor, paste the code, click Save and then Add to chart.
FXP_RoundNumberLevels.pine
71 lines
// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/round-number-levels/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
indicator("FXP Round Number Levels", shorttitle = "FXP Round Numbers", overlay = true, max_lines_count = 100, max_labels_count = 100)
// ── Inputs ─────────────────────────────────────────────────────────────
grpS = "Step"
stepMode = input.string("Auto", "Step mode", options = ["Auto", "Manual"], group = grpS)
autoMult = input.float(1.0, "Auto step multiplier", options = [0.5, 1.0, 2.0, 5.0, 10.0], group = grpS, tooltip = "Scales the automatic step. Example: 5 on EURUSD gives levels every 500 pips instead of 100.")
manStep = input.float(0.01, "Manual '00' step (price units)", minval = 0.0, step = 0.0001, group = grpS)
showHalf = input.bool(true, "Show '50' half levels", group = grpS)
grpL = "Levels"
nLevels = input.int(5, "Major levels above and below price", minval = 1, maxval = 20, group = grpL)
grpVis = "Display"
showLbl = input.bool(true, "Show price labels", group = grpVis)
majCol = input.color(color.new(color.gray, 20), "'00' level color", group = grpVis)
halfCol = input.color(color.new(color.gray, 60), "'50' level color", group = grpVis)
majWidth = input.int(1, "'00' line width", minval = 1, maxval = 4, group = grpVis)
// ── Step size ──────────────────────────────────────────────────────────
// Auto: 10^(digits of the integer part - 3). EURUSD 1.08 -> 0.01 (100 pips), USDJPY 150 -> 1.00,
// a price of 2,400 -> 10, a price of 18,000 -> 100. The multiplier then scales that step.
float autoStep = close > 0 ? math.pow(10, math.floor(math.log10(close)) - 2) * autoMult : na
float step = stepMode == "Auto" ? autoStep : manStep
bool stepOk = not na(step) and step > 0
// Index of the major level at or below price; a tiny epsilon avoids float errors like 1.09 / 0.01 = 108.999...
float eps = 1e-9
int idxNow = stepOk ? math.floor(close / step + eps) : na
int idxPrev = stepOk ? math.floor(close[1] / step + eps) : na
int hIdxNow = stepOk ? math.floor(close / (step / 2.0) + eps) : na
int hIdxPrev = stepOk ? math.floor(close[1] / (step / 2.0) + eps) : na
// A change of index means the close moved across at least one level since the previous close.
bool crossMajor = barstate.isconfirmed and not na(idxPrev) and idxNow != idxPrev
bool crossAny = barstate.isconfirmed and not na(hIdxPrev) and hIdxNow != hIdxPrev
// ── Drawing (objects created once, then moved) ─────────────────────────
int total = 2 * nLevels + 1
var array<line> majLines = array.new_line()
var array<line> halfLines = array.new_line()
var array<label> majLbls = array.new_label()
if barstate.islast and stepOk
if array.size(majLines) == 0
for i = 0 to total - 1
array.push(majLines, line.new(bar_index - 1, close, bar_index, close, extend = extend.both, color = majCol, width = majWidth))
array.push(halfLines, line.new(bar_index - 1, close, bar_index, close, extend = extend.both, color = halfCol, style = line.style_dashed))
array.push(majLbls, label.new(bar_index, close, "", style = label.style_label_left, color = color.new(color.black, 100), textcolor = majCol, size = size.small))
for i = 0 to total - 1
float lvl = (idxNow - nLevels + i) * step
float half = lvl + step / 2.0
line ml = array.get(majLines, i)
line hl = array.get(halfLines, i)
line.set_xy1(ml, bar_index - 1, lvl)
line.set_xy2(ml, bar_index, lvl)
line.set_xy1(hl, bar_index - 1, half)
line.set_xy2(hl, bar_index, half)
line.set_color(hl, showHalf ? halfCol : color.new(halfCol, 100))
label lb = array.get(majLbls, i)
label.set_xy(lb, bar_index + 3, lvl)
label.set_text(lb, showLbl ? str.tostring(lvl, format.mintick) : "")
plot(step, "Step", display = display.data_window)
// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(crossMajor, "Close crossed a '00' level", "FXP Round Numbers: close crossed a major round number on {{ticker}} {{interval}} at {{close}}")
alertcondition(crossAny, "Close crossed a '00' or '50' level", "FXP Round Numbers: close crossed a round or half-round number on {{ticker}} {{interval}} at {{close}}")

