OPEN-SOURCE SCRIPT

Double Top/Bottom Screener - Clean Today Only v5

68
//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)

Thông báo miễn trừ trách nhiệm

Thông tin và ấn phẩm không có nghĩa là và không cấu thành, tài chính, đầu tư, kinh doanh, hoặc các loại lời khuyên hoặc khuyến nghị khác được cung cấp hoặc xác nhận bởi TradingView. Đọc thêm trong Điều khoản sử dụng.