Skip to content

Instantly share code, notes, and snippets.

@dp21g
Created October 13, 2025 18:31
Show Gist options
  • Save dp21g/46f20ee46e68c393e4a7c09aa5ea8df6 to your computer and use it in GitHub Desktop.
Save dp21g/46f20ee46e68c393e4a7c09aa5ea8df6 to your computer and use it in GitHub Desktop.
fvg between specific hours
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//FVG indicator: Paints FVGs and their midlines (CEs). Stops painting when CE is hit, or when fully filled; user choice of threshold. This threshold is also used in the Alert conditions.
//Big thank you to @Bjorgum for his fantastic extendAndRemove method. Modified here for use with boxes and to integrate Alerts
//Modified to include time range filtering
// © twingall
//@version=5
indicator("DP - FVGs & CEs + Alerts: Time Filtered", overlay = true, max_boxes_count = 500, max_lines_count = 500)
// Time Range Settings
var g_TIME = "Time Range Filter"
enableTimeFilter = input.bool(true, "Enable Time Range Filter", group=g_TIME)
timezoneOffset = input.int(-4, "Timezone Offset", minval=-12, maxval=14, tooltip="Timezone offset from GMT. NY: -4 (EDT), -5 (EST)", group=g_TIME)
startHour = input.int(8, "Start Hour", minval=0, maxval=23, group=g_TIME)
startMinute = input.int(0, "Start Minute", minval=0, maxval=59, group=g_TIME)
endHour = input.int(12, "End Hour", minval=0, maxval=23, group=g_TIME)
endMinute = input.int(0, "End Minute", minval=0, maxval=59, group=g_TIME)
overnightSession = input.bool(false, "Overnight Session", tooltip="Enable if end time is on the next day", group=g_TIME)
// Original FVG Settings
numDays = input.int(7, "Number of Days Lookback")
showUP = input.bool(true, "'UP' FVGs:", inline ='1')
colUp = input.color(color.new(color.blue, 86), "", inline ='1')
showDN = input.bool(true, "'DOWN' FVGs:", inline ='2')
colDn = input.color(color.new(color.orange, 86), "", inline ='2')
showCE = input.bool(true, "Show CE", inline ='3')
ceCol = input.color(color.new(color.black, 1), "| Color:", inline ='3')
ceStyle = input.string(line.style_dotted, "| Style:", options=[line.style_dotted,line.style_solid, line.style_dashed], inline ='3')
deleteFilledBoxes = input.bool(false, "Delete Filled Boxes & Lines")
CEcond = input.bool(true, "Use CE (as opposed to Full Fill)", group = 'conditions', tooltip = "If toggled OFF, FVGs and CEs will paint until FVG has been completely filled.\n\nThis threshold is used for Above/Below threshold Alert conditions too (but does not effect the IOFED alerts):\ni.e. this will determine if your 'ABOVE threshold' alert fires when price hits latest active FVG CE ABOVE or latest active FVG Full Fill ABOVE\n\nAlerts are set by clicking the three dots on the indicator display line.")
useBodies = input.bool(false, "Use Candle Bodies (as opposed to wicks)", group = 'conditions', tooltip = "Candle body (open/close) will be used to mitigate FVGs (whether full fill or to CE); NOT wicks")
// Function to create timezone string
getTimezoneStr() =>
timezoneOffset >= 0 ? "GMT+" + str.tostring(timezoneOffset) : "GMT" + str.tostring(timezoneOffset)
// Function to check if current bar is within the specified time range
isInTimeRange() =>
if not enableTimeFilter
true
else
currentTime = time
timezoneStr = getTimezoneStr()
// Get current time components in selected timezone
currentHour = hour(currentTime, timezoneStr)
currentMinute = minute(currentTime, timezoneStr)
// Convert times to minutes for easier comparison
currentMinutes = currentHour * 60 + currentMinute
startMinutes = startHour * 60 + startMinute
endMinutes = endHour * 60 + endMinute
// Check if we're in range based on whether it's an overnight session
if overnightSession
// For overnight sessions (e.g., 8 PM to 6 AM next day)
if startMinutes > endMinutes
// Time range spans midnight
currentMinutes >= startMinutes or currentMinutes <= endMinutes
else
// Same day session
currentMinutes >= startMinutes and currentMinutes <= endMinutes
else
// For same-day sessions (e.g., 8 AM to 12 PM)
currentMinutes >= startMinutes and currentMinutes <= endMinutes
colorNone = color.new(color.white, 100)
_day = 24*3600*1000
var box bxUp = na, var box bxDn = na, var line lnUp = na, var line lnDn = na
var array<box> bxUpArr = array.new<box>(0), var array<line> lnUpArr = array.new<line>(0)
var array<box> bxDnArr = array.new<box>(0), var array<line> lnDnArr = array.new<line>(0)
dnCE = high[1] + (low[3]-high[1])/2
upCE = low[1] - (low[1]-high[3])/2
// Check for Down FVG formation within time range
if low[3] > high[1] and time > timenow - numDays*_day and showDN and isInTimeRange()
bxDnArr.push(box.new(bar_index-3, low[3], bar_index, high[1], bgcolor = colDn, border_color = colorNone))
lnDnArr.push(line.new(bar_index-3, dnCE, bar_index, dnCE, color = showCE?ceCol:colorNone, style =ceStyle))
// Check for Up FVG formation within time range
if high[3] < low[1] and time > timenow - numDays*_day and showUP and isInTimeRange()
bxUpArr.push(box.new(bar_index-3, low[1], bar_index, high[3], bgcolor = colUp, border_color = colorNone))
lnUpArr.push(line.new(bar_index-3, upCE, bar_index, upCE, color = showCE?ceCol:colorNone, style =ceStyle))
var array<int> _countArr = array.new<int>(0)
var array<int> _countArrIOFED = array.new<int>(0)
// Modified form of @Bjorgum's looping function. This stops boxes/lines painting when price passes to or through them
extendAndRemoveBx(array<box> boxArray, array<line> lineArray, array<int> countArr1, array<int> countArr2, simple bool isBull, int maxSize) =>
if boxArray.size() > 0
for i = boxArray.size() -1 to 0
line ln = lineArray.get(i)
box bx = boxArray.get(i)
bx.set_right(bar_index)
ln.set_x2(bar_index)
float price = CEcond ? ln.get_price(bar_index) : (isBull ? bx.get_top() : bx.get_bottom())
float price_IOFED = isBull ? bx.get_bottom() : bx.get_top()
int m = isBull ? 1 : -1
float hiLo = isBull ? (useBodies ? math.max(open, close) : high) : (useBodies ? math.min(open, close) : low)
if hiLo * m > price * m
boxArray.remove(i)
lineArray.remove(i)
countArr1.push(isBull ? 1 : -1) // For 'above/below threshold alerts; counter sum will decrement 1 on lower threshold hit, increment 1 on upper threshold hit
if deleteFilledBoxes
bx.set_bgcolor(colorNone)
ln.set_color(colorNone)
if hiLo * m > price_IOFED * m
countArr2.push(isBull ? 1 : -1)
if boxArray.size() > maxSize
box.delete(boxArray.shift())
line.delete(lineArray.shift())
extendAndRemoveBx(bxDnArr, lnDnArr, _countArr, _countArrIOFED, true, 12) // 12 should be good for around 2200 bars of history
extendAndRemoveBx(bxUpArr, lnUpArr, _countArr, _countArrIOFED, false, 12)
upThresholdLst = array.sum(_countArr) > array.sum(_countArr)[1]
dnThresholdLst = array.sum(_countArr) < array.sum(_countArr)[1]
upIOFEDlast = array.sum(_countArrIOFED) > array.sum(_countArrIOFED)[1]
dnIOFEDlast = array.sum(_countArrIOFED) < array.sum(_countArrIOFED)[1]
// Alert conditions
alertcondition(upThresholdLst, "ABOVE threshold of latest active Up FVG (CE or fvg High)", "Price has crossed threshold of latest active Up FVG")
alertcondition(dnThresholdLst, "BELOW threshold of latest active Down FVG (CE or fvg low)", "Price has crossed threshold of latest active Down FVG")
alertcondition(upIOFEDlast, "IOFED into latest active Up FVG", "Price has entered latest active UP FVG")
alertcondition(dnIOFEDlast, "IOFED into latest active Down FVG", "Price has entered latest active Down FVG")
// Time-filtered alert conditions
alertcondition(low[3] > high[1] and isInTimeRange(), "Simple alert: Down FVG (confirmed) - Time Filtered", "Down FVG has formed (confirmed) within time range")
alertcondition(high[3] < low[1] and isInTimeRange(), "Simple alert: Up FVG (confirmed) - Time Filtered", "Up FVG has formed (confirmed) within time range")
alertcondition(low[2] > high and isInTimeRange(), "Simple alert: Down FVG (UN-confirmed) - Time Filtered", "Down FVG has formed (un-confirmed) within time range")
alertcondition(high[2] < low and isInTimeRange(), "Simple alert: Up FVG (UN-confirmed) - Time Filtered", "Up FVG has formed (un-confirmed) within time range")
// Background highlighting for time range (optional visual aid)
bgcolor(enableTimeFilter and isInTimeRange() ? color.new(color.gray, 95) : na, title="Time Range Background")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment