//@version=6
// ============================================================================
// Edge Atlas [WavesUnchained]
// Version: 1.7.2
// Author: WavesUnchained
// Build: 2026-10-02
// ============================================================================
// Right-edge price-level atlas.
//
// Core idea:
// • Calculation modules produce normalized price levels.
// • A shared registry filters and renders them.
// • Each level renders as a label a fixed bar count past the last real
// candle — pointer facing right, price/zone detail in the hover tooltip
// only — with a short, fixed-length rail picking up just past the label.
// The rail does not reach the chart's true right edge; keeping the
// current price area clear of labels matters more than the rail
// visually touching the price scale.
// • Nothing about the label/rail's position depends on the current
// viewport (no chart.right_visible_bar_time/left_visible_bar_time reads,
// no xloc.bar_time) — panning or zooming the chart moves them exactly
// like any normal drawing, with nothing to jump or freeze mid-drag.
// • Line width and label/rail opacity scale with each level's priority —
// high-priority levels (PDH/PDL-class) render bolder and more opaque,
// low-priority ones fainter — the same "visual weight tracks
// significance" principle Fib Reaction Memory uses for its Memory Score.
//
// Features:
// ✓ Previous/current day, week, month, and year reference levels
// ✓ Traditional and Fibonacci pivot points
// ✓ Swing-based Fibonacci retracements/extensions
// ✓ Pivot-cluster support/resistance zones
// ✓ Asia, London, and New York session ranges
// ✓ Automatic or manual round-number levels
// ✓ Session, weekly, and monthly VWAP
// ✓ Up to 4 manually entered price levels
// ✓ Two freely nameable custom sessions, in addition to Asia/London/NY
// ✓ Traditional, Fibonacci, Camarilla, Woodie, and DeMark pivot methods
// ✓ Trend Curves optionally feed the right-edge Level registry too, and
// can compute on a higher/lower timeframe than the chart's own
// ✓ Daily, weekly, and monthly opening ranges
// ✓ Granular sub-level switches and explanatory tooltips
// ✓ Distance, priority, count, and side filters
// ✓ Minimum label spacing scaled to the instrument's own ATR, so crowded
// levels don't collapse into a solid overlapping block, and which levels
// are shown stays stable while scrolling or zooming the chart
// ✓ Line width and opacity scale with priority, so the most significant
// levels stand out from the rest at a glance
// ✓ Optional source zones in the chart
// ✓ Trend Curves — up to three JMA-style/EMA/SMA moving averages with
// Responsive/Balanced/Structural/Custom presets, plotted as familiar,
// always-on chart lines (separate from the right-edge level registry)
//
// Important TradingView limitation:
// Pine drawings can bind to chart time and price, but not to arbitrary screen
// pixels or the price scale itself. The label anchor is a simple forward
// offset — last real bar's bar_index plus a fixed bar count ("Label offset ·
// bars") — deliberately NOT a percentage of the visible bar range, and NOT
// computed by subtracting from chart.right_visible_bar_time either. Both of
// those were tried:
// • A percentage of the visible range recomputes on every scroll/zoom.
// Since Pine only redraws when the script recalculates (not continuously
// during a drag), the label would freeze mid-pan and then visibly jump
// to a new position whenever the next recalculation happened, instead of
// panning smoothly like a normal drawing.
// • Subtracting from chart.right_visible_bar_time is even less stable: that
// live edge value can itself shift because of this script's own
// future-projected drawings, which made labels behave unpredictably
// (ending up right back at the last candle instead of near the edge).
// A forward offset from bar_index (which only changes when new bars close,
// never on chart navigation) keeps the label at a fixed position — redraws
// land exactly where it already was, so there is nothing to jump. This is
// the same positioning principle Fib Reaction Memory uses for its projected
// Fib lines (`bar_index + rightProjection`, xloc.bar_index) — this script
// used an equivalent xloc.bar_time offset from `time` through v1.6.0; both
// forms are fixed, viewport-independent offsets, but bar_index is simpler
// and needs no per-bar duration bookkeeping.
//
// Labels use label.style_label_right — the pointer sits at that anchor,
// facing right, and the box extends back toward price from there. That
// growth direction is inherently riskier than growing away from price (an
// earlier version deliberately avoided it after repeated overlap bugs), so
// two things keep it safe here: the label body is short (code/name only, no
// price — full detail is in the hover tooltip), and the offset defaults
// generously (180 bars, capped at 300). If labels ever start touching
// candles again on an extremely zoomed-out chart, increase "Label offset ·
// bars" first.
//
// Two independent bugs produced the same visual symptom — a vertical
// multi-colored bar through the label stack — while this was being tuned,
// and both are relevant if it ever reappears:
// 1. Every level's rail shared the exact same starting x-coordinate
// (computed once outside the per-level loop). Several differently-
// colored horizontal lines beginning at the identical pixel column,
// stacked closely in price, read as a vertical bar regardless of
// segment length — fixed by staggering each rail's start by render
// rank, so no two levels can ever share an origin.
// 2. Pine only allows drawings a bounded number of bars into the future
// (roughly 500). A percentage of a wide 15m/1h visible history could
// exceed that, silently clamping every level to the same maximum
// allowed point regardless of the stagger above — fixed by capping
// every rail endpoint well under that ceiling.
// The rail no longer uses extend.right to reach the true edge (dropped on
// request), which incidentally removes most of the exposure to bug #2 for
// the rail itself — but the label's own anchor is still a raw future
// bar_index, so the cap remains necessary regardless.
// ============================================================================
indicator(
"Edge Atlas [WavesUnchained]",
shorttitle = "Edge Atlas",
overlay = true,
max_lines_count = 500,
max_labels_count = 500,
max_boxes_count = 100
)
// ============================================================================
// Constants
// ============================================================================
string G_GENERAL = "00 — General"
string G_RENDER = "01 — Right-edge rendering"
string G_DAY = "02 — Period levels · Day"
string G_WEEK = "03 — Period levels · Week"
string G_MONTH = "04 — Period levels · Month"
string G_YEAR = "05 — Period levels · Year"
string G_SESSIONS = "06 — Sessions"
string G_PIVOTS = "07 — Pivot points"
string G_FIB = "08 — Swing Fibonacci"
string G_SR = "09 — Support / Resistance"
string G_ROUND = "10 — Round numbers"
string G_VWAP = "11 — VWAP"
string G_MANUAL = "12 — Manual levels"
string G_OR = "13 — Opening ranges"
string G_TREND = "14 — Trend Curves"
string TT_EDGE =
"How many bars past the last real candle the label sits — higher values keep the current price area clearer of labels.\n" +
"A fixed bar count rather than a percentage of the visible time range on purpose: a percentage-based offset changes every time the visible range changes (any scroll or zoom), which made the label freeze mid-drag and then jump to a new position on the next recalculation instead of following the pan smoothly. A fixed bar count stays at the same calendar time regardless of scroll position, so it pans with the chart like a normal drawing.\n" +
"On a chart showing a lot of history (many bars in view), a fixed bar count is a smaller fraction of the visible width, so it can still end up looking tight against the last candle — increase it if that happens; there is no single value that looks the same at every zoom level.\n" +
"Limited to 400 bars here, well clear of Pine's bounded future-drawing distance (roughly 500 bars) — the rails for the highest-priority rendered levels are separately clamped even closer to that limit, so raising \"Maximum rendered levels\" a lot at the same time as this can't push anything past it."
string TT_DISTANCE =
"Only levels within this ATR distance from the current close are eligible.\n" +
"Set a high value to deliberately allow a dense or overloaded display."
string TT_MISSING =
"A level is not registered when its source value is na.\n" +
"Session ranges therefore remain absent when no chart bars exist inside the configured window."
string TT_TREND_PRESET =
"Responsive (JMA-style 9 / 21 / 50): intraday and short-term swing trading, fast markets (oil, indices, forex, crypto) — early pullback and trend-change detection. Three fast adaptive lines react alike but can still whipsaw in choppy sideways phases.\n" +
"Balanced (JMA-style 21 / EMA 50 / SMA 200): classic day/swing trading, stocks/indices/commodities — a movement line, a tradable-trend line, and a slow regime anchor. Default.\n" +
"Structural (EMA 20 / SMA 50 / SMA 200): swing/position trading, daily/weekly charts — filters short-term noise, reads broader trend structure and regime shifts (e.g. the 50/200 cross).\n" +
"Custom: set type, length, phase, and power per line below. Lengths are always in bars of the current chart timeframe and are not auto-adjusted — 20 always means 20 candles, whatever the timeframe."
string TT_JMA =
"JMA-style: an open-source, Jurik-inspired adaptive smoothing approximation (tunable phase/power) — not Mark Jurik's patented, proprietary original algorithm."
// ============================================================================
// Inputs · General
// ============================================================================
string presetInput = input.string(
"Clean",
"Preset",
options = ["Clean", "Intraday", "Structure", "Everything", "Custom"],
tooltip =
"Clean: period references + nearest S/R.\n" +
"Intraday: adds session ranges, daily pivots, and Session VWAP.\n" +
"Structure: adds swing Fibonacci and S/R.\n" +
"Everything: activates every calculation module.\n" +
"Custom: uses only the individual module switches below.",
group = G_GENERAL
)
bool masterEnableInput = input.bool(true, "Enable indicator", group = G_GENERAL)
int maxLevelsInput = input.int(
24, "Maximum rendered levels",
minval = 1, maxval = 120,
tooltip = "Hard cap after all active modules have registered their levels.",
group = G_GENERAL
)
float maxDistanceAtrInput = input.float(
12.0, "Maximum distance from price · ATR",
minval = 0.25, maxval = 500.0, step = 0.25,
tooltip = TT_DISTANCE,
group = G_GENERAL
)
bool showAboveInput = input.bool(true, "Levels above price", inline = "sides", group = G_GENERAL)
bool showBelowInput = input.bool(true, "Levels below price", inline = "sides", group = G_GENERAL)
int minPriorityInput = input.int(
1, "Minimum priority",
minval = 1, maxval = 10,
tooltip = "1 keeps everything. Higher values remove less important levels first.",
group = G_GENERAL
)
// ============================================================================
// Inputs · Rendering
// ============================================================================
int labelOffsetBarsInput = input.int(
200, "Label offset · bars",
minval = 5, maxval = 400, step = 5,
tooltip = TT_EDGE,
group = G_RENDER
)
float minLabelSpacingAtrInput = input.float(
3.0, "Minimum label spacing · ATR",
minval = 0.0, maxval = 20.0, step = 0.1,
tooltip =
"Levels closer than this ATR distance to an already-selected, higher-priority level are skipped.\n" +
"Tied to the instrument's own volatility rather than the current scroll position, so which levels are shown stays stable while panning the chart — an earlier percentage-of-visible-range version changed the level selection as you scrolled, since the visible price range itself changed.\n" +
"ATR is an imperfect proxy for the label's actual pixel height: a low-volatility instrument/timeframe combination can still overlap even at a fairly high multiple. If labels still overlap with several modules enabled at once, increase this first; \"Maximum rendered levels\" and \"Maximum distance from price · ATR\" (both in Right-edge rendering) are the other two levers if a preset simply has too many modules active for the available vertical space.\n" +
"Without this, nearby levels stack into overlapping label boxes that read as a solid colored block.\n" +
"Set to 0 to disable and show every eligible level regardless of crowding.",
group = G_RENDER
)
string labelContentInput = input.string(
"Code",
"Label content",
options = ["Code", "Full name"],
tooltip = "Price, zone bounds, and touch count are never shown in the label body — they're always in the hover tooltip, keeping the label itself short.",
group = G_RENDER
)
string labelSizeInput = input.string(
"Small",
"Label size",
options = ["Tiny", "Small", "Normal"],
group = G_RENDER
)
int lineWidthInput = input.int(2, "Line width", minval = 1, maxval = 4, group = G_RENDER)
int lineTransparencyInput = input.int(
10, "Line transparency",
minval = 0, maxval = 95,
group = G_RENDER
)
int labelTransparencyInput = input.int(
10, "Label transparency",
minval = 0, maxval = 95,
group = G_RENDER
)
bool showCategoryInput = input.bool(
false, "Prefix category",
tooltip = "Example: PERIOD · PDH 24,500.00",
group = G_RENDER
)
bool showSourceZonesInput = input.bool(
false, "Show S/R source zones in chart",
tooltip = "Draws the selected support/resistance zones backward through the chart.",
group = G_RENDER
)
// ============================================================================
// Inputs · Period levels
// ============================================================================
bool periodModuleInput = input.bool(
true, "Enable day levels",
tooltip = "Master switch for daily period references. Only takes effect on the Custom preset — the other presets decide this module on/off themselves.",
group = G_DAY, active = presetInput == "Custom"
)
bool showPDHInput = input.bool(
true, "PDH",
tooltip = "PDH — Previous Day High\nHighest price of the previous daily period.\nOften watched as intraday resistance/liquidity — a clean break can extend a rally, a rejection can mark the day's high.",
inline = "d1", group = G_DAY, active = presetInput == "Custom" and periodModuleInput
)
bool showPDLInput = input.bool(
true, "PDL",
tooltip = "PDL — Previous Day Low\nLowest price of the previous daily period.\nOften watched as intraday support/liquidity — a clean break can extend a decline, a rejection can mark the day's low.",
inline = "d1", group = G_DAY, active = presetInput == "Custom" and periodModuleInput
)
bool showPDCInput = input.bool(
true, "PDC",
tooltip = "PDC — Previous Day Close\nClosing price of the previous daily period.\nReference for gap analysis (today's open above/below PDC = gap up/down) and a common mean-reversion magnet.",
inline = "d1", group = G_DAY, active = presetInput == "Custom" and periodModuleInput
)
bool showPDOInput = input.bool(
false, "PDO",
tooltip = "PDO — Previous Day Open\nOpening price of the previous daily period.\nMarks where the previous day's own trend began — useful for judging how far that day's move ran.",
inline = "d2", group = G_DAY, active = presetInput == "Custom" and periodModuleInput
)
bool showDOInput = input.bool(
true, "DO",
tooltip = "DO — Current Day Open\nOpening price of the current daily period.\nMany intraday systems key bias off price's position relative to DO (above = long-favored, below = short-favored).",
inline = "d2", group = G_DAY, active = presetInput == "Custom" and periodModuleInput
)
bool showPDMInput = input.bool(
false, "PDM",
tooltip = "PDM — Previous Day Midpoint\nArithmetic midpoint of previous day high and low.\nRough fair-value reference for the prior day — trading in the upper/lower half frames intraday strength/weakness.",
inline = "d2", group = G_DAY, active = presetInput == "Custom" and periodModuleInput
)
bool weekModuleInput = input.bool(
true, "Enable week levels",
tooltip = "Master switch for weekly period references. Only takes effect on the Custom preset.",
group = G_WEEK, active = presetInput == "Custom"
)
bool showPWHInput = input.bool(
true, "PWH",
tooltip = "PWH — Previous Week High.\nSwing resistance/liquidity — stops often cluster just above it, so a sweep followed by a reclaim is a common reversal pattern here.",
inline = "w1", group = G_WEEK, active = presetInput == "Custom" and weekModuleInput
)
bool showPWLInput = input.bool(
true, "PWL",
tooltip = "PWL — Previous Week Low.\nSwing support/liquidity — stops often cluster just below it, so a sweep followed by a reclaim is a common reversal pattern here.",
inline = "w1", group = G_WEEK, active = presetInput == "Custom" and weekModuleInput
)
bool showPWCInput = input.bool(
false, "PWC",
tooltip = "PWC — Previous Week Close.\nReference for weekly gap analysis and a slower-moving mean-reversion magnet than PDC.",
inline = "w1", group = G_WEEK, active = presetInput == "Custom" and weekModuleInput
)
bool showPWOInput = input.bool(
false, "PWO",
tooltip = "PWO — Previous Week Open.\nMarks where the previous week's own trend began — useful for judging how far that week's move ran.",
inline = "w2", group = G_WEEK, active = presetInput == "Custom" and weekModuleInput
)
bool showWOInput = input.bool(
true, "WO",
tooltip = "WO — Current Week Open.\nCommon weekly bias reference — many swing traders read above/below WO as the week's directional lean.",
inline = "w2", group = G_WEEK, active = presetInput == "Custom" and weekModuleInput
)
bool showPWMInput = input.bool(
false, "PWM",
tooltip = "PWM — Previous Week Midpoint.\nFair-value reference for the prior week — trading in the upper/lower half frames swing strength/weakness.",
inline = "w2", group = G_WEEK, active = presetInput == "Custom" and weekModuleInput
)
bool monthModuleInput = input.bool(
true, "Enable month levels",
tooltip = "Master switch for monthly period references. Only takes effect on the Custom preset.",
group = G_MONTH, active = presetInput == "Custom"
)
bool showPMHInput = input.bool(
false, "PMH",
tooltip = "PMH — Previous Month High.\nHigher-timeframe structure — a major liquidity/resistance level that tends to matter across many lower timeframes at once.",
inline = "m1", group = G_MONTH, active = presetInput == "Custom" and monthModuleInput
)
bool showPMLInput = input.bool(
false, "PML",
tooltip = "PML — Previous Month Low.\nHigher-timeframe structure — a major liquidity/support level that tends to matter across many lower timeframes at once.",
inline = "m1", group = G_MONTH, active = presetInput == "Custom" and monthModuleInput
)
bool showPMCInput = input.bool(
false, "PMC",
tooltip = "PMC — Previous Month Close.\nReference for monthly gap analysis and a slow, higher-timeframe mean-reversion magnet.",
inline = "m1", group = G_MONTH, active = presetInput == "Custom" and monthModuleInput
)
bool showPMOInput = input.bool(
false, "PMO",
tooltip = "PMO — Previous Month Open.\nMarks where the previous month's own trend began — useful for judging how far that month's move ran.",
inline = "m2", group = G_MONTH, active = presetInput == "Custom" and monthModuleInput
)
bool showMOInput = input.bool(
true, "MO",
tooltip = "MO — Current Month Open.\nCommon macro bias reference — many position traders read above/below MO as the month's directional lean.",
inline = "m2", group = G_MONTH, active = presetInput == "Custom" and monthModuleInput
)
bool showPMMInput = input.bool(
false, "PMM",
tooltip = "PMM — Previous Month Midpoint.\nFair-value reference for the prior month — trading in the upper/lower half frames longer-term strength/weakness.",
inline = "m2", group = G_MONTH, active = presetInput == "Custom" and monthModuleInput
)
// ============================================================================
// Inputs · Year levels
// ============================================================================
bool yearModuleInput = input.bool(
false, "Enable year levels",
tooltip = "Master switch for yearly period references. Only takes effect on the Custom preset.",
group = G_YEAR, active = presetInput == "Custom"
)
bool showPYHInput = input.bool(
false, "PYH",
tooltip = "PYH — Previous Year High.\nMajor higher-timeframe structure — a level that can stay relevant across many lower timeframes for a long time.",
inline = "y1", group = G_YEAR, active = presetInput == "Custom" and yearModuleInput
)
bool showPYLInput = input.bool(
false, "PYL",
tooltip = "PYL — Previous Year Low.\nMajor higher-timeframe structure — a level that can stay relevant across many lower timeframes for a long time.",
inline = "y1", group = G_YEAR, active = presetInput == "Custom" and yearModuleInput
)
bool showPYCInput = input.bool(
false, "PYC",
tooltip = "PYC — Previous Year Close.\nReference for yearly gap analysis and a very slow, macro mean-reversion magnet.",
inline = "y1", group = G_YEAR, active = presetInput == "Custom" and yearModuleInput
)
bool showPYOInput = input.bool(
false, "PYO",
tooltip = "PYO — Previous Year Open.\nMarks where the previous year's own trend began — useful for judging how far that year's move ran.",
inline = "y2", group = G_YEAR, active = presetInput == "Custom" and yearModuleInput
)
bool showYOInput = input.bool(
false, "YO",
tooltip = "YO — Current Year Open.\nMacro bias reference — position traders sometimes read above/below YO as the year's directional lean.",
inline = "y2", group = G_YEAR, active = presetInput == "Custom" and yearModuleInput
)
bool showPYMInput = input.bool(
false, "PYM",
tooltip = "PYM — Previous Year Midpoint.\nFair-value reference for the prior year — trading in the upper/lower half frames very long-term strength/weakness.",
inline = "y2", group = G_YEAR, active = presetInput == "Custom" and yearModuleInput
)
// ============================================================================
// Inputs · Sessions
// ============================================================================
bool sessionsModuleInput = input.bool(
false, "Enable session ranges",
tooltip = TT_MISSING + "\nOnly takes effect on the Custom preset — Intraday/Everything decide this module themselves.",
group = G_SESSIONS, active = presetInput == "Custom"
)
string sessionTimezoneInput = input.string(
"Etc/UTC", "Session timezone",
tooltip =
"Use an IANA timezone, e.g. America/New_York, Europe/London, Asia/Tokyo, or Etc/UTC.\n" +
"Session relevance depends on the instrument and exchange.",
group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput
)
bool asiaEnableInput = input.bool(false, "Asia", inline = "asia0", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput)
string asiaSessionInput = input.session("0000-0800", "", inline = "asia0", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and asiaEnableInput)
bool asiaHighInput = input.bool(true, "ASH", inline = "asia1", tooltip = "ASH — Asia Session High.\nOften used as a breakout reference for the following London/NY session (e.g. \"London breaks the Asia range\").", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and asiaEnableInput)
bool asiaLowInput = input.bool(true, "ASL", inline = "asia1", tooltip = "ASL — Asia Session Low.\nOften used as a breakout reference for the following London/NY session (e.g. \"London breaks the Asia range\").", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and asiaEnableInput)
bool asiaMidInput = input.bool(false, "ASM", inline = "asia1", tooltip = "ASM — Asia Session Midpoint.\nFair-value reference within the Asia range.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and asiaEnableInput)
bool londonEnableInput = input.bool(false, "London", inline = "lon0", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput)
string londonSessionInput = input.session("0700-1600", "", inline = "lon0", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and londonEnableInput)
bool londonHighInput = input.bool(true, "LDH", inline = "lon1", tooltip = "LDH — London Session High.\nOften used as a breakout/liquidity reference for the New York session that follows.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and londonEnableInput)
bool londonLowInput = input.bool(true, "LDL", inline = "lon1", tooltip = "LDL — London Session Low.\nOften used as a breakout/liquidity reference for the New York session that follows.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and londonEnableInput)
bool londonMidInput = input.bool(false, "LDM", inline = "lon1", tooltip = "LDM — London Session Midpoint.\nFair-value reference within the London range.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and londonEnableInput)
bool nyEnableInput = input.bool(false, "New York", inline = "ny0", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput)
string nySessionInput = input.session("1330-2100", "", inline = "ny0", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and nyEnableInput)
bool nyHighInput = input.bool(true, "NYH", inline = "ny1", tooltip = "NYH — New York Session High.\nOften the most liquid session's range extreme — a common intraday reversal/breakout reference into the close.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and nyEnableInput)
bool nyLowInput = input.bool(true, "NYL", inline = "ny1", tooltip = "NYL — New York Session Low.\nOften the most liquid session's range extreme — a common intraday reversal/breakout reference into the close.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and nyEnableInput)
bool nyMidInput = input.bool(false, "NYM", inline = "ny1", tooltip = "NYM — New York Session Midpoint.\nFair-value reference within the New York range.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and nyEnableInput)
bool custom1EnableInput = input.bool(false, "Custom 1", inline = "cs1a", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput)
string custom1SessionInput = input.session("0800-1200", "", inline = "cs1a", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom1EnableInput)
string custom1NameInput = input.string("C1", "", inline = "cs1a", tooltip = "Prefix used for this session's High/Low/Mid labels (e.g. \"C1H\").", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom1EnableInput)
bool custom1HighInput = input.bool(true, "High", inline = "cs1b", tooltip = "Custom Session 1 High.\nFreely timed session — name and hours are set above.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom1EnableInput)
bool custom1LowInput = input.bool(true, "Low", inline = "cs1b", tooltip = "Custom Session 1 Low.\nFreely timed session — name and hours are set above.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom1EnableInput)
bool custom1MidInput = input.bool(false, "Mid", inline = "cs1b", tooltip = "Custom Session 1 Midpoint.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom1EnableInput)
bool custom2EnableInput = input.bool(false, "Custom 2", inline = "cs2a", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput)
string custom2SessionInput = input.session("1200-1600", "", inline = "cs2a", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom2EnableInput)
string custom2NameInput = input.string("C2", "", inline = "cs2a", tooltip = "Prefix used for this session's High/Low/Mid labels (e.g. \"C2H\").", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom2EnableInput)
bool custom2HighInput = input.bool(true, "High", inline = "cs2b", tooltip = "Custom Session 2 High.\nFreely timed session — name and hours are set above.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom2EnableInput)
bool custom2LowInput = input.bool(true, "Low", inline = "cs2b", tooltip = "Custom Session 2 Low.\nFreely timed session — name and hours are set above.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom2EnableInput)
bool custom2MidInput = input.bool(false, "Mid", inline = "cs2b", tooltip = "Custom Session 2 Midpoint.", group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput and custom2EnableInput)
bool useCompletedSessionInput = input.bool(
true, "Use last completed range",
tooltip =
"On: the previous completed session remains stable.\n" +
"Off: the current developing session is displayed while it is active.",
group = G_SESSIONS, active = presetInput == "Custom" and sessionsModuleInput
)
// ============================================================================
// Inputs · Pivot points
// ============================================================================
bool pivotModuleInput = input.bool(
false, "Enable pivot points",
tooltip = "Only takes effect on the Custom preset — Intraday/Everything decide this module themselves.",
group = G_PIVOTS, active = presetInput == "Custom"
)
string pivotMethodInput = input.string(
"Traditional", "Method",
options = ["Traditional", "Fibonacci", "Camarilla", "Woodie", "DeMark"],
tooltip =
"Traditional: PP=(H+L+C)/3, R/S from the prior period's range — the classic floor-trader formula.\n" +
"Fibonacci: same PP, R/S at Fibonacci ratios (38.2/61.8/100%) of the range instead of the traditional multiples.\n" +
"Camarilla: tighter R1-3/S1-3 clustered close to the prior close — designed for intraday mean-reversion rather than breakout targets.\n" +
"Woodie: PP weights the prior close double, R/S otherwise use the traditional formula — puts more emphasis on where the period closed.\n" +
"DeMark: PP/R1/S1 only (R2/R3/S2/S3 checkboxes have no effect for this method) — the formula itself depends on whether the prior period closed above or below its own open.",
group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput
)
string pivotPeriodInput = input.string(
"Daily", "Reference period",
options = ["Daily", "Weekly", "Monthly"],
group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput
)
bool showPPInput = input.bool(true, "PP", inline = "p1", tooltip = "PP — Central Pivot Point.\nCommon intraday bias line: trading above PP favors longs, below favors shorts.", group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput)
bool showR1Input = input.bool(true, "R1", inline = "p1", tooltip = "R1 — First pivot resistance.\nClosest, most commonly reached target/reaction level above PP.", group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput)
bool showS1Input = input.bool(true, "S1", inline = "p1", tooltip = "S1 — First pivot support.\nClosest, most commonly reached target/reaction level below PP.", group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput)
bool showR2Input = input.bool(true, "R2", inline = "p2", tooltip = "R2 — Second pivot resistance.\nLess frequently reached than R1 — often used as a stronger, higher-conviction target.", group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput)
bool showS2Input = input.bool(true, "S2", inline = "p2", tooltip = "S2 — Second pivot support.\nLess frequently reached than S1 — often used as a stronger, higher-conviction target.", group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput)
bool showR3Input = input.bool(false, "R3", inline = "p2", tooltip = "R3 — Third pivot resistance.\nReached only on strong trend days — a stretch/exhaustion reference more than a typical target.", group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput)
bool showS3Input = input.bool(false, "S3", inline = "p2", tooltip = "S3 — Third pivot support.\nReached only on strong trend days — a stretch/exhaustion reference more than a typical target.", group = G_PIVOTS, active = presetInput == "Custom" and pivotModuleInput)
// ============================================================================
// Inputs · Swing Fibonacci
// ============================================================================
bool fibModuleInput = input.bool(
false, "Enable swing Fibonacci",
tooltip =
"Uses the latest confirmed opposite pivot pair.\n" +
"The newest pivot determines whether the active leg is bullish or bearish.\n" +
"Only takes effect on the Custom preset — Structure/Everything decide this module themselves.",
group = G_FIB, active = presetInput == "Custom"
)
int fibPivotLeftInput = input.int(
5, "Pivot left", minval = 1, maxval = 50, inline = "fp", group = G_FIB, active = fibModuleInput,
tooltip = "The swing-pivot calculation itself always runs (feeds fib validity across every preset) — only \"Enable swing Fibonacci\" is Custom-only."
)
int fibPivotRightInput = input.int(5, "Pivot right", minval = 1, maxval = 50, inline = "fp", group = G_FIB, active = fibModuleInput)
float fibMinLegAtrInput = input.float(
2.0, "Minimum leg · ATR",
minval = 0.25, maxval = 100.0, step = 0.25,
group = G_FIB, active = fibModuleInput
)
bool fib236Input = input.bool(false, "23.6", inline = "f1", tooltip = "Shallow retracement — in a strong trend, price often barely pulls back this far before continuing.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
bool fib382Input = input.bool(true, "38.2", inline = "f1", tooltip = "Shallow-to-moderate retracement — a common pullback-continuation entry zone in a healthy trend.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
bool fib500Input = input.bool(true, "50.0", inline = "f1", tooltip = "Not a true Fibonacci ratio, but widely watched as the psychological halfway retracement of the leg.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
bool fib618Input = input.bool(true, "61.8", inline = "f1", tooltip = "The \"golden ratio\" — the classic deep-retracement, last-defense level before the leg is considered structurally invalidated.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
bool fib786Input = input.bool(false, "78.6", inline = "f2", tooltip = "Very deep retracement — often marks trend exhaustion or elevated reversal risk rather than a continuation entry.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
bool fib100Input = input.bool(false, "100", inline = "f2", tooltip = "Full retracement back to the leg's origin — beyond this the leg is fully round-tripped.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
bool fib1272Input = input.bool(false, "127.2", inline = "f2", tooltip = "Extension level — a common first profit-taking/target zone for the continuation leg beyond the prior extreme.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
bool fib1618Input = input.bool(false, "161.8", inline = "f2", tooltip = "Extension level — a common further profit-taking/target zone for a strongly continuing leg.", group = G_FIB, active = presetInput == "Custom" and fibModuleInput)
// ============================================================================
// Inputs · Support / Resistance
// ============================================================================
bool srModuleInput = input.bool(
true, "Enable pivot-cluster S/R",
tooltip =
"Confirmed pivot highs and lows are clustered by ATR distance.\n" +
"The nearest qualifying support and resistance zones are sent to the edge rail.\n" +
"×N in the label is the number of confirmed pivots clustered at the zone since it last broke. A close beyond the zone center by more than the cluster tolerance (above a resistance, below a support) resets the count to zero, so ×N only counts pivots since the last break.\n" +
"Every non-Custom preset includes S/R, so this checkbox only takes effect on Custom — but the calculation params below it always run.",
group = G_SR, active = presetInput == "Custom"
)
int srPivotLeftInput = input.int(5, "Pivot left", minval = 1, maxval = 50, inline = "srp", group = G_SR, active = srModuleInput)
int srPivotRightInput = input.int(5, "Pivot right", minval = 1, maxval = 50, inline = "srp", group = G_SR, active = srModuleInput)
float srClusterAtrInput = input.float(
0.35, "Cluster tolerance · ATR",
minval = 0.05, maxval = 5.0, step = 0.05,
group = G_SR, active = srModuleInput
)
float srZoneWidthAtrInput = input.float(
0.25, "Zone width · ATR",
minval = 0.05, maxval = 3.0, step = 0.05,
group = G_SR, active = srModuleInput
)
int srMinTouchesInput = input.int(
2, "Minimum touches",
minval = 1, maxval = 20,
group = G_SR, active = srModuleInput
)
int srMaxAgeInput = input.int(
1500, "Maximum age · bars",
minval = 20, maxval = 20000,
group = G_SR, active = srModuleInput
)
int srSupportsInput = input.int(
2, "Nearest supports",
minval = 0, maxval = 10,
inline = "src", group = G_SR, active = srModuleInput
)
int srResistancesInput = input.int(
2, "Nearest resistances",
minval = 0, maxval = 10,
inline = "src", group = G_SR, active = srModuleInput
)
// ============================================================================
// Inputs · Round numbers
// ============================================================================
bool roundModuleInput = input.bool(
false, "Enable round numbers",
tooltip = "Only takes effect on the Custom preset — Everything always includes round numbers regardless of this checkbox.",
group = G_ROUND, active = presetInput == "Custom"
)
bool roundActive = presetInput == "Custom" ? roundModuleInput : presetInput == "Everything"
bool roundAutoStepInput = input.bool(
true, "Automatic step",
tooltip = "Chooses a visually useful 1/2/5 × 10ⁿ step from current price magnitude.",
group = G_ROUND, active = roundActive
)
float roundManualStepInput = input.float(
100.0, "Manual major step",
minval = 0.0000001, step = 0.1,
group = G_ROUND, active = roundActive and not roundAutoStepInput
)
bool roundMajorInput = input.bool(true, "Major", inline = "rn", tooltip = "Major whole-step levels.\nRound numbers attract resting orders and often cause price to pause, stall, or reverse nearby, independent of any chart structure.", group = G_ROUND, active = roundActive)
bool roundHalfInput = input.bool(false, "Half", inline = "rn", tooltip = "Half-step levels.\nSecondary psychological levels — usually weaker magnets than Major but still occasionally watched.", group = G_ROUND, active = roundActive)
bool roundQuarterInput = input.bool(false, "Quarter", inline = "rn", tooltip = "Quarter-step levels.\nThe weakest, most granular round-number tier — mainly useful on instruments with very fine tick sizes.", group = G_ROUND, active = roundActive)
int roundLevelsEachSideInput = input.int(
3, "Levels each side",
minval = 1, maxval = 20,
group = G_ROUND, active = roundActive
)
// ============================================================================
// Inputs · VWAP
// ============================================================================
bool vwapModuleInput = input.bool(
false, "Enable VWAP",
tooltip = "Volume-weighted average price, anchored per period. Only takes effect on the Custom preset — Intraday/Everything decide the session line themselves.",
group = G_VWAP, active = presetInput == "Custom"
)
bool vwapSessionInput = input.bool(
true, "Session", inline = "vw1",
tooltip = "VWAP — Session Volume-Weighted Average Price.\nResets each session/day. A common intraday fair-value magnet and mean-reversion reference — institutional execution is often judged against it.",
group = G_VWAP, active = presetInput == "Custom" and vwapModuleInput
)
bool vwapWeeklyInput = input.bool(
false, "Weekly", inline = "vw1",
tooltip = "WVWAP — Weekly Volume-Weighted Average Price.\nResets each week. A slower fair-value reference for swing positioning.",
group = G_VWAP, active = presetInput == "Custom" and vwapModuleInput
)
bool vwapMonthlyInput = input.bool(
false, "Monthly", inline = "vw1",
tooltip = "MVWAP — Monthly Volume-Weighted Average Price.\nResets each month. A macro fair-value reference for position-level bias.",
group = G_VWAP, active = presetInput == "Custom" and vwapModuleInput
)
// ============================================================================
// Inputs · Manual levels
// ============================================================================
bool manualModuleInput = input.bool(
false, "Enable manual levels",
tooltip = "Up to 4 freely priced levels — entries, stops, targets, or anything the other modules don't cover. Only takes effect on the Custom preset.\nA slot registers only once its own checkbox is on and its price is set away from 0 (0 is treated as \"unset\", not a real level).",
group = G_MANUAL, active = presetInput == "Custom"
)
bool manual1EnableInput = input.bool(false, "", inline = "man1", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
float manual1PriceInput = input.price(0.0, "Level 1", inline = "man1", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
string manual1NameInput = input.string("ML1", "Label", inline = "man1", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
bool manual2EnableInput = input.bool(false, "", inline = "man2", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
float manual2PriceInput = input.price(0.0, "Level 2", inline = "man2", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
string manual2NameInput = input.string("ML2", "Label", inline = "man2", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
bool manual3EnableInput = input.bool(false, "", inline = "man3", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
float manual3PriceInput = input.price(0.0, "Level 3", inline = "man3", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
string manual3NameInput = input.string("ML3", "Label", inline = "man3", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
bool manual4EnableInput = input.bool(false, "", inline = "man4", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
float manual4PriceInput = input.price(0.0, "Level 4", inline = "man4", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
string manual4NameInput = input.string("ML4", "Label", inline = "man4", group = G_MANUAL, active = presetInput == "Custom" and manualModuleInput)
// ============================================================================
// Inputs · Opening ranges
// ============================================================================
bool orModuleInput = input.bool(
false, "Enable opening ranges",
tooltip = "High/Low of the first N minutes after each new day/week/month starts — a fixed reference for the rest of that period, shown once the window closes (the still-forming range isn't registered, same as every other module here doesn't guess at an incomplete value). Only takes effect on the Custom preset.",
group = G_OR, active = presetInput == "Custom"
)
bool dailyOREnableInput = input.bool(true, "Daily", inline = "or1", group = G_OR, active = presetInput == "Custom" and orModuleInput)
int dailyORMinutesInput = input.int(30, "min", inline = "or1", minval = 1, maxval = 1000, group = G_OR, active = presetInput == "Custom" and orModuleInput and dailyOREnableInput)
bool dailyORHighInput = input.bool(true, "High", inline = "or1b", tooltip = "ORH — Daily Opening Range High.\nThe classic opening-range-breakout reference — a clean break of either side after the window closes is a common intraday trigger.", group = G_OR, active = presetInput == "Custom" and orModuleInput and dailyOREnableInput)
bool dailyORLowInput = input.bool(true, "Low", inline = "or1b", tooltip = "ORL — Daily Opening Range Low.\nThe classic opening-range-breakout reference — a clean break of either side after the window closes is a common intraday trigger.", group = G_OR, active = presetInput == "Custom" and orModuleInput and dailyOREnableInput)
bool weeklyOREnableInput = input.bool(false, "Weekly", inline = "or2", group = G_OR, active = presetInput == "Custom" and orModuleInput)
int weeklyORMinutesInput = input.int(1440, "min", inline = "or2", minval = 1, maxval = 10000, group = G_OR, active = presetInput == "Custom" and orModuleInput and weeklyOREnableInput)
bool weeklyORHighInput = input.bool(true, "High", inline = "or2b", tooltip = "WORH — Weekly Opening Range High.\nDefault window is 1440 minutes (the first trading day of the week) — a slower breakout reference for the rest of the week.", group = G_OR, active = presetInput == "Custom" and orModuleInput and weeklyOREnableInput)
bool weeklyORLowInput = input.bool(true, "Low", inline = "or2b", tooltip = "WORL — Weekly Opening Range Low.\nDefault window is 1440 minutes (the first trading day of the week) — a slower breakout reference for the rest of the week.", group = G_OR, active = presetInput == "Custom" and orModuleInput and weeklyOREnableInput)
bool monthlyOREnableInput = input.bool(false, "Monthly", inline = "or3", group = G_OR, active = presetInput == "Custom" and orModuleInput)
int monthlyORMinutesInput = input.int(1440, "min", inline = "or3", minval = 1, maxval = 44640, group = G_OR, active = presetInput == "Custom" and orModuleInput and monthlyOREnableInput)
bool monthlyORHighInput = input.bool(true, "High", inline = "or3b", tooltip = "MORH — Monthly Opening Range High.\nDefault window is 1440 minutes (the first trading day of the month) — a macro breakout reference for the rest of the month.", group = G_OR, active = presetInput == "Custom" and orModuleInput and monthlyOREnableInput)
bool monthlyORLowInput = input.bool(true, "Low", inline = "or3b", tooltip = "MORL — Monthly Opening Range Low.\nDefault window is 1440 minutes (the first trading day of the month) — a macro breakout reference for the rest of the month.", group = G_OR, active = presetInput == "Custom" and orModuleInput and monthlyOREnableInput)
// ============================================================================
// Inputs · Trend Curves
// ============================================================================
bool trendModuleInput = input.bool(false, "Show Trend Curves", group = G_TREND)
string trendPresetInput = input.string(
"Balanced", "Preset",
options = ["Responsive", "Balanced", "Structural", "Custom"],
tooltip = TT_TREND_PRESET,
group = G_TREND
)
float trendSourceInput = input.source(close, "Source", group = G_TREND, active = trendModuleInput)
int trendOffsetInput = input.int(
0, "Line offset · bars",
minval = -500, maxval = 500,
group = G_TREND, active = trendModuleInput
)
string trendEmphasisInput = input.string(
"Normal", "Visual emphasis",
options = ["Subtle", "Normal", "Bold"],
tooltip = "Line thickness only — colors stay editable per line in the chart's Style tab.",
group = G_TREND, active = trendModuleInput
)
bool trendAsLevelsInput = input.bool(
false, "Also show as right-edge levels",
tooltip = "Feeds each active curve's current value into the same right-edge Level registry as every other module (rail + label, priority 6, subject to the usual spacing/distance/count filters) — in addition to the always-on chart plot, not instead of it.",
group = G_TREND, active = trendModuleInput
)
string trendMtfInput = input.timeframe(
"", "MTF timeframe",
tooltip = "Compute all three curves on a higher (or lower) timeframe instead of the chart's own — e.g. a Daily SMA 200 on an hourly chart. Blank = chart's own timeframe (default, no MTF, matches every version before this one).\nThe requested timeframe's still-forming bar updates live, the same as any other MTF plot in Pine — the value can repaint intrabar until that higher-timeframe bar closes. That's expected behavior for a live MTF moving average, not a bug specific to this script.",
group = G_TREND, active = trendModuleInput
)
bool showFastInput = input.bool(true, "Show Fast Curve", inline = "tf0", group = G_TREND, active = trendModuleInput)
string fastTypeInput = input.string(
"JMA-style", "Type", inline = "tf1",
options = ["JMA-style", "EMA", "SMA"],
tooltip = TT_JMA,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom"
)
int fastLengthInput = input.int(
21, "Length", inline = "tf1",
minval = 1, maxval = 1000,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom"
)
float fastPhaseInput = input.float(
0.0, "Phase", inline = "tf2",
minval = -100.0, maxval = 100.0,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom" and fastTypeInput == "JMA-style"
)
float fastPowerInput = input.float(
2.0, "Power", inline = "tf2",
minval = 0.5, maxval = 4.0, step = 0.1,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom" and fastTypeInput == "JMA-style"
)
bool showBaseInput = input.bool(true, "Show Base Curve", inline = "tb0", group = G_TREND, active = trendModuleInput)
string baseTypeInput = input.string(
"EMA", "Type", inline = "tb1",
options = ["JMA-style", "EMA", "SMA"],
tooltip = TT_JMA,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom"
)
int baseLengthInput = input.int(
50, "Length", inline = "tb1",
minval = 1, maxval = 1000,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom"
)
float basePhaseInput = input.float(
0.0, "Phase", inline = "tb2",
minval = -100.0, maxval = 100.0,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom" and baseTypeInput == "JMA-style"
)
float basePowerInput = input.float(
2.0, "Power", inline = "tb2",
minval = 0.5, maxval = 4.0, step = 0.1,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom" and baseTypeInput == "JMA-style"
)
bool showAnchorInput = input.bool(true, "Show Anchor Curve", inline = "ta0", group = G_TREND, active = trendModuleInput)
string anchorTypeInput = input.string(
"SMA", "Type", inline = "ta1",
options = ["JMA-style", "EMA", "SMA"],
tooltip = TT_JMA,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom"
)
int anchorLengthInput = input.int(
200, "Length", inline = "ta1",
minval = 1, maxval = 2000,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom"
)
float anchorPhaseInput = input.float(
0.0, "Phase", inline = "ta2",
minval = -100.0, maxval = 100.0,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom" and anchorTypeInput == "JMA-style"
)
float anchorPowerInput = input.float(
2.0, "Power", inline = "ta2",
minval = 0.5, maxval = 4.0, step = 0.1,
group = G_TREND, active = trendModuleInput and trendPresetInput == "Custom" and anchorTypeInput == "JMA-style"
)
// ============================================================================
// Preset resolution
// ============================================================================
bool presetClean = presetInput == "Clean"
bool presetIntraday = presetInput == "Intraday"
bool presetStructure = presetInput == "Structure"
bool presetEverything = presetInput == "Everything"
bool presetCustom = presetInput == "Custom"
bool useDayLevels = presetCustom ? periodModuleInput : true
bool useWeekLevels = presetCustom ? weekModuleInput : true
bool useMonthLevels = presetCustom ? monthModuleInput : presetEverything
bool useYearLevels = presetCustom ? yearModuleInput : presetEverything
bool useSessions = presetCustom ? sessionsModuleInput : (presetIntraday or presetEverything)
bool usePivots = presetCustom ? pivotModuleInput : (presetIntraday or presetEverything)
bool useFib = presetCustom ? fibModuleInput : (presetStructure or presetEverything)
bool useSR = presetCustom ? srModuleInput : (presetClean or presetIntraday or presetStructure or presetEverything)
bool useRound = presetCustom ? roundModuleInput : presetEverything
bool useVWAP = presetCustom ? vwapModuleInput : (presetIntraday or presetEverything)
bool useManual = presetCustom and manualModuleInput
bool useOR = presetCustom and orModuleInput
// ============================================================================
// Types and registries
// ============================================================================
type Level
float price
float upper
float lower
string code
string fullName
string category
int priority
color levelColor
bool isZone
int touches
var array<Level> levels = array.new<Level>()
var array<line> renderedLines = array.new<line>()
var array<label> renderedLabels = array.new<label>()
var array<box> renderedBoxes = array.new<box>()
f_clearDrawings() =>
for ln in renderedLines
line.delete(ln)
for lb in renderedLabels
label.delete(lb)
for bx in renderedBoxes
box.delete(bx)
array.clear(renderedLines)
array.clear(renderedLabels)
array.clear(renderedBoxes)
f_addLevel(
float price,
float upper,
float lower,
string code,
string fullName,
string category,
int priority,
color levelColor,
bool isZone,
int touches,
bool enabled
) =>
if enabled and not na(price)
array.push(
levels,
Level.new(
price,
na(upper) ? price : upper,
na(lower) ? price : lower,
code,
fullName,
category,
priority,
levelColor,
isZone,
touches
)
)
f_labelSize() =>
labelSizeInput == "Tiny" ? size.tiny :
labelSizeInput == "Normal" ? size.normal : size.small
f_text(Level level) =>
string base = labelContentInput == "Full name" ? level.fullName : level.code
string withTouches = level.isZone and level.touches > 0 ? base + " ×" + str.tostring(level.touches) : base
showCategoryInput ? level.category + " · " + withTouches : withTouches
// Line width and opacity scale with priority (1-10), so the most
// significant levels stand out — same "visual weight tracks significance"
// principle Fib Reaction Memory applies to its Memory Score tiers.
// Multi-line ternaries over series values are unreliable in Pine v6
// (CE10156); use explicit if/else instead.
f_priorityWidth(int priority) =>
int result = lineWidthInput
if priority >= 8
result := lineWidthInput + 1
else if priority <= 3
result := math.max(lineWidthInput - 1, 1)
result
f_priorityTransparency(int baseTransparency, int priority) =>
int result = baseTransparency
if priority >= 8
result := math.max(baseTransparency - 15, 0)
else if priority <= 3
result := math.min(baseTransparency + 20, 95)
result
f_autoRoundStep(float price) =>
float safePrice = math.max(math.abs(price), syminfo.mintick)
float exponent = math.floor(math.log10(safePrice))
float magnitude = math.pow(10.0, exponent)
float normalized = safePrice / magnitude
float factor = normalized < 2.0 ? 0.02 : normalized < 5.0 ? 0.05 : 0.10
math.max(magnitude * factor, syminfo.mintick)
// JMA-style adaptive smoothing — an open-source, Jurik-inspired approximation,
// not Mark Jurik's patented, proprietary original algorithm. Same formula used
// elsewhere in this repo (jma_struct.pine, candle_pressure_response_jma.pine).
f_jma(float src, int length, float phase, float power) =>
float phaseRatio = phase < -100 ? 0.5 : phase > 100 ? 2.5 : phase / 100 + 1.5
float beta = 0.45 * (length - 1) / (0.45 * (length - 1) + 2)
float alpha = math.pow(beta, power)
var float e0 = na
var float e1 = na
var float e2 = na
var float jma = na
e0 := (1 - alpha) * src + alpha * nz(e0[1], src)
e1 := (src - e0) * (1 - beta) + beta * nz(e1[1], 0)
e2 := (e0 + phaseRatio * e1 - nz(jma[1], src)) * math.pow(1 - alpha, 2) + math.pow(alpha, 2) * nz(e2[1], 0)
jma := e2 + nz(jma[1], src)
jma
f_maValue(string maType, float src, int length, float phase, float power) =>
switch maType
"JMA-style" => f_jma(src, length, phase, power)
"EMA" => ta.ema(src, length)
=> ta.sma(src, length)
// ============================================================================
// Core series
// ============================================================================
float atr = ta.atr(14)
float safeAtr = math.max(nz(atr, syminfo.mintick), syminfo.mintick)
float pdh = request.security(syminfo.tickerid, "D", high[1], lookahead = barmerge.lookahead_on)
float pdl = request.security(syminfo.tickerid, "D", low[1], lookahead = barmerge.lookahead_on)
float pdo = request.security(syminfo.tickerid, "D", open[1], lookahead = barmerge.lookahead_on)
float pdc = request.security(syminfo.tickerid, "D", close[1], lookahead = barmerge.lookahead_on)
float dayOpen = request.security(syminfo.tickerid, "D", open, lookahead = barmerge.lookahead_on)
float pwh = request.security(syminfo.tickerid, "W", high[1], lookahead = barmerge.lookahead_on)
float pwl = request.security(syminfo.tickerid, "W", low[1], lookahead = barmerge.lookahead_on)
float pwo = request.security(syminfo.tickerid, "W", open[1], lookahead = barmerge.lookahead_on)
float pwc = request.security(syminfo.tickerid, "W", close[1], lookahead = barmerge.lookahead_on)
float weekOpen = request.security(syminfo.tickerid, "W", open, lookahead = barmerge.lookahead_on)
float pmh = request.security(syminfo.tickerid, "M", high[1], lookahead = barmerge.lookahead_on)
float pml = request.security(syminfo.tickerid, "M", low[1], lookahead = barmerge.lookahead_on)
float pmo = request.security(syminfo.tickerid, "M", open[1], lookahead = barmerge.lookahead_on)
float pmc = request.security(syminfo.tickerid, "M", close[1], lookahead = barmerge.lookahead_on)
float monthOpen = request.security(syminfo.tickerid, "M", open, lookahead = barmerge.lookahead_on)
float pyh = request.security(syminfo.tickerid, "12M", high[1], lookahead = barmerge.lookahead_on)
float pyl = request.security(syminfo.tickerid, "12M", low[1], lookahead = barmerge.lookahead_on)
float pyo = request.security(syminfo.tickerid, "12M", open[1], lookahead = barmerge.lookahead_on)
float pyc = request.security(syminfo.tickerid, "12M", close[1], lookahead = barmerge.lookahead_on)
float yearOpen = request.security(syminfo.tickerid, "12M", open, lookahead = barmerge.lookahead_on)
// VWAP — anchored via timeframe.change, which flags the first bar of a new
// higher-timeframe period regardless of the chart's own resolution (the same
// mechanism TradingView's built-in VWAP script uses for its anchor options).
bool vwapNewDay = timeframe.change("D")
bool vwapNewWeek = timeframe.change("W")
bool vwapNewMonth = timeframe.change("M")
[sessionVwapValue, sessionVwapUpperBand, sessionVwapLowerBand] = ta.vwap(hlc3, vwapNewDay, 1.0)
[weeklyVwapValue, weeklyVwapUpperBand, weeklyVwapLowerBand] = ta.vwap(hlc3, vwapNewWeek, 1.0)
[monthlyVwapValue, monthlyVwapUpperBand, monthlyVwapLowerBand] = ta.vwap(hlc3, vwapNewMonth, 1.0)
// ============================================================================
// Session tracking
// ============================================================================
bool inAsia = not na(time(timeframe.period, asiaSessionInput, sessionTimezoneInput))
bool asiaStart = inAsia and not inAsia[1]
bool asiaEnd = not inAsia and inAsia[1]
var float asiaDevHigh = na
var float asiaDevLow = na
var float asiaDoneHigh = na
var float asiaDoneLow = na
if asiaStart
asiaDevHigh := high
asiaDevLow := low
else if inAsia
asiaDevHigh := math.max(nz(asiaDevHigh, high), high)
asiaDevLow := math.min(nz(asiaDevLow, low), low)
if asiaEnd
asiaDoneHigh := asiaDevHigh
asiaDoneLow := asiaDevLow
bool inLondon = not na(time(timeframe.period, londonSessionInput, sessionTimezoneInput))
bool londonStart = inLondon and not inLondon[1]
bool londonEnd = not inLondon and inLondon[1]
var float londonDevHigh = na
var float londonDevLow = na
var float londonDoneHigh = na
var float londonDoneLow = na
if londonStart
londonDevHigh := high
londonDevLow := low
else if inLondon
londonDevHigh := math.max(nz(londonDevHigh, high), high)
londonDevLow := math.min(nz(londonDevLow, low), low)
if londonEnd
londonDoneHigh := londonDevHigh
londonDoneLow := londonDevLow
bool inNY = not na(time(timeframe.period, nySessionInput, sessionTimezoneInput))
bool nyStart = inNY and not inNY[1]
bool nyEnd = not inNY and inNY[1]
var float nyDevHigh = na
var float nyDevLow = na
var float nyDoneHigh = na
var float nyDoneLow = na
if nyStart
nyDevHigh := high
nyDevLow := low
else if inNY
nyDevHigh := math.max(nz(nyDevHigh, high), high)
nyDevLow := math.min(nz(nyDevLow, low), low)
if nyEnd
nyDoneHigh := nyDevHigh
nyDoneLow := nyDevLow
bool inCustom1 = not na(time(timeframe.period, custom1SessionInput, sessionTimezoneInput))
bool custom1Start = inCustom1 and not inCustom1[1]
bool custom1End = not inCustom1 and inCustom1[1]
var float custom1DevHigh = na
var float custom1DevLow = na
var float custom1DoneHigh = na
var float custom1DoneLow = na
if custom1Start
custom1DevHigh := high
custom1DevLow := low
else if inCustom1
custom1DevHigh := math.max(nz(custom1DevHigh, high), high)
custom1DevLow := math.min(nz(custom1DevLow, low), low)
if custom1End
custom1DoneHigh := custom1DevHigh
custom1DoneLow := custom1DevLow
bool inCustom2 = not na(time(timeframe.period, custom2SessionInput, sessionTimezoneInput))
bool custom2Start = inCustom2 and not inCustom2[1]
bool custom2End = not inCustom2 and inCustom2[1]
var float custom2DevHigh = na
var float custom2DevLow = na
var float custom2DoneHigh = na
var float custom2DoneLow = na
if custom2Start
custom2DevHigh := high
custom2DevLow := low
else if inCustom2
custom2DevHigh := math.max(nz(custom2DevHigh, high), high)
custom2DevLow := math.min(nz(custom2DevLow, low), low)
if custom2End
custom2DoneHigh := custom2DevHigh
custom2DoneLow := custom2DevLow
float asiaHigh = useCompletedSessionInput ? asiaDoneHigh : (inAsia ? asiaDevHigh : asiaDoneHigh)
float asiaLow = useCompletedSessionInput ? asiaDoneLow : (inAsia ? asiaDevLow : asiaDoneLow)
float londonHigh = useCompletedSessionInput ? londonDoneHigh : (inLondon ? londonDevHigh : londonDoneHigh)
float londonLow = useCompletedSessionInput ? londonDoneLow : (inLondon ? londonDevLow : londonDoneLow)
float nyHigh = useCompletedSessionInput ? nyDoneHigh : (inNY ? nyDevHigh : nyDoneHigh)
float nyLow = useCompletedSessionInput ? nyDoneLow : (inNY ? nyDevLow : nyDoneLow)
float custom1High = useCompletedSessionInput ? custom1DoneHigh : (inCustom1 ? custom1DevHigh : custom1DoneHigh)
float custom1Low = useCompletedSessionInput ? custom1DoneLow : (inCustom1 ? custom1DevLow : custom1DoneLow)
float custom2High = useCompletedSessionInput ? custom2DoneHigh : (inCustom2 ? custom2DevHigh : custom2DoneHigh)
float custom2Low = useCompletedSessionInput ? custom2DoneLow : (inCustom2 ? custom2DevLow : custom2DoneLow)
// ============================================================================
// Opening ranges — high/low of the first N minutes after each new day/week/
// month starts. Unlike the sessions above, the still-forming range is never
// shown (na, which f_addLevel already skips) — only the completed window,
// once it closes, so this doesn't need its own "developing vs completed"
// toggle the way sessions do.
// ============================================================================
int dayStartTime = time("D")
int weekStartTime = time("W")
int monthStartTime = time("M")
bool inDailyOR = (time - dayStartTime) < dailyORMinutesInput * 60000
bool inWeeklyOR = (time - weekStartTime) < weeklyORMinutesInput * 60000
bool inMonthlyOR = (time - monthStartTime) < monthlyORMinutesInput * 60000
bool dailyORNewPeriod = ta.change(dayStartTime) != 0
var float dorHigh = na
var float dorLow = na
var float dorDoneHigh = na
var float dorDoneLow = na
if dailyORNewPeriod
dorHigh := high
dorLow := low
else if inDailyOR
dorHigh := math.max(nz(dorHigh, high), high)
dorLow := math.min(nz(dorLow, low), low)
if inDailyOR[1] and not inDailyOR
dorDoneHigh := dorHigh
dorDoneLow := dorLow
bool weeklyORNewPeriod = ta.change(weekStartTime) != 0
var float worHigh = na
var float worLow = na
var float worDoneHigh = na
var float worDoneLow = na
if weeklyORNewPeriod
worHigh := high
worLow := low
else if inWeeklyOR
worHigh := math.max(nz(worHigh, high), high)
worLow := math.min(nz(worLow, low), low)
if inWeeklyOR[1] and not inWeeklyOR
worDoneHigh := worHigh
worDoneLow := worLow
bool monthlyORNewPeriod = ta.change(monthStartTime) != 0
var float morHigh = na
var float morLow = na
var float morDoneHigh = na
var float morDoneLow = na
if monthlyORNewPeriod
morHigh := high
morLow := low
else if inMonthlyOR
morHigh := math.max(nz(morHigh, high), high)
morLow := math.min(nz(morLow, low), low)
if inMonthlyOR[1] and not inMonthlyOR
morDoneHigh := morHigh
morDoneLow := morLow
// ============================================================================
// Pivot points
// ============================================================================
string pivotTf = pivotPeriodInput == "Weekly" ? "W" : pivotPeriodInput == "Monthly" ? "M" : "D"
float pivotHigh = request.security(syminfo.tickerid, pivotTf, high[1], lookahead = barmerge.lookahead_on)
float pivotLow = request.security(syminfo.tickerid, pivotTf, low[1], lookahead = barmerge.lookahead_on)
float pivotClose = request.security(syminfo.tickerid, pivotTf, close[1], lookahead = barmerge.lookahead_on)
float pivotOpen = request.security(syminfo.tickerid, pivotTf, open[1], lookahead = barmerge.lookahead_on)
float pivotRange = pivotHigh - pivotLow
// DeMark's own PP/R1/S1 formula depends on how the prior period's close
// compares to its own open — computed up front so the switch below can stay
// a flat set of assignments per method.
float demarkX = na
if pivotClose < pivotOpen
demarkX := pivotHigh + 2.0 * pivotLow + pivotClose
else if pivotClose > pivotOpen
demarkX := 2.0 * pivotHigh + pivotLow + pivotClose
else
demarkX := pivotHigh + pivotLow + 2.0 * pivotClose
// Multi-line ternaries on series floats risk CE10156 in Pine v6 — if/else
// used deliberately instead, one branch per method.
float pp = na
float r1 = na
float s1 = na
float r2 = na
float s2 = na
float r3 = na
float s3 = na
if pivotMethodInput == "Fibonacci"
pp := (pivotHigh + pivotLow + pivotClose) / 3.0
r1 := pp + 0.382 * pivotRange
s1 := pp - 0.382 * pivotRange
r2 := pp + 0.618 * pivotRange
s2 := pp - 0.618 * pivotRange
r3 := pp + 1.000 * pivotRange
s3 := pp - 1.000 * pivotRange
else if pivotMethodInput == "Camarilla"
pp := (pivotHigh + pivotLow + pivotClose) / 3.0
r1 := pivotClose + pivotRange * 1.1 / 12.0
s1 := pivotClose - pivotRange * 1.1 / 12.0
r2 := pivotClose + pivotRange * 1.1 / 6.0
s2 := pivotClose - pivotRange * 1.1 / 6.0
r3 := pivotClose + pivotRange * 1.1 / 4.0
s3 := pivotClose - pivotRange * 1.1 / 4.0
else if pivotMethodInput == "Woodie"
pp := (pivotHigh + pivotLow + 2.0 * pivotClose) / 4.0
r1 := 2.0 * pp - pivotLow
s1 := 2.0 * pp - pivotHigh
r2 := pp + pivotRange
s2 := pp - pivotRange
r3 := pivotHigh + 2.0 * (pp - pivotLow)
s3 := pivotLow - 2.0 * (pivotHigh - pp)
else if pivotMethodInput == "DeMark"
pp := demarkX / 4.0
r1 := demarkX / 2.0 - pivotLow
s1 := demarkX / 2.0 - pivotHigh
// No R2/R3/S2/S3 in DeMark's own definition — left na, which f_addLevel
// already skips, so those checkboxes simply have no level to register.
else
// Traditional
pp := (pivotHigh + pivotLow + pivotClose) / 3.0
r1 := 2.0 * pp - pivotLow
s1 := 2.0 * pp - pivotHigh
r2 := pp + pivotRange
s2 := pp - pivotRange
r3 := pivotHigh + 2.0 * (pp - pivotLow)
s3 := pivotLow - 2.0 * (pivotHigh - pp)
// ============================================================================
// Swing Fibonacci tracking
// ============================================================================
float fibPH = ta.pivothigh(high, fibPivotLeftInput, fibPivotRightInput)
float fibPL = ta.pivotlow(low, fibPivotLeftInput, fibPivotRightInput)
var float lastFibHigh = na
var float lastFibLow = na
var int lastFibHighBar = na
var int lastFibLowBar = na
if not na(fibPH)
lastFibHigh := fibPH
lastFibHighBar := bar_index - fibPivotRightInput
if not na(fibPL)
lastFibLow := fibPL
lastFibLowBar := bar_index - fibPivotRightInput
bool fibBullLeg = not na(lastFibHighBar) and not na(lastFibLowBar) and lastFibLowBar < lastFibHighBar
bool fibBearLeg = not na(lastFibHighBar) and not na(lastFibLowBar) and lastFibHighBar < lastFibLowBar
float fibLegSize = math.abs(lastFibHigh - lastFibLow)
bool validFibLeg = fibLegSize >= fibMinLegAtrInput * safeAtr
f_fibPrice(float ratio) =>
fibBullLeg ? lastFibHigh - fibLegSize * ratio :
fibBearLeg ? lastFibLow + fibLegSize * ratio : na
f_extensionPrice(float ratio) =>
fibBullLeg ? lastFibLow + fibLegSize * ratio :
fibBearLeg ? lastFibHigh - fibLegSize * ratio : na
// ============================================================================
// Pivot-cluster S/R
// ============================================================================
var array<float> supportCenters = array.new_float()
var array<int> supportTouches = array.new_int()
var array<int> supportLastBars = array.new_int()
var array<float> resistanceCenters = array.new_float()
var array<int> resistanceTouches = array.new_int()
var array<int> resistanceLastBars = array.new_int()
f_updateCluster(
array<float> centers,
array<int> touches,
array<int> lastBars,
float pivotPrice,
int pivotBar,
float tolerance
) =>
int bestIndex = -1
float bestDistance = 1e100
if array.size(centers) > 0
for i = 0 to array.size(centers) - 1
float distance = math.abs(array.get(centers, i) - pivotPrice)
if distance <= tolerance and distance < bestDistance
bestDistance := distance
bestIndex := i
if bestIndex >= 0
int oldTouches = array.get(touches, bestIndex)
float oldCenter = array.get(centers, bestIndex)
float newCenter = (oldCenter * oldTouches + pivotPrice) / (oldTouches + 1)
array.set(centers, bestIndex, newCenter)
array.set(touches, bestIndex, oldTouches + 1)
array.set(lastBars, bestIndex, pivotBar)
else
array.push(centers, pivotPrice)
array.push(touches, 1)
array.push(lastBars, pivotBar)
float srPH = ta.pivothigh(high, srPivotLeftInput, srPivotRightInput)
float srPL = ta.pivotlow(low, srPivotLeftInput, srPivotRightInput)
if not na(srPH)
f_updateCluster(
resistanceCenters,
resistanceTouches,
resistanceLastBars,
srPH,
bar_index - srPivotRightInput,
srClusterAtrInput * safeAtr
)
if not na(srPL)
f_updateCluster(
supportCenters,
supportTouches,
supportLastBars,
srPL,
bar_index - srPivotRightInput,
srClusterAtrInput * safeAtr
)
// Prevent unlimited historical growth.
int MAX_CLUSTERS = 80
while array.size(supportCenters) > MAX_CLUSTERS
array.shift(supportCenters)
array.shift(supportTouches)
array.shift(supportLastBars)
while array.size(resistanceCenters) > MAX_CLUSTERS
array.shift(resistanceCenters)
array.shift(resistanceTouches)
array.shift(resistanceLastBars)
// A close beyond a cluster by more than the cluster tolerance breaks it:
// the touch count restarts at zero, so a later pivot there re-seeds the
// center instead of adding to the pre-break count.
float srBreakTolerance = srClusterAtrInput * safeAtr
if array.size(resistanceCenters) > 0
for i = 0 to array.size(resistanceCenters) - 1
if close > array.get(resistanceCenters, i) + srBreakTolerance
array.set(resistanceTouches, i, 0)
if array.size(supportCenters) > 0
for i = 0 to array.size(supportCenters) - 1
if close < array.get(supportCenters, i) - srBreakTolerance
array.set(supportTouches, i, 0)
// ============================================================================
// Build normalized level registry
// ============================================================================
array.clear(levels)
color dayColor = color.rgb(68, 138, 255)
color weekColor = color.rgb(139, 92, 246)
color monthColor = color.rgb(236, 72, 153)
color sessionColor = color.rgb(14, 165, 233)
color pivotColor = color.rgb(245, 158, 11)
color fibColor = color.rgb(34, 197, 94)
color resistanceColor = color.rgb(239, 68, 68)
color supportColor = color.rgb(16, 185, 129)
color roundColor = color.rgb(148, 163, 184)
color yearColor = color.rgb(121, 85, 72)
color vwapColor = color.rgb(255, 87, 34)
color manualColor = color.rgb(103, 58, 183)
color orColor = color.rgb(0, 172, 193)
bool customOrEverything = presetCustom or presetEverything
// Day levels
f_addLevel(pdh, na, na, "PDH", "Previous Day High", "DAY", 9, dayColor, false, 0,
useDayLevels and (presetCustom ? showPDHInput : true))
f_addLevel(pdl, na, na, "PDL", "Previous Day Low", "DAY", 9, dayColor, false, 0,
useDayLevels and (presetCustom ? showPDLInput : true))
f_addLevel(pdc, na, na, "PDC", "Previous Day Close", "DAY", 7, dayColor, false, 0,
useDayLevels and (presetCustom ? showPDCInput : true))
f_addLevel(pdo, na, na, "PDO", "Previous Day Open", "DAY", 5, dayColor, false, 0,
useDayLevels and customOrEverything and showPDOInput)
f_addLevel(dayOpen, na, na, "DO", "Current Day Open", "DAY", 8, dayColor, false, 0,
useDayLevels and (presetCustom ? showDOInput : true))
f_addLevel((pdh + pdl) / 2.0, na, na, "PDM", "Previous Day Midpoint", "DAY", 4, dayColor, false, 0,
useDayLevels and customOrEverything and showPDMInput)
// Week levels
f_addLevel(pwh, na, na, "PWH", "Previous Week High", "WEEK", 9, weekColor, false, 0,
useWeekLevels and (presetCustom ? showPWHInput : true))
f_addLevel(pwl, na, na, "PWL", "Previous Week Low", "WEEK", 9, weekColor, false, 0,
useWeekLevels and (presetCustom ? showPWLInput : true))
f_addLevel(pwc, na, na, "PWC", "Previous Week Close", "WEEK", 6, weekColor, false, 0,
useWeekLevels and customOrEverything and showPWCInput)
f_addLevel(pwo, na, na, "PWO", "Previous Week Open", "WEEK", 5, weekColor, false, 0,
useWeekLevels and customOrEverything and showPWOInput)
f_addLevel(weekOpen, na, na, "WO", "Current Week Open", "WEEK", 8, weekColor, false, 0,
useWeekLevels and (presetCustom ? showWOInput : true))
f_addLevel((pwh + pwl) / 2.0, na, na, "PWM", "Previous Week Midpoint", "WEEK", 4, weekColor, false, 0,
useWeekLevels and customOrEverything and showPWMInput)
// Month levels
f_addLevel(pmh, na, na, "PMH", "Previous Month High", "MONTH", 8, monthColor, false, 0,
useMonthLevels and (presetCustom ? showPMHInput : true))
f_addLevel(pml, na, na, "PML", "Previous Month Low", "MONTH", 8, monthColor, false, 0,
useMonthLevels and (presetCustom ? showPMLInput : true))
f_addLevel(pmc, na, na, "PMC", "Previous Month Close", "MONTH", 5, monthColor, false, 0,
useMonthLevels and (presetCustom ? showPMCInput : true))
f_addLevel(pmo, na, na, "PMO", "Previous Month Open", "MONTH", 5, monthColor, false, 0,
useMonthLevels and (presetCustom ? showPMOInput : true))
f_addLevel(monthOpen, na, na, "MO", "Current Month Open", "MONTH", 7, monthColor, false, 0,
useMonthLevels and (presetCustom ? showMOInput : true))
f_addLevel((pmh + pml) / 2.0, na, na, "PMM", "Previous Month Midpoint", "MONTH", 4, monthColor, false, 0,
useMonthLevels and (presetCustom ? showPMMInput : true))
// Year levels
f_addLevel(pyh, na, na, "PYH", "Previous Year High", "YEAR", 8, yearColor, false, 0,
useYearLevels and (presetCustom ? showPYHInput : true))
f_addLevel(pyl, na, na, "PYL", "Previous Year Low", "YEAR", 8, yearColor, false, 0,
useYearLevels and (presetCustom ? showPYLInput : true))
f_addLevel(pyc, na, na, "PYC", "Previous Year Close", "YEAR", 5, yearColor, false, 0,
useYearLevels and (presetCustom ? showPYCInput : true))
f_addLevel(pyo, na, na, "PYO", "Previous Year Open", "YEAR", 5, yearColor, false, 0,
useYearLevels and (presetCustom ? showPYOInput : true))
f_addLevel(yearOpen, na, na, "YO", "Current Year Open", "YEAR", 6, yearColor, false, 0,
useYearLevels and (presetCustom ? showYOInput : true))
f_addLevel((pyh + pyl) / 2.0, na, na, "PYM", "Previous Year Midpoint", "YEAR", 4, yearColor, false, 0,
useYearLevels and (presetCustom ? showPYMInput : true))
// Sessions
bool asiaActive = useSessions and (presetCustom ? asiaEnableInput : presetEverything)
bool londonActive = useSessions and (presetCustom ? londonEnableInput : presetIntraday or presetEverything)
bool nyActive = useSessions and (presetCustom ? nyEnableInput : presetIntraday or presetEverything)
// Custom sessions have no sensible default name/hours, so — like manual levels —
// they only ever activate on the Custom preset, never via Intraday/Everything.
bool custom1Active = useSessions and presetCustom and custom1EnableInput
bool custom2Active = useSessions and presetCustom and custom2EnableInput
f_addLevel(asiaHigh, na, na, "ASH", "Asia Session High", "SESSION", 7, sessionColor, false, 0,
asiaActive and (presetCustom ? asiaHighInput : true))
f_addLevel(asiaLow, na, na, "ASL", "Asia Session Low", "SESSION", 7, sessionColor, false, 0,
asiaActive and (presetCustom ? asiaLowInput : true))
f_addLevel((asiaHigh + asiaLow) / 2.0, na, na, "ASM", "Asia Session Midpoint", "SESSION", 4, sessionColor, false, 0,
asiaActive and presetCustom and asiaMidInput)
f_addLevel(londonHigh, na, na, "LDH", "London Session High", "SESSION", 7, sessionColor, false, 0,
londonActive and (presetCustom ? londonHighInput : true))
f_addLevel(londonLow, na, na, "LDL", "London Session Low", "SESSION", 7, sessionColor, false, 0,
londonActive and (presetCustom ? londonLowInput : true))
f_addLevel((londonHigh + londonLow) / 2.0, na, na, "LDM", "London Session Midpoint", "SESSION", 4, sessionColor, false, 0,
londonActive and presetCustom and londonMidInput)
f_addLevel(nyHigh, na, na, "NYH", "New York Session High", "SESSION", 7, sessionColor, false, 0,
nyActive and (presetCustom ? nyHighInput : true))
f_addLevel(nyLow, na, na, "NYL", "New York Session Low", "SESSION", 7, sessionColor, false, 0,
nyActive and (presetCustom ? nyLowInput : true))
f_addLevel((nyHigh + nyLow) / 2.0, na, na, "NYM", "New York Session Midpoint", "SESSION", 4, sessionColor, false, 0,
nyActive and presetCustom and nyMidInput)
f_addLevel(custom1High, na, na, custom1NameInput + "H", "Custom Session 1 High", "SESSION", 6, sessionColor, false, 0,
custom1Active and custom1HighInput)
f_addLevel(custom1Low, na, na, custom1NameInput + "L", "Custom Session 1 Low", "SESSION", 6, sessionColor, false, 0,
custom1Active and custom1LowInput)
f_addLevel((custom1High + custom1Low) / 2.0, na, na, custom1NameInput + "M", "Custom Session 1 Midpoint", "SESSION", 4, sessionColor, false, 0,
custom1Active and custom1MidInput)
f_addLevel(custom2High, na, na, custom2NameInput + "H", "Custom Session 2 High", "SESSION", 6, sessionColor, false, 0,
custom2Active and custom2HighInput)
f_addLevel(custom2Low, na, na, custom2NameInput + "L", "Custom Session 2 Low", "SESSION", 6, sessionColor, false, 0,
custom2Active and custom2LowInput)
f_addLevel((custom2High + custom2Low) / 2.0, na, na, custom2NameInput + "M", "Custom Session 2 Midpoint", "SESSION", 4, sessionColor, false, 0,
custom2Active and custom2MidInput)
// Opening ranges
bool dailyORActive = useOR and dailyOREnableInput
bool weeklyORActive = useOR and weeklyOREnableInput
bool monthlyORActive = useOR and monthlyOREnableInput
f_addLevel(dorDoneHigh, na, na, "ORH", "Daily Opening Range High", "OR", 7, orColor, false, 0,
dailyORActive and dailyORHighInput)
f_addLevel(dorDoneLow, na, na, "ORL", "Daily Opening Range Low", "OR", 7, orColor, false, 0,
dailyORActive and dailyORLowInput)
f_addLevel(worDoneHigh, na, na, "WORH", "Weekly Opening Range High", "OR", 6, orColor, false, 0,
weeklyORActive and weeklyORHighInput)
f_addLevel(worDoneLow, na, na, "WORL", "Weekly Opening Range Low", "OR", 6, orColor, false, 0,
weeklyORActive and weeklyORLowInput)
f_addLevel(morDoneHigh, na, na, "MORH", "Monthly Opening Range High", "OR", 5, orColor, false, 0,
monthlyORActive and monthlyORHighInput)
f_addLevel(morDoneLow, na, na, "MORL", "Monthly Opening Range Low", "OR", 5, orColor, false, 0,
monthlyORActive and monthlyORLowInput)
// Pivot points
f_addLevel(pp, na, na, "PP", "Central Pivot Point", "PIVOT", 8, pivotColor, false, 0,
usePivots and (presetCustom ? showPPInput : true))
f_addLevel(r1, na, na, "R1", "Pivot Resistance 1", "PIVOT", 7, pivotColor, false, 0,
usePivots and (presetCustom ? showR1Input : true))
f_addLevel(s1, na, na, "S1", "Pivot Support 1", "PIVOT", 7, pivotColor, false, 0,
usePivots and (presetCustom ? showS1Input : true))
f_addLevel(r2, na, na, "R2", "Pivot Resistance 2", "PIVOT", 6, pivotColor, false, 0,
usePivots and (presetCustom ? showR2Input : true))
f_addLevel(s2, na, na, "S2", "Pivot Support 2", "PIVOT", 6, pivotColor, false, 0,
usePivots and (presetCustom ? showS2Input : true))
f_addLevel(r3, na, na, "R3", "Pivot Resistance 3", "PIVOT", 5, pivotColor, false, 0,
usePivots and presetCustom and showR3Input)
f_addLevel(s3, na, na, "S3", "Pivot Support 3", "PIVOT", 5, pivotColor, false, 0,
usePivots and presetCustom and showS3Input)
// Swing Fibonacci
bool fibActive = useFib and validFibLeg
f_addLevel(f_fibPrice(0.236), na, na, "F23.6", "Fibonacci 23.6%", "FIB", 5, fibColor, false, 0,
fibActive and (presetCustom ? fib236Input : presetEverything))
f_addLevel(f_fibPrice(0.382), na, na, "F38.2", "Fibonacci 38.2%", "FIB", 7, fibColor, false, 0,
fibActive and (presetCustom ? fib382Input : true))
f_addLevel(f_fibPrice(0.500), na, na, "F50.0", "Fibonacci 50.0%", "FIB", 7, fibColor, false, 0,
fibActive and (presetCustom ? fib500Input : true))
f_addLevel(f_fibPrice(0.618), na, na, "F61.8", "Fibonacci 61.8%", "FIB", 8, fibColor, false, 0,
fibActive and (presetCustom ? fib618Input : true))
f_addLevel(f_fibPrice(0.786), na, na, "F78.6", "Fibonacci 78.6%", "FIB", 5, fibColor, false, 0,
fibActive and (presetCustom ? fib786Input : presetEverything))
f_addLevel(f_fibPrice(1.000), na, na, "F100", "Fibonacci 100.0%", "FIB", 5, fibColor, false, 0,
fibActive and (presetCustom ? fib100Input : presetEverything))
f_addLevel(f_extensionPrice(1.272), na, na, "F127", "Fibonacci Extension 127.2%", "FIB", 5, fibColor, false, 0,
fibActive and (presetCustom ? fib1272Input : presetEverything))
f_addLevel(f_extensionPrice(1.618), na, na, "F162", "Fibonacci Extension 161.8%", "FIB", 6, fibColor, false, 0,
fibActive and (presetCustom ? fib1618Input : presetEverything))
// S/R candidates: collect eligible indices and sort by distance.
if useSR
array<float> supportDistances = array.new_float()
array<int> supportIndices = array.new_int()
if array.size(supportCenters) > 0
for i = 0 to array.size(supportCenters) - 1
float center = array.get(supportCenters, i)
int touches = array.get(supportTouches, i)
int age = bar_index - array.get(supportLastBars, i)
if center < close and touches >= srMinTouchesInput and age <= srMaxAgeInput
array.push(supportDistances, close - center)
array.push(supportIndices, i)
array<int> supportOrder = array.sort_indices(supportDistances)
int supportsToRender = math.min(srSupportsInput, array.size(supportOrder))
if supportsToRender > 0
for rank = 0 to supportsToRender - 1
int distanceIndex = array.get(supportOrder, rank)
int clusterIndex = array.get(supportIndices, distanceIndex)
float center = array.get(supportCenters, clusterIndex)
int touches = array.get(supportTouches, clusterIndex)
float halfWidth = srZoneWidthAtrInput * safeAtr * 0.5
f_addLevel(center, center + halfWidth, center - halfWidth, "S" + str.tostring(rank + 1),
"Support Zone " + str.tostring(rank + 1), "S/R", 8, supportColor, true, touches, true)
array<float> resistanceDistances = array.new_float()
array<int> resistanceIndices = array.new_int()
if array.size(resistanceCenters) > 0
for i = 0 to array.size(resistanceCenters) - 1
float center = array.get(resistanceCenters, i)
int touches = array.get(resistanceTouches, i)
int age = bar_index - array.get(resistanceLastBars, i)
if center > close and touches >= srMinTouchesInput and age <= srMaxAgeInput
array.push(resistanceDistances, center - close)
array.push(resistanceIndices, i)
array<int> resistanceOrder = array.sort_indices(resistanceDistances)
int resistancesToRender = math.min(srResistancesInput, array.size(resistanceOrder))
if resistancesToRender > 0
for rank = 0 to resistancesToRender - 1
int distanceIndex = array.get(resistanceOrder, rank)
int clusterIndex = array.get(resistanceIndices, distanceIndex)
float center = array.get(resistanceCenters, clusterIndex)
int touches = array.get(resistanceTouches, clusterIndex)
float halfWidth = srZoneWidthAtrInput * safeAtr * 0.5
f_addLevel(center, center + halfWidth, center - halfWidth, "R" + str.tostring(rank + 1),
"Resistance Zone " + str.tostring(rank + 1), "S/R", 8, resistanceColor, true, touches, true)
// Round numbers
if useRound
float majorStep = roundAutoStepInput ? f_autoRoundStep(close) : roundManualStepInput
float smallestStep = roundQuarterInput ? majorStep * 0.25 : roundHalfInput ? majorStep * 0.5 : majorStep
int baseIndex = int(math.floor(close / smallestStep))
for offset = -roundLevelsEachSideInput to roundLevelsEachSideInput
if offset != 0
float levelPrice = (baseIndex + offset) * smallestStep
float normalizedMajor = levelPrice / majorStep
float fraction = math.abs(normalizedMajor - math.round(normalizedMajor))
bool isMajor = fraction < 0.000001
bool isHalf = math.abs(fraction - 0.5) < 0.000001
bool allowed = isMajor ? roundMajorInput : isHalf ? roundHalfInput : roundQuarterInput
string code = isMajor ? "RN" : isHalf ? "RN½" : "RN¼"
f_addLevel(levelPrice, na, na, code, "Round Number", "ROUND", isMajor ? 5 : 3,
roundColor, false, 0, allowed)
// VWAP
bool vwapSessionActive = useVWAP and (presetCustom ? vwapSessionInput : true)
bool vwapWeeklyActive = useVWAP and presetCustom and vwapWeeklyInput
bool vwapMonthlyActive = useVWAP and presetCustom and vwapMonthlyInput
f_addLevel(sessionVwapValue, na, na, "VWAP", "Session VWAP", "VWAP", 7, vwapColor, false, 0, vwapSessionActive)
f_addLevel(weeklyVwapValue, na, na, "WVWAP", "Weekly VWAP", "VWAP", 6, vwapColor, false, 0, vwapWeeklyActive)
f_addLevel(monthlyVwapValue, na, na, "MVWAP", "Monthly VWAP", "VWAP", 5, vwapColor, false, 0, vwapMonthlyActive)
// Manual levels — a slot with price left at its 0.0 default is treated as
// unset, not a real level at zero.
f_addLevel(manual1PriceInput, na, na, manual1NameInput, manual1NameInput, "MANUAL", 6, manualColor, false, 0,
useManual and manual1EnableInput and manual1PriceInput != 0.0)
f_addLevel(manual2PriceInput, na, na, manual2NameInput, manual2NameInput, "MANUAL", 6, manualColor, false, 0,
useManual and manual2EnableInput and manual2PriceInput != 0.0)
f_addLevel(manual3PriceInput, na, na, manual3NameInput, manual3NameInput, "MANUAL", 6, manualColor, false, 0,
useManual and manual3EnableInput and manual3PriceInput != 0.0)
f_addLevel(manual4PriceInput, na, na, manual4NameInput, manual4NameInput, "MANUAL", 6, manualColor, false, 0,
useManual and manual4EnableInput and manual4PriceInput != 0.0)
// ============================================================================
// Trend Curves — preset resolution
// ============================================================================
// Lengths are always in bars of the current chart timeframe, never auto-scaled
// by resolution — a "20" means 20 candles whether the chart is on 5m or 1D.
string fastTrendType = switch trendPresetInput
"Custom" => fastTypeInput
"Structural" => "EMA"
=> "JMA-style"
int fastTrendLength = switch trendPresetInput
"Custom" => fastLengthInput
"Responsive" => 9
"Structural" => 20
=> 21
float fastTrendPhase = switch trendPresetInput
"Custom" => fastPhaseInput
=> 0.0
float fastTrendPower = switch trendPresetInput
"Custom" => fastPowerInput
=> 2.0
string baseTrendType = switch trendPresetInput
"Custom" => baseTypeInput
"Responsive" => "JMA-style"
"Balanced" => "EMA"
=> "SMA"
int baseTrendLength = switch trendPresetInput
"Custom" => baseLengthInput
"Responsive" => 21
=> 50
float baseTrendPhase = switch trendPresetInput
"Custom" => basePhaseInput
=> 0.0
float baseTrendPower = switch trendPresetInput
"Custom" => basePowerInput
=> 2.0
string anchorTrendType = switch trendPresetInput
"Custom" => anchorTypeInput
"Responsive" => "JMA-style"
=> "SMA"
int anchorTrendLength = switch trendPresetInput
"Custom" => anchorLengthInput
"Responsive" => 50
=> 200
float anchorTrendPhase = switch trendPresetInput
"Custom" => anchorPhaseInput
=> 0.0
float anchorTrendPower = switch trendPresetInput
"Custom" => anchorPowerInput
=> 2.0
// MTF mode passes the whole f_maValue(...) call into request.security() so it
// gets computed against the target timeframe's own OHLC series (not just
// resampled from an already chart-timeframe-computed value) — the standard
// pattern for a genuine "Daily SMA 200 on an hourly chart" style MTF line.
// Blank trendMtfInput (the default) skips request.security entirely and
// keeps the exact same local computation every version before this one used.
bool trendUseMtf = trendMtfInput != ""
float fastTrendValue = na
float baseTrendValue = na
float anchorTrendValue = na
if trendUseMtf
fastTrendValue := request.security(syminfo.tickerid, trendMtfInput, f_maValue(fastTrendType, trendSourceInput, fastTrendLength, fastTrendPhase, fastTrendPower))
baseTrendValue := request.security(syminfo.tickerid, trendMtfInput, f_maValue(baseTrendType, trendSourceInput, baseTrendLength, baseTrendPhase, baseTrendPower))
anchorTrendValue := request.security(syminfo.tickerid, trendMtfInput, f_maValue(anchorTrendType, trendSourceInput, anchorTrendLength, anchorTrendPhase, anchorTrendPower))
else
fastTrendValue := f_maValue(fastTrendType, trendSourceInput, fastTrendLength, fastTrendPhase, fastTrendPower)
baseTrendValue := f_maValue(baseTrendType, trendSourceInput, baseTrendLength, baseTrendPhase, baseTrendPower)
anchorTrendValue := f_maValue(anchorTrendType, trendSourceInput, anchorTrendLength, anchorTrendPhase, anchorTrendPower)
int trendLineWidth = switch trendEmphasisInput
"Subtle" => 1
"Bold" => 3
=> 2
color fastTrendColor = color.rgb(41, 98, 255)
color baseTrendColor = color.rgb(255, 152, 0)
color anchorTrendColor = color.rgb(120, 123, 134)
// Trend Curves have always been a separate always-on chart plot, independent
// of the right-edge Level registry — this optionally also feeds each curve's
// current value into that registry, so it can be ranked/filtered/spaced
// alongside every other level instead of only ever being a full-chart line.
f_addLevel(fastTrendValue, na, na, "FAST", "Fast Curve", "TREND", 6, fastTrendColor, false, 0,
trendModuleInput and trendAsLevelsInput and showFastInput)
f_addLevel(baseTrendValue, na, na, "BASE", "Base Curve", "TREND", 6, baseTrendColor, false, 0,
trendModuleInput and trendAsLevelsInput and showBaseInput)
f_addLevel(anchorTrendValue, na, na, "ANCHOR", "Anchor Curve", "TREND", 6, anchorTrendColor, false, 0,
trendModuleInput and trendAsLevelsInput and showAnchorInput)
plot(masterEnableInput and trendModuleInput and showFastInput ? fastTrendValue : na, "Fast Curve",
color = fastTrendColor, linewidth = trendLineWidth, offset = trendOffsetInput)
plot(masterEnableInput and trendModuleInput and showBaseInput ? baseTrendValue : na, "Base Curve",
color = baseTrendColor, linewidth = trendLineWidth, offset = trendOffsetInput)
plot(masterEnableInput and trendModuleInput and showAnchorInput ? anchorTrendValue : na, "Anchor Curve",
color = anchorTrendColor, linewidth = trendLineWidth, offset = trendOffsetInput)
// ============================================================================
// Render
// ============================================================================
if barstate.islast
f_clearDrawings()
if masterEnableInput
// Label anchor is always a simple forward offset from the last real bar —
// a fixed bar count, not a percentage of the visible bar range. That
// earlier version recomputed a different offset on every scroll/zoom, and
// since Pine only redraws when the script recalculates (not continuously
// during a drag), the label would freeze mid-pan and then jump to the new
// position once a recalculation happened, instead of panning smoothly like
// a normal drawing. A fixed bar count keeps the label at the same position
// regardless of scroll, so redraws land exactly where the label already
// was — nothing to jump. bar_index itself only moves when a new bar closes,
// not on chart navigation, so the anchor is fully stable — the same
// principle Fib Reaction Memory uses for its projected Fib lines
// (`bar_index + rightProjection`, xloc.bar_index).
//
// The rail no longer tries to reach the chart's true right edge (dropped on
// request — keeping the current price area uncluttered matters more than
// the rail visually touching the price scale). It's a fixed-length segment
// starting just past the label, extend.none instead of extend.right.
int labelAnchorBar = bar_index + labelOffsetBarsInput
// Visual gap between the label and where the rail picks up.
int labelToRailGap = int(math.max(labelOffsetBarsInput * 0.03, 2))
int railStartBar = labelAnchorBar + labelToRailGap
int railSeedEnd = railStartBar + int(math.max(labelOffsetBarsInput * 0.5, 5))
// Hard ceiling on how far into the future ANY rail endpoint can land, well
// under Pine's bounded future-drawing distance (roughly 500 bars). Without
// this, a large "Label offset · bars" combined with a high "Maximum
// rendered levels" (which pushes each rank further out via the stagger
// below) could push the highest-ranked rails past that limit and get
// silently clamped — recreating the shared-coordinate vertical-bar bug
// this script has already hit twice, just via inputs instead of zoom.
int futureLimitBar = bar_index + 480
// Build candidate distances and sort by priority first, then distance.
array<float> sortKeys = array.new_float()
array<int> candidateIndices = array.new_int()
if array.size(levels) > 0
for i = 0 to array.size(levels) - 1
Level level = array.get(levels, i)
float distanceAtr = math.abs(level.price - close) / safeAtr
bool sideAllowed = level.price >= close ? showAboveInput : showBelowInput
bool eligible = sideAllowed and distanceAtr <= maxDistanceAtrInput and level.priority >= minPriorityInput
if eligible
// Lower key sorts first: priority dominates, distance breaks ties.
float key = (10 - level.priority) * 1000000.0 + distanceAtr
array.push(sortKeys, key)
array.push(candidateIndices, i)
// ATR-based rather than visible-price-range-based: tied to the instrument's
// own volatility, so which levels get culled doesn't change just because the
// user scrolled or zoomed to a different price range.
float minLabelSpacing = minLabelSpacingAtrInput * safeAtr
array<int> order = array.sort_indices(sortKeys)
// Greedily accept candidates in priority/distance order, skipping any level
// that sits too close (relative to ATR) to an already-accepted level so the
// right-edge stack does not collapse into overlapping label boxes.
array<float> acceptedPrices = array.new_float()
array<int> acceptedIndices = array.new_int()
if array.size(order) > 0
for rank = 0 to array.size(order) - 1
int candidatePos = array.get(order, rank)
int levelIndex = array.get(candidateIndices, candidatePos)
Level candidate = array.get(levels, levelIndex)
bool tooClose = false
if minLabelSpacing > 0 and array.size(acceptedPrices) > 0
for j = 0 to array.size(acceptedPrices) - 1
if math.abs(array.get(acceptedPrices, j) - candidate.price) < minLabelSpacing
tooClose := true
if not tooClose
array.push(acceptedPrices, candidate.price)
array.push(acceptedIndices, levelIndex)
if array.size(acceptedIndices) >= maxLevelsInput
break
int renderCount = array.size(acceptedIndices)
if renderCount > 0
for rank = 0 to renderCount - 1
int levelIndex = array.get(acceptedIndices, rank)
Level level = array.get(levels, levelIndex)
// Higher-priority levels render bolder and more opaque — the same
// "visual weight tracks significance" principle Fib Reaction Memory
// applies to its Memory Score tiers.
int tierWidth = f_priorityWidth(level.priority)
int tierLineTransparency = f_priorityTransparency(lineTransparencyInput, level.priority)
int tierLabelTransparency = f_priorityTransparency(labelTransparencyInput, level.priority)
color railColor = color.new(level.levelColor, tierLineTransparency)
color labelColor = color.new(level.levelColor, tierLabelTransparency)
string labelText = f_text(level)
// Every rail is staggered by rank so no two levels ever share the exact
// same starting bar. Without this, several differently-colored
// horizontal lines all beginning at the identical column, stacked
// closely in price, read as a vertical multi-colored bar at that column
// regardless of how long each segment then runs — tuning the segment
// length alone (tried repeatedly) only ever reduces the odds at whatever
// zoom level happens to get tested, it doesn't remove the shared origin.
int levelRailStart = math.min(railStartBar + rank, futureLimitBar - 1)
int levelRailEnd = math.min(railSeedEnd + rank, futureLimitBar)
line rail = line.new(
levelRailStart,
level.price,
levelRailEnd,
level.price,
xloc = xloc.bar_index,
extend = extend.none,
color = railColor,
width = tierWidth
)
array.push(renderedLines, rail)
label tag = label.new(
labelAnchorBar,
level.price,
labelText,
xloc = xloc.bar_index,
yloc = yloc.price,
style = label.style_label_right,
color = labelColor,
textcolor = color.white,
size = f_labelSize(),
tooltip =
level.code + " — " + level.fullName + "\n" +
"Price: " + str.tostring(level.price, format.mintick) +
(level.isZone ? "\nZone: " + str.tostring(level.lower, format.mintick) +
" – " + str.tostring(level.upper, format.mintick) +
"\nTouches: " + str.tostring(level.touches) : "")
)
array.push(renderedLabels, tag)
if showSourceZonesInput and level.isZone
int zoneStartBar = bar_index - math.min(srMaxAgeInput, bar_index)
box zoneBox = box.new(
zoneStartBar,
level.upper,
bar_index,
level.lower,
xloc = xloc.bar_index,
bgcolor = color.new(level.levelColor, 90),
border_color = color.new(level.levelColor, 65)
)
array.push(renderedBoxes, zoneBox)
Overview
Modules produce day, week, and other period highs, lows, opens, and closes, plus pivot points, Fibonacci levels, support and resistance zones, session ranges, round numbers, and VWAP. A shared registry filters by priority, ATR distance, side, and count, then places labels a fixed bar offset past the last candle with short rails whose width and opacity scale with priority. Settings control presets, label spacing, and which modules appear. It can request higher-timeframe data with lookahead.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Preset
string
Clean
presetInput
Enable indicator
bool
true
masterEnableInput
Maximum rendered levels
int
24
maxLevelsInput
Maximum distance from price · ATR
float
12.0
maxDistanceAtrInput
Levels above price
bool
true
showAboveInput
Levels below price
bool
true
showBelowInput
Minimum priority
int
1
minPriorityInput
Label offset · bars
int
200
labelOffsetBarsInput
Minimum label spacing · ATR
float
3.0
minLabelSpacingAtrInput
Label content
string
Code
labelContentInput
Label size
string
Small
labelSizeInput
Line width
int
2
lineWidthInput
Show all 40 settings
Setting
Type
Default
Variable
Line transparency
int
10
lineTransparencyInput
Label transparency
int
10
labelTransparencyInput
Prefix category
bool
false
showCategoryInput
Show S/R source zones in chart
bool
false
showSourceZonesInput
Enable day levels
bool
true
periodModuleInput
PDH
bool
true
showPDHInput
PDL
bool
true
showPDLInput
PDC
bool
true
showPDCInput
PDO
bool
false
showPDOInput
DO
bool
true
showDOInput
PDM
bool
false
showPDMInput
Enable week levels
bool
true
weekModuleInput
PWH
bool
true
showPWHInput
PWL
bool
true
showPWLInput
PWC
bool
false
showPWCInput
PWO
bool
false
showPWOInput
WO
bool
true
showWOInput
PWM
bool
false
showPWMInput
Enable month levels
bool
true
monthModuleInput
PMH
bool
false
showPMHInput
PML
bool
false
showPMLInput
PMC
bool
false
showPMCInput
PMO
bool
false
showPMOInput
MO
bool
true
showMOInput
PMM
bool
false
showPMMInput
Enable year levels
bool
false
yearModuleInput
PYH
bool
false
showPYHInput
PYL
bool
false
showPYLInput
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
The Midnight VWAP indicator calculates and overlays two distinct Volume Weighted Average Price (VWAP) lines on the price chart based on customizable session timeframes. It tracks an overnight or pre-market session alongside regular trading hours, resetting cal
This indicator estimates gamma exposure levels and intraday context for index instruments like SPY and ES. It calculates VWAP with standard deviation bands as a gamma flip proxy, highlighting market regimes through background colors. It plots prior day high, l
LunarTick Levels plots key daily reference levels and multi-timeframe simple moving averages directly on the price chart. It highlights key price points including the Opening Print, Previous Day High and Low, and Overnight High and Low. Additionally, it projec
This indicator plots price action tools directly on the main price chart. It shades candles according to Internal Bar Strength (IBS) and optional wick-length thresholds to differentiate trend bars from trading ranges. The script tracks session bar counts durin
TradeKit is an all-in-one technical analysis indicator that consolidates multiple chart overlays into a single script on the price chart. It plots up to four customizable moving averages (SMA, EMA, SMMA, WMA, or VWMA), Bollinger Bands, VWAP, and reference leve
Hercules Indicator 1.7 is a multi-filter trade signal generator designed to overlay entry conditions directly on the price chart. It evaluates engulfing candle patterns alongside multiple configurable filters, including trading session windows (Sydney, Tokyo,