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

Reaction Level Matrix

indicator reaction level matrix for tradingview.pine

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

ares_indicator.pineTradingView · 1 общих теговgold_trend_revers.mq5MQL5 / MetaTrader 5 · 1 общих тегов!!!-mt4 x-xardg-rsi.mq5MQL5 / MetaTrader 5 · 1 общих тегов! cycle-identifier.mq5MQL5 / MetaTrader 5 · 1 общих тегов!!! rsi ma simple+arrows 1.2 (mtf).mq5MQL5 / MetaTrader 5 · 1 общих теговprecisionflowea.mq5MQL5 / MetaTrader 5 · 1 общих тегов
Вы автор или правообладатель?

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

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

О программе

Reaction Level Matrix (RLM) — это индикатор для TradingView, который строит зоны поддержки и сопротивления на основе свечных реакций (свинг-хаев и свинг-лоу). Каждая зона накапливает «силу» по мере касаний и со временем затухает, а также получает числовой балл от 0 до 100. Индикатор отображает ближайшие активные уровни выше и ниже цены и отмечает потенциальные точки входа в виде меток Long ▲ и Short ▼. Встроенный движок торговли отслеживает сделки с уровнями стоп-лосса и тейк-профита, фиксирует статистику (выигрыши/проигрыши, процент побед) и отображает её на дашборде. Поддерживаются оповещения через alert() для входов, закрытий, разворотов и пробоев уровней.

Индикатор работает поверх графика (overlay = true) и использует технические расчёты Pine Script: ATR для определения ширины зон и риск-параметров, pivot high/low для обнаружения свингов, EMA и RSI для контекста. Данные более высокого таймфрейма запрашиваются через request.security для отображения направления bias без перерисовки. Визуальная часть включает настраиваемые уровни, линии входа/SL/TP, дашборд с несколькими разделами и водяной знак.

Система управления рисками позволяет выбрать предустановку (Conservative, Balanced, Aggressive, Scalping или Custom) и настроить режим стоп-лосса (Wick-Anchored или ATR), мультипликаторы трёх тейк-профитов, а также перевод стопа на безубыток после достижения TP1. Все параметры доступны через пользовательские настройки индикатора. Статистика сбрасывается при перезагрузке графика и не является результатом бэктеста.

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

