OPEN-SOURCE SCRIPT

Double Pattern Screener v7

45
//version=6
indicator("Double Pattern Screener", shorttitle="DPS", overlay=false)

// Screener 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", step=0.01)
atrLength = input.int(14, "ATR Length", minval=1)

// NEW: Filter for number of equal peaks
filterPeaks = input.int(3, "Filter: Show Only X Equal Peaks", minval=2, maxval=5)
enableFilter = input.bool(true, "Enable Peak Count Filter")

// Arrays for tracking swings
var array<float> todaySwingLevels = array.new<float>(0)
var array<int> swingCounts = array.new<int>(0)
var array<bool> isResistance = array.new<bool>(0)
var array<int> swingBars = array.new<int>(0)

var int maxEqualPeaks = 0
var float nearestEqualLevel = na
var float distanceToNearest = na
var bool hasPattern = false

// Detect swings
swingHigh = ta.pivothigh(high, leftBars, rightBars)
swingLow = ta.pivotlow(low, leftBars, rightBars)

// Track current day
currentDay = dayofmonth
isNewDay = currentDay != currentDay[1]

// Clear on new day
if barstate.isfirst or isNewDay
array.clear(todaySwingLevels)
array.clear(swingCounts)
array.clear(isResistance)
array.clear(swingBars)
maxEqualPeaks := 0
nearestEqualLevel := na
distanceToNearest := na
hasPattern := false

// Function to find matching level
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
if not na(swingHigh)
matchIndex = findMatchingLevel(swingHigh, true)
if matchIndex >= 0
newCount = array.get(swingCounts, matchIndex) + 1
array.set(swingCounts, matchIndex, newCount)
// Update max equal peaks
if newCount > maxEqualPeaks
maxEqualPeaks := newCount
else
array.push(todaySwingLevels, swingHigh)
array.push(swingCounts, 1)
array.push(isResistance, true)
array.push(swingBars, bar_index)

// Process swing lows
if not na(swingLow)
matchIndex = findMatchingLevel(swingLow, false)
if matchIndex >= 0
newCount = array.get(swingCounts, matchIndex) + 1
array.set(swingCounts, matchIndex, newCount)
// Update max equal peaks
if newCount > maxEqualPeaks
maxEqualPeaks := newCount
else
array.push(todaySwingLevels, swingLow)
array.push(swingCounts, 1)
array.push(isResistance, false)
array.push(swingBars, bar_index)

// Remove broken levels
if array.size(todaySwingLevels) > 0
for i = array.size(todaySwingLevels) - 1 to 0
level = array.get(todaySwingLevels, i)
isRes = array.get(isResistance, i)
levelBroken = isRes ? close > level : close < level

if levelBroken
removedCount = array.get(swingCounts, i)
array.remove(todaySwingLevels, i)
array.remove(swingCounts, i)
array.remove(isResistance, i)
array.remove(swingBars, i)

// Recalculate max if we removed the highest count
if removedCount == maxEqualPeaks
maxEqualPeaks := 0
if array.size(swingCounts) > 0
for j = 0 to array.size(swingCounts) - 1
count = array.get(swingCounts, j)
if count > maxEqualPeaks
maxEqualPeaks := count

// Calculate nearest equal level and distance
nearestEqualLevel := na
distanceToNearest := na
smallestDistance = 999999.0

if array.size(todaySwingLevels) > 0
for i = 0 to array.size(todaySwingLevels) - 1
count = array.get(swingCounts, i)
level = array.get(todaySwingLevels, i)

// Only consider levels with 2+ touches
if count >= 2
distance = math.abs(close - level)
if distance < smallestDistance
smallestDistance := distance
nearestEqualLevel := level
distanceToNearest := distance

// Pattern detection with filter
hasPattern := false
if maxEqualPeaks >= 2
if enableFilter
hasPattern := maxEqualPeaks == filterPeaks
else
hasPattern := true

// Screener outputs
patternSignal = hasPattern ? 1 : 0
numberEqualPeaks = maxEqualPeaks
nearestLevelPrice = nearestEqualLevel
distanceCents = distanceToNearest

// Calculate ATR normalized distance
atr = ta.atr(atrLength)
atrDistance = not na(distanceToNearest) and not na(atr) and atr > 0 ? distanceToNearest / atr : na

// Plot screener values
plot(patternSignal, title="Pattern Signal", display=display.data_window)
plot(numberEqualPeaks, title="Number Equal Peaks", display=display.data_window)
plot(nearestLevelPrice, title="Nearest Equal Level Price", display=display.data_window)
plot(distanceCents, title="Distance to Nearest (Price Units)", display=display.data_window)
plot(atrDistance, title="ATR Normalized Distance", display=display.data_window)

// Table for current symbol info
if barstate.islast
var table infoTable = table.new(position.top_right, 2, 6, bgcolor=color.new(color.black, 70))

table.cell(infoTable, 0, 0, "Symbol:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 0, syminfo.ticker, bgcolor=color.new(color.blue, 50), text_color=color.white)

table.cell(infoTable, 0, 1, "Pattern:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 1, str.tostring(patternSignal), bgcolor=patternSignal == 1 ? color.new(color.purple, 50) : color.new(color.gray, 50), text_color=color.white)

table.cell(infoTable, 0, 2, "Equal Peaks:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 2, str.tostring(numberEqualPeaks), bgcolor=color.new(color.yellow, 50), text_color=color.black)

table.cell(infoTable, 0, 3, "Nearest Level:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 3, str.tostring(nearestLevelPrice, "#.####"), bgcolor=color.new(color.orange, 50), text_color=color.white)

table.cell(infoTable, 0, 4, "Distance:", bgcolor=color.new(color.gray, 50), text_color=color.white)
table.cell(infoTable, 1, 4, str.tostring(distanceCents, "#.####"), bgcolor=color.new(color.green, 50), text_color=color.white)

table.cell(infoTable, 0, 5, "Filter Active:", bgcolor=color.new(color.gray, 50), text_color=color.white)
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)

Clause de non-responsabilité

Les informations et les publications ne sont pas destinées à être, et ne constituent pas, des conseils ou des recommandations en matière de finance, d'investissement, de trading ou d'autres types de conseils fournis ou approuvés par TradingView. Pour en savoir plus, consultez les Conditions d'utilisation.