← К каталогу
Исходный код · ID 4138

HARMONIC Lab

indicator harmonic lab for tradingview.pine

Похожие материалы по тегам

smc+ict+indicators+scalping master.pineTradingView · 2 общих теговliquidity trendline with signals(1).pineTradingView · 2 общих теговtrading view script rsi by secret solution_6266878028173809325.pineTradingView · 2 общих теговsmc & ict & indicators + scalping master(1)_5976621784668052077.pineTradingView · 2 общих теговares_indicator.pineTradingView · 1 общих теговgold_trend_revers.mq5MQL5 / MetaTrader 5 · 1 общих тегов
Вы автор или правообладатель?

Сообщите нам, если считаете, что материал размещён без разрешения.

Краткий обзор

О программе

HARMONIC Lab — индикатор TradingView для обнаружения гармонических и классических ценовых паттернов на графике. Он выявляет структуры XABCD (Gartley, Butterfly, Bat, Crab и другие), классические формации (голова и плечи, двойная вершина/дно, треугольники, 3-Drive) и зоны Supply | Demand. Для каждого обнаруженного паттерна рассчитываются уровни входа, цели и стоп-лосс, отображаются линии, заливки, метки и зоны подтверждения. Индикатор работает на основе свинг-пивотов и не использует внешние библиотеки или данные будущих баров.

Настройки программы

НастройкаОписание
depth Минимальная длина ZigZag — Минимальное количество баров между пивотами для построения волн ZigZag.
dMode Режим обнаружения D — Способ подтверждения точки D: по закрытию бара (развивающаяся) или по подтверждённому пивоту.
confirmBars Бары подтверждения пивота — Количество баров после пивота, необходимых для его подтверждения.
multiscale Поиск по нескольким масштабам — Искать паттерны на глубинах 1x, 2x и 3x от базовой длины ZigZag.
searchNested Поиск вложенных комбинаций — Разрешить вложенные XABCD-структуры внутри одной волны.
candidateLimit Максимум кандидатов — Максимальное количество кандидатов на паттерн на каждую глубину.
errorPct Допуск ошибки — Процентная допустимая погрешность соотношений паттерна.
minSize Минимум баров паттерна — Минимальное количество баров от X до D в паттерне.
maxSize Максимум баров паттерна — Максимальное количество баров от X до D в паттерне.
minHeight Минимальная высота паттерна — Минимальная высота паттерна в процентах от диапазона.
minCD Минимум баров C–D — Минимальное количество баров между точками C и D.
direction Направление — Направление поиска паттернов: оба, только длинные или только короткие.
sessionHours Сессия обнаружения — Временной интервал, в течение которого разрешено обнаружение паттернов.
utcOffset Смещение сессии UTC — Часовое смещение сессии обнаружения от UTC.
maxPerBar Максимум новых паттернов за бар — Максимальное количество новых паттернов, добавляемых за один бар.
maxActive Максимум активных паттернов — Максимальное количество одновременно активных паттернов на графике.
noSame Блокировка дублей — Запрещать одновременно активные паттерны одного семейства и направления.
maxAge Срок жизни паттерна — Количество баров после обнаружения, через которое паттерн считается истёкшим.
useGartley Gartley — Включить обнаружение паттерна Gartley.
useButterfly Butterfly — Включить обнаружение паттерна Butterfly.
useBat Bat — Включить обнаружение паттерна Bat.
useCrab Crab — Включить обнаружение паттерна Crab.
useAltBat Alternate Bat — Включить обнаружение альтернативного Bat.
useDeepCrab Deep Crab — Включить обнаружение Deep Crab.
useCypher Cypher — Включить обнаружение паттерна Cypher.
cypherMin Минимум XC/XA для Cypher — Минимальное соотношение XC к XA для паттерна Cypher.
useABCD AB=CD — Включить обнаружение паттерна AB=CD.
useWhiteSwan White Swan — Включить обнаружение White Swan.
useBlackSwan Black Swan — Включить обнаружение Black Swan.
levelBasis Основа расчёта уровней — База для расчёта уровней входа и целей: по семейству, по AD или по CD.
entryPct Процент входа — Процент от выбранного отрезка для уровня входа.
t1Pct Процент цели 1 — Процент от выбранного отрезка для первой цели.
t2Pct Процент цели 2 — Процент от выбранного отрезка для второй цели.
keepUntil Завершение паттерна при — Условие завершения паттерна: достижение Target 1 или Target 2.
useRR Цели по отношению риск/доход — Рассчитывать цели по соотношению reward/risk вместо процентного отрезка.
rr1 Отношение риск/доход цели 1 — Соотношение reward/risk для первой цели.
rr2 Отношение риск/доход цели 2 — Соотношение reward/risk для второй цели.
entryMode Триггер входа — Условие подтверждения входа: по закрытию, по касанию или по свечному паттерну.
stopMode Режим стопа — Способ расчёта стоп-лосса: по зоне, вручную, по точке безубыточности или трейлинг.
manualPct Ручной стоп % — Процент от цены входа для ручного стоп-лосса.
zonePadPct Отступ зоны проверки — Процентное расширение зоны проверки от отрезка CD.
bePct Безубыточность % — Процент смещения стопа в точку безубыточности от входа к T1.
trailAfter Начало трейлинга — После какого события начинается трейлинг-стоп: после входа или после T1.
trailMult Множитель трейлинга — Множитель расстояния от входа до T1 для расчёта трейлинга.
invalidSource Источник инвалидации — Источник цены для проверки инвалидации до входа: закрытие или высокий/низкий.
requireEntrySide Требование стороны цены — Требовать, чтобы цена была ниже/выше уровня входа при подтверждении.
entryOvershootPct Допуск отклонения входа — Допустимое отклонение цены от уровня входа в процентах.
zoneStyle Стиль линии зоны — Стиль линии зоны подтверждения: сплошной, пунктирный или точечный.
requireZone Требовать зону подтверждения — Требовать, чтобы закрытие оставалось внутри зоны подтверждения.
showPatterns Рисовать паттерны — Отображать линии и фигуры паттернов на графике.
hideLetters Скрыть подписи XABCD — Не отображать буквенные метки точек XABCD.
showOnlyLevels Только уровни — Показывать только уровни входа, целей и стопа без линий паттерна.
showRatios Показывать соотношения — Отображать числовые значения соотношений на графике.
showLevels Рисовать уровни — Отображать уровни входа, целей и стопа на графике.
showZones Показать зоны подтверждения — Отображать зоны валидации паттернов на графике.
showFill Заливка паттернов — Заливать треугольники паттернов цветом.
fillAlpha Прозрачность заливки — Уровень прозрачности заливки паттернов.
colorMode Цвета паттернов — Режим окраски паттернов: классическая палитра или по направлению.
labelSize Размер подписей — Размер текста подписей на графике.
showGuides Внутренние диагонали — Показывать вспомогательные измерительные линии внутри паттернов.
levelLength Длина сегмента уровня — Длина горизонтального сегмента уровня на графике.
palette1 Цвет паттерна 1 — Первый цвет палитры для паттернов.
palette2 Цвет паттерна 2 — Второй цвет палитры для паттернов.
palette3 Цвет паттерна 3 — Третий цвет палитры для паттернов.
palette4 Цвет паттерна 4 — Четвёртый цвет палитры для паттернов.
palette5 Цвет паттерна 5 — Пятый цвет палитры для паттернов.
palette6 Цвет паттерна 6 — Шестой цвет палитры для паттернов.
palette7 Цвет паттерна 7 — Седьмой цвет палитры для паттернов.
palette8 Цвет паттерна 8 — Восьмой цвет палитры для паттернов.
palette9 Цвет паттерна 9 — Девятый цвет палитры для паттернов.
palette10 Цвет паттерна 10 — Десятый цвет палитры для паттернов.
longColor Цвет длинной позиции — Цвет для обозначения бычьих (длинных) паттернов.
shortColor Цвет короткой позиции — Цвет для обозначения медвежьих (коротких) паттернов.
textColor Цвет текста — Цвет текста на графике.
lineWidth Толщина линий — Толщина линий паттернов на графике.
patternStyle Стиль линий паттерна — Стиль линий паттернов: сплошной, пунктирный или точечный.
levelStyle Стиль линий уровней — Стиль линий уровней: сплошной, пунктирный или точечный.
projection Проекция уровней — Количество баров вправо для проекции горизонтальных уровней.
showOnlyActive Только активные паттерны — Показывать на графике только активные (незавершённые) паттерны.
keepLast Сохранять последний паттерн — Оставлять последний завершённый паттерн на графике.
maxShown Максимум отображаемых — Максимальное количество паттернов, отображаемых на графике.
historyCount Минимум сохранённых — Минимальное количество завершённых паттернов, оставляемых на графике.
predict Показать прогноз зоны D — Отображать возможную зону D на основе текущей структуры свинг-пивотов.
predDisplay Отображение прогноза — Что показывать для прогноза: паттерн с меткой, только паттерн или только метку.
predMaxFamilies Максимум семейств в прогнозе — Максимальное количество семейств паттернов в метке прогноза (0 — все).
predDirection Направление прогноза — Направление для прогноза: оба, только длинные или только короткие.
predColor Цвет линии прогноза — Цвет линий прогнозной зоны D.
predZoneColor Цвет зоны прогноза — Цвет заливки прогнозной зоны.
predLabelColor Цвет фона метки прогноза — Фон метки прогнозной зоны.
predTextColor Цвет текста прогноза — Цвет текста в метке прогноза.
predWidth Толщина линии прогноза — Толщина линий прогноза.
predStyle Стиль линии прогноза — Стиль линий прогноза: сплошной, пунктирный, точечный или отсутствует.
showPanel Панель активных паттернов — Показывать таблицу активных паттернов на графике.
showStats Счётчики жизненного цикла — Показывать счётчики найденных, вошедших, достигших целей и стопов.
panelSize Размер панели — Размер панели активных паттернов.
panelPos Позиция панели — Вертикальное расположение панели на графике.
panelAlign Выравнивание панели — Горизонтальное выравнивание панели.
panelLongColor Цвет покупки в панели — Фон ячейки направления Long в панели.
panelShortColor Цвет продажи в панели — Фон ячейки направления Short в панели.
panelDoneColor Цвет достигнутого — Фон ячейки для достигнутого уровня в панели.
Получить полный код