НастройкаОписание
swingLenInput Количество баров с каждой стороны, определяющее свинг-хай/лоу. Свинг подтверждается этим количеством баров после экстремума. Более высокое значение — только значимые свинги с поздним подтверждением, меньшее — больше свингов и быстрее появление уровней.
levelTolInput Допуск для объединения нового свинга с существующим уровнем: расстояние от центра уровня. Также максимальная половинная ширина зоны уровня. Большее значение — меньше и шире уровни, меньшее — больше и плотнее.
minTouchesInput Количество свингов, необходимых для активации уровня (отображения и возможности генерации сигналов).
halfLifeInput Количество баров, после которых сила уровня уменьшается вдвое при отсутствии новых касаний. Большее значение сохраняет старые уровни дольше.
levelsPerSideInput Количество активных уровней, отображаемых выше (сопротивление) и ниже (поддержка) цены. Отображаются только эти уровни и только они могут генерировать сигналы.
htfInput Таймфрейм для отображения направления bias в дашборде: цена закрытия относительно EMA 50 предыдущего закрытого бара HTF (без перерисовки). Пустое значение отключает bias. Только информационный параметр, не фильтрует сигналы.
themeInput Цветовая тема графика: Auto (автоопределение от фона), Dark (для тёмных фонов), Light (для светлых фонов).
showLevelsInput Отрисовка активных уровней ближайших к цене. Влияет только на отображение: сигналы и сделки не зависят от этой настройки.
drawFromFirstInput При включении уровень начинается от самого раннего свинга (полная история уровня). При выключении — от бара, на котором уровень стал активным. Влияет только на отображение, не на сигналы.
showLevelLabelsInput Отображение балла уровня (0–100), количества касаний (T) и символа ⇅ при касании и свинг-хаев, и свинг-лоу.
zoneTranspInput Прозрачность заливки зон уровней. 0 — авто (зависит от темы), 60–95 — ручная настройка (больше = прозрачнее).
showSignalsInput Отметка каждого входа метками Long ▲ / Short ▼. Оповещения работают независимо от этой настройки.
signalFontSizeInput Размер шрифта меток входа Long ▲ / Short ▼.
labelFontSizeInput Размер шрифта меток входа, стоп-лосса и тейк-профита.
levelFontSizeInput Размер шрифта меток балла уровней на графике.
showWatermarkInput Отображение водяного знака на графике.
minScoreInput Минимальный балл уровня (0–100) для генерации сигнала. Более высокое значение — меньше сигналов, но на более сильных уровнях.
rejectClvInput Требование к закрытию сигнального бара внутри своего диапазона. Для покупки: закрытие в верхней части, (close − low) / (high − low) ≥ значение. Для продажи — зеркально. Более высокое значение — только сильные свечные реакции.
cooldownInput Минимальное количество баров между двумя сигналами на одном и том же уровне. Значение 0 отключает охлаждение.
riskPresetInput Быстрая настройка риск-параметров: Conservative (SL 2.5×ATR, TP 1R/2R/4R), Balanced (SL 1.5×ATR, TP 1R/2R/3R), Aggressive (SL 1.0×ATR, TP 1.5R/2.5R/4R), Scalping (SL 0.8×ATR, TP 0.8R/1.5R/2R) или Custom (ручные мультипликаторы).
slModeInput Способ установки стоп-лосса при входе. Wick-Anchored: стоп за свингом сигнального бара с буфером 0.25×ATR (минимум 0.5×ATR). ATR: фиксированное расстояние SL Multiplier × ATR от входа.
atrLenRiskInput Период ATR, используемый для расчёта стоп-лосса. Стандартное значение 14 подходит для большинства таймфреймов. Меньшее значение даёт более узкие стопы, большее — широкие.
slMultInput Мультипликатор стоп-лосса в ATR (только при режиме Custom). 1.0 — агрессивный, 1.5 — сбалансированный, 2.0–3.0 — консервативный.
tp1MultInput Мультипликатор TP1 в ATR (только при режиме Custom). Множитель фактического расстояния SL. 1.0 = соотношение риск/награда 1:1.
tp2MultInput Мультипликатор TP2 в ATR (только при режиме Custom). 2.0 = соотношение риск/награда 1:2.
tp3MultInput Мультипликатор TP3 в ATR (только при режиме Custom). 3.0 = соотношение риск/награда 1:3. TP3 закрывает сделку.
beInput При активации стоп-лосс перемещается на цену входа после достижения TP1. Сделки, остановленные на безубытке после TP1, считаются выигрышными.
beOffsetInput Расстояние безубыточного стопа от цены входа, выраженное в ATR на момент входа. Значение 0 означает стоп ровно на уровне входа.
showSlTpInput Отрисовка линий входа, стоп-лосса и тейк-профитов текущей сделки. После закрытия сделки линии остаются на графике и заменяются при следующем входе.
showSlTpLabelsInput Отображение ценовых меток на линиях входа, стоп-лосса и тейк-профита.
showPctOnLabelsInput Добавляет процентное расстояние от входа до SL/TP на метках, например 'SL 1.2300 (-0.36%)'.
entryLineStyleInput Стиль линии входа (точка входа — справочный, не уровень действия).
slLineStyleInput Стиль линии стоп-лосса (толщина 2, уровень действия).
tpLineStyleInput Стиль линий TP1/TP2/TP3. При достижении тейк-профит линия становится сплошной бирюзовой.
showDashInput Отображение таблицы дашборда на графике.
dashPosInput Позиция таблицы дашборда на графике.
dashFontSizeInput Размер шрифта таблицы дашборда. Разделители секций и подвал отображаются на один размер меньше.
showMarketSecInput Включает раздел «Market» в дашборде с информацией о тренде, возрасте тренда, HTF bias, позиции, последнем сигнале и таймфрейме.
showLevelSecInput Включает раздел «Levels» в дашборде с информацией о ближайшем сопротивлении, поддержке, количестве отображаемых уровней и последнем пробое.
showTradeSecInput Включает раздел «Trade» в дашборде с информацией о входе, SL, TP1–TP3, R:R и расстоянии до стоп-лосса текущей открытой сделки.
showStatsSecInput Включает раздел «Stats» в дашборде с информацией о закрытых сделках, выигрышах, проигрышах, проценте побед и форме.
statsPeriodInput Период для раздела статистики: 24 Hours — сделки за последние 24 часа, 30 Days — за 30 дней, All-Time — все сделки на загруженной истории. Форма всегда показывает последние сделки вне зависимости от периода. Статистика сбрасывается при перезагрузке графика.
webhookInput Отправка оповещений о входах в формате JSON для вебхук-интеграций. Создавать оповещение нужно через 'Any alert() function call'.
levelBreakAlertInput Информационное оповещение при закрытии бара через отображаемый уровень (сопротивление → поддержка или наоборот).
slRevAlertInput Оповещение при остановке сделки (SL или безубыток) или развороте противоположным сигналом.
tpBeAlertInput Оповещения при касании TP1/TP2, переходе на безубыток и выходе по TP3.
bullColorInput Цвет фона меток покупок (Long ▲) на графике.
bearColorInput Цвет фона меток продажи (Short ▼) на графике.
supLevelColorInput Цвет заливки, центральной линии и меток уровней поддержки.
resLevelColorInput Цвет заливки, центральной линии и меток уровней сопротивления.
Получить полный код

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

indicator(title = "Reaction Level Matrix", shorttitle = "RLM", overlay = true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500)

INDICATOR_VERSION = "v1.0.0"
SHORT_NAME        = "RLM"

GRP_MAIN   = "⚙️ Main Settings"
GRP_VISUAL = "🎨 Visual Settings"
GRP_FILTER = "🔍 Filters"
GRP_RISK   = "🛡️ Risk Management"
GRP_DASH   = "📊 Dashboard"
GRP_ALERT  = "🔔 Alerts"
GRP_COLORS = "🎨 Colors"

STRUCT_ATR_LEN     = 34     // ATR for level tolerance, band width and swing reactions
MIN_HALF_WIDTH_ATR = 0.15   // narrowest level band: ±0.15 × ATR around its center
REACTION_CAP       = 2.5    // a single swing adds at most 1 + 2.5 to a level's strength
SCORE_SCALE        = 6.0    // strength that maps to a score of ~63 (score = 100 × (1 − e^(−s / scale)))
POLARITY_BONUS     = 1.25   // levels touched by both swing highs and swing lows score higher
CROSS_PENALTY      = 0.7    // strength kept when price closes through a level
MIN_STRENGTH       = 0.25   // levels decayed below this strength are forgotten
MAX_LEVELS         = 60     // memory cap; the weakest level is evicted first
MERGE_PASSES       = 5      // passes to absorb levels that overlap after a touch
LEVEL_RIGHT_EXT    = 5      // bars to extend levels to the right of the last bar

WICK_BUFFER_ATR   = 0.25 // Wick-Anchored SL buffer beyond the wick, × ATR
WICK_MIN_ATR      = 0.5  // Wick-Anchored minimum SL distance, × ATR
LINE_FORWARD_BARS = 20   // length of trade lines when an entry is drawn
LINE_UPDATE_BARS  = 8    // extension beyond the current bar while a trade is open
LABEL_OFFSET_BARS = 1    // label offset from the end of trade lines
BE_DIM_TRANSP     = 70   // SL dimming once the stop is at break-even
FORM_LEN          = 10   // length of the Form strip
GAUGE_WIDTH       = 8    // win-rate gauge cells
MS_DAY            = 86400000
STATS_PRUNE_DAYS  = 31   // rolling stats buffer horizon
RSI_LEN           = 14   // context oscillator shown in entry tooltips
HTF_EMA_LEN       = 50   // HTF bias: close vs EMA of this length

swingLenInput      = input.int(6, "Swing Length", minval = 2, maxval = 30, group = GRP_MAIN, tooltip = "Bars on each side that define a swing high/low. A swing is confirmed this many bars after its extreme.\n• Higher = only major swings, later confirmation\n• Lower = more swings, faster levels\n• Recommended: 4–10")
levelTolInput      = input.float(0.6, "Level Tolerance (×ATR)", minval = 0.2, maxval = 2.0, step = 0.05, group = GRP_MAIN, tooltip = "A new swing joins an existing level if it lies within this distance of the level's center.\nAlso the maximum half-width of a level band.\n• Higher = fewer, wider levels with more touches\n• Lower = more, tighter levels\n• Recommended: 0.4–1.0")
minTouchesInput    = input.int(2, "Touches to Activate", minval = 2, maxval = 10, group = GRP_MAIN, tooltip = "Swings needed before a level is shown and can produce signals.\n• Higher = only well-tested levels\n• Recommended: 2–3")
halfLifeInput      = input.int(500, "Level Half-Life (bars)", minval = 50, maxval = 5000, step = 50, group = GRP_MAIN, tooltip = "Level strength halves after this many bars without new touches.\n• Higher = old levels stay relevant longer\n• Lower = only recent structure")
levelsPerSideInput = input.int(3, "Levels Above / Below Price", minval = 1, maxval = 8, group = GRP_MAIN, tooltip = "How many active levels nearest to price are shown above (resistance) and below (support).\nOnly shown levels can produce signals.\n• Recommended: 2–4")
htfInput           = input.timeframe("", "HTF Bias Timeframe", group = GRP_MAIN, tooltip = "Higher timeframe for the dashboard bias: close vs EMA 50 of the previous closed HTF bar (no repaint).\nLeave empty to turn the HTF bias off.\nInformational only: it does not filter signals.")

themeInput           = input.string("Auto", "Theme", options = ["Auto", "Dark", "Light"], group = GRP_VISUAL, tooltip = "Chart color theme.\n• Auto: detects from chart background\n• Dark: optimized for dark backgrounds\n• Light: optimized for light/white backgrounds")
showLevelsInput      = input.bool(true, "Show Levels", group = GRP_VISUAL, tooltip = "Draw the active levels nearest to price.\nDrawing only: signals and trades are unaffected.")
drawFromFirstInput   = input.bool(true, "Draw Levels From First Touch", group = GRP_VISUAL, tooltip = "• On: a level starts at its earliest swing (the level's full history)\n• Off: a level starts on the bar it became active\nDrawing only: signals are unaffected.")
showLevelLabelsInput = input.bool(true, "Show Level Score", group = GRP_VISUAL, tooltip = "Label each level with its score (0–100), its touch count (T) and ⇅ if both swing highs and swing lows touched it.\nHover the label for details.")
zoneTranspInput      = input.int(0, "Level Transparency", minval = 0, maxval = 95, group = GRP_VISUAL, tooltip = "Fill transparency of level bands.\n• 0 = Auto (theme-aware: 88 on dark, 82 on light)\n• 60–95 = manual, higher = more transparent")
showSignalsInput     = input.bool(true, "Show Buy/Sell Labels", group = GRP_VISUAL, tooltip = "Mark every trade entry with a Long ▲ / Short ▼ label.\nAlerts work regardless of this setting.")
signalFontSizeInput  = input.string("Small", "Signal Label Size", options = ["Tiny", "Small", "Normal", "Large", "Huge"], group = GRP_VISUAL, tooltip = "Font size for the Long ▲ / Short ▼ entry labels.")
labelFontSizeInput   = input.string("Small", "SL/TP Label Font Size", options = ["Tiny", "Small", "Normal", "Large", "Huge"], group = GRP_VISUAL, tooltip = "Font size for the Entry / SL / TP labels.")
levelFontSizeInput   = input.string("Small", "Level Label Size", options = ["Tiny", "Small", "Normal", "Large", "Huge"], group = GRP_VISUAL, tooltip = "Font size for the level score labels.")
showWatermarkInput   = input.bool(true, "Show Watermark", group = GRP_VISUAL, tooltip = "Display 'WillyAlgoTrader' watermark on chart")

