Skip to content

Instantly share code, notes, and snippets.

@yaakov123
Created April 9, 2025 15:00
Show Gist options
  • Save yaakov123/b43d6618e6b60b842363d161192716b2 to your computer and use it in GitHub Desktop.
Save yaakov123/b43d6618e6b60b842363d161192716b2 to your computer and use it in GitHub Desktop.
Interval Vertical Lines
//@version=5
indicator(title="Interval Vertical Lines", shorttitle="Int Lines", overlay=true, max_lines_count=500)
// --- User Inputs ---
group_tf = "Timeframe & Interval Settings"
targetTfInput = input.string("D", title="Reference Timeframe", tooltip="Timeframe to base intervals on (e.g., '1', '5', '15', '60', '240', 'D', 'W', 'M'). Use TradingView format.", group = group_tf, inline="tf")
intervalValInput = input.int(1, title="Interval Value", minval=1, group = group_tf, inline="interval")
intervalUnitInput = input.string("Hours", title="Interval Unit", options=["Minutes", "Hours", "Days", "Weeks", "Months"], group = group_tf, inline="interval")
group_style = "Line Appearance"
lineColorInput = input.color(color.new(color.gray, 50), title="Line Color", group=group_style, inline="style")
lineStyleInput = input.string(line.style_dotted, title="Line Style", options=[line.style_solid, line.style_dashed, line.style_dotted], group=group_style, inline="style")
lineWidthInput = input.int(1, title="Line Width", minval=1, group=group_style, inline="style")
// --- Calculations ---
// Get the time from the target timeframe for the current bar.
// Use lookahead=barmerge.lookahead_on to ensure we get the time of the *completed* target timeframe bar, avoiding repainting.
t = request.security(syminfo.tickerid, targetTfInput, time, lookahead=barmerge.lookahead_on)
// Function to calculate the start time (Unix millis) of the interval block containing a given time 'currentTime'
f_getIntervalStartTime(currentTime, _intervalVal, _intervalUnit, _tz) =>
// Extract time components in the exchange's timezone
_yr = year(currentTime, _tz)
_mo = month(currentTime, _tz)
_dy = dayofmonth(currentTime, _tz)
_hr = hour(currentTime, _tz)
_mn = minute(currentTime, _tz)
intervalStartUnix = 0 // Initialize
// Calculate the start of the interval based on the unit
if _intervalUnit == "Minutes"
totalMinsFromMidnight = _hr * 60 + _mn
intervalsPassed = math.floor(totalMinsFromMidnight / _intervalVal)
startMinuteOfInterval = intervalsPassed * _intervalVal
startHr = math.floor(startMinuteOfInterval / 60)
startMn = startMinuteOfInterval % 60
intervalStartUnix := timestamp(_tz, _yr, _mo, _dy, startHr, startMn, 0)
else if _intervalUnit == "Hours"
intervalsPassed = math.floor(_hr / _intervalVal)
startHr = intervalsPassed * _intervalVal
intervalStartUnix := timestamp(_tz, _yr, _mo, _dy, startHr, 0, 0) // Minute is 0 for hour intervals
else if _intervalUnit == "Days"
// For daily intervals, we need the start of the day based on the interval count since the epoch or start of month/year?
// Simplest approach: start of the day modulo interval days (relative to start of month perhaps)
// More robust: Calculate days passed in the month/year and use modulo
// Let's try relative to the start of the month for simplicity
dayIndexInMonth = _dy - 1 // 0-based index
intervalsPassed = math.floor(dayIndexInMonth / _intervalVal)
startDayOfMonth = intervalsPassed * _intervalVal + 1 // 1-based day
startDayOfMonth := math.max(1, startDayOfMonth) // Ensure it's at least 1
// We need to handle crossing into a new month carefully - this approach might be flawed.
// Alternative: Calculate days since a fixed point (like Unix epoch start).
// Let's use the timestamp directly for simplicity here, aligning to the start of the day.
// This might not be perfect for multi-day intervals if not aligned with weeks/months easily.
// A simpler interpretation: just mark every N days starting from the first day seen.
// We will use the change detection method below, which is more reliable for this.
// So, for 'Days', 'Weeks', 'Months', the start time is simply the start of that day/week/month.
intervalStartUnix := timestamp(_tz, _yr, _mo, _dy, 0, 0, 0) // Start of the day
// More complex logic would be needed for true 'every N days' if N > 1 across months/years.
// The change detection below handles 'Day', 'Week', 'Month' intervals correctly when intervalVal=1.
// For intervalVal > 1 with Days/Weeks/Months, the logic gets complex. We'll rely on change detection.
// If intervalVal > 1 for Days, the change detection will work fine.
else if _intervalUnit == "Weeks"
// Find the start of the week (assuming Sunday or Monday, depending on locale/exchange)
// PineScript doesn't have a direct 'start of week' timestamp function easily accessible for arbitrary times.
// We rely on the change detection using `request.security` on 'W' timeframe.
// So, calculate the start of the day, the change detection will handle the week boundary.
intervalStartUnix := timestamp(_tz, _yr, _mo, _dy, 0, 0, 0) // Start of the day
else if _intervalUnit == "Months"
// Start of the month
intervalStartUnix := timestamp(_tz, _yr, _mo, 1, 0, 0, 0) // First day, 00:00
intervalStartUnix // Return the calculated start time (Unix millis)
// Calculate the interval start time for the current bar's target time 't'
currentIntervalStart = f_getIntervalStartTime(t, intervalValInput, intervalUnitInput, syminfo.timezone)
// Get the previous bar's interval start time to detect changes
// Using nz() to handle the first bars where t[1] would be na
previousIntervalStart = f_getIntervalStartTime(nz(t[1], t), intervalValInput, intervalUnitInput, syminfo.timezone)
// --- Condition to Draw Line ---
// Draw line if the calculated interval start time has changed since the previous bar,
// and the time 't' is valid (not na).
// Use nz(t) to handle potential na values at the beginning of the series.
// The check `nz(t) > nz(t[1])` ensures we only trigger on *forward* time changes.
// The check `currentIntervalStart > previousIntervalStart` is the core logic.
bool isNewInterval = not na(t) and nz(t) > nz(t[1]) and currentIntervalStart > previousIntervalStart
// --- Drawing ---
if isNewInterval
// Draw a vertical line extending top to bottom
line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = lineColorInput, style = lineStyleInput, width = lineWidthInput, extend = extend.both)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment