ajiim

🎁

Aug 28th, 2026
1,602
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 32.49 KB | None | 0 0
  1. //@version=5
  2. indicator("Inverse Fair Value Gap° [Pro+]", shorttitle="IFVG° [Pro+]", overlay=true,
  3. max_lines_count=500, max_boxes_count=500, max_labels_count=500, max_bars_back=5000)
  4.  
  5. type SwingPoint
  6. float price
  7. int barIndex
  8. bool taken = false
  9. bool visualsDrawn = false
  10. line poolLine = na
  11. label poolLabel = na
  12. label sweepMarker = na
  13.  
  14. type RawFVG
  15. float top
  16. float bottom
  17. int leftBar
  18. int rightBar
  19. bool isBullishOrigin
  20. bool sweepContext = false
  21. float originSweepPrice = na
  22. int originSweepBar = na
  23. box ghostBox = na
  24. label ghostLabel = na
  25.  
  26. type IFVG_Setup
  27. float top
  28. float bottom
  29. float mid
  30. int leftBar
  31. int rightBar
  32. bool isBullish
  33. string state = "INVERTED"
  34. float targetPrice = na
  35. int targetBarIndex = na
  36. bool targetHit = false
  37. float invalidLevel = na
  38. float invalidSwingLevel = na
  39. int invalidBarIndex = na
  40. float entryPrice = na
  41. box visualBox = na
  42. line ceLine = na
  43. line targetLine = na
  44. label tagLabel = na
  45. label targetLabel = na
  46. line entryLine = na
  47. int creationBar = na
  48. int inversionBar = na
  49.  
  50. grpAlerts = "ALERTS SETTINGS"
  51. i_alertOnInversion = input.bool(true, "Alert on IFVG Inversion?", group=grpAlerts)
  52. i_alertOnTarget = input.bool(true, "Alert on Target Hit?", group=grpAlerts)
  53. i_alertOnInvalid = input.bool(false, "Alert on Invalidated?", group=grpAlerts)
  54. i_alertOnSweep = input.bool(false, "Alert on Liquidity Sweep?",group=grpAlerts)
  55.  
  56. grpGeneral = "GENERAL SETTINGS"
  57. i_maxSetups = input.int(15, "Max Active Setups", minval=1, maxval=60, group=grpGeneral, tooltip="Maximum number of confirmed IFVG setups kept on chart.")
  58. i_showInvalidated = input.bool(false, "Show Invalidated Setups", group=grpGeneral, tooltip="If disabled, stopped-out trades are completely removed.")
  59. i_bias = input.string("Neutral", "Bias", options=["Neutral", "Bullish", "Bearish"], group=grpGeneral)
  60. i_fvgThresholdMode = input.string("All", "FVG Minimum Threshold", options=["All", "Ticks", "Percent (ATR)"], group=grpGeneral, inline="thresh")
  61. i_fvgThresholdTicks = input.float(0.3, "", minval=0.0, step=0.5, group=grpGeneral, inline="thresh")
  62.  
  63. grpSession = "SESSION FILTERS"
  64. i_useSession = input.bool(false, "Enable Session Filter?", group=grpSession)
  65. i_sessionTZ = input.string("UTC-4", "Timezone", group=grpSession, inline="s1")
  66. i_session1 = input.session("0200-0500", "", group=grpSession, inline="s1")
  67. i_useSession2 = input.bool(false, "Enable 2nd Session?", group=grpSession, inline="s2")
  68. i_session2 = input.session("0800-1200", "", group=grpSession, inline="s2")
  69. i_macrosOnly = input.bool(false, "Macros Only (First/Last 10m)", group=grpSession)
  70.  
  71. grpLiquidity = "MODEL & LIQUIDITY SETTINGS"
  72. i_liqTF = input.timeframe("15", "Liquidity Timeframe", group=grpLiquidity, inline="liq1")
  73. i_swingLen = input.int(15, "Swing Lookback", minval=2, maxval=50, group=grpLiquidity, inline="liq1")
  74. i_useFvgFilterTF = input.bool(false, "HTF FVG Filter?", group=grpLiquidity, inline="liq2")
  75. i_fvgFilterTF = input.timeframe("60", "", group=grpLiquidity, inline="liq2")
  76. i_requireSweep = input.bool(true, "Require Liquidity Sweep?", group=grpLiquidity, inline="swp")
  77. i_sweepWindow = input.int(8, "Valid Window", minval=1, maxval=200, group=grpLiquidity, inline="swp")
  78. i_invalidCond = input.string("Close", "Invalidation Mode", options=["Close", "Swing"], group=grpLiquidity)
  79. i_maxSwings = input.int(8, "Max Swings Tracked", minval=5, maxval=100, group=grpLiquidity)
  80. i_rawExpiry = input.int(80, "Raw FVG Expiry (bars)", minval=10, maxval=500, group=grpLiquidity)
  81.  
  82. grpStyle = "DISPLAY & STYLING"
  83. i_labelSize = input.string("Small", "Global Label Size", options=["Tiny", "Small", "Normal"], group=grpStyle)
  84. i_showSweepLines = input.bool(true, "Sweep Lines", group=grpStyle, inline="swp_disp")
  85. i_showSweepMarkers = input.bool(true, "Markers", group=grpStyle, inline="swp_disp")
  86. i_liqPoolColor = input.color(color.new(#4e5057, 0), "", group=grpStyle, inline="swp_disp")
  87. i_showGhost = input.bool(true, "Potential IFVG (Ghost)", group=grpStyle, inline="ghst")
  88. i_ghostColor = input.color(color.new(#9598A1, 0), "", group=grpStyle, inline="ghst")
  89. i_ghostTransparency = input.int(92, "", minval=0, maxval=100, group=grpStyle, inline="ghst")
  90. i_showBoxes = input.bool(true, "Show IFVG Boxes", group=grpStyle)
  91. i_extendBoxes = input.bool(false, "Extend Active Boxes", group=grpStyle)
  92. i_borderWidth = input.int(0, "Box Border Width", minval=0, maxval=3, group=grpStyle)
  93. i_bullColor = input.color(color.new(#2962FF, 0), "Bullish (+IFVG)", group=grpStyle, inline="bll")
  94. i_boxTransparency = input.int(85, "Transp.", minval=0, maxval=100, group=grpStyle, inline="bll")
  95. i_bearColor = input.color(color.new(#FF5252, 0), "Bearish (-IFVG)", group=grpStyle, inline="brr")
  96. i_invalidColor = input.color(color.new(#787B86, 0), "Invalid Color", group=grpStyle, inline="inv")
  97. i_invalidTransparency = input.int(90, "Transp.", minval=0, maxval=100, group=grpStyle, inline="inv")
  98. i_showCE = input.bool(false, "Show Midpoint (CE)", group=grpStyle)
  99.  
  100. grpTargets = "TARGET PROJECTIONS"
  101. i_showTargets = input.bool(true, "Show Target Lines", group=grpTargets)
  102. i_targetLineStyle = input.string("Dotted", "Style", options=["Dotted", "Dashed", "Solid"], group=grpTargets, inline="tgt")
  103. i_stopAtTarget = input.bool(true, "Stop Exactly at Hit", group=grpTargets, inline="tgt")
  104.  
  105. grpTable = "DASHBOARD TABLE"
  106. i_showTable = input.bool(true, "Show Dashboard?", group=grpTable, inline="tbl1")
  107. i_tablePosition = input.string("Bottom Center", "Location", options=["Bottom Left", "Bottom Center", "Bottom Right", "Top Right"], group=grpTable, inline="tbl1")
  108. i_tableTextSize = input.string("Tiny", "Text Size", options=["Tiny", "Small", "Normal"], group=grpTable, inline="tbl2")
  109. i_tableBgTransp = input.int(100, "Background Transp.", minval=0, maxval=100, group=grpTable, inline="tbl2")
  110.  
  111. var array<SwingPoint> bslArray = array.new<SwingPoint>()
  112. var array<SwingPoint> sslArray = array.new<SwingPoint>()
  113. var array<RawFVG> rawFvgArray = array.new<RawFVG>()
  114. var array<IFVG_Setup> activeIFVGs = array.new<IFVG_Setup>()
  115.  
  116. var int prevLiqTime = na
  117. var int barsSinceBSLSweep = 9999
  118. var int barsSinceSSLSweep = 9999
  119. var int ghostGracePeriod = 150
  120.  
  121. f_tablePosOf(_pos) =>
  122. switch _pos
  123. "Bottom Left" => position.bottom_left
  124. "Bottom Center" => position.bottom_center
  125. "Bottom Right" => position.bottom_right
  126. "Top Right" => position.top_right
  127. => position.bottom_right
  128.  
  129. var table hud = table.new(f_tablePosOf(i_tablePosition), 1, 6,
  130. bgcolor = color.new(color.black, i_tableBgTransp), border_width = 0, frame_width = 0)
  131.  
  132. f_secReq(_tf, _src) =>
  133. request.security(syminfo.tickerid, _tf, _src, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
  134.  
  135. f_labelSize(_s) =>
  136. _s == "Tiny" ? size.tiny : _s == "Small" ? size.small : size.normal
  137.  
  138. f_lineStyle() =>
  139. i_targetLineStyle == "Dotted" ? line.style_dotted : i_targetLineStyle == "Dashed" ? line.style_dashed : line.style_solid
  140.  
  141. f_dirColor(_isBullish, _ghost, _invalid) =>
  142. _invalid ? i_invalidColor : _ghost ? i_ghostColor : (_isBullish ? i_bullColor : i_bearColor)
  143.  
  144. f_deleteSwingVisuals(SwingPoint sp) =>
  145. if not na(sp.poolLine)
  146. line.delete(sp.poolLine)
  147. if not na(sp.poolLabel)
  148. label.delete(sp.poolLabel)
  149. if not na(sp.sweepMarker)
  150. label.delete(sp.sweepMarker)
  151.  
  152. f_trimSwingArray(arr, maxLen) =>
  153. while array.size(arr) > maxLen
  154. SwingPoint oldest = array.shift(arr)
  155. f_deleteSwingVisuals(oldest)
  156.  
  157. f_deleteGhostVisuals(RawFVG rf) =>
  158. if not na(rf.ghostBox)
  159. box.delete(rf.ghostBox)
  160. if not na(rf.ghostLabel)
  161. label.delete(rf.ghostLabel)
  162.  
  163. f_deleteSetupVisuals(IFVG_Setup s) =>
  164. if not na(s.visualBox)
  165. box.delete(s.visualBox)
  166. if not na(s.ceLine)
  167. line.delete(s.ceLine)
  168. if not na(s.targetLine)
  169. line.delete(s.targetLine)
  170. if not na(s.entryLine)
  171. line.delete(s.entryLine)
  172. if not na(s.tagLabel)
  173. label.delete(s.tagLabel)
  174. if not na(s.targetLabel)
  175. label.delete(s.targetLabel)
  176.  
  177. f_trimActiveIFVGs(maxLen) =>
  178. while array.size(activeIFVGs) > maxLen
  179. IFVG_Setup oldest = array.shift(activeIFVGs)
  180. f_deleteSetupVisuals(oldest)
  181.  
  182. f_passesThreshold(_top, _bottom, _atr) =>
  183. gapSize = _top - _bottom
  184. bool pass = true
  185. if i_fvgThresholdMode == "Ticks"
  186. pass := gapSize >= i_fvgThresholdTicks * syminfo.mintick
  187. else if i_fvgThresholdMode == "Percent (ATR)"
  188. pass := gapSize >= i_fvgThresholdTicks * _atr
  189. pass
  190.  
  191. f_nearestUntakenBSL(_fromBar) =>
  192. float result = na
  193. if array.size(bslArray) > 0
  194. for i = array.size(bslArray) - 1 to 0
  195. SwingPoint sp = array.get(bslArray, i)
  196. if not sp.taken and sp.barIndex < _fromBar
  197. result := sp.price
  198. break
  199. result
  200.  
  201. f_nearestUntakenSSL(_fromBar) =>
  202. float result = na
  203. if array.size(sslArray) > 0
  204. for i = array.size(sslArray) - 1 to 0
  205. SwingPoint sp = array.get(sslArray, i)
  206. if not sp.taken and sp.barIndex < _fromBar
  207. result := sp.price
  208. break
  209. result
  210.  
  211. f_thresholdText() =>
  212. switch i_fvgThresholdMode
  213. "All" => "All"
  214. "Ticks" => str.tostring(i_fvgThresholdTicks) + " Ticks"
  215. "Percent (ATR)" => str.tostring(i_fvgThresholdTicks) + "x ATR"
  216. => "All"
  217.  
  218. f_sessionText() =>
  219. string txt = ""
  220. if i_useSession
  221. txt := i_session1
  222. if i_useSession2
  223. txt += " | " + i_session2
  224. txt
  225.  
  226. f_dateText() =>
  227. str.format("{0,date,dd MMM yyyy}", time)
  228.  
  229. f_biasAllows(_isBullish) =>
  230. switch i_bias
  231. "Bullish" => _isBullish
  232. "Bearish" => not _isBullish
  233. => true
  234.  
  235. f_findExactHighBar(float val) =>
  236. int targetBar = bar_index
  237. float minDiff = 1e10
  238. for i = 0 to 150
  239. diff = math.abs(high[i] - val)
  240. if diff < minDiff
  241. minDiff := diff
  242. targetBar := bar_index - i
  243. if diff == 0
  244. break
  245. targetBar
  246.  
  247. f_findExactLowBar(float val) =>
  248. int targetBar = bar_index
  249. float minDiff = 1e10
  250. for i = 0 to 150
  251. diff = math.abs(low[i] - val)
  252. if diff < minDiff
  253. minDiff := diff
  254. targetBar := bar_index - i
  255. if diff == 0
  256. break
  257. targetBar
  258.  
  259. f_formatTimeframe(string _tf) =>
  260. float val = str.tonumber(_tf)
  261. string result = _tf
  262. if not na(val)
  263. if val >= 1440
  264. result := str.tostring(math.round(val / 1440)) + "D"
  265. else if val >= 60
  266. result := str.tostring(math.round(val / 60)) + "H"
  267. else
  268. result := str.tostring(math.round(val)) + "m"
  269. else
  270. if _tf == "D" or _tf == "1D"
  271. result := "1D"
  272. else if _tf == "W" or _tf == "1W"
  273. result := "1W"
  274. else if _tf == "M" or _tf == "1M"
  275. result := "1M"
  276. result
  277.  
  278. liq_high = f_secReq(i_liqTF, high)
  279. liq_low = f_secReq(i_liqTF, low)
  280. liq_time = f_secReq(i_liqTF, time)
  281.  
  282. bool isNewLiqBar = liq_time != prevLiqTime
  283. prevLiqTime := liq_time
  284.  
  285. ffilt_high = i_useFvgFilterTF ? f_secReq(i_fvgFilterTF, high) : na
  286. ffilt_low = i_useFvgFilterTF ? f_secReq(i_fvgFilterTF, low) : na
  287. ffilt_time = i_useFvgFilterTF ? f_secReq(i_fvgFilterTF, time) : na
  288.  
  289. pivotHighVal = ta.pivothigh(liq_high, i_swingLen, i_swingLen)
  290. pivotLowVal = ta.pivotlow(liq_low, i_swingLen, i_swingLen)
  291.  
  292. if not na(pivotHighVal)
  293. exactBar = f_findExactHighBar(pivotHighVal)
  294. sp = SwingPoint.new(price = pivotHighVal, barIndex = exactBar, taken = false)
  295. array.push(bslArray, sp)
  296. f_trimSwingArray(bslArray, i_maxSwings)
  297.  
  298. if not na(pivotLowVal)
  299. exactBar = f_findExactLowBar(pivotLowVal)
  300. sp = SwingPoint.new(price = pivotLowVal, barIndex = exactBar, taken = false)
  301. array.push(sslArray, sp)
  302. f_trimSwingArray(sslArray, i_maxSwings)
  303.  
  304. bool bslSweptThisBar = false
  305. bool sslSweptThisBar = false
  306. var float lastSweptBSLPrice = na
  307. var float lastSweptSSLPrice = na
  308. var int lastSweptBSLBar = na
  309. var int lastSweptSSLBar = na
  310.  
  311. if array.size(bslArray) > 0
  312. for i = array.size(bslArray) - 1 to 0
  313. SwingPoint sp = array.get(bslArray, i)
  314. if not sp.taken and high > sp.price and bar_index > sp.barIndex
  315. sp.taken := true
  316. bslSweptThisBar := true
  317. lastSweptBSLPrice := sp.price
  318. lastSweptBSLBar := sp.barIndex
  319.  
  320. if i_showSweepLines
  321. sp.poolLine := line.new(x1 = sp.barIndex, y1 = sp.price, x2 = bar_index, y2 = sp.price,
  322. color = color.new(i_liqPoolColor, 35), width = 1, style = line.style_dotted)
  323. sp.poolLabel := label.new(x = bar_index - 5, y = sp.price, text = "𝗕𝗦𝗟",
  324. style = label.style_label_down, color = color.new(color.black, 100),
  325. textcolor = color.new(i_liqPoolColor, 10), size = f_labelSize(i_labelSize))
  326. if i_showSweepMarkers
  327. sp.sweepMarker := label.new(x = sp.barIndex, y = sp.price, text = "▲",
  328. style = label.style_label_down, color = color.new(color.black, 100),
  329. textcolor = color.new(i_liqPoolColor, 25), size = size.tiny)
  330. sp.visualsDrawn := true
  331.  
  332. if i_alertOnSweep
  333. alert("Buyside Liquidity (BSL) Swept @ " + str.tostring(sp.price), alert.freq_once_per_bar_close)
  334.  
  335. if array.size(sslArray) > 0
  336. for i = array.size(sslArray) - 1 to 0
  337. SwingPoint sp = array.get(sslArray, i)
  338. if not sp.taken and low < sp.price and bar_index > sp.barIndex
  339. sp.taken := true
  340. sslSweptThisBar := true
  341. lastSweptSSLPrice := sp.price
  342. lastSweptSSLBar := sp.barIndex
  343.  
  344. if i_showSweepLines
  345. sp.poolLine := line.new(x1 = sp.barIndex, y1 = sp.price, x2 = bar_index, y2 = sp.price,
  346. color = color.new(i_liqPoolColor, 35), width = 1, style = line.style_dotted)
  347. sp.poolLabel := label.new(x = bar_index - 5, y = sp.price, text = "𝗦𝗦𝗟",
  348. style = label.style_label_up, color = color.new(color.black, 100),
  349. textcolor = color.new(i_liqPoolColor, 10), size = f_labelSize(i_labelSize))
  350. if i_showSweepMarkers
  351. sp.sweepMarker := label.new(x = sp.barIndex, y = sp.price, text = "▼",
  352. style = label.style_label_up, color = color.new(color.black, 100),
  353. textcolor = color.new(i_liqPoolColor, 25), size = size.tiny)
  354. sp.visualsDrawn := true
  355.  
  356. if i_alertOnSweep
  357. alert("Sellside Liquidity (SSL) Swept @ " + str.tostring(sp.price), alert.freq_once_per_bar_close)
  358.  
  359. barsSinceBSLSweep := bslSweptThisBar ? 0 : barsSinceBSLSweep + 1
  360. barsSinceSSLSweep := sslSweptThisBar ? 0 : barsSinceSSLSweep + 1
  361.  
  362. bool bslSweepActive = barsSinceBSLSweep <= i_sweepWindow
  363. bool sslSweepActive = barsSinceSSLSweep <= i_sweepWindow
  364.  
  365. atrVal = ta.atr(14)
  366.  
  367. bool rawBullishCond = low > high[2] and f_passesThreshold(low, high[2], atrVal)
  368. bool rawBearishCond = high < low[2] and f_passesThreshold(low[2], high, atrVal)
  369.  
  370. bool registerBullish = i_requireSweep ? bslSweepActive : true
  371. bool registerBearish = i_requireSweep ? sslSweepActive : true
  372.  
  373. if rawBullishCond and registerBullish
  374. rf = RawFVG.new(top = low, bottom = high[2], leftBar = bar_index - 2, rightBar = bar_index,
  375. isBullishOrigin = true, sweepContext = bslSweepActive,
  376. originSweepPrice = lastSweptBSLPrice, originSweepBar = lastSweptBSLBar)
  377. array.push(rawFvgArray, rf)
  378.  
  379. if rawBearishCond and registerBearish
  380. rf = RawFVG.new(top = low[2], bottom = high, leftBar = bar_index - 2, rightBar = bar_index,
  381. isBullishOrigin = false, sweepContext = sslSweepActive,
  382. originSweepPrice = lastSweptSSLPrice, originSweepBar = lastSweptSSLBar)
  383. array.push(rawFvgArray, rf)
  384.  
  385. bool inSession1 = not na(time(timeframe.period, i_session1, i_sessionTZ))
  386. bool inSession2 = not na(time(timeframe.period, i_session2, i_sessionTZ))
  387. bool isSessionValid = not i_useSession or inSession1 or (i_useSession2 and inSession2)
  388.  
  389. if array.size(rawFvgArray) > 0
  390. for i = array.size(rawFvgArray) - 1 to 0
  391. RawFVG rf = array.get(rawFvgArray, i)
  392. bool expired = (bar_index - rf.rightBar) > i_rawExpiry
  393. bool priceInside = close <= rf.top and close >= rf.bottom
  394. bool invertsUp = not rf.isBullishOrigin and close > rf.top
  395. bool invertsDown = rf.isBullishOrigin and close < rf.bottom
  396.  
  397. if expired
  398. f_deleteGhostVisuals(rf)
  399. array.remove(rawFvgArray, i)
  400.  
  401. else if invertsUp
  402. if isSessionValid and (not i_requireSweep or sslSweepActive) and (not i_macrosOnly or (minute(time) <= 10 or minute(time) >= 50))
  403. tgt = f_nearestUntakenBSL(bar_index)
  404. f_deleteGhostVisuals(rf)
  405. setup = IFVG_Setup.new(
  406. top = rf.top, bottom = rf.bottom, mid = (rf.top + rf.bottom) / 2.0,
  407. leftBar = rf.leftBar, rightBar = bar_index, isBullish = true,
  408. state = "INVERTED", targetPrice = tgt, targetBarIndex = na, targetHit = false,
  409. invalidLevel = rf.bottom, invalidSwingLevel = rf.originSweepPrice, entryPrice = close, creationBar = bar_index, inversionBar = bar_index)
  410. array.push(activeIFVGs, setup)
  411. f_deleteGhostVisuals(rf)
  412. array.remove(rawFvgArray, i)
  413.  
  414. else if invertsDown
  415. if isSessionValid and (not i_requireSweep or bslSweepActive) and (not i_macrosOnly or (minute(time) <= 10 or minute(time) >= 50))
  416. tgt = f_nearestUntakenSSL(bar_index)
  417. f_deleteGhostVisuals(rf)
  418. setup = IFVG_Setup.new(
  419. top = rf.top, bottom = rf.bottom, mid = (rf.top + rf.bottom) / 2.0,
  420. leftBar = rf.leftBar, rightBar = bar_index, isBullish = false,
  421. state = "INVERTED", targetPrice = tgt, targetBarIndex = na, targetHit = false,
  422. invalidLevel = rf.top, invalidSwingLevel = rf.originSweepPrice, entryPrice = close, creationBar = bar_index, inversionBar = bar_index)
  423. array.push(activeIFVGs, setup)
  424. f_deleteGhostVisuals(rf)
  425. array.remove(rawFvgArray, i)
  426.  
  427. else if i_showGhost and priceInside
  428. if na(rf.ghostBox)
  429. rf.ghostBox := box.new(left = rf.leftBar, top = rf.top, right = bar_index, bottom = rf.bottom, border_color = color.new(i_ghostColor, 60), border_width = i_borderWidth, bgcolor = color.new(i_ghostColor, i_ghostTransparency), extend = extend.none)
  430. rf.ghostLabel := label.new(x = bar_index, y = (rf.top + rf.bottom) / 2.0, text = "?IFVG", style = label.style_label_left, color = color.new(color.black, 100), textcolor = color.new(i_ghostColor, 10), size = f_labelSize(i_labelSize))
  431. else
  432. box.set_right(rf.ghostBox, bar_index)
  433. label.set_x(rf.ghostLabel, bar_index)
  434. else
  435. na
  436.  
  437. if array.size(activeIFVGs) > 0
  438. for i = array.size(activeIFVGs) - 1 to 0
  439. IFVG_Setup s = array.get(activeIFVGs, i)
  440. if s.state == "INVERTED" and not na(s.targetPrice)
  441. if s.isBullish and high >= s.targetPrice
  442. s.targetHit := true
  443. s.targetBarIndex := bar_index
  444. s.state := "TARGET_HIT"
  445. if i_alertOnTarget
  446. alert("+IFVG Target (BSL) Hit @ " + str.tostring(s.targetPrice), alert.freq_once_per_bar_close)
  447. else if not s.isBullish and low <= s.targetPrice
  448. s.targetHit := true
  449. s.targetBarIndex := bar_index
  450. s.state := "TARGET_HIT"
  451. if i_alertOnTarget
  452. alert("-IFVG Target (SSL) Hit @ " + str.tostring(s.targetPrice), alert.freq_once_per_bar_close)
  453.  
  454. f_checkInvalidation(IFVG_Setup s) =>
  455. bool invalid = false
  456. if s.state == "INVERTED"
  457. if i_invalidCond == "Close"
  458. if s.isBullish and close < s.invalidLevel
  459. invalid := true
  460. else if not s.isBullish and close > s.invalidLevel
  461. invalid := true
  462. else if i_invalidCond == "Swing"
  463. sweptLevel = na(s.invalidSwingLevel) ? s.invalidLevel : s.invalidSwingLevel
  464. if s.isBullish and close < sweptLevel
  465. invalid := true
  466. else if not s.isBullish and close > sweptLevel
  467. invalid := true
  468. invalid
  469.  
  470. if array.size(activeIFVGs) > 0
  471. for i = array.size(activeIFVGs) - 1 to 0
  472. IFVG_Setup s = array.get(activeIFVGs, i)
  473. if s.state == "INVERTED" and f_checkInvalidation(s)
  474. s.state := "INVALIDATED"
  475. s.invalidBarIndex := bar_index
  476. if i_alertOnInvalid
  477. alert((s.isBullish ? "+IFVG" : "-IFVG") + " Invalidated @ " + str.tostring(close), alert.freq_once_per_bar_close)
  478.  
  479. f_createSetupVisuals(IFVG_Setup s) =>
  480. bool ghost = false
  481. bool invalid = s.state == "INVALIDATED"
  482. col = f_dirColor(s.isBullish, ghost, invalid)
  483.  
  484. boxRight = i_extendBoxes ? bar_index : (s.leftBar + 2)
  485. boxCol = i_showBoxes ? color.new(col, invalid ? i_invalidTransparency : i_boxTransparency) : color.new(color.black, 100)
  486. borderColor = i_showBoxes ? color.new(col, i_borderWidth == 0 ? 100 : 40) : color.new(color.black, 100)
  487. s.visualBox := box.new(left = s.leftBar, top = s.top, right = boxRight, bottom = s.bottom, border_color = borderColor, border_width = i_borderWidth, bgcolor = boxCol, extend = extend.none)
  488.  
  489. if i_showCE
  490. s.ceLine := line.new(x1 = s.leftBar, y1 = s.mid, x2 = boxRight, y2 = s.mid, color = color.new(col, 20), width = 1, style = line.style_dotted)
  491.  
  492. entryPrice = na(s.entryPrice) ? (s.isBullish ? s.top : s.bottom) : s.entryPrice
  493. int limitX = math.min(bar_index, s.inversionBar + 4)
  494. s.entryLine := line.new(x1 = s.inversionBar, y1 = entryPrice, x2 = limitX, y2 = entryPrice,
  495. color = col, width = 2, style = line.style_solid)
  496.  
  497. string tagTxt = (s.isBullish ? "+𝗜𝗙𝗩𝗚" : "-𝗜𝗙𝗩𝗚") + (invalid ? " ✗" : "")
  498. s.tagLabel := label.new(x = limitX, y = entryPrice, text = tagTxt, style = label.style_label_left, color = color.new(color.black, 100), textcolor = col, size = f_labelSize(i_labelSize))
  499.  
  500. if i_showTargets and not na(s.targetPrice) and not invalid
  501. targetSwingBar = s.isBullish ? f_findExactHighBar(s.targetPrice) : f_findExactLowBar(s.targetPrice)
  502.  
  503. s.targetLine := line.new(x1 = targetSwingBar, y1 = s.targetPrice, x2 = bar_index, y2 = s.targetPrice, color = color.new(col, 30), width = 1, style = f_lineStyle())
  504. string tTxt = s.isBullish ? "𝗕𝗦𝗟" : "𝗦𝗦🇱"
  505. s.targetLabel := label.new(x = bar_index, y = s.targetPrice, text = tTxt, style = label.style_label_left, color = color.new(color.black, 100), textcolor = col, size = f_labelSize(i_labelSize))
  506.  
  507. f_updateSetupVisuals(IFVG_Setup s) =>
  508. bool ghost = false
  509. bool invalid = s.state == "INVALIDATED"
  510. col = f_dirColor(s.isBullish, ghost, invalid)
  511. boxCol = color.new(col, invalid ? i_invalidTransparency : i_boxTransparency)
  512.  
  513. int boxRight = s.leftBar + 2
  514. if i_extendBoxes
  515. if s.state == "INVERTED"
  516. boxRight := bar_index
  517. else if invalid and not na(s.invalidBarIndex)
  518. boxRight := s.invalidBarIndex
  519. else if s.state == "TARGET_HIT" and not na(s.targetBarIndex)
  520. boxRight := s.targetBarIndex
  521.  
  522. if not na(s.visualBox)
  523. boxCol = i_showBoxes ? color.new(col, invalid ? i_invalidTransparency : i_boxTransparency) : color.new(color.black, 100)
  524. borderColor = i_showBoxes ? color.new(col, i_borderWidth == 0 ? 100 : 40) : color.new(color.black, 100)
  525. box.set_right(s.visualBox, boxRight)
  526. box.set_border_color(s.visualBox, borderColor)
  527. box.set_bgcolor(s.visualBox, boxCol)
  528.  
  529. if i_showCE and not na(s.ceLine)
  530. line.set_x2(s.ceLine, boxRight)
  531. line.set_color(s.ceLine, color.new(col, 20))
  532.  
  533. if not na(s.entryLine)
  534. stopX = math.min(bar_index, s.inversionBar + 4)
  535. line.set_x2(s.entryLine, stopX)
  536. line.set_color(s.entryLine, col)
  537.  
  538. if not na(s.tagLabel)
  539. int labelX = math.min(bar_index, s.inversionBar + 4)
  540. if invalid and not na(s.invalidBarIndex)
  541. labelX := math.min(s.invalidBarIndex, s.inversionBar + 4)
  542.  
  543. label.set_x(s.tagLabel, labelX)
  544.  
  545. bool hasWarning = false
  546. if s.state == "INVERTED" and i_invalidCond == "Swing"
  547. if s.isBullish and close < s.invalidLevel
  548. hasWarning := true
  549. else if not s.isBullish and close > s.invalidLevel
  550. hasWarning := true
  551.  
  552. string tagTxt = (s.isBullish ? "+𝗜𝗙𝗩𝗚" : "-𝗜𝗙𝗩𝗚") + (invalid ? " ✗" : (hasWarning ? " ⚠️" : ""))
  553. label.set_text(s.tagLabel, tagTxt)
  554. label.set_textcolor(s.tagLabel, col)
  555.  
  556. if i_showTargets and not na(s.targetLine)
  557. if invalid
  558. line.set_color(s.targetLine, color.new(color.black, 100))
  559. if not na(s.targetLabel)
  560. label.set_textcolor(s.targetLabel, color.new(color.black, 100))
  561. else
  562. int stopX = (i_stopAtTarget and s.targetHit) ? s.targetBarIndex : bar_index
  563. line.set_x2(s.targetLine, stopX)
  564. line.set_color(s.targetLine, color.new(col, s.targetHit ? 0 : 30))
  565. if not na(s.targetLabel)
  566. label.set_x(s.targetLabel, stopX)
  567. label.set_textcolor(s.targetLabel, col)
  568.  
  569. if s.targetHit and not na(s.targetBarIndex)
  570. offset = bar_index - s.targetBarIndex
  571. targetY = s.isBullish ? high[offset] : low[offset]
  572. label.set_y(s.targetLabel, targetY)
  573. label.set_text(s.targetLabel, "𝗧")
  574. label.set_style(s.targetLabel, s.isBullish ? label.style_label_down : label.style_label_up)
  575. else
  576. label.set_y(s.targetLabel, s.targetPrice)
  577. label.set_text(s.targetLabel, s.isBullish ? "𝗕𝗦𝗟" : "𝗦𝗦𝗟")
  578. label.set_style(s.targetLabel, label.style_label_left)
  579.  
  580. if array.size(activeIFVGs) > 0
  581. for i = 0 to array.size(activeIFVGs) - 1
  582. IFVG_Setup s = array.get(activeIFVGs, i)
  583. if na(s.visualBox)
  584. f_createSetupVisuals(s)
  585. else
  586. f_updateSetupVisuals(s)
  587.  
  588. f_trimActiveIFVGs(i_maxSetups)
  589.  
  590. if array.size(activeIFVGs) > 0
  591. for i = array.size(activeIFVGs) - 1 to 0
  592. IFVG_Setup s = array.get(activeIFVGs, i)
  593. bool resolved = s.state == "TARGET_HIT" or s.state == "INVALIDATED"
  594. int resolvedBar = s.state == "TARGET_HIT" ? s.targetBarIndex : s.invalidBarIndex
  595. if resolved and not na(resolvedBar) and (bar_index - resolvedBar) > ghostGracePeriod
  596. f_deleteSetupVisuals(s)
  597. array.remove(activeIFVGs, i)
  598.  
  599. f_trimSwingArray(bslArray, i_maxSwings)
  600. f_trimSwingArray(sslArray, i_maxSwings)
  601.  
  602. if array.size(activeIFVGs) > 0
  603. for i = 0 to array.size(activeIFVGs) - 1
  604. IFVG_Setup s = array.get(activeIFVGs, i)
  605. bool visible = f_biasAllows(s.isBullish)
  606. bool invalid = s.state == "INVALIDATED"
  607.  
  608. if invalid and not i_showInvalidated
  609. visible := false
  610.  
  611. col = f_dirColor(s.isBullish, false, invalid)
  612.  
  613. if not na(s.visualBox)
  614. boxCol = i_showBoxes ? color.new(col, invalid ? i_invalidTransparency : i_boxTransparency) : color.new(color.black, 100)
  615. borderColor = i_showBoxes ? color.new(col, i_borderWidth == 0 ? 100 : 40) : color.new(color.black, 100)
  616. box.set_bgcolor(s.visualBox, visible ? boxCol : color.new(color.black, 100))
  617. box.set_border_color(s.visualBox, visible ? borderColor : color.new(color.black, 100))
  618. if not na(s.ceLine)
  619. line.set_color(s.ceLine, visible ? color.new(col, 20) : color.new(color.black, 100))
  620. if not na(s.targetLine)
  621. line.set_color(s.targetLine, visible ? color.new(col, s.targetHit ? 0 : 30) : color.new(color.black, 100))
  622. if not na(s.tagLabel)
  623. label.set_textcolor(s.tagLabel, visible ? col : color.new(color.black, 100))
  624. if not na(s.targetLabel)
  625. label.set_textcolor(s.targetLabel, visible ? col : color.new(color.black, 100))
  626. if not na(s.entryLine)
  627. line.set_color(s.entryLine, visible ? col : color.new(color.black, 100))
  628.  
  629. if i_showTable and barstate.islast
  630. table.clear(hud, 0, 0, 0, 5)
  631.  
  632. chartTF = timeframe.isminutes ? (timeframe.multiplier >= 60 ? str.tostring(math.round(timeframe.multiplier / 60)) + "H" : str.tostring(timeframe.multiplier) + "m") : timeframe.period
  633. titleTxt = "Inversion FVG (" + chartTF + ")"
  634.  
  635. headerSize = i_tableTextSize == "Tiny" ? size.small : i_tableTextSize == "Small" ? size.normal : size.normal
  636. rowSize = f_labelSize(i_tableTextSize)
  637.  
  638. table.cell(hud, 0, 0, titleTxt, text_color = color.new(#000000, 0), text_size = headerSize)
  639. table.cell(hud, 0, 1, syminfo.ticker, text_color = #000000, text_size = headerSize)
  640. table.cell(hud, 0, 2, "Liquidity: " + f_formatTimeframe(i_liqTF), text_color = color.new(#000000, 0), text_size = rowSize)
  641.  
  642. thresholdLine = "IFVG Threshold: " + f_thresholdText() + (i_useFvgFilterTF ? " | FVG Filter: " + i_fvgFilterTF : "")
  643. table.cell(hud, 0, 3, thresholdLine, text_color = color.new(#000000, 0), text_size = rowSize)
  644. table.cell(hud, 0, 4, "Bias: " + i_bias, text_color = color.new(#000000, 0), text_size = rowSize)
  645.  
  646. string bottomLine = i_useSession ? f_sessionText() : f_dateText()
  647. table.cell(hud, 0, 5, bottomLine, text_color = color.new(#000000, 0), text_size = rowSize)
  648.  
  649. alertcondition(bslSweptThisBar, title="Buyside Liquidity (BSL) Swept", message="{{ticker}}: Buyside Liquidity swept on {{interval}}")
  650. alertcondition(sslSweptThisBar, title="Sellside Liquidity (SSL) Swept", message="{{ticker}}: Sellside Liquidity swept on {{interval}}")
  651.  
  652. bool anyBullishInversionThisBar = false
  653. bool anyBearishInversionThisBar = false
  654. bool anyTargetHitThisBar = false
  655. bool anyInvalidationThisBar = false
  656.  
  657. if array.size(activeIFVGs) > 0
  658. for i = 0 to array.size(activeIFVGs) - 1
  659. IFVG_Setup s = array.get(activeIFVGs, i)
  660. if s.inversionBar == bar_index
  661. if s.isBullish
  662. anyBullishInversionThisBar := true
  663. else
  664. anyBearishInversionThisBar := true
  665. if s.targetHit and s.targetBarIndex == bar_index
  666. anyTargetHitThisBar := true
  667. if s.state == "INVALIDATED" and s.invalidBarIndex == bar_index
  668. anyInvalidationThisBar := true
  669.  
  670. alertcondition(anyBullishInversionThisBar, title="+IFVG Inversion Confirmed", message="{{ticker}}: Bullish IFVG (+IFVG) confirmed on {{interval}}")
  671. alertcondition(anyBearishInversionThisBar, title="-IFVG Inversion Confirmed", message="{{ticker}}: Bearish IFVG (-IFVG) confirmed on {{interval}}")
  672. alertcondition(anyTargetHitThisBar, title="IFVG Target Hit", message="{{ticker}}: IFVG target hit on {{interval}}")
  673. alertcondition(anyInvalidationThisBar, title="IFVG Invalidated", message="{{ticker}}: IFVG invalidated on {{interval}}")
Advertisement
Add Comment
Please, Sign In to add comment