minScoreInput  = input.int(50, "Min Level Score for Signals", minval = 0, maxval = 100, group = GRP_FILTER, tooltip = "A rejection counts only at levels scoring at least this much (absolute 0–100 scale).\n• Higher = fewer signals at stronger levels\n• 0 = every shown level\n• Recommended: 40–70")
rejectClvInput = input.float(0.6, "Rejection Close Strength", minval = 0.5, maxval = 0.9, step = 0.05, group = GRP_FILTER, tooltip = "Where the signal bar must close inside its own range.\n• Buy: close in the top part, (close − low) / (high − low) ≥ value\n• Sell: close in the bottom part, mirrored\n• Higher = stronger rejection candles only")
cooldownInput  = input.int(10, "Cooldown per Level (bars)", minval = 0, maxval = 100, group = GRP_FILTER, tooltip = "Minimum bars between two signals at the same level.\n• 0 = no cooldown")

riskPresetInput      = input.string("Balanced", "Risk Preset", options = ["Conservative", "Balanced", "Aggressive", "Scalping", "Custom"], group = GRP_RISK, tooltip = "Quick risk presets:\n• Conservative: SL 2.5×ATR, TP 1R/2R/4R\n• Balanced: SL 1.5×ATR, TP 1R/2R/3R\n• Aggressive: SL 1.0×ATR, TP 1.5R/2.5R/4R\n• Scalping: SL 0.8×ATR, TP 0.8R/1.5R/2R\n• Custom: use the manual multipliers below\nThe SL part of a preset applies to ATR mode. Wick-Anchored places the stop beyond the signal bar's wick.")
slModeInput          = input.string("Wick-Anchored", "SL Mode", options = ["Wick-Anchored", "ATR"], group = GRP_RISK, tooltip = "How the stop-loss is placed at entry:\n• Wick-Anchored: beyond the signal bar's wick + 0.25×ATR buffer\n  (min distance 0.5×ATR enforced). Structure-aware, recommended\n• ATR: fixed SL Multiplier × ATR distance from entry")
atrLenRiskInput      = input.int(14, "ATR Length (Risk)", minval = 5, maxval = 50, group = GRP_RISK, tooltip = "ATR period for stop-loss calculation.\n• 14 = standard, works on most timeframes\n• Lower = tighter stops, more stops hit\n• Higher = wider stops, fewer stops hit")
slMultInput          = input.float(1.5, "SL Multiplier (×ATR)", minval = 0.5, maxval = 5.0, step = 0.1, group = GRP_RISK, tooltip = "Custom preset only. Stop-loss distance as a multiple of ATR in ATR mode.\n• 1.0 = tight, aggressive\n• 1.5 = balanced\n• 2.0–3.0 = wide, conservative")
tp1MultInput         = input.float(1.0, "TP1 Multiplier (×Risk)", minval = 0.5, maxval = 5.0, step = 0.1, group = GRP_RISK, tooltip = "Custom preset only. Take-profit 1 as a multiple of the actual SL distance.\n1.0 = 1:1 Risk:Reward")
tp2MultInput         = input.float(2.0, "TP2 Multiplier (×Risk)", minval = 1.0, maxval = 10.0, step = 0.1, group = GRP_RISK, tooltip = "Custom preset only. Take-profit 2 as a multiple of the actual SL distance.\n2.0 = 1:2 Risk:Reward")
tp3MultInput         = input.float(3.0, "TP3 Multiplier (×Risk)", minval = 1.5, maxval = 15.0, step = 0.1, group = GRP_RISK, tooltip = "Custom preset only. Take-profit 3 as a multiple of the actual SL distance.\n3.0 = 1:3 Risk:Reward")
beInput              = input.bool(true, "Break-Even After TP1", group = GRP_RISK, tooltip = "Move stop-loss to entry price once TP1 is reached.\nPositions stopped at break-even after TP1 count as wins")
beOffsetInput        = input.float(0.0, "BE Offset (×ATR)", minval = 0.0, maxval = 1.0, step = 0.05, group = GRP_RISK, tooltip = "Break-even stop placed this far beyond entry, in ATR at entry.\n0 = exactly at entry (default)")
showSlTpInput        = input.bool(true, "Show SL/TP Lines", group = GRP_RISK, tooltip = "Draw Entry / SL / TP lines for the current trade.\nLines stay on the chart after the trade closes and are replaced at the next entry.")
showSlTpLabelsInput  = input.bool(true, "Show SL/TP Labels", group = GRP_RISK, tooltip = "Show price labels on the Entry / SL / TP lines.")
showPctOnLabelsInput = input.bool(true, "Show % Distance on Labels", group = GRP_RISK, tooltip = "Append % distance from entry to SL/TP labels,\ne.g. 'SL 1.2300 (-0.36%)'")
entryLineStyleInput  = input.string("Dotted", "Entry Line Style", options = ["Solid", "Dashed", "Dotted"], group = GRP_RISK, tooltip = "Entry is a reference, not an action level — keep it subtle")
slLineStyleInput     = input.string("Solid", "SL Line Style", options = ["Solid", "Dashed", "Dotted"], group = GRP_RISK, tooltip = "Style of the stop-loss line (width 2, action level).")
tpLineStyleInput     = input.string("Dashed", "TP Line Style", options = ["Solid", "Dashed", "Dotted"], group = GRP_RISK, tooltip = "Style for TP1/TP2/TP3. On hit, a TP line turns solid teal")