Поиск по коду файла

indicator("HARMONIC Lab", "Harmonic Lab", overlay = true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 200, max_polylines_count = 100, max_bars_back = 5000)

gSetup = "01 · Detection | Detection"
depth = input.int(8, "Minimum ZigZag Period | Minimum ZigZag Period", minval = 4, maxval = 50, group = gSetup)
dMode = input.string("Developing D (bar close)", "D detection mode | D detection mode", options = ["Developing D (bar close)", "Confirmed pivot"], group = gSetup, tooltip = "Developing D is tested at candle close before right-side pivot confirmation. It can be superseded by a later extreme; detected patterns remain frozen.")
confirmBars = input.int(2, "Pivot confirmation bars | Pivot confirmation bars", minval = 1, maxval = 20, group = gSetup, tooltip = "A pivot is known this many bars after its location. Signals are emitted only when known.")
multiscale = input.bool(false, "Search 1x / 2x / 3x depth | Search 1x / 2x / 3x depth", group = gSetup)
searchNested = input.bool(true, "Search nested XABCD combinations | Search nested XABCD combinations", group = gSetup)
candidateLimit = input.int(40, "Maximum candidate geometries per depth | Maximum candidate geometries per depth", minval = 1, maxval = 81, group = gSetup)
errorPct = input.float(8, "Error Rate % | Error Rate %", minval = 1, maxval = 20, step = 0.5, group = gSetup)
minSize = input.int(20, "Minimum pattern bars | Minimum pattern bars", minval = 10, maxval = 160, group = gSetup)
maxSize = input.int(150, "Maximum pattern bars | Maximum pattern bars", minval = 50, maxval = 200, group = gSetup)

windowLow = ta.lowest(low, maxSize)
windowHigh = ta.highest(high, maxSize)
minHeight = input.float(0, "Minimum pattern height % | Minimum pattern height %", minval = 0, step = 0.1, group = gSetup)
minCD = input.int(1, "Minimum C–D bars | Minimum C–D bars", minval = 1, maxval = 100, group = gSetup)
direction = input.string("Both", "Direction | Direction", options = ["Both", "Long", "Short"], group = gSetup)
sessionHours = input.session("0000-0000", "Detection session | Detection session", group = gSetup)
utcOffset = input.int(0, "Session UTC offset | Session UTC offset", minval = -12, maxval = 14, group = gSetup)
maxPerBar = input.int(2, "Maximum new patterns per bar | Maximum new patterns per bar", minval = 1, maxval = 3, group = gSetup)
maxActive = input.int(8, "Maximum active patterns | Maximum active patterns", minval = 1, maxval = 20, group = gSetup)
noSame = input.bool(true, "Block same active pattern / direction | Block same active pattern / direction", group = gSetup)
maxAge = input.int(160, "Expire after bars from detection | Expire after bars from detection", minval = 1, maxval = 2000, group = gSetup)

gPatterns = "02 · Pattern families | Pattern families"
useGartley = input.bool(true, "Gartley | Gartley", group = gPatterns)
useButterfly = input.bool(true, "Butterfly | Butterfly", group = gPatterns)
useBat = input.bool(true, "Bat | Bat", group = gPatterns)
useCrab = input.bool(true, "Crab | Crab", group = gPatterns)
useAltBat = input.bool(true, "Alternate Bat | Alternate Bat", group = gPatterns)
useDeepCrab = input.bool(true, "Deep Crab | Deep Crab", group = gPatterns)
useCypher = input.bool(true, "Cypher | Cypher", group = gPatterns)
cypherMin = input.float(1.13, "Cypher minimum XC / XA | Cypher minimum XC / XA", options = [1.13, 1.272], group = gPatterns)
useABCD = input.bool(true, "AB=CD", group = gPatterns)
useWhiteSwan = input.bool(true, "White Swan | White Swan", group = gPatterns)
useBlackSwan = input.bool(true, "Black Swan | Black Swan", group = gPatterns)

gLevels = "03 · Entry and targets | Entry and targets"
levelBasis = input.string("Standard (by family)", "Level calculation basis | Level calculation basis", options = ["Standard (by family)", "Standard (legacy)", "CD for all", "AD for XABCD"], group = gLevels, tooltip = "Calibrated modes use AD for Black Swan, Bat and Anti-Cypher, CD for White Swan and uncalibrated harmonic families. The older Swan option is retained as an alias for saved settings.")
entryPct = input.float(10, "Entry % of selected leg | Entry % of selected leg", minval = 0, maxval = 99, group = gLevels)
t1Pct = input.float(40, "Target 1 % of selected leg | Target 1 % of selected leg", minval = 1, maxval = 300, group = gLevels)
t2Pct = input.float(94, "Target 2 % of selected leg | Target 2 % of selected leg", minval = 2, maxval = 500, group = gLevels)
keepUntil = input.string("Target 2", "Finish pattern at | Finish pattern at", options = ["Target 1", "Target 2"], group = gLevels)
useRR = input.bool(false, "Set targets by reward / risk | Set targets by reward / risk", group = gLevels)
rr1 = input.float(1, "Target 1 reward / risk | Target 1 reward / risk", minval = 0.1, step = 0.1, group = gLevels)
rr2 = input.float(2, "Target 2 reward / risk | Target 2 reward / risk", minval = 0.2, step = 0.1, group = gLevels)
entryMode = input.string("High / low touch", "Entry trigger | Entry trigger", options = ["Close confirmation", "High / low touch", "Candle confirmation"], group = gLevels, tooltip = "All modes evaluate at bar close. Close confirmation needs a close beyond the entry level; High / low touch needs a wick touch; Candle confirmation needs a bullish/bearish engulfing or pin bar that touches the validation zone, and enters at that close.")

gRisk = "04 · Stop and validation | Stop and validation"
stopMode = input.string("Zone", "Stop mode | Stop mode", options = ["Zone", "Manual %", "Break even", "Trailing"], group = gRisk)
manualPct = input.float(1, "Manual stop % of entry | Manual stop % of entry", minval = 0.01, maxval = 50, step = 0.1, group = gRisk)
zonePadPct = input.float(0, "Validation band padding % of CD | Validation band padding % of CD", minval = 0, maxval = 25, step = 0.5, group = gRisk)
bePct = input.float(0, "Break-even % from entry toward T1 | Break-even % from entry toward T1", minval = 0, maxval = 100, group = gRisk)
trailAfter = input.string("Target 1", "Trail after | Trail after", options = ["Entry", "Target 1"], group = gRisk)
trailMult = input.float(1, "Trail distance / entry-to-T1 | Trail distance / entry-to-T1", minval = 0.1, step = 0.1, group = gRisk)
invalidSource = input.string("Close", "Pending invalidation source | Pending invalidation source", options = ["Close", "High / low"], group = gRisk)
requireEntrySide = input.bool(false, "Price must be lower/higher than Entry | Price must be lower/higher than Entry", group = gRisk)
entryOvershootPct = input.float(0, "Can be lower/higher % | Can be lower/higher %", minval = 0, maxval = 5, step = 0.1, group = gRisk)
zoneStyle = input.string("Dotted", "Validation line style | Validation line style", options = ["Solid", "Dashed", "Dotted"], group = gRisk)
requireZone = input.bool(false, "Confirmation close must remain in validation band | Confirmation close must remain in validation band", group = gRisk)

