OPEN-SOURCE SCRIPT
Actualizado Double Top/Bottom Screener - Clean Today Only v5

//version=6
indicator("Double Top/Bottom Screener - Clean Today Only", overlay=true, max_lines_count=100)
// Inputs
leftBars = input.int(5, "Left Bars", minval=3, maxval=10)
rightBars = input.int(5, "Right Bars", minval=3, maxval=10)
tolerance = input.float(0.02, "Max Difference (e.g., 0.02 for 2 cents)", step=0.01)
atrLength = input.int(14, "ATR Length for Normalized Distance", minval=1)
requiredPeaks = input.int(3, "Required Identical Peaks", minval=2, maxval=5)
// Arrays to store today's swing levels only
var array<float> todaySwingLevels = array.new<float>(0)
var array<int> swingCounts = array.new<int>(0)
var array<line> swingLines = array.new<line>(0)
var array<bool> isResistance = array.new<bool>(0)
var array<int> swingBars = array.new<int>(0)
var bool hasDoubleTop = false
var bool hasDoubleBottom = false
var float doubleTopLevel = na
var float doubleBottomLevel = na
var float nearestDoubleLevel = na
var int currentDay = na
// Detect significant swings only
swingHigh = ta.pivothigh(high, leftBars, rightBars)
swingLow = ta.pivotlow(low, leftBars, rightBars)
// Track current day
currentDay := dayofmonth
isNewDay = currentDay != currentDay[1]
// Clear everything on new day
if barstate.isfirst or isNewDay
// Delete all existing lines
if array.size(swingLines) > 0
for i = array.size(swingLines) - 1 to 0
line.delete(array.get(swingLines, i))
// Clear all arrays
array.clear(todaySwingLevels)
array.clear(swingCounts)
array.clear(swingLines)
array.clear(isResistance)
array.clear(swingBars)
// Reset flags
hasDoubleTop := false
hasDoubleBottom := false
doubleTopLevel := na
doubleBottomLevel := na
nearestDoubleLevel := na
// Function to find matching level within today's swings only
findMatchingLevel(newLevel, isHigh) =>
matchIndex = -1
if array.size(todaySwingLevels) > 0
for i = 0 to array.size(todaySwingLevels) - 1
existingLevel = array.get(todaySwingLevels, i)
existingIsResistance = array.get(isResistance, i)
if math.abs(newLevel - existingLevel) <= tolerance and existingIsResistance == isHigh
matchIndex := i
break
matchIndex
// Process swing highs (resistance levels) - today only
if not na(swingHigh)
matchIndex = findMatchingLevel(swingHigh, true)
if matchIndex >= 0
// Found matching resistance level
newCount = array.get(swingCounts, matchIndex) + 1
array.set(swingCounts, matchIndex, newCount)
// Delete old line and create new one with appropriate color
line.delete(array.get(swingLines, matchIndex))
lineColor = newCount >= requiredPeaks ? color.yellow : color.red
// Start line from the original swing bar, not current bar
originalBar = array.get(swingBars, matchIndex)
newLine = line.new(originalBar, swingHigh, bar_index + 20, swingHigh,
color=lineColor, width=2, extend=extend.right)
array.set(swingLines, matchIndex, newLine)
// Update pattern detection
if newCount >= requiredPeaks
hasDoubleTop := true
doubleTopLevel := swingHigh
else
// New resistance level - add to today's levels
array.push(todaySwingLevels, swingHigh)
array.push(swingCounts, 1)
array.push(isResistance, true)
array.push(swingBars, bar_index)
// Create red horizontal line starting from swing bar
newLine = line.new(bar_index, swingHigh, bar_index + 20, swingHigh,
color=color.red, width=2, extend=extend.right)
array.push(swingLines, newLine)
// Process swing lows (support levels) - today only
if not na(swingLow)
matchIndex = findMatchingLevel(swingLow, false)
if matchIndex >= 0
// Found matching support level
newCount = array.get(swingCounts, matchIndex) + 1
array.set(swingCounts, matchIndex, newCount)
// Delete old line and create new one with appropriate color
line.delete(array.get(swingLines, matchIndex))
lineColor = newCount >= requiredPeaks ? color.yellow : color.green
// Start line from the original swing bar, not current bar
originalBar = array.get(swingBars, matchIndex)
newLine = line.new(originalBar, swingLow, bar_index + 20, swingLow,
color=lineColor, width=2, extend=extend.right)
array.set(swingLines, matchIndex, newLine)
// Update pattern detection
if newCount >= requiredPeaks
hasDoubleBottom := true
doubleBottomLevel := swingLow
else
// New support level - add to today's levels
array.push(todaySwingLevels, swingLow)
array.push(swingCounts, 1)
array.push(isResistance, false)
array.push(swingBars, bar_index)
// Create green horizontal line starting from swing bar
newLine = line.new(bar_index, swingLow, bar_index + 20, swingLow,
color=color.green, width=2, extend=extend.right)
array.push(swingLines, newLine)
// Remove broken levels immediately (strict enforcement)
if array.size(todaySwingLevels) > 0
for i = array.size(todaySwingLevels) - 1 to 0
level = array.get(todaySwingLevels, i)
isRes = array.get(isResistance, i)
// Check if level is broken (even slightly)
levelBroken = isRes ? close > level : close < level
if levelBroken
// Delete line and remove from arrays
line.delete(array.get(swingLines, i))
array.remove(swingLines, i)
array.remove(todaySwingLevels, i)
array.remove(swingCounts, i)
array.remove(isResistance, i)
array.remove(swingBars, i)
// Reset pattern flags if broken level was part of pattern
if level == doubleTopLevel
hasDoubleTop := false
doubleTopLevel := na
if level == doubleBottomLevel
hasDoubleBottom := false
doubleBottomLevel := na
// Calculate pattern signal
patternSignal = (hasDoubleTop or hasDoubleBottom) ? 1 : 0
// Find nearest double level (only from unbroken levels)
if patternSignal == 1
if hasDoubleTop and not na(doubleTopLevel)
nearestDoubleLevel := doubleTopLevel
if hasDoubleBottom and not na(doubleBottomLevel)
if na(nearestDoubleLevel)
nearestDoubleLevel := doubleBottomLevel
else
nearestDoubleLevel := math.abs(close - doubleBottomLevel) < math.abs(close - nearestDoubleLevel) ? doubleBottomLevel : nearestDoubleLevel
else
nearestDoubleLevel := na
// Distance calculation
atr = ta.atr(atrLength)
distanceNormalizedATR = not na(nearestDoubleLevel) and not na(atr) and atr > 0 ? math.abs(close - nearestDoubleLevel) / atr : na
// Outputs (minimal plotting)
plot(patternSignal, title="Pattern Signal", color=patternSignal == 1 ? color.purple : na, style=plot.style_circles)
plot(nearestDoubleLevel, title="Nearest Double Level Price", color=color.orange)
plot(distanceNormalizedATR, title="Normalized Distance (ATR)", color=color.blue)
// Background highlight for pattern
bgcolor(patternSignal == 1 ? color.new(color.purple, 80) : na)
// Alert for pattern detection
if patternSignal == 1 and barstate.isconfirmed
alert("Double Pattern detected on " + syminfo.ticker + " at " + str.tostring(close), alert.freq_once_per_bar_close)
// Clean information table
if barstate.islast
var table infoTable = table.new(position.top_right, 2, 4, bgcolor=color.new(color.black, 70))
table.cell(infoTable, 0, 0, "Pattern:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 0, str.tostring(patternSignal), bgcolor=patternSignal == 1 ? color.new(color.purple, 50) : color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 0, 1, "Level:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 1, str.tostring(nearestDoubleLevel, "#.####"), bgcolor=color.new(color.orange, 50), text_color=color.white)
table.cell(infoTable, 0, 2, "Unbroken Swings:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 2, str.tostring(array.size(todaySwingLevels)), bgcolor=color.new(color.gray, 50), text_color=color.white)
// Count double levels
doubleCount = 0
if array.size(swingCounts) > 0
for i = 0 to array.size(swingCounts) - 1
if array.get(swingCounts, i) >= requiredPeaks
doubleCount += 1
table.cell(infoTable, 0, 3, "Double Levels:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 3, str.tostring(doubleCount), bgcolor=color.new(color.yellow, 50), text_color=color.black)
indicator("Double Top/Bottom Screener - Clean Today Only", overlay=true, max_lines_count=100)
// Inputs
leftBars = input.int(5, "Left Bars", minval=3, maxval=10)
rightBars = input.int(5, "Right Bars", minval=3, maxval=10)
tolerance = input.float(0.02, "Max Difference (e.g., 0.02 for 2 cents)", step=0.01)
atrLength = input.int(14, "ATR Length for Normalized Distance", minval=1)
requiredPeaks = input.int(3, "Required Identical Peaks", minval=2, maxval=5)
// Arrays to store today's swing levels only
var array<float> todaySwingLevels = array.new<float>(0)
var array<int> swingCounts = array.new<int>(0)
var array<line> swingLines = array.new<line>(0)
var array<bool> isResistance = array.new<bool>(0)
var array<int> swingBars = array.new<int>(0)
var bool hasDoubleTop = false
var bool hasDoubleBottom = false
var float doubleTopLevel = na
var float doubleBottomLevel = na
var float nearestDoubleLevel = na
var int currentDay = na
// Detect significant swings only
swingHigh = ta.pivothigh(high, leftBars, rightBars)
swingLow = ta.pivotlow(low, leftBars, rightBars)
// Track current day
currentDay := dayofmonth
isNewDay = currentDay != currentDay[1]
// Clear everything on new day
if barstate.isfirst or isNewDay
// Delete all existing lines
if array.size(swingLines) > 0
for i = array.size(swingLines) - 1 to 0
line.delete(array.get(swingLines, i))
// Clear all arrays
array.clear(todaySwingLevels)
array.clear(swingCounts)
array.clear(swingLines)
array.clear(isResistance)
array.clear(swingBars)
// Reset flags
hasDoubleTop := false
hasDoubleBottom := false
doubleTopLevel := na
doubleBottomLevel := na
nearestDoubleLevel := na
// Function to find matching level within today's swings only
findMatchingLevel(newLevel, isHigh) =>
matchIndex = -1
if array.size(todaySwingLevels) > 0
for i = 0 to array.size(todaySwingLevels) - 1
existingLevel = array.get(todaySwingLevels, i)
existingIsResistance = array.get(isResistance, i)
if math.abs(newLevel - existingLevel) <= tolerance and existingIsResistance == isHigh
matchIndex := i
break
matchIndex
// Process swing highs (resistance levels) - today only
if not na(swingHigh)
matchIndex = findMatchingLevel(swingHigh, true)
if matchIndex >= 0
// Found matching resistance level
newCount = array.get(swingCounts, matchIndex) + 1
array.set(swingCounts, matchIndex, newCount)
// Delete old line and create new one with appropriate color
line.delete(array.get(swingLines, matchIndex))
lineColor = newCount >= requiredPeaks ? color.yellow : color.red
// Start line from the original swing bar, not current bar
originalBar = array.get(swingBars, matchIndex)
newLine = line.new(originalBar, swingHigh, bar_index + 20, swingHigh,
color=lineColor, width=2, extend=extend.right)
array.set(swingLines, matchIndex, newLine)
// Update pattern detection
if newCount >= requiredPeaks
hasDoubleTop := true
doubleTopLevel := swingHigh
else
// New resistance level - add to today's levels
array.push(todaySwingLevels, swingHigh)
array.push(swingCounts, 1)
array.push(isResistance, true)
array.push(swingBars, bar_index)
// Create red horizontal line starting from swing bar
newLine = line.new(bar_index, swingHigh, bar_index + 20, swingHigh,
color=color.red, width=2, extend=extend.right)
array.push(swingLines, newLine)
// Process swing lows (support levels) - today only
if not na(swingLow)
matchIndex = findMatchingLevel(swingLow, false)
if matchIndex >= 0
// Found matching support level
newCount = array.get(swingCounts, matchIndex) + 1
array.set(swingCounts, matchIndex, newCount)
// Delete old line and create new one with appropriate color
line.delete(array.get(swingLines, matchIndex))
lineColor = newCount >= requiredPeaks ? color.yellow : color.green
// Start line from the original swing bar, not current bar
originalBar = array.get(swingBars, matchIndex)
newLine = line.new(originalBar, swingLow, bar_index + 20, swingLow,
color=lineColor, width=2, extend=extend.right)
array.set(swingLines, matchIndex, newLine)
// Update pattern detection
if newCount >= requiredPeaks
hasDoubleBottom := true
doubleBottomLevel := swingLow
else
// New support level - add to today's levels
array.push(todaySwingLevels, swingLow)
array.push(swingCounts, 1)
array.push(isResistance, false)
array.push(swingBars, bar_index)
// Create green horizontal line starting from swing bar
newLine = line.new(bar_index, swingLow, bar_index + 20, swingLow,
color=color.green, width=2, extend=extend.right)
array.push(swingLines, newLine)
// Remove broken levels immediately (strict enforcement)
if array.size(todaySwingLevels) > 0
for i = array.size(todaySwingLevels) - 1 to 0
level = array.get(todaySwingLevels, i)
isRes = array.get(isResistance, i)
// Check if level is broken (even slightly)
levelBroken = isRes ? close > level : close < level
if levelBroken
// Delete line and remove from arrays
line.delete(array.get(swingLines, i))
array.remove(swingLines, i)
array.remove(todaySwingLevels, i)
array.remove(swingCounts, i)
array.remove(isResistance, i)
array.remove(swingBars, i)
// Reset pattern flags if broken level was part of pattern
if level == doubleTopLevel
hasDoubleTop := false
doubleTopLevel := na
if level == doubleBottomLevel
hasDoubleBottom := false
doubleBottomLevel := na
// Calculate pattern signal
patternSignal = (hasDoubleTop or hasDoubleBottom) ? 1 : 0
// Find nearest double level (only from unbroken levels)
if patternSignal == 1
if hasDoubleTop and not na(doubleTopLevel)
nearestDoubleLevel := doubleTopLevel
if hasDoubleBottom and not na(doubleBottomLevel)
if na(nearestDoubleLevel)
nearestDoubleLevel := doubleBottomLevel
else
nearestDoubleLevel := math.abs(close - doubleBottomLevel) < math.abs(close - nearestDoubleLevel) ? doubleBottomLevel : nearestDoubleLevel
else
nearestDoubleLevel := na
// Distance calculation
atr = ta.atr(atrLength)
distanceNormalizedATR = not na(nearestDoubleLevel) and not na(atr) and atr > 0 ? math.abs(close - nearestDoubleLevel) / atr : na
// Outputs (minimal plotting)
plot(patternSignal, title="Pattern Signal", color=patternSignal == 1 ? color.purple : na, style=plot.style_circles)
plot(nearestDoubleLevel, title="Nearest Double Level Price", color=color.orange)
plot(distanceNormalizedATR, title="Normalized Distance (ATR)", color=color.blue)
// Background highlight for pattern
bgcolor(patternSignal == 1 ? color.new(color.purple, 80) : na)
// Alert for pattern detection
if patternSignal == 1 and barstate.isconfirmed
alert("Double Pattern detected on " + syminfo.ticker + " at " + str.tostring(close), alert.freq_once_per_bar_close)
// Clean information table
if barstate.islast
var table infoTable = table.new(position.top_right, 2, 4, bgcolor=color.new(color.black, 70))
table.cell(infoTable, 0, 0, "Pattern:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 0, str.tostring(patternSignal), bgcolor=patternSignal == 1 ? color.new(color.purple, 50) : color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 0, 1, "Level:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 1, str.tostring(nearestDoubleLevel, "#.####"), bgcolor=color.new(color.orange, 50), text_color=color.white)
table.cell(infoTable, 0, 2, "Unbroken Swings:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 2, str.tostring(array.size(todaySwingLevels)), bgcolor=color.new(color.gray, 50), text_color=color.white)
// Count double levels
doubleCount = 0
if array.size(swingCounts) > 0
for i = 0 to array.size(swingCounts) - 1
if array.get(swingCounts, i) >= requiredPeaks
doubleCount += 1
table.cell(infoTable, 0, 3, "Double Levels:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 3, str.tostring(doubleCount), bgcolor=color.new(color.yellow, 50), text_color=color.black)
Notas de prensa
filterText = enableFilter ? str.tostring(filterPeaks) + " peaks" : "OFF"
table.cell(infoTable, 1, 5, filterText, bgcolor=enableFilter ? color.new(color.red, 50) : color.new(color.gray, 50), text_color=color.white)
Script de código abierto
Siguiendo fielmente el espíritu de TradingView, el creador de este script lo ha publicado en código abierto, permitiendo que otros traders puedan revisar y verificar su funcionalidad. ¡Enhorabuena al autor! Puede utilizarlo de forma gratuita, pero tenga en cuenta que la publicación de este código está sujeta a nuestras Normas internas.
Exención de responsabilidad
La información y las publicaciones que ofrecemos, no implican ni constituyen un asesoramiento financiero, ni de inversión, trading o cualquier otro tipo de consejo o recomendación emitida o respaldada por TradingView. Puede obtener información adicional en las Condiciones de uso.
Script de código abierto
Siguiendo fielmente el espíritu de TradingView, el creador de este script lo ha publicado en código abierto, permitiendo que otros traders puedan revisar y verificar su funcionalidad. ¡Enhorabuena al autor! Puede utilizarlo de forma gratuita, pero tenga en cuenta que la publicación de este código está sujeta a nuestras Normas internas.
Exención de responsabilidad
La información y las publicaciones que ofrecemos, no implican ni constituyen un asesoramiento financiero, ni de inversión, trading o cualquier otro tipo de consejo o recomendación emitida o respaldada por TradingView. Puede obtener información adicional en las Condiciones de uso.