showDashInput      = input.bool(true, "Show Dashboard", group = GRP_DASH, tooltip = "Show the dashboard table.")
dashPosInput       = input.string("Top Right", "Dashboard Position", options = ["Top Left", "Top Right", "Bottom Left", "Bottom Right", "Middle Right"], group = GRP_DASH, tooltip = "Where the dashboard is placed on the chart.")
dashFontSizeInput  = input.string("Small", "Dashboard Font Size", options = ["Small", "Normal", "Large", "Huge"], group = GRP_DASH, tooltip = "Font size for the dashboard table. Section dividers and the\nversion footer render one step smaller to preserve hierarchy")
showMarketSecInput = input.bool(true, "  ├ Market section", group = GRP_DASH, tooltip = "Trend, trend age, HTF bias, position, last signal and timeframe.")
showLevelSecInput  = input.bool(true, "  ├ Levels section", group = GRP_DASH, tooltip = "Nearest resistance and support, shown level count and the last level break.")
showTradeSecInput  = input.bool(true, "  ├ Trade section", group = GRP_DASH, tooltip = "Entry, SL, TP1–TP3, R:R and SL distance of the open trade.")
showStatsSecInput  = input.bool(true, "  └ Stats section", group = GRP_DASH, tooltip = "Closed trades, wins, losses, win rate and recent form.\nAll stats reset on chart reload.")
statsPeriodInput   = input.string("All-Time", "Win Rate Period", options = ["24 Hours", "30 Days", "All-Time"], group = GRP_DASH, tooltip = "Time window for the Stats section (Trades / Wins / Losses / Win rate):\n• 24 Hours: trades closed in the last 24h (relative to the latest bar)\n• 30 Days: trades closed in the last 30 days\n• All-Time: all trades on the loaded chart history\nForm strip always shows the last trades regardless of period.\nAll stats reset on chart reload")

webhookInput         = input.bool(false, "Webhook JSON Format", group = GRP_ALERT, tooltip = "Send entry alerts in JSON format for webhook integrations.\nCreate the alert with 'Any alert() function call'.")
levelBreakAlertInput = input.bool(true, "Alert on Level Breaks", group = GRP_ALERT, tooltip = "Informational alert when a bar closes through a shown level\n(resistance → support or support → resistance).")
slRevAlertInput      = input.bool(true, "Alert on SL Hit & Reversal", group = GRP_ALERT, tooltip = "Alerts when a trade is stopped out (SL or break-even stop) or reversed by an opposite signal.")
tpBeAlertInput       = input.bool(true, "Alert on TP & Break-Even", group = GRP_ALERT, tooltip = "Alerts on TP1 / TP2 touches, the break-even move and the final TP3 exit.")

bullColorInput    = input.color(#00E676, "Buy", inline = "sig", group = GRP_COLORS, tooltip = "Background of the Long ▲ / Short ▼ entry labels.")
bearColorInput    = input.color(#FF5252, "Sell", inline = "sig", group = GRP_COLORS, tooltip = "Background of the Long ▲ / Short ▼ entry labels.")
supLevelColorInput = input.color(#089981, "Support Level", inline = "lvl", group = GRP_COLORS, tooltip = "Band, center line and label color of the levels.")
resLevelColorInput = input.color(#F23645, "Resistance Level", inline = "lvl", group = GRP_COLORS, tooltip = "Band, center line and label color of the levels.")

[effSLMult, effTP1m, effTP2m, effTP3m] = switch riskPresetInput
    "Conservative" => [2.5, 1.0, 2.0, 4.0]
    "Aggressive"   => [1.0, 1.5, 2.5, 4.0]
    "Scalping"     => [0.8, 0.8, 1.5, 2.0]
    "Custom"       => [slMultInput, tp1MultInput, tp2MultInput, tp3MultInput]
    => [1.5, 1.0, 2.0, 3.0]

if riskPresetInput == "Custom" and (tp1MultInput >= tp2MultInput or tp2MultInput >= tp3MultInput)
    runtime.error("Invalid TP multipliers: required TP1 < TP2 < TP3")
if effSLMult <= 0
    runtime.error("SL Multiplier must be greater than 0")
if htfInput != "" and timeframe.in_seconds(htfInput) < timeframe.in_seconds(timeframe.period)
    runtime.error("HTF Bias Timeframe must be greater than or equal to the chart timeframe")

if beInput and beOffsetInput >= effTP1m * (slModeInput == "ATR" ? effSLMult : WICK_MIN_ATR)
    runtime.error("BE Offset too large: break-even level would be at or beyond TP1")
if zoneTranspInput != 0 and zoneTranspInput < 60
    runtime.error("Level Transparency must be 0 (Auto) or between 60 and 95")

isDark = switch themeInput
    "Dark"  => true
    "Light" => false
    =>         color.r(chart.bg_color) < 128

TEXT_COLOR   = isDark ? #E0E0E0 : #1A1A1A
TEXT_MUTED   = isDark ? color.new(#9E9E9E, 0) : color.new(#757575, 0)
TEXT_BRIGHT  = isDark ? #FFFFFF : #000000

BULL_TEXT    = isDark ? #00E676 : #00A152
BEAR_TEXT    = isDark ? #FF5252 : #D32F2F
NEUTRAL_TEXT = isDark ? #FFEB3B : #F57F17

BULL_LABEL_TEXT = #004D25  // dark green on #00E676 (contrast 7.8:1)
BEAR_LABEL_TEXT = #FFFFFF  // white on #FF5252 (contrast ~4.0:1)

TABLE_BG       = isDark ? color.new(#131722, 5) : color.new(#FFFFFF, 5)
TABLE_BORDER   = isDark ? color.new(#2A2E39, 0) : color.new(#D0D0D0, 0)
TABLE_ROW_ALT  = isDark ? color.new(#1C2030, 0) : color.new(#F0F4F8, 0)
HEADER_BG      = isDark ? color.new(#0D47A1, 0) : color.new(#1565C0, 0)
HEADER_BG_BULL = color.new(#0F6E56, 0)
HEADER_BG_BEAR = color.new(#A32D2D, 0)
HEADER_TEXT    = #FFFFFF

LINE_SUBTLE  = isDark ? color.new(#666666, 0) : color.new(#999999, 0)
TRANSP_COLOR = color.new(#000000, 100)
WM_COLOR     = isDark ? color.new(#FFFFFF, 80) : color.new(#000000, 80)

SL_COLOR       = isDark ? color.new(#E57373, 0)  : color.new(#B71C1C, 0)
TP1_COLOR      = isDark ? color.new(#66BB6A, 30) : color.new(#388E3C, 30)
TP2_COLOR      = isDark ? color.new(#66BB6A, 40) : color.new(#388E3C, 40)
TP3_COLOR      = isDark ? color.new(#66BB6A, 0)  : color.new(#388E3C, 0)
ENTRY_COLOR    = isDark ? color.new(#5C8AAE, 0)  : color.new(#34495E, 0)
TP_LABEL_COLOR = isDark ? color.new(#66BB6A, 0)  : color.new(#388E3C, 0)
TP_HIT_COLOR   = isDark ? color.new(#4DB6AC, 0)  : color.new(#00695C, 0)
BE_COLOR       = isDark ? color.new(#FFA726, 0)  : color.new(#E65100, 0)
LABEL_TEXT     = isDark ? #1A1A1A                : #FFFFFF   // text on filled Entry / SL / TP labels

int FILL_TRANSP = isDark ? 88 : 82
int ZONE_TRANSP = zoneTranspInput == 0 ? FILL_TRANSP : zoneTranspInput

type Level
    float lo
    float hi
    float mid
    float sumW           // Σ w
    float sumWP          // Σ w · price
    float sumWP2         // Σ w · price²
    float strength       // decaying strength: + w per touch, × decay per bar, × CROSS_PENALTY per cross
    int   touches
    int   highTouches
    int   lowTouches
    int   firstTime      // time of the earliest member swing
    int   firstBar
    int   activeTime     // time the level reached the activation touch count (na before)
    int   lastTouchBar
    int   crosses        // closes through the band while active
    int   side           // +1 = price above (support), −1 = price below (resistance)
    int   sideBar        // bar on which `side` last changed
    int   lastSignalBar
    bool  shown          // among the displayed levels on the last evaluated bar

safeDiv(float num, float den, float fallback = 0.0) =>
    den != 0 and not na(num) and not na(den) ? num / den : fallback

fmtPrice(float v) =>
    na(v) ? "—" : str.tostring(v, format.mintick)

fmtScore(float s) =>
    na(s) ? "—" : str.tostring(math.round(s))

signedPct(float pct) =>
    na(pct) ? "—" : (pct >= 0 ? "+" : "") + str.tostring(pct, "#.##") + "%"

formatPctFromEntry(float level, float entry) =>
    string res = ""
    if showPctOnLabelsInput and not na(level) and not na(entry) and entry != 0
        float pct   = (level - entry) / entry * 100.0
        string sign = pct >= 0 ? "+" : ""
        res := " (" + sign + str.tostring(pct, "#.##") + "%)"
    res

sizeFromString(string s) =>
    s == "Tiny" ? size.tiny : s == "Small" ? size.small : s == "Normal" ? size.normal : s == "Large" ? size.large : s == "Huge" ? size.huge : size.small

sizeDownFromString(string s) =>
    s == "Tiny" ? size.tiny : s == "Small" ? size.tiny : s == "Normal" ? size.small : s == "Large" ? size.normal : s == "Huge" ? size.large : size.tiny

lineStyleFromString(string s) =>
    s == "Dashed" ? line.style_dashed : s == "Dotted" ? line.style_dotted : line.style_solid

buildGauge(float value, float maxVal, int width) =>
    string result = ""
    int filled = maxVal <= 0 ? 0 : math.round(math.min(width, math.max(0.0, value / maxVal * width)))
    for i = 0 to width - 1
        result += i < filled ? "▰" : "▱"
    result

periodStats(array<int> tms, array<bool> wns, int cutoff) =>
    int w = 0
    int l = 0
    int n = array.size(tms)
    if n > 0
        for i = 0 to n - 1
            if array.get(tms, i) >= cutoff
                if array.get(wns, i)
                    w += 1
                else
                    l += 1
    [w, l]

isPolar(Level lv) =>
    lv.highTouches > 0 and lv.lowTouches > 0

levelScore(Level lv) =>
    float s = nz(lv.strength) * (isPolar(lv) ? POLARITY_BONUS : 1.0)
    100.0 * (1.0 - math.exp(-s / SCORE_SCALE))

levelLabel(Level lv) =>
    str.tostring(math.round(levelScore(lv))) + " · " + str.tostring(lv.touches) + "T" + (isPolar(lv) ? " ⇅" : "")

levelTooltip(Level lv) =>
    "Score " + str.tostring(math.round(levelScore(lv))) + "/100\nTouches: " + str.tostring(lv.touches) + " (" + str.tostring(lv.highTouches) + " highs, " + str.tostring(lv.lowTouches) + " lows)\nCrossed: " + str.tostring(lv.crosses) + "×\nBand: " + fmtPrice(lv.lo) + " – " + fmtPrice(lv.hi) + "\nFirst touch: " + str.format_time(lv.firstTime, "yyyy-MM-dd HH:mm", syminfo.timezone)

levelTag(Level lv) =>
    levelLabel(lv) + " @ " + fmtPrice(lv.mid)

levelDashStr(Level lv) =>
    fmtPrice(lv.mid) + " (" + signedPct(safeDiv(lv.mid - close, close) * 100.0) + ") · " + str.tostring(math.round(levelScore(lv)))

refitBand(Level lv, float a) =>
    float m  = safeDiv(lv.sumWP, lv.sumW, lv.mid)
    float vr = safeDiv(lv.sumWP2, lv.sumW) - m * m
    float hw = math.min(math.max(math.sqrt(math.max(vr, 0.0)), a * MIN_HALF_WIDTH_ATR), a * levelTolInput)
    lv.mid := m
    lv.lo  := m - hw
    lv.hi  := m + hw
    hw

addTouch(Level lv, float price, float w, bool isHigh, float a) =>
    lv.sumW         := lv.sumW + w
    lv.sumWP        := lv.sumWP + w * price
    lv.sumWP2       := lv.sumWP2 + w * price * price
    lv.strength     := lv.strength + w
    lv.touches      := lv.touches + 1
    lv.highTouches  := lv.highTouches + (isHigh ? 1 : 0)
    lv.lowTouches   := lv.lowTouches + (isHigh ? 0 : 1)
    lv.lastTouchBar := bar_index
    if na(lv.activeTime) and lv.touches >= minTouchesInput
        lv.activeTime := time
    refitBand(lv, a)

newLevel(float price, float w, bool isHigh, int pBar, int pTime, float a) =>
    lv = Level.new()
    lv.mid           := price
    lv.lo            := price
    lv.hi            := price
    lv.sumW          := 0.0
    lv.sumWP         := 0.0
    lv.sumWP2        := 0.0
    lv.strength      := 0.0
    lv.touches       := 0
    lv.highTouches   := 0
    lv.lowTouches    := 0
    lv.firstTime     := pTime
    lv.firstBar      := pBar
    lv.activeTime    := na
    lv.lastTouchBar  := pBar
    lv.crosses       := 0
    lv.side          := close > price ? 1 : -1
    lv.sideBar       := bar_index
    lv.lastSignalBar := na
    lv.shown         := false
    addTouch(lv, price, w, isHigh, a)
    lv

mergeLevels(Level t, Level o, float a) =>
    t.sumW         := t.sumW + o.sumW
    t.sumWP        := t.sumWP + o.sumWP
    t.sumWP2       := t.sumWP2 + o.sumWP2
    t.strength     := t.strength + o.strength
    t.touches      := t.touches + o.touches
    t.highTouches  := t.highTouches + o.highTouches
    t.lowTouches   := t.lowTouches + o.lowTouches
    t.firstTime    := math.min(t.firstTime, o.firstTime)
    t.firstBar     := math.min(t.firstBar, o.firstBar)
    t.lastTouchBar := math.max(t.lastTouchBar, o.lastTouchBar)
    t.crosses      := math.max(t.crosses, o.crosses)
    t.shown        := t.shown or o.shown
    if not na(o.activeTime)
        t.activeTime := na(t.activeTime) ? o.activeTime : math.min(t.activeTime, o.activeTime)
    if not na(o.lastSignalBar)
        t.lastSignalBar := na(t.lastSignalBar) ? o.lastSignalBar : math.max(t.lastSignalBar, o.lastSignalBar)
    if na(t.activeTime) and t.touches >= minTouchesInput
        t.activeTime := time
    refitBand(t, a)

registerSwing(array<Level> lvls, float price, float w, bool isHigh, int pBar, int pTime, float a) =>
    float tol   = a * levelTolInput
    int   tIdx  = -1
    float bestD = tol
    int   n     = lvls.size()
    if n > 0
        for i = 0 to n - 1
            float d = math.abs(lvls.get(i).mid - price)
            if d <= bestD
                bestD := d
                tIdx  := i
    bool joined = tIdx >= 0
    if joined
        t = lvls.get(tIdx)
        addTouch(t, price, w, isHigh, a)
        bool again  = true
        int  passes = 0
        while again and passes < MERGE_PASSES
            again  := false
            passes += 1
            int k = lvls.size() - 1
            while k >= 0
                if k != tIdx
                    o = lvls.get(k)
                    if o.lo <= t.hi and t.lo <= o.hi
                        mergeLevels(t, o, a)
                        lvls.remove(k)
                        if k < tIdx
                            tIdx -= 1
                        again := true
                k -= 1
    if not joined
        lvls.push(newLevel(price, w, isHigh, pBar, pTime, a))
    while lvls.size() > MAX_LEVELS
        int   weakIdx = 0
        float weakStr = lvls.get(0).strength
        for j = 1 to lvls.size() - 1
            if lvls.get(j).strength < weakStr
                weakStr := lvls.get(j).strength
                weakIdx := j
        lvls.remove(weakIdx)
    lvls.size()
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)

ageAndCross(array<Level> lvls, float decay) =>
    int   nUp   = 0
    float lvlUp = na
    int   nDn   = 0
    float lvlDn = na
    int k = lvls.size() - 1
    while k >= 0
        lv = lvls.get(k)
        lv.strength := lv.strength * decay
        int newSide = close > lv.hi ? 1 : close < lv.lo ? -1 : lv.side
        if newSide != lv.side
            if lv.touches >= minTouchesInput
                lv.crosses  := lv.crosses + 1
                lv.strength := lv.strength * CROSS_PENALTY
                if lv.shown and newSide == 1
                    nUp   += 1
                    lvlUp := lv.mid
                if lv.shown and newSide == -1
                    nDn   += 1
                    lvlDn := lv.mid
            lv.side    := newSide
            lv.sideBar := bar_index
        if lv.strength < MIN_STRENGTH
            lvls.remove(k)
        k -= 1
    [nUp, lvlUp, nDn, lvlDn]

resetShown(array<Level> lvls) =>
    if lvls.size() > 0
        for i = 0 to lvls.size() - 1
            lv = lvls.get(i)
            lv.shown := false

selectNearest(array<Level> lvls, int sideWanted, int n) =>
    out = array.new<Level>()
    int m = lvls.size()
    if m > 0
        used = array.new<bool>(m, false)
        for k = 0 to n - 1
            int   best  = -1
            float bestD = na
            for i = 0 to m - 1
                lv = lvls.get(i)
                if not used.get(i) and lv.touches >= minTouchesInput and lv.side == sideWanted
                    float d = math.abs(close - lv.mid)
                    if na(bestD) or d < bestD
                        bestD := d
                        best  := i
            if best < 0
                break
            used.set(best, true)
            pick = lvls.get(best)
            pick.shown := true
            out.push(pick)
    out

checkBuyRejection(array<Level> sup, float clvMin, bool enabled) =>
    bool   fired = false
    float  score = na
    string info  = ""
    if enabled and sup.size() > 0
        float rng = high - low
        float clv = rng > 0 ? (close - low) / rng : 0.5
        for i = 0 to sup.size() - 1
            lv = sup.get(i)
            bool approached = lv.side == 1 and lv.sideBar < bar_index
            bool touched    = low <= lv.hi
            bool heldAbove  = close > lv.hi
            bool strongBar  = clv >= clvMin
            bool scoreOk    = levelScore(lv) >= minScoreInput
            bool rested     = na(lv.lastSignalBar) or bar_index - lv.lastSignalBar >= cooldownInput
            if approached and touched and heldAbove and strongBar and scoreOk and rested
                lv.lastSignalBar := bar_index
                fired := true
                score := levelScore(lv)
                info  := levelTag(lv)
                break
    [fired, score, info]

checkSellRejection(array<Level> res, float clvMin, bool enabled) =>
    bool   fired = false
    float  score = na
    string info  = ""
    if enabled and res.size() > 0
        float rng = high - low
        float clv = rng > 0 ? (high - close) / rng : 0.5
        for i = 0 to res.size() - 1
            lv = res.get(i)
            bool approached = lv.side == -1 and lv.sideBar < bar_index
            bool touched    = high >= lv.lo
            bool heldBelow  = close < lv.lo
            bool strongBar  = clv >= clvMin
            bool scoreOk    = levelScore(lv) >= minScoreInput
            bool rested     = na(lv.lastSignalBar) or bar_index - lv.lastSignalBar >= cooldownInput
            if approached and touched and heldBelow and strongBar and scoreOk and rested
                lv.lastSignalBar := bar_index
                fired := true
                score := levelScore(lv)
                info  := levelTag(lv)
                break
    [fired, score, info]

purgeDrawings(array<box> bx, array<line> ln, array<label> lb) =>
    while bx.size() > 0
        box.delete(bx.pop())
    while ln.size() > 0
        line.delete(ln.pop())
    while lb.size() > 0
        label.delete(lb.pop())
    bx.size()

drawLevels(array<Level> lvls, color baseCol, array<box> bx, array<line> ln, array<label> lb, string lblSize) =>
    int tfMs   = timeframe.in_seconds() * 1000
    int rightT = time + LEVEL_RIGHT_EXT * tfMs
    if lvls.size() > 0
        for i = 0 to lvls.size() - 1
            lv = lvls.get(i)
            int   leftT   = drawFromFirstInput ? lv.firstTime : nz(lv.activeTime, lv.firstTime)
            color fillCol = color.new(baseCol, ZONE_TRANSP)
            color midCol  = color.new(baseCol, 35)
            bx.push(box.new(leftT, lv.hi, rightT, lv.lo, xloc = xloc.bar_time, border_color = na, bgcolor = fillCol))
            ln.push(line.new(leftT, lv.mid, rightT, lv.mid, xloc = xloc.bar_time, color = midCol, style = line.style_dotted, width = 1))
            if showLevelLabelsInput
                lb.push(label.new(rightT, lv.mid, levelLabel(lv), xloc = xloc.bar_time, style = label.style_label_left, color = color.new(baseCol, 75), textcolor = TEXT_COLOR, size = lblSize, tooltip = levelTooltip(lv)))

htfBiasCalc() =>
    float e = ta.ema(close, HTF_EMA_LEN)
    na(e) ? 0 : close > e ? 1 : -1

dashRow(table t, int row, string

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

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

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

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

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

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

Стоимость полного кода
4 477,10₽ ≈ $54.71