gView = "05 · Appearance | Appearance"
showPatterns = input.bool(true, "Draw patterns | Draw patterns", group = gView)
hideLetters = input.bool(false, "Dont show XABCD labels | Dont show XABCD labels", group = gView)
showOnlyLevels = input.bool(false, "Show Only Levels | Show Only Levels", group = gView)
showRatios = input.bool(true, "Show ratio measurements | Show ratio measurements", group = gView)
showLevels = input.bool(true, "Draw entry / targets / stop | Draw entry / targets / stop", group = gView)
showZones = input.bool(true, "Show Pattern Validation Zones | Show Pattern Validation Zones", group = gView, tooltip = "This is not the displayed prediction PRZ. Visibility never changes detection, stops or targets.")
showFill = input.bool(true, "Shade patterns | Shade patterns", group = gView)
fillAlpha = input.int(70, "Shading transparency | Shading transparency", minval = 0, maxval = 100, group = gView)
colorMode = input.string("Classic palette", "Pattern colors | Pattern colors", options = ["Classic palette", "Direction"], group = gView)
labelSize = input.string("normal", "Pattern text size | Pattern text size", options = ["tiny", "small", "normal", "large"], group = gView)
showGuides = input.bool(true, "Internal measurement diagonals | Internal measurement diagonals", group = gView)
levelLength = input.int(6, "Level segment length | Level segment length", minval = 2, maxval = 40, group = gView)
palette1 = input.color(#2962ff, "Pattern color 1 | Pattern color 1", group = gView)
palette2 = input.color(#F23645, "Pattern color 2 | Pattern color 2", group = gView)
palette3 = input.color(#00E676, "Pattern color 3 | Pattern color 3", group = gView)
palette4 = input.color(#787B86, "Pattern color 4 | Pattern color 4", group = gView)
palette5 = input.color(#FF9800, "Pattern color 5 | Pattern color 5", group = gView)
palette6 = input.color(#B2B5BE, "Pattern color 6 | Pattern color 6", group = gView)
palette7 = input.color(#FDD835, "Pattern color 7 | Pattern color 7", group = gView)
palette8 = input.color(#808000, "Pattern color 8 | Pattern color 8", group = gView)
palette9 = input.color(#512da8, "Pattern color 9 | Pattern color 9", group = gView)
palette10 = input.color(#4dd0e1, "Pattern color 10 | Pattern color 10", group = gView)
longColor = input.color(#22ab94, "Bullish color | Bullish color", group = gView)
shortColor = input.color(#f7525f, "Bearish color | Bearish color", group = gView)
textColor = input.color(color.white, "Text color | Text color", group = gView)
lineWidth = input.int(2, "Pattern width | Pattern width", minval = 1, maxval = 4, group = gView)
patternStyle = input.string("Solid", "Pattern line style | Pattern line style", options = ["Solid", "Dashed", "Dotted"], group = gView)
levelStyle = input.string("Solid", "Level line style | Level line style", options = ["Solid", "Dashed", "Dotted"], group = gView)
projection = input.int(12, "Level projection bars | Level projection bars", minval = 1, maxval = 100, group = gView)
showOnlyActive = input.bool(false, "Show Only Active Patterns | Show Only Active Patterns", group = gView)
keepLast = input.bool(true, "Keep Last Pattern on the Chart | Keep Last Pattern on the Chart", group = gView)
maxShown = input.int(6, "Maximum patterns drawn | Maximum patterns drawn", minval = 1, maxval = 12, group = gView, tooltip = "Only the newest patterns keep their drawings; older ones are removed from the chart but keep running their lifecycle, counters and alerts. The prediction is not counted.")
historyCount = input.int(0, "Completed patterns retained (minimum) | Completed patterns retained (minimum)", minval = 0, maxval = 12, group = gView, tooltip = "Completed patterns stay on the chart until they are older than the expiry age, up to 12. This sets a minimum kept beyond that age.")

gPrediction = "06 · Projection | Projection"
predict = input.bool(true, "Show nearest possible D zone | Show nearest possible D zone", group = gPrediction, tooltip = "Unconfirmed illustration only. Can move or disappear.")
predDisplay = input.string("Pattern&Label", "Show Prediction => | Show Prediction =>", options = ["Pattern&Label", "Only Pattern", "Only Label"], group = gPrediction)
predMaxFamilies = input.int(4, "Families listed in the label (0 = all) | Families listed in the label (0 = all)", minval = 0, maxval = 30, group = gPrediction, tooltip = "Nearest zones first. its cap is unknown.")
predDirection = input.string("Both", "Prediction for | Prediction for", options = ["Both", "Long", "Short"], group = gPrediction)
predColor = input.color(#2962ff, "Predicted Line Color | Predicted Line Color", group = gPrediction)
predZoneColor = input.color(color.new(#2962ff, 75), "Prediction Zone Color | Prediction Zone Color", group = gPrediction)
predLabelColor = input.color(#2962ff, "Prediction Label Color | Prediction Label Color", group = gPrediction)
predTextColor = input.color(color.white, "Prediction Text Color | Prediction Text Color", group = gPrediction)
predWidth = input.int(3, "Prediction line width | Prediction line width", minval = 1, maxval = 4, group = gPrediction)
predStyle = input.string("Dotted", "Prediction line style | Prediction line style", options = ["Solid", "Dashed", "Dotted", "None"], group = gPrediction)

gPanel = "07 · Panels | Panels"
showPanel = input.bool(true, "Show Active Patterns in the Panel | Show Active Patterns in the Panel", group = gPanel)
showStats = input.bool(false, "Lifecycle counters (below the panel) | Lifecycle counters (below the panel)", group = gPanel)
panelSize = input.string("small", "Panel Size | Panel Size", options = ["tiny", "small", "normal", "large"], group = gPanel)
panelPos = input.string("top", "Panel Position | Panel Position", options = ["bottom", "middle", "top"], group = gPanel)
panelAlign = input.string("center", "Panel Align | Panel Align", options = ["left", "center", "right"], group = gPanel)
panelLongColor = input.color(#4caf50, "Panel Long | Panel Long", group = gPanel, inline = "pc")
panelShortColor = input.color(#f23645, "Short | Short", group = gPanel, inline = "pc")
panelDoneColor = input.color(#787b86, "Reached | Reached", group = gPanel, inline = "pc")
panelHeadBg = input.color(#2962ff, "Header | Header", group = gPanel, inline = "ph")
panelHeadText = input.color(color.white, "Header text | Header text", group = gPanel, inline = "ph")
panelBorder = input.color(color.white, "Border | Border", group = gPanel, inline = "ph")
startDate = input.time(timestamp("01 Jan 2021 00:00 +0000"), "Detect from | Detect from", group = gPanel)

gAlert = "08 · Alerts | Alerts"
aNew = input.bool(true, "New pattern | New pattern", group = gAlert)
aEntry = input.bool(true, "Entry | Entry", group = gAlert)
aT1 = input.bool(true, "Target 1 | Target 1", group = gAlert)
aT2 = input.bool(true, "Target 2 | Target 2", group = gAlert)
aStop = input.bool(true, "Stop | Stop", group = gAlert)
aInvalid = input.bool(true, "Invalidation | Invalidation", group = gAlert)
aExpired = input.bool(false, "Expiry | Expiry", group = gAlert)
alertTemplate = input.string("{ticker} | {tf} | {pattern} | {side} | {event} | Entry={entry} T1={t1} T2={t2} SL={sl}", "Dynamic alert template | Dynamic alert template", group = gAlert)
outsideBarMode = input.string("Both (continue, then reverse)", "Two-sided extreme bar | Two-sided extreme bar", options = ["Both (continue, then reverse)", "Continue current leg", "Opposite leg (legacy)"], group = gSetup, tooltip = "A bar that is both the highest high and the lowest low of the window. Continue current leg keeps one endpoint per bar;")
swingMethod = input.string("Rolling extrema", "Swing construction | Swing construction", options = ["Rolling extrema", "Confirmed pivots"], group = gSetup, tooltip = "Rolling extrema updates pivots at bar close without waiting for right-side bars. Confirmed pivots preserves the previous construction and uses D detection mode.")
stopSource = input.string("Close", "Stop trigger source | Stop trigger source", options = ["Close", "High / low"], group = gRisk, tooltip = "Close requires a confirmed candle close at or beyond the stop. High / low counts a wick touch. this indicator does not place broker stop orders.")

useShark = input.bool(true, "Shark | Shark", group = gPatterns)
useNenStar = input.bool(true, "Nen Star | Nen Star", group = gPatterns)
useLeonardo = input.bool(true, "Leonardo | Leonardo", group = gPatterns)
usePartizan = input.bool(true, "Partizan | Partizan", group = gPatterns)
useFiveZero = input.bool(true, "5-0", group = gPatterns)
useAntiGartley = input.bool(true, "Anti-Gartley | Anti-Gartley", group = gPatterns)
useAntiButterfly = input.bool(true, "Anti-Butterfly | Anti-Butterfly", group = gPatterns)
useAntiBat = input.bool(true, "Anti-Bat | Anti-Bat", group = gPatterns)
useAntiCrab = input.bool(true, "Anti-Crab | Anti-Crab", group = gPatterns)
useAntiShark = input.bool(true, "Anti-Shark | Anti-Shark", group = gPatterns)
useAntiCypher = input.bool(true, "Anti-Cypher | Anti-Cypher", group = gPatterns)
useAntiNenStar = input.bool(true, "Anti-Nen Star | Anti-Nen Star", group = gPatterns)
useThreeDrive = input.bool(true, "3-Drive | Three Drives", group = gPatterns)
useDoubleTop = input.bool(true, "Double Top | Double Top", group = gPatterns)
useDoubleBottom = input.bool(true, "Double Bottom | Double Bottom", group = gPatterns)
useHS = input.bool(true, "Head and Shoulders | Head and Shoulders", group = gPatterns)
useIHS = input.bool(true, "Inverse Head and Shoulders | Inverse Head and Shoulders", group = gPatterns)
useAscending = input.bool(true, "Ascending Triangle | Ascending Triangle", group = gPatterns)
useDescending = input.bool(true, "Descending Triangle | Descending Triangle", group = gPatterns)
useSymmetrical = input.bool(true, "Symmetrical Triangle | Symmetrical Triangle", group = gPatterns)
classicTolerance = input.float(8, "Classical symmetry tolerance % of height | Classical symmetry tolerance % of height", minval = 1, maxval = 30, group = gPatterns)
hsSkip = input.int(1, "H&S contained waves skipped per leg | H&S contained waves skipped per leg", minval = 0, maxval = 3, group = gPatterns)
hsTail = input.int(1, "H&S pivots allowed after the right shoulder | H&S pivots allowed after the right shoulder", minval = 0, maxval = 3, group = gPatterns, tooltip = "The break bar usually adds one pivot after the right shoulder.")
hsShoulderTolerance = input.float(50, "H&S shoulder difference % of head depth | H&S shoulder difference % of head depth", minval = 1, maxval = 100, group = gPatterns, tooltip = "Head and shoulders only. Provisional bound;")
triangleNested = input.bool(false, "Triangles from nested legs | Triangles from nested legs", group = gPatterns, tooltip = "Off: X, A, B, C must be consecutive zigzag pivots.")
triangleMinRetrace = input.float(0.618, "Triangle minimum leg retracement | Triangle minimum leg retracement", minval = 0, maxval = 0.95, step = 0.001, group = gPatterns, tooltip = "Provisional hard minimum for AB/XA and BC/AB, without an additional Error Rate allowance. Set 0 to disable this minimum.")
useABCDTime = input.bool(true, "AB=CD time symmetry filter | AB=CD time symmetry filter", group = gPatterns, tooltip = "The original author documents comparing AB/CD durations. The current hidden tolerance is unknown; this independent filter is configurable.")
abcdTimeShort = input.float(20, "AB=CD: CD may be shorter than AB by % | AB=CD: CD may be shorter than AB by %", minval = 0, maxval = 100, step = 1, group = gPatterns)
abcdTimeError = input.float(50, "AB=CD: CD may be longer than AB by % | AB=CD: CD may be longer than AB by %", minval = 0, maxval = 200, step = 1, group = gPatterns, tooltip = "Provisional;")

showPRZ = input.bool(true, "Draw prediction PRZ | Draw prediction PRZ", group = gPrediction, tooltip = "Display only. Uses the family completion range independently of diagnostic acceptance bands. Does not change signals or risk levels.")
cypherOldest = input.bool(true, "Cypher forecast: prefer oldest valid X | Cypher forecast: prefer oldest valid X", group = gPrediction, tooltip = "Keeps all ratio rules unchanged. Disable to restore nearest-price selection; not proven for every market.")

if barstate.isfirst
    if minSize > maxSize
        runtime.error("Minimum pattern bars must not exceed maximum.")
    if not useRR and (entryPct >= t1Pct or t1Pct >= t2Pct)
        runtime.error("Require Entry % < Target 1 % < Target 2 %.")
    if useRR and rr1 >= rr2
        runtime.error("Target 2 reward/risk must exceed Target 1.")

type Rule
    string name
    bool enabled
    float b0
    float b1
    float c0
    float c1
    float e0
    float e1
    float d0
    float d1
    int mode

type Pivot
    float price
    int idx
    int sign

type Swing
    array<Pivot> points

type Candidate
    Pivot x
    Pivot a
    Pivot b
    Pivot c
    Pivot d

type Pattern
    string name
    string key
    int side
    int born
    int state
    int entryBar
    int doneBar
    float entry
    float t1
    float t2
    float stop
    float zlo
    float zhi
    float best
    array<line> legs
    array<label> tags
    array<polyline> fills
    array<line> levels
    array<label> levelTags
    box zone
    color ink
    bool reachedT1
    bool hidden = false
    int dBar = na
    bool inZone = false
    label nameTag = na
    float quality = na
    bool diverge = false
    float risk0 = na
    float worst = na
    bool volSpike = false

var array<Rule> rules = array.new<Rule>()
if barstate.isfirst
    array.push(rules, Rule.new("Gartley", useGartley, .618, .618, .382, .886, 1.13, 1.618, .786, .786, 0))
    array.push(rules, Rule.new("Butterfly", useButterfly, .786, .786, .382, .886, 1.618, 2.618, 1.27, 1.618, 0))
    array.push(rules, Rule.new("Bat", useBat, .382, .50, .382, .886, 1.618, 2.618, .886, .886, 0))
    array.push(rules, Rule.new("Crab", useCrab, .382, .618, .382, .886, 2.24, 3.618, 1.618, 1.618, 0))
    array.push(rules, Rule.new("Alternate Bat", useAltBat, .236, .382, .382, .886, 2.0, 3.618, 1.13, 1.13, 0))
    array.push(rules, Rule.new("Deep Crab", useDeepCrab, .886, .886, .382, .886, 2.0, 3.618, 1.618, 1.618, 0))
    array.push(rules, Rule.new("Cypher", useCypher, .382, .618, cypherMin, 1.414, 0, 0, .786, .786, 1))

    array.push(rules, Rule.new("AB=CD", useABCD, 0, 0, .618, .886, 1.13, 1.618, 1, 1, 2))

    array.push(rules, Rule.new("White Swan", useWhiteSwan, .382, .786, 2.0, 4.237, .5, .886, .236, .886, 0))
    array.push(rules, Rule.new("Black Swan", useBlackSwan, 1.382, 2.618, .236, .5, 1.128, 2.0, 1.128, 2.618, 0))

    array.push(rules, Rule.new("Shark", useShark, 0.382, 0.618, 1.128, 1.618, 1.618, 2.236, 0.886, 1.13, 0))
    array.push(rules, Rule.new("Nen Star", useNenStar, 0.382, 0.618, 1.414, 2.14, 1.128, 2, 1.272, 1.272, 0))
    array.push(rules, Rule.new("Leonardo", useLeonardo, 0.5, 0.5, 0.382, 0.886, 1.128, 2.618, 0.786, 0.786, 0))
    array.push(rules, Rule.new("Partizan", usePartizan, 0.128, 3.618, 0.382, 0.382, 1.618, 1.618, 0.618, 3.618, 0))
    array.push(rules, Rule.new("5-0", useFiveZero, 1.13, 1.618, 1.618, 2.24, 0.5, 0.5, 0.5, 0.5, 3))
    array.push(rules, Rule.new("Anti-Gartley", useAntiGartley, 0.618, 0.786, 1.128, 2.618, 1.618, 1.618, 1.272, 1.272, 0))
    array.push(rules, Rule.new("Anti-Butterfly", useAntiButterfly, 0.382, 0.618, 1.128, 2.618, 1.272, 1.272, 0.618, 0.786, 0))
    array.push(rules, Rule.new("Anti-Bat", useAntiBat, 0.382, 0.618, 1.128, 2.618, 2, 2.618, 1.128, 1.128, 0))
    array.push(rules, Rule.new("Anti-Crab", useAntiCrab, 0.276, 0.446, 1.128, 2.618, 1.618, 2.618, 0.618, 0.618, 0))
    array.push(rules, Rule.new("Anti-Shark", useAntiShark, 0.446, 0.618, 0.618, 0.886, 1.618, 2.618, 1.128, 1.128, 0))

    array.push(rules, Rule.new("Anti-Cypher", useAntiCypher, 0.5, 0.786, 0.476, 0.707, 1.618, 2.618, 1.272, 1.272, 0))
    array.push(rules, Rule.new("Anti-Nen Star", useAntiNenStar, 0.5, 0.886, 0.467, 0.707, 1.618, 2.618, 0.786, 0.786, 0))

var array<Swing> swings = array.new<Swing>()
if barstate.isfirst
    for scale = 0 to 2
        array.push(swings, Swing.new(array.new<Pivot>()))
var array<Pattern> patterns = array.new<Pattern>()

var array<string> seen = array.new<string>()

var array<string> seenGeom = array.new<string>()
var int foundCount = 0
var int enteredCount = 0
var int target1Count = 0
var int target2Count = 0
var int stopCount = 0
var int invalidCount = 0
var int expiredCount = 0

var array<string> perfNames = array.new<string>()
var array<int> perfEntered = array.new<int>()
var array<int> perfT1 = array.new<int>()
var array<int> perfT2 = array.new<int>()
var array<int> perfLoss = array.new<int>()

var array<int> perfBars = array.new<int>()
var array<float> perfR = array.new<float>()
var array<float> perfMAE = array.new<float>()
var array<int> perfClosed = array.new<int>()
var float perfAutoMin = 0
var bool perfDeep = true
f_perfIndex(string name) =>
    int i = array.indexof(perfNames, name)
    if i < 0
        array.push(perfNames, name)
        array.push(perfEntered, 0)
        array.push(perfT1, 0)
        array.push(perfT2, 0)
        array.push(perfLoss, 0)
        array.push(perfBars, 0)
        array.push(perfR, 0.0)
        array.push(perfMAE, 0.0)
        array.push(perfClosed, 0)
        i := array.size(perfNames) - 1
    i
f_perfResult(Pattern p, float exitPrice, bool win) =>
    int i = f_perfIndex(p.name)
    float risk = math.max(p.risk0, syminfo.mintick)
    array.set(perfR, i, array.get(perfR, i) + (exitPrice - p.entry) * p.side / risk)
    array.set(perfMAE, i, array.get(perfMAE, i) + math.max(0, (p.entry - p.worst) * p.side) / risk)
    array.set(perfClosed, i, array.get(perfClosed, i) + 1)
    if win
        array.set(perfBars, i, array.get(perfBars, i) + (bar_index - p.entryBar))

f_autoBlocked(string name) =>
    bool blocked = false
    if perfAutoMin > 0
        int i = array.indexof(perfNames, name)
        if i >= 0
            int w = array.get(perfT1, i)
            int l = array.get(perfLoss, i)
            if w + l >= 5 and 100.0 * w / (w + l) < perfAutoMin
                blocked := true
    blocked
f_perf(string name, int kind) =>
    int i = f_perfIndex(name)
    array<int> target = kind == 0 ? perfEntered : kind == 1 ? perfT1 : kind == 2 ? perfT2 : perfLoss
    array.set(target, i, array.get(target, i) + 1)
var array<line> predLines = array.new<line>()
var array<polyline> predFills = array.new<polyline>()
var array<label> predTags = array.new<label>()
var box predBox = na
var label predLabel = na

f_style(string s) =>
    s == "Solid" ? line.style_solid : s == "Dashed" ? line.style_dashed : line.style_dotted
f_band(float value, float lo, float hi) =>
    value >= lo * (1 - errorPct / 100) and value <= hi * (1 + errorPct / 100)
f_num(float n) =>
    str.tostring(n, format.mintick)
f_state(int n) =>
    switch n
        0 => "PENDING"
        1 => "ENTERED"
        2 => "T1"
        3 => "DONE"
        4 => "STOP"
        5 => "INVALID"
        => "EXPIRED"

f_timeOK(int mode, int aIdx, int bIdx, int cIdx, int dIdx) =>
    int abBars = bIdx - aIdx
    int cdBars = dIdx - cIdx
    mode != 2 or not useABCDTime or (abBars > 0 and cdBars > 0 and cdBars / float(abBars) >= 1 - abcdTimeShort / 100 - 1e-9 and cdBars / float(abBars) <= 1 + abcdTimeError / 100 + 1e-9)
f_timeOpen(int mode, int aIdx, int bIdx, int cIdx) =>
    mode != 2 or not useABCDTime or (bIdx > aIdx and bar_index - cIdx <= (bIdx - aIdx) * (1 + abcdTimeError / 100))

var float trendMa = na
var bool trendOn = false

float rsi14 = ta.rsi(close, 14)
var bool divOn = true
var string divMark = "↯"
var bool aDiv = true

float volAvg20 = ta.sma(volume, 20)
var bool volOn = true
var float volMult = 1.5
var string volMark = "⚡"
var bool volRequire = false
var bool aVol = true
f_volSpike(Pivot d) =>
    int n = bar_index - d.idx
    bool spike = false
    if n >= 0 and not na(volume[n]) and not na(volAvg20[n]) and volAvg20[n] > 0
        spike := volume[n] >= volMult * volAvg20[n]
    spike
f_div(int side, Pivot ref, Pivot d) =>
    int nd = bar_index - d.idx
    int nr = bar_index - ref.idx
    bool beyond = side == 1 ? d.price < ref.price : d.price > ref.price
    beyond and nd >= 0 and nr >= 0 and (side == 1 ? rsi14[nd] > rsi14[nr] : rsi14[nd] < rsi14[nr])
f_allowed(int side) =>
    bool dirOK = direction == "Both" or (direction == "Long" and side == 1) or (direction == "Short" and side == -1)
    bool trendOK = not trendOn or na(trendMa) or (side == 1 ? close > trendMa : close < trendMa)
    dirOK and trendOK
f_active() =>
    int n = 0
    for p in patterns
        if p.state < 3
            n += 1
    n
f_blocked(string name, int side) =>
    bool result = false
    if noSame
        for p in patterns
            if p.name == name and p.side == side and p.state < 3
                result := true
    result

f_update(Swing sw, float ph, float pl) =>
    bool changed = false
    if (not na(ph)) != (not na(pl))
        int sg = not na(ph) ? 1 : -1
        float price = sg == 1 ? ph : pl
        Pivot pt = Pivot.new(price, bar_index - confirmBars, sg)
        int n = array.size(sw.points)
        if n == 0
            array.push(sw.points, pt)
            changed := true
        else
            Pivot prev = array.get(sw.points, n - 1)
            if pt.idx > prev.idx
                if sg == prev.sign
                    if (price - prev.price) * sg > 0
                        array.set(sw.points, n - 1, pt)
                        changed := true
                else if (price - prev.price) * sg > syminfo.mintick
                    array.push(sw.points, pt)
                    changed := true
        if array.size(sw.points) > 20
            array.shift(sw.points)
    changed

f_updateRolling(Swing sw, bool newHigh, bool newLow) =>
    bool changed = false
    if newHigh or newLow

        int previousSign = array.size(sw.points) > 0 ? array.get(sw.points, array.size(sw.points) - 1).sign : 0

        int sg = newHigh and newLow ? (outsideBarMode == "Opposite leg (legacy)" ? -previousSign : previousSign) : newHigh ? 1 : -1
        float value = sg == 1 ? high : low
        Pivot pt = Pivot.new(value, bar_index, sg)
        int n = array.size(sw.points)
        if n == 0 and sg != 0
            array.push(sw.points, pt)
            changed := true
        else if n > 0
            Pivot prev = array.get(sw.points, n - 1)
            if bar_index > prev.idx and (value - prev.price) * sg > syminfo.mintick
                if sg == prev.sign
                    array.set(sw.points, n - 1, pt)
                else
                    array.push(sw.points, pt)
                changed := true

        if newHigh and newLow and outsideBarMode == "Both (continue, then reverse)" and previousSign != 0
            int n2 = array.size(sw.points)
            Pivot last = array.get(sw.points, n2 - 1)
            int sg2 = -last.sign
            float value2 = sg2 == 1 ? high : low
            if (value2 - last.price) * sg2 > syminfo.mintick
                array.push(sw.points, Pivot.new(value2, bar_index, sg2))
                changed := true
        if array.size(sw.points) > 20
            array.shift(sw.points)
    changed

f_legContained(Swing sw, int fromIndex, int toIndex) =>
    Pivot left = array.get(sw.points, fromIndex)
    Pivot right = array.get(sw.points, toIndex)
    float lo = math.min(left.price, right.price) - syminfo.mintick * 0.1
    float hi = math.max(left.price, right.price) + syminfo.mintick * 0.1
    bool ok = true
    if toIndex - fromIndex > 1
        for k = fromIndex + 1 to toIndex - 1
            float v = array.get(sw.points, k).price
            if v < lo or v > hi
                ok := false
                break
    ok

f_xabcdCandidates(Swing sw, int limit) =>
    array<Candidate> result = array.new<Candidate>()
    int di = array.size(sw.points) - 1

    int maxCG = searchNested ? int(math.floor((di - 4) / 2)) : 0
    for cg = 0 to maxCG
        if array.size(result) >= limit
            break
        int ci = di - 1 - 2 * cg
        if not f_legContained(sw, ci, di)
            continue
        int maxBG = searchNested ? int(math.floor((ci - 3) / 2)) : 0
        for bg = 0 to maxBG
            if array.size(result) >= limit
                break
            int bi = ci - 1 - 2 * bg
            if not f_legContained(sw, bi, ci)
                continue
            int maxAG = searchNested ? int(math.floor((bi - 2) / 2)) : 0
            for ag = 0 to maxAG
                if array.size(result) >= limit
                    break
                int ai = bi - 1 - 2 * ag
                if not f_legContained(sw, ai, bi)
                    continue
                int maxXG = searchNested ? int(math.floor((ai - 1) / 2)) : 0
                for xg = 0 to maxXG
                    if array.size(result) >= limit
                        break
                    int xi = ai - 1 - 2 * xg
                    if xi >= 0 and f_legContained(sw, xi, ai)
                        array.push(result, Candidate.new(array.get(sw.points, xi), array.get(sw.points, ai), array.get(sw.points, bi), array.get(sw.points, ci), array.get(sw.points, di)))
    result

f_abcdCandidates(Swing sw, int limit) =>
    array<Candidate> result = array.new<Candidate>()
    int di = array.size(sw.points) - 1
    if di >= 3
        int maxCG = searchNested ? int(math.floor((di - 3) / 2)) : 0
        for cg = 0 to maxCG
            if array.size(result) >= limit
                break
            int ci = di - 1 - 2 * cg
            if not f_legContained(sw, ci, di)
                continue
            int maxBG = searchNested ? int(math.floor((ci - 2) / 2)) : 0
            for bg = 0 to maxBG
                if array.size(result) >= limit
                    break
                int bi = ci - 1 - 2 * bg
                if not f_legContained(sw, bi, ci)
                    continue
                int maxAG = searchNested ? int(math.floor((bi - 1) / 2)) : 0
                for ag = 0 to maxAG
                    if array.size(result) >= limit
                        break
                    int ai = bi - 1 - 2 * ag
                    if ai >= 0 and f_legContained(sw, ai, bi)
                        Pivot a = array.get(sw.points, ai)
                        array.push(result, Candidate.new(a, a, array.get(sw.points, bi), array.get(sw.points, ci), array.get(sw.points, di)))
    result

f_candidates(Swing sw, int limit = 0) =>
    int cap = limit > 0 ? limit : candidateLimit
    array<Candidate> result = array.size(sw.points) >= 5 ? f_xabcdCandidates(sw, cap) : array.new<Candidate>()
    if useABCD
        array.concat(result, f_abcdCandidates(sw, cap))
    result

f_predictionCandidates(Swing sw) =>
    array<Candidate> result = array.new<Candidate>()
    Swing temp = Swing.new(array.copy(sw.points))

    if array.size(temp.points) > 0
        array.pop(temp.points)
    int n = array.size(temp.points)
    if n >= 3
        Pivot c = array.get(temp.points, n - 1)
        array.push(temp.points, Pivot.new(c.price, bar_index + projection, -c.sign))
        array<Candidate> candidates = f_candidates(temp, 81)
        for candidate in candidates
            if candidate.c.idx == c.idx
                array.push(result, candidate)
    result

f_liveCandidates(Swing sw, bool newHigh, bool newLow) =>
    Swing temp = Swing.new(array.copy(sw.points))
    bool ready = false
    if newHigh != newLow and array.size(temp.points) > 0
        int sg = newHigh ? 1 : -1
        float value = newHigh ? high : low
        int lastIndex = array.size(temp.points) - 1
        Pivot prev = array.get(temp.points, lastIndex)
        Pivot live = Pivot.new(value, bar_index, sg)
        if (value - prev.price) * sg > syminfo.mintick and bar_index > prev.idx
            if sg == prev.sign
                array.set(temp.points, lastIndex, live)
            else
                array.push(temp.points, live)
            ready := true
    ready and array.size(temp.points) >= 4 ? f_candidates(temp) : array.new<Candidate>()

f_zone(Rule r, float x, float a, float b, float c, int side) =>
    float xa = math.abs(a - x)
    float ab = math.abs(b - a)
    float bc = math.abs(c - b)
    float xc = math.abs(c - x)
    bool geom = (a - b) * side > 0 and (c - b) * side > 0
    bool valid = ab > syminfo.mintick and bc > syminfo.mintick and geom
    if r.mode != 2
        valid := valid and xa > syminfo.mintick
    if r.mode != 2
        valid := valid and (a - x) * side > 0 and (r.b1 > 1 or (b - x) * side > 0) and f_band(ab / xa, r.b0, r.b1)
    valid := valid and (r.mode == 1 ? (c - a) * side > 0 and f_band(xc / xa, r.c0, r.c1) : (r.c1 > 1 or (a - c) * side > 0) and f_band(bc / ab, r.c0, r.c1))
    float unit = r.mode == 0 ? xa : r.mode == 1 ? xc : r.mode == 3 ? bc : ab
    float anchor = r.mode == 0 ? a : c
    float q0 = anchor - side * unit * r.d0 * (1 - errorPct / 100)
    float q1 = anchor - side * unit * r.d1 * (1 + errorPct / 100)
    float lo = math.min(q0, q1)
    float hi = math.max(q0, q1)
    if r.mode != 1
        float e0 = c - side * bc * r.e0 * (1 - errorPct / 100)
        float e1 = c - side * bc * r.e1 * (1 + errorPct / 100)
        lo := math.max(lo, math.min(e0, e1))
        hi := math.min(hi, math.max(e0, e1))

    bool pastB = r.mode != 1 and r.e0 >= 1
    float boundary = pastB ? (side == 1 ? math.min(b, c) : math.max(b, c)) : c
    if side == 1
        hi := math.min(hi, boundary - syminfo.mintick)
    else
        lo := math.max(lo, boundary + syminfo.mintick)
    if r.mode == 0
        if r.d1 < 1
            if side == 1
                lo := math.max(lo, x + syminfo.mintick)
            else
                hi := math.min(hi, x - syminfo.mintick)
        if r.d0 > 1
            if side == 1
                hi := math.min(hi, x - syminfo.mintick)
            else
                lo := math.max(lo, x + syminfo.mintick)
    [valid and lo <= hi, lo, hi]

f_prz(Rule r, float x, float a, float b, float c, int side) =>
    float displayE1 = r.name == "Shark" ? 2.24 : r.e1
    Rule displayRule = Rule.new(r.name, r.enabled, r.b0, r.b1, r.c0, r.c1, r.e0, displayE1, r.d0, r.mode == 0 and r.d0 >= 1 ? r.d0 : r.d1, r.mode)
    [valid, lo, hi] = f_zone(displayRule, x, a, b, c, side)
    [valid, lo, hi]

f_candleOK(int side) =>
    float body = math.abs(close - open)
    bool bodyOK = (close - open) * side > 0
    bool engulf = bodyOK and (side == 1 ? close > high[1] and open <= close[1] : close < low[1] and open >= close[1])
    float wick = side == 1 ? math.min(open, close) - low : high - math.max(open, close)
    bool pin = wick >= 2 * body and wick > 0 and (side == 1 ? close > (high + low) / 2 : close < (high + low) / 2)
    engulf or pin

var bool alertJson = false
var string alertBotId = ""
f_jsonStr(string v) =>
    "\"" + str.replace_all(str.replace_all(v, "\\", "\\\\"), "\"", "\\\"") + "\""
f_json(Pattern p, string eventName) =>
    string ev = str.lower(str.replace_all(str.replace_all(eventName, " ", "_"), "(", ""))
    ev := str.replace_all(ev, ")", "")
    string j = "{"
    j += "\"event\":" + f_jsonStr(ev)
    j += ",\"symbol\":" + f_jsonStr(syminfo.tickerid)
    j += ",\"ticker\":" + f_jsonStr(syminfo.ticker)
    j += ",\"exchange\":" + f_jsonStr(syminfo.prefix)
    j += ",\"tf\":" + f_jsonStr(timeframe.period)
    j += ",\"pattern\":" + f_jsonStr(p.name)
    j += ",\"side\":" + f_jsonStr(p.side == 1 ? "long" : "short")
    j += ",\"action\":" + f_jsonStr(eventName == "Entry" ? (p.side == 1 ? "buy" : "sell") : eventName == "New pattern" ? "watch" : "close")
    j += ",\"price\":" + f_num(close)
    j += ",\"entry\":" + f_num(p.entry)
    j += ",\"sl\":" + f_num(p.stop)
    j += ",\"tp1\":" + f_num(p.t1)
    j += ",\"tp2\":" + f_num(p.t2)
    j += ",\"quality\":" + (na(p.quality) ? "null" : str.tostring(p.quality, "0"))
    j += ",\"zone\":" + (p.inZone ? "true" : "false")
    j += ",\"divergence\":" + (p.diverge ? "true" : "false")
    j += ",\"volume\":" + (p.volSpike ? "true" : "false")
    j += ",\"time\":" + f_jsonStr(str.format_time(time, "yyyy-MM-dd'T'HH:mm:ss'Z'", "UTC"))
    if alertBotId != ""
        j += ",\"bot_id\":" + f_jsonStr(alertBotId)
    j + "}"

f_emit(Pattern p, string eventName, bool enabled) =>
    if enabled and barstate.isrealtime and barstate.isconfirmed and alertJson
        alert(f_json(p, eventName), alert.freq_all)
    else if enabled and barstate.isrealtime and barstate.isconfirmed
        string msg = alertTemplate
        msg := str.replace_all(msg, "{ticker}", syminfo.tickerid)
        msg := str.replace_all(msg, "{tf}", timeframe.period)
        msg := str.replace_all(msg, "{pattern}", p.name)
        msg := str.replace_all(msg, "{side}", p.side == 1 ? "Long" : "Short")
        msg := str.replace_all(msg, "{event}", eventName)
        msg := str.replace_all(msg, "{entry}", f_num(p.entry))
        msg := str.replace_all(msg, "{t1}", f_num(p.t1))
        msg := str.replace_all(msg, "{t2}", f_num(p.t2))
        msg := str.replace_all(msg, "{sl}", f_num(p.stop))
        msg := str.replace_all(msg, "{price}", f_num(close))
        msg := str.replace_all(msg, "{quality}", na(p.quality) ? "-" : str.tostring(p.quality, "0"))

        alert(msg, alert.freq_all)

f_color(int side) =>
    array<color> palette = array.from(palette1, palette2, palette3, palette4, palette5, palette6, palette7, palette8, palette9, palette10)
    color chosen = side == 1 ? longColor : shortColor
    if colorMode == "Classic palette"
        chosen := palette1
        bool picked = false
        for col in palette
            bool occupied = false
            for old in patterns
                if old.state < 3 and old.ink == col
                    occupied := true
            if not occupied and not picked
                chosen := col
                picked := true
    chosen

f_leg(Pattern p, Pivot u, Pivot v, color col) =>
    line ln = line.new(u.idx, u.price, v.idx, v.price, color = col, width = lineWidth, style = f_style(patternStyle))
    array.push(p.legs, ln)
    ln
f_triangle(Pattern p, Pivot u, Pivot v, Pivot w, color col) =>
    array<chart.point> vertices = array.from(chart.point.from_index(u.idx, u.price), chart.point.from_index(v.idx, v.price), chart.point.from_index(w.idx, w.price))
    array.push(p.fills, polyline.new(vertices, curved = false, closed = true, xloc = xloc.bar_index, line_color = color.new(col, 100), fill_color = color.new(col, fillAlpha)))
f_tag(Pattern p, Pivot pt, string txt, color col) =>
    array.push(p.tags, label.new(pt.idx, pt.price, txt, style = pt.sign == 1 ? label.style_label_down : label.style_label_up, color = color.new(col, 100), textcolor = col, size = labelSize))
f_ratio(Pattern p, Pivot u, Pivot v, float ratio, color col) =>
    int center = int(math.round((u.idx + v.idx) / 2.0))
    float y = (u.price + v.price) / 2
    array.push(p.tags, label.new(center, y, str.tostring(ratio, "0.###"), style = label.style_none, textcolor = col, size = labelSize))
f_guide(Pattern p, Pivot u, Pivot v, color col) =>
    array.push(p.legs, line.new(u.idx, u.price, v.idx, v.price, color = col, width = 1, style = line.style_dotted))
f_level(Pattern p, float price, string title, color col) =>
    int right = bar_index + projection
    array.push(p.levels, line.new(right - levelLength, price, right, price, color = col, style = f_style(levelStyle)))
    array.push(p.levelTags, label.new(right + 1, price, title + ": " + f_num(price), style = label.style_label_left, color = color.new(col, 100), textcolor = col, size = labelSize))

f_stopHit(int side, float stopPrice, float barClose, float barHigh, float barLow) =>
    float price = stopSource == "Close" ? barClose : side == 1 ? barLow : barHigh
    side == 1 ? price <= stopPrice : price >= stopPrice

f_unit(Rule r, float a, float c, float d) =>
    bool calibrated = levelBasis == "Standard (by family)" or levelBasis == "Standard (legacy)"

    bool useAD = (r.mode != 2 and levelBasis == "AD for XABCD") or (calibrated and (r.mode == 2 or r.name == "Black Swan" or r.name == "Bat" or r.name == "Anti-Cypher" or r.name == "Anti-Bat" or r.name == "Anti-Nen Star"))
    useAD ? math.abs(a - d) : math.abs(c - d)

f_ratioScore(float value, float lo, float hi) =>
    float tol = errorPct / 100
    float score = 1.0
    if value < lo
        score := 1 - (lo - value) / math.max(lo * tol, syminfo.mintick)
    else if value > hi
        score := 1 - (value - hi) / math.max(hi * tol, syminfo.mintick)
    math.max(0, math.min(1, score))

f_quality(Rule r, float x, float a, float b, float c, float d) =>
    float result = na
    if r.mode < 10
        float xa = math.abs(a - x)
        float ab = math.abs(b - a)
        float bc = math.abs(c - b)
        float cd = math.abs(d - c)
        float xc = math.abs(c - x)
        float sum = 0.0
        int n = 0
        if r.mode != 2 and xa > 0
            sum += f_ratioScore(ab / xa, r.b0, r.b1)
            n += 1
        if r.mode == 1
            if xa > 0
                sum += f_ratioScore(xc / xa, r.c0, r.c1)
                n += 1
        else if ab > 0
            sum += f_ratioScore(bc / ab, r.c0, r.c1)
            n += 1
        if r.mode != 1 and r.e0 > 0 and bc > 0
            sum += f_ratioScore(cd / bc, r.e0, r.e1)
            n += 1
        float unit = r.mode == 0 ? xa : r.mode == 1 ? xc : r.mode == 3 ? bc : ab
        float anchor = r.mode == 0 ? a : c
        if unit > 0
            sum += f_ratioScore(math.abs(anchor - d) / unit, math.min(r.d0, r.d1), math.max(r.d0, r.d1))
            n += 1
        result := n > 0 ? 100 * sum / n : na
    result

var float qualityMin = 0
var bool qualityInName = true

f_create(Rule r, Pivot x, Pivot a, Pivot b, Pivot c, Pivot d, float lo, float hi, string key) =>
    int side = -d.sign
    float span = r.mode >= 10 ? r.d0 : f_unit(r, a.price, c.price, d.price)
    float en = math.round_to_mintick(d.price + side * span * entryPct / 100)
    float stopLo = lo
    float stopHi = hi

    if r.name == "Anti-Bat"
        Rule stopRule = Rule.new(r.name, r.enabled, r.b0, r.b1, r.c0, r.c1, r.e0, r.e1, r.d0, 1.13, r.mode)
        [stopValid, slLo, slHi] = f_zone(stopRule, x.price, a.price, b.price, c.price, side)
        if stopValid
            stopLo := slLo
            stopHi := slHi
    float zl = stopLo - math.abs(c.price - d.price) * zonePadPct / 100
    float zh = stopHi + math.abs(c.price - d.price) * zonePadPct / 100
    float sl = stopMode == "Manual %" ? en - side * math.abs(en) * manualPct / 100 : side == 1 ? zl : zh

    sl := side == 1 ? math.min(sl, en - syminfo.mintick) : math.max(sl, en + syminfo.mintick)
    float risk = math.abs(en - sl)
    float t1 = useRR ? en + side * risk * rr1 : d.price + side * span * t1Pct / 100
    float t2 = useRR ? en + side * risk * rr2 : d.price + side * span * t2Pct / 100
    t1 := math.round_to_mintick(t1)
    t2 := math.round_to_mintick(t2)

    t1 := side == 1 ? math.max(t1, en + syminfo.mintick) : math.min(t1, en - syminfo.mintick)
    t2 := side == 1 ? math.max(t2, t1 + syminfo.mintick) : math.min(t2, t1 - syminfo.mintick)
    color col = f_color(side)
    Pattern p = Pattern.new(r.name, key, side, bar_index, 0, na, na, en, t1, t2, sl, zl, zh, en, array.new<line>(), array.new<label>(), array.new<polyline>(), array.new<line>(), array.new<label>(), na, col, false)
    p.dBar := d.idx
    p.risk0 := math.abs(en - sl)
    p.worst := en
    p.quality := f_quality(r, x.price, a.price, b.price, c.price, d.price)
    p.diverge := divOn and r.mode < 10 and (f_div(side, b, d) or (r.mode != 2 and x.sign == d.sign and f_div(side, x, d)))
    p.volSpike := volOn and r.mode < 10 and f_volSpike(d)
    if showPatterns and not showOnlyLevels and r.mode < 10

        if r.mode != 2
            f_leg(p, x, a, col)

            if r.mode == 3
                if showGuides
                    f_guide(p, x, b, col)
                    f_guide(p, x, d, col)
            else
                f_leg(p, x, b, col)
            if showFill
                f_triangle(p, x, a, b, col)
            if showGuides and r.mode != 3
                f_guide(p, x, d, col)
        f_leg(p, a, b, col)
        f_leg(p, b, c, col)
        f_leg(p, c, d, col)

        if r.mode != 2
            if r.mode != 3
                f_leg(p, b, d, col)
            if showFill
                f_triangle(p, b, c, d, col)
        if showGuides
            f_guide(p, a, c, col)
            if r.mode == 2
                f_guide(p, b, d, col)
        if not hideLetters
            if r.mode != 2
                f_tag(p, x, "X", col)
            f_tag(p, a, "A", col)
            f_tag(p, b, "B", col)
            f_tag(p, c, "C", col)
            f_tag(p, d, "D", col)
        if showRatios
            float ab = math.abs(a.price - b.price)
            float bc = math.abs(b.price - c.price)
            float cd = math.abs(c.price - d.price)
            f_ratio(p, a, c, r.mode == 1 ? math.abs(c.price - x.price) / math.abs(a.price - x.price) : bc / ab, col)

            if r.mode == 3
                f_ratio(p, c, d, cd / bc, col)
            else
                f_ratio(p, b, d, cd / bc, col)
            if r.mode != 2
                f_ratio(p, x, b, ab / math.abs(x.price - a.price), col)

                if r.mode != 3
                    float endRatio = r.mode == 1 ? cd / math.abs(c.price - x.price) : math.abs(a.price - d.price) / math.abs(x.price - a.price)
                    f_ratio(p, x, d, endRatio, col)

        string nameText = r.name + (side == 1 ? " ▲" : " ▼") + (qualityInName and not na(p.quality) ? " " + str.tostring(p.quality, "0") : "") + (p.diverge ? " " + divMark : "") + (p.volSpike ? " " + volMark : "")
        p.nameTag := label.new(d.idx, d.price, nameText, style = side == 1 ? label.style_label_upper_right : label.style_label_lower_right, color = color.new(col, 100), textcolor = col, size = labelSize)
        array.push(p.tags, p.nameTag)
    if showLevels
        f_level(p, en, "En", col)
        f_level(p, t1, "T1", col)
        f_level(p, t2, "T2", col)
        f_level(p, sl, "SL", col)
    if showZones and not showOnlyLevels
        int zoneLeft = d.idx - 11
        array.push(p.legs, line.new(zoneLeft, zl, d.idx, zl, color = col, width = 1, style = f_style(zoneStyle)))
        array.push(p.legs, line.new(zoneLeft, zh, d.idx, zh, color = col, width = 1, style = f_style(zoneStyle)))
        array.push(p.legs, line.new(zoneLeft, zl, zoneLeft, zh, color = col, width = 1, style = f_style(zoneStyle)))
    p
var table wm = table.new(position.bottom_right, 1, 1)
if barstate.isfirst
    table.cell(wm, 0, 0, "Telegram @SimpleForexTools", text_color = color.new(color.gray, 70), text_size = size.large)

type ExtraFormation
    string name
    array<Pivot> points
    int side
    float trigger
    float invalidation
    float unit
    string key
    int kind
    int dBar = 0
    float dPrice = na

f_segmentPrice(Pivot p, Pivot q, int idx) =>
    p.price + (q.price - p.price) * (idx - p.idx) / math.max(q.idx - p.idx, 1)

f_extremeBetween(Swing sw, int fromIndex, int toIndex, int sign) =>
    int best = fromIndex + 1
    if toIndex - fromIndex > 2
        for k = fromIndex + 2 to toIndex - 1
            Pivot q = array.get(sw.points, k)
            if q.sign == sign and (q.price - array.get(sw.points, best).price) * sign > 0
                best := k
    best

f_extraCandidates(Swing sw) =>
    array<ExtraFormation> result = array.new<ExtraFormation>()
    int n = array.size(sw.points)

    if n >= 4 and (useDoubleTop or useDoubleBottom)
        for offset = 0 to math.min(3, n - 3)
            int ci = n - 1 - offset
            Pivot c = array.get(sw.points, ci)
            int side = -c.sign
            if (side == 1 and useDoubleBottom) or (side == -1 and useDoubleTop)
                for bi = ci - 1 to 1
                    Pivot b = array.get(sw.points, bi)
                    if b.sign == side and bi >= 2
                        for ai = bi - 1 to 1
                            Pivot a = array.get(sw.points, ai)
                            Pivot lead = array.get(sw.points, ai - 1)
                            float height = (b.price - c.price) * side
                            float tolerance = height * classicTolerance / 100
                            bool symmetric = a.sign == c.sign and height > syminfo.mintick and math.abs(a.price - c.price) <= tolerance
                            bool approach = (lead.price - b.price) * side > 0
                            bool contained = symmetric and f_legContained(sw, ai, bi) and f_legContained(sw, bi, ci)
                            bool breakout = (side == 1 ? high >= b.price : low <= b.price) and bar_index > c.idx
                            if symmetric and approach and contained and breakout and bar_index - a.idx >= minSize and bar_index - a.idx <= maxSize and array.size(result) < candidateLimit
                                string name = side == 1 ? "Double Bottom" : "Double Top"
                                string key = name + "*" + str.tostring(a.idx) + "*" + str.tostring(b.idx) + "*" + str.tostring(c.idx)
                                array.push(result, ExtraFormation.new(name, array.from(lead, a, b, c), side, b.price, c.price, height * 1.5, key, 10))

    if n >= 4 and (useAscending or useDescending or useSymmetrical)
        for offset = 0 to math.min(1, n - 4)
            Swing boundary = Swing.new(array.copy(sw.points))
            if offset == 1
                array.pop(boundary.points)
            for triCandidate in f_abcdCandidates(boundary, triangleNested ? candidateLimit : 1)
                Pivot x = triCandidate.a
                Pivot a = triCandidate.b
                Pivot b = triCandidate.c
                Pivot c = triCandidate.d
                float width = math.abs(x.price - a.price)
                float eps = width * classicTolerance / 100
                Pivot h0 = x.sign == 1 ? x : a
                Pivot h1 = x.sign == 1 ? b : c
                Pivot l0 = x.sign == -1 ? x : a
                Pivot l1 = x.sign == -1 ? b : c
                bool flatHigh = math.abs(h1.price - h0.price) <= eps
                bool flatLow = math.abs(l1.price - l0.price) <= eps
                bool lowerHigh = h1.price < h0.price - eps
                bool higherLow = l1.price > l0.price + eps
                string name = flatHigh and higherLow and useAscending ? "Ascending Triangle" : flatLow and lowerHigh and useDescending ? "Descending Triangle" : lowerHigh and higherLow and useSymmetrical ? "Symmetrical Triangle" : ""
                float upper = f_segmentPrice(h0, h1, bar_index)
                float lower = f_segmentPrice(l0, l1, bar_index)
                int side = -c.sign
                float trigger = side == 1 ? upper : lower
                bool sized = bar_index - x.idx >= minSize and bar_index - x.idx <= maxSize and bar_index - c.idx >= minCD

                bool crossed = (close - trigger) * side > 0
                float ab = math.abs(b.price - a.price)
                float bc = math.abs(c.price - b.price)
                bool retracements = width > syminfo.mintick and ab > syminfo.mintick and ab / width >= triangleMinRetrace and bc / ab >= triangleMinRetrace

                bool contracting = h1.price <= h0.price and l1.price >= l0.price
                if contracting and retracements and sized and width > syminfo.mintick and upper > lower and name != "" and crossed and array.size(result) < candidateLimit
                    string key = name + "*" + str.tostring(x.idx) + "*" + str.tostring(a.idx) + "*" + str.tostring(b.idx) + "*" + str.tostring(c.idx) + "*" + str.tostring(side)
                    int dBar = bar_index
                    float dPrice = side == 1 ? high : low
                    for k = c.idx + 1 to bar_index
                        int back = bar_index - k
                        float v = side == 1 ? high[back] : low[back]
                        if (v - dPrice) * side > 0
                            dPrice := v
                            dBar := k
                    array.push(result, ExtraFormation.new(name, array.from(x, a, b, c), side, trigger, c.price, width, key, 12, dBar, dPrice))

    if n >= 6
        int lastIdx = n - 1
        for tail = 0 to hsTail
            int rIdx = lastIdx - tail
            if rIdx < 5
                break
            Pivot p4 = array.get(sw.points, rIdx)
            int side = -p4.sign
            bool tailOK = bar_index > p4.idx
            if tail > 0
                for k = rIdx + 1 to lastIdx
                    if (array.get(sw.points, k).price - p4.price) * -side > 0
                        tailOK := false
            if tailOK and ((side == 1 and useIHS) or (side == -1 and useHS))
                bool taken = false
                for skipH = 0 to hsSkip
                    int hIdx = rIdx - 2 - 2 * skipH
                    if hIdx < 3 or taken
                        break
                    Pivot p2 = array.get(sw.points, hIdx)
                    int m2 = f_extremeBetween(sw, hIdx, rIdx, side)
                    Pivot p3 = array.get(sw.points, m2)
                    if (p2.price - p4.price) * -side > 0 and f_legContained(sw, hIdx, m2) and f_legContained(sw, m2, rIdx)
                        for skipL = 0 to hsSkip
                            int lIdx = hIdx - 2 - 2 * skipL
                            if lIdx < 1 or taken
                                break
                            Pivot p0 = array.get(sw.points, lIdx)
                            int m1 = f_extremeBetween(sw, lIdx, hIdx, side)
                            Pivot p1 = array.get(sw.points, m1)
                            Pivot lead = array.get(sw.points, lIdx - 1)
                            if (p2.price - p0.price) * -side > 0 and f_legContained(sw, lIdx, m1) and f_legContained(sw, m1, hIdx)
                                bool sized = bar_index - lead.idx >= minSize and bar_index - lead.idx <= maxSize
                                float neck = p1.price
                                float height = (neck - p2.price) * side
                                bool shoulders = height > syminfo.mintick and (p0.price - p2.price) * side > 0 and (p4.price - p2.price) * side > 0 and (neck - p4.price) * side > 0 and math.abs(p0.price - p4.price) <= height * hsShoulderTolerance / 100

                                bool approach = (lead.price - neck) * side > 0

                                bool flatNeck = math.abs(p3.price - neck) <= height * errorPct / 100
                                float rightDepth = (p2.price - p3.price) * -side
                                float lsRatio = (p0.price - neck) * -side / height
                                float rsRatio = rightDepth > syminfo.mintick ? (p4.price - p3.price) * -side / rightDepth : 0
                                bool shoulderRatios = f_band(lsRatio, .382, .786) and f_band(rsRatio, .382, .786)

                                float outerNeck = side == 1 ? math.max(neck, p3.price) : math.min(neck, p3.price)
                                bool broken = side == 1 ? high > outerNeck : low < outerNeck

                                bool dominant = side == 1 ? p2.price <= windowLow + syminfo.mintick * 0.1 : p2.price >= windowHigh - syminfo.mintick * 0.1
                                if sized and shoulders and approach and flatNeck and shoulderRatios and dominant and broken and array.size(result) < candidateLimit
                                    string name = side == 1 ? "Inverse Head and Shoulders" : "Head and Shoulders"
                                    string key = name + "*" + str.tostring(p0.idx) + "*" + str.tostring(p2.idx) + "*" + str.tostring(p4.idx)

                                    float outer = side == 1 ? math.max(neck, p3.price) : math.min(neck, p3.price)

                                    array.push(result, ExtraFormation.new(name, array.from(lead, p0, p1, p2, p3, p4), side, outer, p2.price, height, key, 11))
                                    taken := true

    if useThreeDrive and n >= 5
        array<Pivot> pts = array.new<Pivot>()
        for k = n - 5 to n - 1
            array.push(pts, array.get(sw.points, k))
        Pivot p1 = array.get(pts, 0)
        Pivot p2 = array.get(pts, 1)
        Pivot p3 = array.get(pts, 2)
        Pivot p4 = array.get(pts, 3)
        Pivot p5 = array.get(pts, 4)
        int side = -p5.sign
        float retrace1 = math.abs(p2.price - p1.price)
        float drive2 = math.abs(p3.price - p2.price)
        float retrace2 = math.abs(p4.price - p3.price)
        float drive3 = math.abs(p5.price - p4.price)
        bool positive = math.min(math.min(retrace1, drive2), retrace2) > syminfo.mintick
        bool geometry = (p1.price - p3.price) * side > 0 and (p3.price - p5.price) * side > 0 and (p2.price - p4.price) * side > 0
        bool ratios = positive and f_band(drive2 / retrace1, 1.272, 1.618) and f_band(retrace2 / drive2, .618, .786) and f_band(drive3 / retrace2, 1.272, 1.618)

        bool firstOK = true
        if n >= 6
            Pivot p0 = array.get(sw.points, n - 6)
            float first = math.abs(p1.price - p0.price)
            firstOK := first > syminfo.mintick and (p0.price - p2.price) * side > 0 and f_band(retrace1 / first, .618, .786)
        if geometry and ratios and firstOK and bar_index - p1.idx >= minSize and bar_index - p1.idx <= maxSize and bar_index - p5.idx <= confirmBars
            string key = "********" + str.tostring(p1.idx) + "*" + str.tostring(p5.idx)

            array.push(result, ExtraFormation.new("3-Drive", pts, side, p5.price, p5.price - side * drive3 * errorPct / 100, math.abs(p2.price - p5.price), key, 13))
    result

f_createExtra(ExtraFormation e) =>
    Pivot first = array.get(e.points, 0)
    Pivot last = array.get(e.points, array.size(e.points) - 1)
    Pivot anchor = Pivot.new(e.trigger, bar_index, -e.side)
    Rule r = Rule.new(e.name, true, 0, 0, 0, 0, 0, 0, e.unit, 0, e.kind)
    float lo = e.side == 1 ? e.invalidation : e.trigger
    float hi = e.side == 1 ? e.trigger : e.invalidation
    if e.kind == 10
        float ext = (hi - lo) * (1 + errorPct / 100)
        if e.side == 1
            hi := lo + ext
        else
            lo := hi - ext
    if e.kind == 11
        float n1 = array.get(e.points, 2).price
        float n2 = array.get(e.points, 4).price
        float innerNeck = e.side == 1 ? math.min(n1, n2) : math.max(n1, n2)
        if e.side == 1
            hi := innerNeck
        else
            lo := innerNeck
    if e.kind == 13
        Pivot bPt = array.get(e.points, 2)
        Pivot cPt = array.get(e.points, 3)
        float bc = math.abs(cPt.price - bPt.price)
        float near = cPt.price - e.side * bc * 1.272 * (1 - errorPct / 100)
        float far = cPt.price - e.side * bc * 1.618 * (1 + errorPct / 100)
        lo := math.min(near, far)
        hi := math.max(near, far)
    Pattern p = f_create(r, first, first, last, last, anchor, lo, hi, e.key)
    color col = p.ink
    if showPatterns and not showOnlyLevels
        if e.kind == 12
            Pivot x = first
            Pivot a = array.get(e.points, 1)
            Pivot b = array.get(e.points, 2)
            Pivot c = last

            f_leg(p, x, Pivot.new(f_segmentPrice(x, b, bar_index), bar_index, x.sign), col)
            f_leg(p, a, Pivot.new(f_segmentPrice(a, c, bar_index), bar_index, a.sign), col)
            if showGuides
                f_guide(p, x, a, col)
                f_guide(p, a, b, col)
                f_guide(p, b, c, col)
                f_guide(p, c, anchor, col)
            if showRatios
                f_ratio(p, x, b, math.abs(b.price - a.price) / e.unit, col)
                f_ratio(p, a, c, math.abs(c.price - b.price) / math.abs(b.price - a.price), col)
            if not hideLetters
                f_tag(p, Pivot.new(e.dPrice, e.dBar, e.side), "D", col)
        else
            for k = 0 to array.size(e.points) - 2
                f_leg(p, array.get(e.points, k), array.get(e.points, k + 1), col)
            if e.kind == 10

                Pivot neck = array.get(e.points, 2)
                f_leg(p, last, anchor, col)
                int neckStart = first.idx + 1
                for k = first.idx + 1 to array.get(e.points, 1).idx
                    int back = bar_index - k
                    if back >= 0 and (e.side == 1 ? low[back] <= neck.price : high[back] >= neck.price)
                        neckStart := k
                        break
                f_leg(p, Pivot.new(neck.price, neckStart, neck.sign), neck, col)
                f_leg(p, neck, Pivot.new(neck.price, bar_index, neck.sign), col)
            if e.kind == 11

                Pivot lead = first
                Pivot n1 = array.get(e.points, 2)
                Pivot n2 = array.get(e.points, 4)
                float inner = e.side == 1 ? math.min(n1.price, n2.price) : math.max(n1.price, n2.price)
                Pivot neckEnd = Pivot.new(inner, bar_index, n1.sign)
                f_leg(p, last, neckEnd, col)

                int neckStart = lead.idx + 1
                for k = lead.idx + 1 to array.get(e.points, 1).idx
                    int back = bar_index - k
                    if back >= 0 and (e.side == 1 ? low[back] <= inner : high[back] >= inner)
                        neckStart :=

Показано первые 50% кода (символов: 91640)

Открыть по тарифу

Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.

Разовая покупка файла

На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.

Чтобы оформить заказ, введите email и перейдите к оплате.

Стоимость полного кода
9 164₽ ≈ $112.02