Created
April 9, 2025 15:00
-
-
Save yaakov123/b43d6618e6b60b842363d161192716b2 to your computer and use it in GitHub Desktop.
Interval Vertical Lines
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//@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