//@version=6 indicator("Price Action Toolkit (structure, zones, MTF liquidity)", shorttitle = "PA Toolkit", overlay = true, max_boxes_count = 500, max_lines_count = 500, max_labels_count = 500, max_bars_back = 2000) // ============================================================================ // PRICE ACTION TOOLKIT // // Original code. Every module has its own switch in the settings: // 1. Market structure internal + swing: BOS, BOS+, CHoCH, CHoCH+, // HH/HL/LH/LL at every swing point, level lines at // the open swing high and low, strong / weak high and // low, candles coloured by structure (partly after a // CHoCH, fully after a BOS) // 2. Order blocks with breaker blocks (show or hide) // 3. Fair value gaps with inverse gaps (IFVG): a gap that price closes // through flips colour and stays as support or // resistance until price closes back through it // 4. Liquidity zones wick-to-body zones at swing highs and lows (green), // merged when highs or lows are nearly equal, // a box around the wick of a sweep candle, // equal highs / lows // 5. Higher timeframes the same liquidity zones from up to four other // timeframes, each tagged with its timeframe; // overlapping zones are combined into one // Zone odds counted on this chart: after a zone was taken, how // often price went up or down first // 6. Premium / discount with equilibrium, in its own column right of the // zones, so it never covers them // 7. Previous day / week / month highs and lows, and the day / week / // month open, as a tag with a short line (close levels share one tag) // Sessions high and low of the Asia, London and New York session, // drawn as lines over the session itself. Asia starts // when the trading day opens (18:00 New York time) or at // the Tokyo open; London and New York start at their // opens; New York runs to 17:00 New York time // Opening range the New York opening range (ORB), which side broke // first, and how past days went on this chart // Session opens a vertical line at the Tokyo, London and New York // open (Asia / London / New York), each in its own local time // Engulfing candles a triangle below a bullish and above a bearish // engulfing candle // Liquidity grabs a wick through the previous day's or a session's high // or low (and optionally a recent swing), with the close // back inside. Each level counts once: only the first // candle that trades through it. Marked in orange with // a dashed box around the wick // Day range how much of an average day is already used, the two // levels of a normal-sized day, round numbers, the // daily and 4-hour trend // // Clean by design: order blocks, gaps and liquidity zones are drawn short and // only where at least two things meet (confluence): an order block, a gap, a // liquidity zone from any timeframe, or a key level (PDH / PDL, PWH / PWL, // PMH / PML, a session high or low, equilibrium, the open swing high or low). // Confluence zones get a thick border. Premium / discount runs long, across // the whole swing range, as a light background with an equilibrium line. // Busy extras (internal swing labels, internal BOS / CHoCH lines, equal highs / // lows, engulfing marks, round numbers, ADR levels, opens, opening range) // start switched off. Built for the 1D, 1H and 15m charts. // Focus on price (on by default): the chart moves with price. Only the zones // and level tags nearest to price are drawn; what is far away is hidden until // price comes near it again. Only liquidity zones and premium / discount are // drawn long; everything else stays short. // // Candle colours: after a CHoCH the bodies are only lightly coloured in the new // direction; the full colour comes only with a BOS in that direction. // // Higher-timeframe values come from closed bars only, so they do not repaint. // Swing points are confirmed some bars late by nature. Zones and labels are // descriptions of what price did, not signals. // ============================================================================ // ---------------------------------------------------------------- inputs ---- // The settings page: the everyday switches first, section by section; all // fine-tuning sits in the last section. G_TRD = "โ”โ” ๐Ÿญ ๐—ง๐—ฅ๐—”๐——๐—˜ ๐—ฆ๐—˜๐—ง๐—จ๐—ฃ โ”โ” buy / sell zone, entry, SL, TP" G_FIL = "โ”โ” ๐Ÿฎ ๐—™๐—œ๐—Ÿ๐—ง๐—˜๐—ฅ โ”โ” show only what matters" G_MS = "โ”โ” ๐Ÿฏ ๐— ๐—”๐—ฅ๐—ž๐—˜๐—ง ๐—ฆ๐—ง๐—ฅ๐—จ๐—–๐—ง๐—จ๐—ฅ๐—˜ โ”โ”" G_LIQ = "โ”โ” ๐Ÿฐ ๐—Ÿ๐—œ๐—ค๐—จ๐—œ๐——๐—œ๐—ง๐—ฌ โ”โ”" G_FVG = "โ”โ” ๐Ÿฑ ๐—™๐—”๐—œ๐—ฅ ๐—ฉ๐—”๐—Ÿ๐—จ๐—˜ ๐—š๐—”๐—ฃ๐—ฆ โ”โ”" G_OB = "โ”โ” ๐Ÿฒ ๐—ข๐—ฅ๐——๐—˜๐—ฅ ๐—•๐—Ÿ๐—ข๐—–๐—ž๐—ฆ โ”โ”" G_PD = "โ”โ” ๐Ÿณ ๐—ฃ๐—ฅ๐—˜๐— ๐—œ๐—จ๐—  / ๐——๐—œ๐—ฆ๐—–๐—ข๐—จ๐—ก๐—ง โ”โ”" G_LV = "โ”โ” ๐Ÿด ๐—Ÿ๐—˜๐—ฉ๐—˜๐—Ÿ๐—ฆ โ”โ” day, week, month" G_SES = "โ”โ” ๐Ÿต ๐—ฆ๐—˜๐—ฆ๐—ฆ๐—œ๐—ข๐—ก๐—ฆ ๐—”๐—ก๐—— ๐—ข๐—ฃ๐—˜๐—ก๐—œ๐—ก๐—š ๐—ฅ๐—”๐—ก๐—š๐—˜ โ”โ”" G_EMA = "โ”โ” ๐Ÿญ๐Ÿฌ ๐—˜๐— ๐—” โ”โ”" G_ENG = "โ”โ” ๐Ÿญ๐Ÿญ ๐—˜๐—ก๐—š๐—จ๐—Ÿ๐—™๐—œ๐—ก๐—š ๐—–๐—”๐—ก๐——๐—Ÿ๐—˜๐—ฆ โ”โ”" G_ALR = "โ”โ” ๐Ÿญ๐Ÿฎ ๐—”๐—Ÿ๐—˜๐—ฅ๐—ง๐—ฆ โ”โ” one alert covers everything ticked" G_UI = "โ”โ” ๐Ÿญ๐Ÿฏ ๐——๐—œ๐—ฆ๐—ฃ๐—Ÿ๐—”๐—ฌ โ”โ”" G_COL = "โ”โ” ๐Ÿญ๐Ÿฐ ๐—–๐—ข๐—Ÿ๐—ข๐—จ๐—ฅ๐—ฆ โ”โ”" G_ADV = "โ”โ” ๐Ÿญ๐Ÿฑ ๐—™๐—œ๐—ก๐—˜-๐—ง๐—จ๐—ก๐—œ๐—ก๐—š โ”โ” only if needed" showBS = input.bool(true, "Buy zone and sell zone", group = G_TRD, tooltip = "BUY ZONE = the strongest support below price, SELL ZONE = the strongest resistance above. The % in the box says how likely it is to hold (confluence, how often such zones held on this chart, size of the liquidity, minus retests). When no zone is strong, the nearest one is used and marked weak, so both are always on the chart. A zone stays until price closes through it or it is taken.") bsTfIn = input.string("Any", "Buy / sell zone: only on charts of at least", options = ["Any", "15m", "1H", "4H", "D"], group = G_TRD, tooltip = "On small timeframes the zones are not strong enough to trade from, so the buy / sell zone, its entry, stop and target are only drawn on this timeframe and above.") entryIn = input.string("Middle of the zone", "Entry at", options = ["Middle of the zone", "Near edge of the zone"], group = G_TRD, tooltip = "Middle = a limit order at 50% of the zone: a smaller stop, a better reward to risk, but it fills less often. Near edge = fills more often, but the stop covers the whole zone.") slMult = input.float(1.5, "Stop loss: zone heights past the zone (1.5 = 1.5 x the zone)", minval = 0.0, maxval = 5.0, step = 0.1, group = G_TRD, tooltip = "The stop sits this many zone heights past the far side of the zone: below a buy zone, above a sell zone. Example: a zone 4 dollars high and 1.5 = the stop 6 dollars past the zone.") tpMode = input.string("Fixed R:R", "Take profit", options = ["Fixed R:R", "Nearest target"], group = G_TRD, tooltip = "Fixed R:R = one TP per side: the stop distance times the reward to risk below, measured from the zone edge (3 = three times the stop distance). Nearest target = the nearest real target (zone, liquidity, day / week / month or session high / low) that pays at least that reward to risk.") minRR = input.float(3.0, "Take profit: reward to risk (3 = 1:3)", minval = 1.0, maxval = 10.0, step = 0.5, group = G_TRD, tooltip = "3 = the TP is three times as far as the stop. With Nearest target: when no real target pays this much, no TP is drawn and the zone says so.") rndOn = input.bool(true, "SL and TP on round numbers", group = G_TRD, tooltip = "The stop moves out to the next round number past it (further from the zone, never closer). The take profit (fixed R:R) goes to the round number nearest to its spot, and the label shows the reward to risk that results.") rndStep = input.float(0.0, "Round number step (0 = automatic)", minval = 0, step = 1, group = G_TRD, tooltip = "Automatic = the roundest number (100, 50, 10, 5, 1) that moves the stop by at most a quarter of the risk, so the stop does not grow much. Or set a step by hand, e.g. 5 or 10.") showTp = input.bool(true, "Take profit line", group = G_TRD, tooltip = "One TP per side, as set above. The text shows the price and the reward to risk. A guide only, not advice.") focusOn = input.bool(true, "Focus on price: show only what is near price now", group = G_FIL, tooltip = "Moves with price. Only the nearest order blocks and gaps above and below price are drawn, the nearest liquidity zone above and below, and any zone price is inside. Of the level tags (PDH, PWH, sessions, swing levels and so on) only the nearest ones above and below. Everything further away is hidden until price comes near it again. Premium / discount and the session lines always stay.") minImp = input.int(3, "Hide weak zones: strength below (1-5)", minval = 1, maxval = 5, group = G_FIL, tooltip = "Every zone gets a strength from 1 to 5: how many things meet there (order block, gap, liquidity, key level, more timeframes) and, for liquidity, its size in $. Zones weaker than this are not drawn. 1 = show all, 3 = only solid zones, 4-5 = only the very strongest.") combZn = input.bool(true, "Zones on the same price become one box", group = G_FIL, tooltip = "Order blocks, gaps and liquidity zones that overlap are drawn as one box with one combined text, so nothing is drawn on top of each other. The strongest zone gives the colour; each extra zone makes the box a step brighter.") liqMinUsd = input.int(2, "Liquidity: always show zones of at least ($)", minval = 1, maxval = 5, group = G_FIL, tooltip = "Liquidity zones of this size are drawn even without other confluence. $ = this chart's own, $$ = 1H / 4H or a zone hit twice, $$$ = daily, $$$$ = weekly; each extra timeframe on the same price adds one. 1 = every liquidity zone.") fvgSig = input.float(0.6, "Significant gap: at least (x ATR)", minval = 0.0, maxval = 5.0, step = 0.1, group = G_FIL, tooltip = "A gap (or inverse gap) at least this tall is always drawn near price. Smaller ones are drawn only when they meet other confluence. 0 = every gap counts as significant.") focusZn = input.int(2, "Focus: order blocks and gaps kept above and below price", minval = 1, maxval = 10, group = G_FIL) focusLq = input.int(1, "Focus: liquidity zones kept above and below price", minval = 1, maxval = 10, group = G_FIL, tooltip = "The nearest liquidity above and below price: where price is likely to reach for next.") focusLv = input.int(2, "Focus: level tags kept above and below price", minval = 1, maxval = 20, group = G_FIL) showSw = input.bool(true, "Swing structure", group = G_MS) intMode = input.string("All", "Internal BOS / CHoCH lines", options = ["All", "Only with confluence", "Off"], group = G_MS, tooltip = "All = every internal BOS, BOS+, CHoCH and CHoCH+ gets its dashed line and its name. Only with confluence = only the breaks where price came from a support / resistance zone (order block, gap, liquidity) or swept liquidity in the last 10 candles, or the breaking candle left a gap or was a strong candle. Swing breaks (solid line, bold name) are always drawn. The last 30 of each are kept.") showInt = input.bool(true, "Internal structure", group = G_MS, tooltip = "The small swings inside the big ones (thin labels). Its BOS / CHoCH lines are the switch above: only the ones with confluence.") showPts = input.bool(true, "Swing point labels: swing (HH, HL, LH, LL, in bold)", group = G_MS) intPtsIn = input.string("Labels", "Swing points: internal (thin, smaller)", options = ["Labels", "Triangles", "Off"], group = G_MS, tooltip = "Swing points (the big structure) are written in bold. Internal points (the small structure) are written thin, smaller and a little faded, so the two are easy to tell apart. Triangles only marks the internal points (the name is in the tooltip of each triangle).") showPlus = input.bool(true, "Mark strong breaks as BOS+", group = G_MS, tooltip = "CHoCH+ = a change of character that was already hinted at by a failed swing: a lower high before a break down, or a higher low before a break up. BOS+ = a break of structure made by a strong candle. Both are labels of this script, not proof of a stronger signal.") showLvSw = input.bool(true, "Level lines at the open swing high and low", group = G_MS, tooltip = "Draws the last unbroken swing high and swing low as a level, labelled HH, LH, HL or LL. In brackets: what a break of that level would be (BOS, CHoCH or CHoCH+).") showSW = input.bool(true, "Strong / weak high and low", group = G_MS) showEQ = input.bool(true, "Equal highs and lows", group = G_MS) storyOn = input.bool(true, "Story line: joins the swing points", group = G_MS, tooltip = "Read the chart like a path: a line from swing point to swing point, up legs in the bullish colour and down legs in the bearish colour. The last part, dashed, runs from the last swing point to the current price.") storyTgt = input.bool(true, "Next target: arrow to the nearest liquidity in the trend direction", group = G_MS, tooltip = "In a bullish swing trend the nearest liquidity zone above price gets a bright, thick border and an arrow points to it; in a bearish trend the nearest one below. It shows where price is likely to reach for next, not a promise. A zone price is inside right now is drawn brighter too.") colorBars = input.bool(true, "Colour candles by the last break", group = G_MS, tooltip = "The candles take the colour of the last BOS / CHoCH drawn on the chart (swing or internal): BOS+ full colour, BOS strong, CHoCH+ medium, CHoCH pale. Bullish colour after a break up, bearish after a break down.") showLiq = input.bool(true, "Liquidity zones at swing highs and lows", group = G_LIQ) tf1On = input.bool(false, "", inline = "t1", group = G_LIQ) tf1 = input.timeframe("60", "Timeframe 1", inline = "t1", group = G_LIQ) tf1C = input.color(#7DFF66, "", inline = "t1", group = G_LIQ) tf2On = input.bool(true, "", inline = "t2", group = G_LIQ) tf2 = input.timeframe("240", "Timeframe 2", inline = "t2", group = G_LIQ) tf2C = input.color(#5CFF4A, "", inline = "t2", group = G_LIQ) tf3On = input.bool(true, "", inline = "t3", group = G_LIQ) tf3 = input.timeframe("D", "Timeframe 3", inline = "t3", group = G_LIQ) tf3C = input.color(#39FF14, "", inline = "t3", group = G_LIQ) tf4On = input.bool(true, "", inline = "t4", group = G_LIQ) tf4 = input.timeframe("W", "Timeframe 4", inline = "t4", group = G_LIQ) tf4C = input.color(#2BF000, "", inline = "t4", group = G_LIQ) liqUsd = input.bool(true, "Liquidity size in dollars ($ to $$$$$)", group = G_LIQ, tooltip = "More dollars = more liquidity resting there. One point for the timeframe (this chart $, 1H / 4H $$, daily $$$, weekly $$$$, monthly $$$$$), plus one for each extra touch at the same price (equal highs or lows, up to two), plus one for each other timeframe that has liquidity in the same place. At most $$$$$.") showSweep = input.bool(true, "Box around the wick of a sweep or liquidity grab candle", group = G_LIQ, tooltip = "A sweep is a candle whose wick goes through a liquidity zone and whose close is back inside (outlined in orange, plus a small dashed zone to the right). A liquidity grab is the same at a session or previous-day level (outlined in the grab colour). Master switch for both.") grabSess = input.bool(true, "Liquidity grabs at the session highs and lows", group = G_LIQ, tooltip = "Asia, London and New York high and low, once the session is over. Each level counts only once: the first candle that trades through it. If that candle closes back inside, it is a grab; if not, the level is simply broken and no later candle can grab it. Intraday charts up to 1 hour.") grabPd = input.bool(true, "Liquidity grabs at the previous day's high and low", group = G_LIQ, tooltip = "Same rule: only the first candle that trades through the level counts. Intraday charts only.") showGrab = input.bool(false, "Liquidity grabs at recent swing highs and lows", group = G_LIQ, tooltip = "Also marks a grab of small recent swings. Off by default: these happen on many wicks.") grabTxt = input.bool(true, "Grabs and sweeps: write 'Liquidity swept' (and the level) above / below", group = G_LIQ, tooltip = "PDH, PDL, Asia H, Asia L, Lon H, Lon L, NY H, NY L (Swing H / Swing L for the swing grabs).") showFVG = input.bool(true, "Fair value gaps", group = G_FVG) showIFVG = input.bool(true, "Inverse gaps (IFVG)", group = G_FVG, tooltip = "A gap that price closes through flips into an inverse gap: it takes the colour of the other side, gets a dashed border and the text IFVG, and stays as support or resistance until price closes back through it. With 'Full fill' or 'Half fill' below, a gap that is filled before any close through it is removed and does not flip.") gapDedupe = input.bool(true, "Broken gap on the same prices: show only the IFVG", group = G_FVG, tooltip = "When a gap is broken it turns into an inverse gap (IFVG). If a normal gap that works the same way (both resistance or both support) lies on the same prices, only the IFVG is drawn, so one area = one box.") fvgAll = input.bool(false, "Show every gap (FVG and IFVG)", group = G_FVG, tooltip = "Every fair value gap and inverse gap is drawn, also without confluence and also away from price (up to 'Gaps kept'). Off = gaps are filtered by strength and focus like the other zones.") gapLen = input.int(0, "Gap boxes: length (candles, 0 = to the common right edge)", minval = 0, maxval = 500, group = G_FVG, tooltip = "0 = every gap starts at the candles that formed it and ends on the same line as the order blocks and liquidity zones, so all boxes end in one line. A number = the gap runs only that many candles (an inverse gap: from its candles to the candle that broke it, then that many more), never past that line.") showOB = input.bool(true, "Order blocks", group = G_OB) showBrk = input.bool(true, "Breaker blocks", group = G_OB, tooltip = "When an order block is broken it stays on the chart as a breaker until price closes back through it. Turn off to delete broken order blocks.") obTfIn = input.string("1H", "Order blocks: only on charts of at least", options = ["Any", "15m", "1H", "4H", "D"], group = G_OB, tooltip = "Order blocks on small timeframes are mostly noise, so they are only looked for on this timeframe and above.") obStrong = input.bool(true, "Only strong blocks (where institutions likely stepped in)", group = G_OB, tooltip = "A block is kept only when the move away from it was real: it broke structure (always), it travelled far (size below), it left a fair value gap behind (displacement), and, if switched on, it came on above-average volume or the block first ran a previous high / low. Off = every block that broke structure.") showPD = input.bool(true, "Premium, equilibrium and discount zones", group = G_PD) pdStyle = input.string("Full halves", "Zone size", options = ["Full halves", "Thin bands"], group = G_PD, tooltip = "Full halves = the upper half of the range is premium, the lower half discount, with an equilibrium band in the middle. Thin bands = only a band at the top, the middle and the bottom.") showD = input.bool(true, "Previous day high and low", group = G_LV) showW = input.bool(true, "Previous week high and low", group = G_LV) showM = input.bool(true, "Previous month high and low", group = G_LV) showOpen = input.bool(false, "Open of the day, week and month", group = G_LV) adrTags = input.bool(false, "Levels of a normal-sized day (ADR High, ADR Low)", group = G_LV, tooltip = "ADR High = today's low plus the average day. ADR Low = today's high minus the average day. Price beyond them means today is already larger than an average day.") showRound = input.bool(false, "Round numbers (nearest above and below)", group = G_LV) showSess = input.bool(true, "Session highs and lows (Asia, London, New York)", group = G_SES, tooltip = "The high and low of each session, drawn as a line over the session with its name just above the high and just below the low. During a session its lines follow the running high and low.") sessDrawIn = input.string("Over the session + tag", "Session highs and lows are drawn", options = ["Over the session + tag", "Over the session", "Tag at the right edge"], group = G_SES, tooltip = "Over the session = a line above and a line below the session, from its first to its last candle, with the name on the line. Tag = a label at the right edge of the chart, like the other levels.") showOrb = input.bool(false, "Opening range (ORB)", group = G_SES, tooltip = "The high and low of the opening range, a mark on the first candle that closes outside it, and which side broke first. The two times below are always New York time.") orbMark = input.bool(true, "Arrow on the candle that breaks the opening range", group = G_SES, tooltip = "The direction is always written on the opening range box.") showOpn = input.bool(true, "Session open lines", group = G_SES, tooltip = "A vertical line at the Tokyo, London and New York open. Each open is set in its own local time, so daylight-saving changes are handled automatically. The line is put on the candle that starts nearest to the open (for example 09:02 for a 09:00 open). The labels stay at the height of the highest candle on screen.") tyoOn = input.bool(true, "Open line: Asia (Tokyo)", group = G_SES) lonOn = input.bool(true, "Open line: London", group = G_SES) nycOn = input.bool(true, "Open line: New York", group = G_SES) emaOn = input.bool(false, "EMA 1", inline = "e1", group = G_EMA) ema1Len = input.int(50, "", minval = 1, maxval = 1000, inline = "e1", group = G_EMA) ema1C = input.color(#FFB000, "", inline = "e1", group = G_EMA) ema2On = input.bool(false, "EMA 2", inline = "e2", group = G_EMA) ema2Len = input.int(200, "", minval = 1, maxval = 1000, inline = "e2", group = G_EMA) ema2C = input.color(#2E9BFF, "", inline = "e2", group = G_EMA) emaW = input.int(2, "EMA line width", minval = 1, maxval = 4, group = G_EMA) showEng = input.bool(true, "Engulfing candles", group = G_ENG, tooltip = "A triangle below a bullish engulfing candle and above a bearish one, in the bullish and bearish colour. Only the latest few are kept (setting below).") engMax = input.int(3, "Show the last ... engulfing candles", minval = 1, maxval = 50, group = G_ENG, tooltip = "Only the latest engulfing candles keep their mark (3 = the last three); older marks are removed.") engMode = input.string("Body engulfs body", "An engulfing candle is", options = ["Body engulfs body", "Close beyond the previous wick"], group = G_ENG, tooltip = "Body engulfs body = the candle turns against the previous candle and its body covers the whole previous body. Close beyond the previous wick = the same, and it also closes above the previous high (bullish) or below the previous low (bearish).") engSzIn = input.string("Normal", "Mark size", options = ["Tiny", "Small", "Normal", "Large"], group = G_ENG) alertsOn = input.bool(true, "Alerts on (how to set: see the โ“˜)", group = G_ALR, tooltip = "1) Tick below what you want to be told about. 2) On the chart click Alert (the clock with +) or press Alt + A. 3) Condition: Price Action Toolkit, then 'Any alert() function call'. 4) Expiration: open-ended, then Create. That one alert covers everything ticked here, on this symbol and timeframe. TradingView saves the settings with the alert: after you change a tick here, delete the alert and create it again. Every message starts with symbol and timeframe, e.g. 'XAUUSD 15m ยท ...'.") alZone = input.bool(true, "Price reaches the buy or sell zone", group = G_ALR, tooltip = "Once when price first touches the zone, with the zone, its strength, SL and TP in the message.") alNear = input.bool(true, "Heads-up: price comes close to the buy or sell zone", group = G_ALR, tooltip = "Once when price comes within 1 ATR (one average candle) of the zone, so you have time to get ready. It can tell you again after price has moved away by 2 ATR.") alTpSl = input.bool(true, "Take profit or stop loss reached", group = G_ALR, tooltip = "After price has touched the buy or sell zone: once when it reaches that zone's TP or its SL.") alSweep = input.bool(true, "Liquidity sweep or grab", group = G_ALR) alStruct = input.bool(true, "Swing BOS / CHoCH", group = G_ALR) alInt = input.bool(false, "Internal BOS / CHoCH", group = G_ALR) alGap = input.bool(false, "A gap flips into an IFVG", group = G_ALR) alOb = input.bool(false, "Order block broken", group = G_ALR) alOrb = input.bool(true, "Opening range broken", group = G_ALR) opnAlert = input.bool(false, "Session opens (Asia, London, New York)", group = G_ALR) engAlert = input.bool(false, "Engulfing candles", group = G_ALR) txtIn = input.string("Small", "Text size", options = ["Tiny", "Small", "Normal"], group = G_UI) lnFade = input.int(80, "Lines: transparency (0 = solid, 95 = almost invisible)", minval = 0, maxval = 95, group = G_UI, tooltip = "For the guide lines: session opens, level tags, premium / discount. BOS / CHoCH, equal highs / lows, session highs and lows, entry, stop loss and take profit keep their full colour.") cBull = input.color(#FFF44F, "Bullish", group = G_COL) cBear = input.color(#ffffff, "Bearish", group = G_COL) cFvgUp = input.color(#FFF44F, "Fair value gap up", group = G_COL) cFvgDn = input.color(#ababab, "Fair value gap down", group = G_COL) cLiq = input.color(#8CFF6B, "Liquidity zones (this chart)", group = G_COL) cPrem = input.color(#ababab, "Premium", group = G_COL) cEq = input.color(#5b6f88, "Equilibrium", group = G_COL) cDisc = input.color(#969549, "Discount", group = G_COL) cLevel = input.color(#5AB8FF, "Previous highs and lows", group = G_COL) cAsia = input.color(color.new(color.white, 25), "Asia session", group = G_COL) cLon = input.color(#5AB8FF, "London session", group = G_COL) cNy = input.color(#FFF44F, "New York session", group = G_COL) cGrab = input.color(#FF3D3D, "Liquidity sweeps and grabs", group = G_COL) cGrey = input.color(#8a8a8a, "Taken zones and small text", group = G_COL) cBuy = input.color(#FFF44F, "Buy zone", group = G_COL) cSell = input.color(#FFFFFF, "Sell zone", group = G_COL) cStop = input.color(#FF3B5C, "Stop loss line", group = G_COL) cTp = input.color(#85BB65, "Take profit line", group = G_COL) confMode = input.string("Only confluence", "Trade / filter ยท Order blocks, gaps and liquidity zones", options = ["Only confluence", "Highlight confluence", "All"], group = G_ADV, tooltip = "Only confluence = a zone is drawn only where at least the number of things below meet. Highlight confluence = every zone is drawn, the ones without confluence very faint. All = every zone, as before. Confluence zones always get a thick border.") confMin = input.int(2, "Trade / filter ยท Confluence: things that must meet", minval = 2, maxval = 4, group = G_ADV, tooltip = "Counted kinds: order block (or breaker), gap (FVG or IFVG), liquidity zone (any timeframe), key level. A liquidity zone made of two or more timeframes counts one extra.") confLv = input.bool(true, "Trade / filter ยท Key levels count as confluence", group = G_ADV, tooltip = "Previous day, week and month high and low, the latest Asia, London and New York high and low, equilibrium, and the open swing high and low.") innerIn = input.string("Off", "Trade / filter ยท Combined box: show each zone inside it", options = ["Gaps only", "All", "Off"], group = G_ADV, tooltip = "Inside a combined box each zone keeps its own band at its exact prices, with its short name (FVG, IFVG, OB, BRK, 1H, 4H, D, W, Liq) at its left end. Gaps only = only for fair value gaps and inverse gaps.") bsWeak = input.bool(true, "Trade / filter ยท No strong zone on a side: use the nearest liquidity (marked 'weak')", group = G_ADV, tooltip = "Without this, a side with no strong zone simply has no buy / sell zone.") bsHtf = input.bool(true, "Trade / filter ยท Buy / sell zone: only higher-timeframe zones", group = G_ADV, tooltip = "Only a zone that is a higher-timeframe liquidity zone (1H, 4H, D, W ...), or that lies on one, can become the buy or sell zone. A zone made only on the chart timeframe does not qualify.") bsAtr = input.float(15.0, "Trade / filter ยท Buy / sell zone: look this far from price (x ATR)", minval = 1.0, maxval = 100.0, step = 1.0, group = G_ADV) tpBuf = input.float(0.2, "Trade / filter ยท Take profit: this far in front of the target (x ATR)", minval = 0.0, maxval = 5.0, step = 0.1, group = G_ADV) swLen = input.int(50, "Structure ยท Swing length", minval = 10, maxval = 500, group = G_ADV) intLen = input.int(3, "Structure ยท Internal length", minval = 2, maxval = 50, group = G_ADV, tooltip = "How many candles on each side make an internal swing point. 3 = many internal CHoCH / BOS (the default). 5 = fewer, bigger ones. 2 = very many tiny ones (each line only 2-3 candles long).") msMode = input.string("CHoCH and BOS", "Structure ยท Show", options = ["CHoCH and BOS", "CHoCH only", "BOS only"], group = G_ADV) breakBy = input.string("Close", "Structure ยท A level is broken by", options = ["Close", "Wick"], group = G_ADV) ptsMax = 6 ptsInMax = 10 showLvInt = input.bool(false, "Structure ยท Level lines at the open internal high and low", group = G_ADV) plusAtr = input.float(1.5, "Structure ยท BOS+ : the breaking candle is at least (x ATR)", minval = 0.5, step = 0.1, group = G_ADV) msMax = 30 storyLen = 10 storyMax = 6 storyTr = 35 obLen = input.int(4, "Order blocks ยท Swing length used to find order blocks", minval = 2, maxval = 50, group = G_ADV, tooltip = "Order blocks have their own swing length, separate from the internal and swing structure.") obMit = input.string("Close", "Order blocks ยท Order block is broken by", options = ["Close", "Wick", "Close past the middle"], group = G_ADV) obBody = input.bool(false, "Order blocks ยท Use candle body only", group = G_ADV) obMid = false obOverlap = true obMax = input.int(3, "Order blocks ยท Blocks kept", minval = 1, maxval = 30, group = G_ADV) obDispAtr = input.float(2.0, "Order blocks ยท Strong: the move away from the block is at least (x ATR)", minval = 0.5, maxval = 10.0, step = 0.25, group = G_ADV) obNeedGap = input.bool(true, "Order blocks ยท Strong: the move left a fair value gap (displacement)", group = G_ADV) obNeedVol = false obNeedSwp = false ifvgAlways = input.bool(false, "Gaps ยท Always show inverse gaps (IFVG)", group = G_ADV, tooltip = "An inverse gap is shown even without other confluence, and the focus keeps the nearest one above and below price on top of the other gaps and order blocks. Off = inverse gaps are filtered like every other zone.") fvgThr = input.float(0.25, "Gaps ยท Minimum size (x ATR)", minval = 0.0, step = 0.05, group = G_ADV) fvgMit = input.string("Close through", "Gaps ยท Gap is removed on", options = ["Full fill", "Half fill", "Close through"], group = G_ADV) fvgMax = input.int(4, "Gaps ยท Gaps kept", minval = 1, maxval = 30, group = G_ADV) fvgExt = 0 liqLen = input.int(20, "Liquidity ยท Swing length", minval = 3, maxval = 200, group = G_ADV) liqTol = input.float(0.15, "Liquidity ยท Merge highs or lows closer than (x ATR)", minval = 0.0, step = 0.05, group = G_ADV) liqMax = input.int(3, "Liquidity ยท Zones kept", minval = 2, maxval = 40, group = G_ADV) zoneThick = input.float(1.0, "Liquidity ยท Zone thickness at most (x ATR of its timeframe)", minval = 0.1, step = 0.1, group = G_ADV, tooltip = "A zone runs from the wick to the candle body, but never thicker than this. Also used for the higher-timeframe zones.") liqKeep = input.string("Delete", "Liquidity ยท When a zone is taken", options = ["Grey out", "Delete"], group = G_ADV) grabLen = 5 grabBox = false grabExt = 3 grabMax = 6 wickPad = 0.04 grabGap = 0.3 sweepBox = false sweepExt = 4 liqFull = true liqBack = 0 mergeZn = true eqLen = 3 eqTol = 0.2 mtfLen = input.int(5, "HTF liquidity ยท Swing length (bars of that timeframe)", minval = 2, maxval = 20, group = G_ADV) mtfTol = input.float(0.25, "HTF liquidity ยท Merge highs or lows closer than (x ATR of that timeframe)", minval = 0.0, step = 0.05, group = G_ADV) mtfMax = input.int(2, "HTF liquidity ยท Zones kept per timeframe", minval = 2, maxval = 20, group = G_ADV) showOdds = input.bool(false, "Odds ยท Show up / down odds on the zone labels", group = G_ADV, tooltip = "Counted on the bars loaded on this chart. Each time a liquidity zone was taken, the script checks which came first: price moving the distance below back away from the zone edge, or the same distance further through it. The zone label shows how often price went up first and how often down first. These are past frequencies on a small sample, not win probabilities.") oddsAtr = 1.0 oddsBars = 48 oddsMin = 10 pdPct = input.float(10.0, "Premium / discount ยท Equilibrium band (thin bands: every band) in % of the range", minval = 1.0, maxval = 50.0, step = 1.0, group = G_ADV) pdW = 8 pdBars = 0 pdDim = 60 pdDimC = 90 tagIn = input.string("Soft", "Levels ยท Level labels", options = ["Soft", "Filled", "Plain text"], group = G_ADV, tooltip = "Soft = a lightly tinted label with coloured text. Filled = a solid label. Plain text = text only. Applies to all level tags at the right edge.") lvMergeAtr = input.float(0.5, "Levels ยท Levels closer than this share one label (x ATR)", minval = 0.0, step = 0.05, group = G_ADV) tagPad = 18 sessW = 2 sessGap = 0.5 sessKeep = 1 asStartIn = input.string("Tokyo open", "Sessions ยท Asia session starts at", options = ["Trading day open (18:00 New York)", "Tokyo open"], group = G_ADV, tooltip = "Trading day open = Asia starts when gold reopens after the daily break, 18:00 New York time (00:00 in Zurich), so the first hours of the day count. Tokyo open = Asia starts at the Tokyo open set further down (Session opens) (02:00 in Zurich in summer).") nyEndH = input.int(17, "Sessions ยท New York session ends at: hour (New York time)", minval = 10, maxval = 23, group = G_ADV, tooltip = "17:00 New York time is the end of the trading day for gold (23:00 in Zurich). The stock market closes at 16:00.") nyEndM = input.int(0, "Sessions ยท New York session ends at: minute", minval = 0, maxval = 59, group = G_ADV) orbS = input.session("0930-0945", "Sessions ยท Opening range (New York time)", group = G_ADV) orbDayS = input.session("0930-1600", "Sessions ยท Breaks are tracked during (New York time)", group = G_ADV) opnMaxTf = 60 opnCol = color.new(color.white, 40) opnStyIn = "Dashed" opnWidth = 1 opnTxtC = color.white opnSide = "Right" opnSzIn = "Small" opnKeep = 6 tyoTxt = "Asia" tyoH = 9 tyoM = 0 lonTxt = "London" lonH = 8 lonM = 0 nycTxt = "New York" nycH = 9 nycM = 30 engMin = input.float(0.0, "Engulfing ยท Body at least (x ATR, 0 = any size)", minval = 0.0, step = 0.1, group = G_ADV) engGap = 0.3 adrLen = input.int(14, "Day ยท Average day: number of days", minval = 3, maxval = 60, group = G_ADV) roundStep = input.float(50.0, "Day ยท Round numbers: step (price)", minval = 0.0001, group = G_ADV) lbGap = 4 txtGap = 0.25 padBars = input.int(0, "Display ยท Boxes end, and the buy / sell column starts, this many candles past the last candle", minval = 0, maxval = 100, group = G_ADV, tooltip = "Smaller = everything on the right (box ends, BUY / SELL ZONE, SL, TP, level tags, premium / discount) sits closer to the candles, so more of the chart shows price. Bigger = more room between the candles and the right side.") leftBars = input.int(0, "Display ยท Zones are drawn back at most (bars, 0 = from their origin)", minval = 0, maxval = 5000, group = G_ADV, tooltip = "0 = every zone starts at the candle where it formed. A number keeps zones shorter. Premium / discount is not limited by this.") drawBack = input.int(3000, "Speed ยท Marks are drawn on the last (candles)", minval = 300, maxval = 20000, step = 500, group = G_ADV, tooltip = "Swing names, BOS / CHoCH lines, engulfing arrows, sweep labels, EQH / EQL, the story line and session lines are only drawn on this many recent candles. Older ones would be deleted anyway (only the latest few are kept), so this saves calculation time without changing what you see. Zones, liquidity, structure and the % are still worked out on every candle. Raise it only if a mark you expect is missing.") nearAtr = input.float(10.0, "Display ยท Hide liquidity zones further from price than (x ATR, 0 = show all)", minval = 0.0, step = 1.0, group = G_ADV) // ----------------------------------------------------------- shared values --- float atr14 = ta.atr(14) // marks are only drawn on recent candles (saves time; older marks would be deleted anyway) bool drawNow = last_bar_index - bar_index <= drawBack bool intConf = intMode == "Only with confluence" // confluence for a break: [0] = last candle that swept a low or traded in a support zone, // [1] = the same for a high / resistance zone. A break up within 10 candles of [0] (or // with a gap or a strong candle) has confluence; the same for a break down with [1]. var array znHit = array.new(2, -100000) bool cfUp = false bool cfDn = false float atr200 = ta.atr(200) float atrBig = na(atr200) ? atr14 : atr200 float volAvg = ta.sma(volume, 20) float tGap = txtGap * nz(atr14) float sGap = sessGap * nz(atr14) float gGap = grabGap * nz(atr14) string szTxt = txtIn == "Tiny" ? size.tiny : (txtIn == "Small" ? size.small : size.normal) bool wantBos = msMode != "CHoCH only" bool wantChoch = msMode != "BOS only" bool byClose = breakBy == "Close" float barRng = high - low bool strongUp = showPlus and barRng >= plusAtr * atr14 and close > open and close >= low + barRng * 0.6 int tfMs = timeframe.in_seconds() * 1000 bool strongDn = showPlus and barRng >= plusAtr * atr14 and close < open and close <= high - barRng * 0.6 bool sessOver = sessDrawIn != "Tag at the right edge" bool sessTag = sessDrawIn != "Over the session" bool pdFull = pdStyle == "Full halves" // layout right of the last candle: zones up to padBars, then the premium / // discount column, then the level tags int bsW = 7 int pdColL = padBars + bsW + 1 int pdColR = pdColL + pdW int tagBars = showPD ? math.max(tagPad, pdColR + 2) : tagPad string opnLnSty = opnStyIn == "Dotted" ? line.style_dotted : (opnStyIn == "Dashed" ? line.style_dashed : line.style_solid) string opnSz = opnSzIn == "Tiny" ? size.tiny : (opnSzIn == "Normal" ? size.normal : size.small) // "label_right" puts the text on the left of the line, "label_left" on the right string opnLbSty = opnSide == "Left" ? label.style_label_right : label.style_label_left // engulfing candles: the candle turns against the previous one and its body covers the previous body float engBody = math.abs(close - open) float engSlack = nz(atr14) * 0.05 bool engBig = engMin <= 0 or engBody >= engMin * nz(atr14) bool engWick = engMode == "Close beyond the previous wick" bool engUp = showEng and close > open and close[1] < open[1] and close > open[1] and open <= close[1] + engSlack and (not engWick or close > high[1]) and engBig bool engDn = showEng and close < open and close[1] > open[1] and close < open[1] and open >= close[1] - engSlack and (not engWick or close < low[1]) and engBig // ------------------------------------------------------------------ types --- // state: 0 active, 1 flipped (breaker or inverse gap), 2 taken and greyed type Zone box bx float top float bot int dir int state int hits string tag line ml color col string t1 string t2 int t0 bool off = false int tfs = 1 int pts = 0 string txt = "" int imp = 0 int knd = 0 int gid = -1 int fb = na // a liquidity zone that was just taken and is waiting for its outcome type Pend float edge int dir float dist int bar0 int grp var array obs = array.new() var array fvgs = array.new() var array liq = array.new() var array mtf1 = array.new() var array mtf2 = array.new() var array mtf3 = array.new() var array mtf4 = array.new() var array swLines = array.new() var array