What it does
Pivot points turn the previous period's high, low and close into a set of horizontal levels for the current period: a central pivot (P), resistance levels above (R1, R2...) and support levels below (S1, S2...). Because every trader using the same formula sees the same levels, they are widely watched, especially on intraday charts.
This indicator offers four common formulas, picks a sensible pivot timeframe automatically, and only uses completed higher-timeframe bars, so the levels for a period never change once that period has started.
How it works
Let H, L and C be the high, low and close of the previous completed pivot period, and R = H − L.
| Type | Pivot | Levels |
|---|---|---|
| Classic | P = (H + L + C) ÷ 3 | R1 = 2P − L, S1 = 2P − H, R2 = P + R, S2 = P − R, R3 = H + 2(P − L), S3 = L − 2(H − P) |
| Fibonacci | P = (H + L + C) ÷ 3 | R1/S1 = P ± 0.382R, R2/S2 = P ± 0.618R, R3/S3 = P ± 1.000R |
| Camarilla | P = (H + L + C) ÷ 3 | R1/S1 = C ± 1.1R ÷ 12, R2/S2 = C ± 1.1R ÷ 6, R3/S3 = C ± 1.1R ÷ 4, R4/S4 = C ± 1.1R ÷ 2 |
| Woodie | P = (H + L + 2C) ÷ 4 | Same R1 to S3 structure as Classic, using the Woodie pivot |
Auto timeframe: daily pivots on charts up to 1 hour, weekly on charts up to 4 hours, monthly on daily charts and yearly above that.
The previous period's values are requested with lookahead off. On historical bars the result is offset by one chart bar; on live bars the previous higher-timeframe bar is used directly. Both give the completed period's values without peeking into the future.
How to use it
Common readings:
- Price above P is often treated as a bullish bias for the period, below P as bearish.
- R1 and S1 are frequent first targets or reaction points intraday. R2/S2 and R3/S3 are reached less often and usually on more volatile days.
- Camarilla levels cluster close to the previous close. Many traders watch R3/S3 for reversals and R4/S4 for breakouts.
- Pivots work best alongside other support and resistance, such as previous highs and lows or round numbers.
Alerts are available for crosses of P, closes above R1 and closes below S1.
Settings
| Input | Default | What it changes |
|---|---|---|
| Pivot type | Classic | Formula used. |
| Pivot timeframe | Auto | Daily, Weekly, Monthly, Yearly or automatic. |
| Show level labels | on | Labels with prices at the last bar. |
| Show R3 / S3 (and R4 / S4) | on | Outer levels. |
| Pivot / resistance / support color | orange / red / teal | Line colors. |
Limitations
- Pivots are calculated from the data feed's daily boundary. Forex brokers and feeds use different rollover times, so levels can differ slightly between platforms.
- They are static for the whole period and ignore anything that happens after it starts.
- Several formulas exist for Woodie and Camarilla pivots. This tool uses the versions shown above.
- The pivot timeframe must be equal to or higher than the chart timeframe; otherwise the script stops with an error.
Pine Script source
In TradingView open Pine Editor, paste the code, click Save and then Add to chart.
FXP_PivotPointsMulti.pine
118 lines
// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/pivot-points-multi/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
indicator("FXP Pivot Points Multi", shorttitle = "FXP Pivots", overlay = true, max_labels_count = 20)
// ── Inputs ─────────────────────────────────────────────────────────────
grpP = "Pivots"
pivType = input.string("Classic", "Pivot type", options = ["Classic", "Fibonacci", "Camarilla", "Woodie"], group = grpP)
tfSel = input.string("Auto", "Pivot timeframe", options = ["Auto", "Daily", "Weekly", "Monthly", "Yearly"], group = grpP, tooltip = "Auto: Daily up to 1h charts, Weekly up to 4h, Monthly on daily charts, Yearly above that.")
grpVis = "Display"
showLbl = input.bool(true, "Show level labels on the last bar", group = grpVis)
showS3R3 = input.bool(true, "Show R3 / S3 (and R4 / S4 for Camarilla)", group = grpVis)
pCol = input.color(color.orange, "Pivot color", group = grpVis)
rCol = input.color(color.red, "Resistance color", group = grpVis)
sCol = input.color(color.teal, "Support color", group = grpVis)
// ── Timeframe selection ────────────────────────────────────────────────
string autoTf = timeframe.isintraday and timeframe.multiplier <= 60 ? "D" : timeframe.isintraday ? "W" : timeframe.isdaily ? "M" : "12M"
string htf = tfSel == "Auto" ? autoTf : tfSel == "Daily" ? "D" : tfSel == "Weekly" ? "W" : tfSel == "Monthly" ? "M" : "12M"
if timeframe.in_seconds() > timeframe.in_seconds(htf)
runtime.error("The pivot timeframe must be equal to or higher than the chart timeframe.")
// ── Previous completed higher-timeframe bar (non-repainting) ──────────
// Historical bars: take the HTF value and offset it one chart bar. Realtime bars: take the previous HTF bar.
int htfIdx = barstate.isrealtime ? 1 : 0
int chIdx = barstate.isrealtime ? 0 : 1
[hRaw, lRaw, cRaw] = request.security(syminfo.tickerid, htf, [high[htfIdx], low[htfIdx], close[htfIdx]], lookahead = barmerge.lookahead_off)
float pH = hRaw[chIdx]
float pL = lRaw[chIdx]
float pC = cRaw[chIdx]
float rng = pH - pL
// ── Levels ─────────────────────────────────────────────────────────────
float pp = na
float r1 = na
float r2 = na
float r3 = na
float r4 = na
float s1 = na
float s2 = na
float s3 = na
float s4 = na
if pivType == "Classic"
pp := (pH + pL + pC) / 3.0
r1 := 2.0 * pp - pL
s1 := 2.0 * pp - pH
r2 := pp + rng
s2 := pp - rng
r3 := pH + 2.0 * (pp - pL)
s3 := pL - 2.0 * (pH - pp)
else if pivType == "Fibonacci"
pp := (pH + pL + pC) / 3.0
r1 := pp + 0.382 * rng
s1 := pp - 0.382 * rng
r2 := pp + 0.618 * rng
s2 := pp - 0.618 * rng
r3 := pp + 1.000 * rng
s3 := pp - 1.000 * rng
else if pivType == "Camarilla"
pp := (pH + pL + pC) / 3.0
r1 := pC + rng * 1.1 / 12.0
s1 := pC - rng * 1.1 / 12.0
r2 := pC + rng * 1.1 / 6.0
s2 := pC - rng * 1.1 / 6.0
r3 := pC + rng * 1.1 / 4.0
s3 := pC - rng * 1.1 / 4.0
r4 := pC + rng * 1.1 / 2.0
s4 := pC - rng * 1.1 / 2.0
else
pp := (pH + pL + 2.0 * pC) / 4.0
r1 := 2.0 * pp - pL
s1 := 2.0 * pp - pH
r2 := pp + rng
s2 := pp - rng
r3 := pH + 2.0 * (pp - pL)
s3 := pL - 2.0 * (pH - pp)
// Break the lines at each new pivot period so levels do not connect vertically.
bool newPeriod = timeframe.change(htf)
f_show(float v, bool enabled) => newPeriod or not enabled ? float(na) : v
plot(f_show(pp, true), "P", color = pCol, linewidth = 2, style = plot.style_linebr)
plot(f_show(r1, true), "R1", color = rCol, style = plot.style_linebr)
plot(f_show(r2, true), "R2", color = rCol, style = plot.style_linebr)
plot(f_show(r3, showS3R3), "R3", color = rCol, style = plot.style_linebr)
plot(f_show(r4, showS3R3), "R4", color = rCol, style = plot.style_linebr)
plot(f_show(s1, true), "S1", color = sCol, style = plot.style_linebr)
plot(f_show(s2, true), "S2", color = sCol, style = plot.style_linebr)
plot(f_show(s3, showS3R3), "S3", color = sCol, style = plot.style_linebr)
plot(f_show(s4, showS3R3), "S4", color = sCol, style = plot.style_linebr)
// ── Labels (reused, last bar only) ─────────────────────────────────────
var array<label> lbls = array.new_label()
if barstate.islast and showLbl
array<float> vals = array.from(pp, r1, r2, r3, r4, s1, s2, s3, s4)
array<string> names = array.from("P", "R1", "R2", "R3", "R4", "S1", "S2", "S3", "S4")
if array.size(lbls) == 0
for i = 0 to 8
array.push(lbls, label.new(bar_index, close, "", style = label.style_label_left, color = color.new(color.black, 100), size = size.small))
for i = 0 to 8
label lb = array.get(lbls, i)
float v = array.get(vals, i)
bool hideExtra = not showS3R3 and (i == 3 or i == 4 or i == 7 or i == 8)
label.set_xy(lb, bar_index + 1, hideExtra or na(v) ? close : v)
label.set_text(lb, na(v) or hideExtra ? "" : array.get(names, i) + " " + str.tostring(v, format.mintick))
label.set_textcolor(lb, i == 0 ? pCol : i <= 4 ? rCol : sCol)
// ── Alerts ─────────────────────────────────────────────────────────────
bool xP = ta.cross(close, pp) and barstate.isconfirmed and not newPeriod
bool xR1 = ta.crossover(close, r1) and barstate.isconfirmed and not newPeriod
bool xS1 = ta.crossunder(close, s1) and barstate.isconfirmed and not newPeriod
alertcondition(xP, "Price crossed P", "FXP Pivots: price crossed the pivot on {{ticker}} {{interval}}")
alertcondition(xR1, "Close above R1", "FXP Pivots: close above R1 on {{ticker}} {{interval}}")
alertcondition(xS1, "Close below S1", "FXP Pivots: close below S1 on {{ticker}} {{interval}}")

