Привет, Хабр!

Если вы увлечены трейдингом и хотите автоматизировать анализ рынка по концепции Smart Money Concept, то в этом вам может помочь собственноручно написанный индикатор. Написание индикаторов и систем по концепту smart mpney(ict) - часто довольно сложная история.

В этой статье я разберу, как создать такой индикатор в Pine Script версии 6 шаг за шагом. Мы пройдемся по коду, выделяя ключевые фрагменты с объяснениями, чтобы вы могли не только применить его на TradingView, но и понять логику, доработать или даже интегрировать в свои стратегии. Давайте нырнем в детали, начиная с основ.

Весь код есть на github.

Выглядеть наш индикатор минимально будет следующим образом:

Отрисовка уровней и свингов - его ключевая задача. А исходя из этого мы уже сможет работать с позициями.

Настройка индикатора и входные параметры

Первый шаг — объявление индикатора и его параметров. Это позволяет пользователям кастомизировать поведение без редактирования кода.

Вот фрагмент кода для инициализации:

//@version=6
indicator("Smart Money Structure Analyzer", overlay=true, max_labels_count=500)
// ===== INPUTS =====
swingPeriod = input.int(3, "Swing Period", minval=3, maxval=21)
showFractals = input.bool(false, "Show Fractals")
showStructure = input.bool(false, "Show Structure Lines")
showBOS = input.bool(true, "Show Break of Structure")
showCHoCH = input.bool(true, "Show Change of Character")
showMitigation = input.bool(false, "Show Mitigation Levels")

Здесь мы используем indicator с параметром overlay=true, чтобы индикатор накладывался на график цены, и max_labels_count=500 для ограничения меток, чтобы избежать перегрузки. Входные данные — это swingPeriod для длины окна свингов (от 3 до 21, чтобы захватывать разные таймфреймы), и булевы флаги для визуализации: от фракталов (свингов) до уровней смены характера.

По умолчанию BOS и CHoCH включены, так как они являются ключевыми факторами структуры, а остальные - опциональны, чтобы не перегружать чарт.

Выявление свингов: основа анализа

Свинги — это пики и впадины, вокруг которых строится структура. Мы реализуем их детекцию аккуратно, чтобы избежать ложных сигналов.

Рассмотрим функцию swingHigh():

swingHigh() =>
    middle = math.floor(swingPeriod / 2)
    if bar_index < swingPeriod
        false
    else
        // Центральная свеча - наивысшая в окне
        isHighest = high[middle] == ta.highest(high, swingPeriod)[middle]
        // Проверяем lower highs слева
        leftCondition = true
        for i = 1 to middle
            leftCondition := leftCondition and high[middle] > high[middle - i]
        // Проверяем lower highs справа
        rightCondition = true
        for i = 1 to middle
            rightCondition := rightCondition and high[middle] > high[middle + i]
        isHighest and leftCondition and rightCondition

Эта функция проверяет, является ли центральная свеча в окне свингом: она должна иметь наивысший high, и соседние high слева/справа должны быть ниже (фланкирование). Мы используем ta.highest для сравнения, а циклы for обеспечивают строгую проверку. Если баров меньше периода, возвращаем false - это предотвращает ошибки на начале графика. Аналогично работает swingLow(), только для low.

Затем мы присваиваем currentSwingHigh = swingHigh() и currentSwingLow = swingLow() для использования на каждом баре.

Хранение свингов для исторического анализа

Чтобы анализировать структуру, нужно помнить прошлые свинги. Мы используем массивы для этого.

Фрагмент добавления свингов:

var float[] swingHighPrices = array.new_float()
var int[] swingHighBars = array.new_int()
var float[] swingLowPrices = array.new_float()
var int[] swingLowBars = array.new_int()
if currentSwingHigh
    middle = math.floor(swingPeriod / 2)
    swingPrice = high[middle]
    swingBar = bar_index - middle
   
    // Проверяем, чтобы это был действительно новый свинг (не рядом с предыд��щим)
    shouldAdd = true
    if array.size(swingHighBars) > 0
        lastBar = array.get(swingHighBars, 0)
        if math.abs(swingBar - lastBar) < swingPeriod
            shouldAdd := false
   
    if shouldAdd
        array.unshift(swingHighPrices, swingPrice)
        array.unshift(swingHighBars, swingBar)

Мы создаем массивы для цен и баров свингов. При обнаружении свинга рассчитываем цену и индекс (с учетом смещения), но добавляем проверку shouldAdd: если свинг слишком близко к предыдущему, игнорируем — это фильтрует шум. array.unshift добавляет в начало, сохраняя порядок от новых к старым. Ограничиваем размер до 20 с array.pop, чтобы оптимизировать производительность. То же для low. Это умный способ хранить только релевантную историю.

Анализ структуры рынка

Теперь переходим к сути: определение тренда на основе свингов.

Функции для ключевых уровней:

getLastHigherLow() =>
    if array.size(swingLowPrices) < 2
        na
    else
        lastLow = array.get(swingLowPrices, 0)
        prevLow = array.get(swingLowPrices, 1)
        lastLow > prevLow ? lastLow : na
// Аналогично для getLastLowerHigh(), getLastLowerLow(), getLastHigherHigh()

Эти функции сравнивают последние два свинга: если последний low выше предыдущего, это Higher Low (HL). Аналогично есть и higher high (hh), а также lower low и lower high для нисходящей структуры. Мы получаем lastHL, lastLH и т.д. Возвращаем na, если данных мало — это предотвращает ложные выводы.

Затем состояние структуры:

getStructureState() =>
    hasEnoughData = array.size(swingHighPrices) >= 2 and array.size(swingLowPrices) >= 2
   
    if not hasEnoughData
        "INSUFFICIENT_DATA"
    else
        hhExists = not na(lastHH)
        llExists = not na(lastLL)
        hlExists = not na(lastHL)
        lhExists = not na(lastLH)
       
        if hhExists and hlExists
            "UPTREND"
        else if llExists and lhExists
            "DOWNTREND"
        else if (hhExists and llExists) or (hlExists and lhExists)
            "CONSOLIDATION"
        else
            "UNDEFINED"

Здесь мы классифицируем: HH + HL = аптренд, LL + LH = даунтренд, смешанное = консолидация. Это основано на последовательных свингах, что отражает реальное поведение "умных денег".

Детекция BOS и CHoCH: сигналы сдвига

BOS — прорыв структуры, CHoCH — подтверждение смены.

Для BOS вниз:

bosDown() =>
    if not na(lastHL)
        isBearish = close < open
        closeBelowHL = close < lastHL and low < lastHL
        isBearish and closeBelowHL
    else
        false

Прорыв, когда медвежья свеча закрывается ниже HL. Симметрично для bosUp() выше LH. Ключ: проверка на бычью/медвежью свечу дополняет фильтр.

Для CHoCH:

chochDown() =>
    if hasBOSDown and currentStructure == "UPTREND"
        not na(lastLL)
    else
        false

После BOS в аптренде ждем LL для подтверждения даунтренда.

Уровни смены характера:

getMitigationLevel() =>
    if hasBOSDown and not na(lastHL)
        lastHL * 1.002
    else if hasBOSUp and not na(lastLH)
        lastLH * 0.998
    else
        na

Небольшой отступ (0.2%) для потенциального возврата — нужно для грамотных входов.

Визуализация и алерты

Визуалы помогают быстро читать график.

Для свингов:

plotshape(swingHighToShow, "Swing High", shape.xcross,
     location.abovebar, color=color.new(color.red, 0), size=size.small,
     offset = -math.floor(swingPeriod / 2))

Крестики с offset для точного позиционирования.

Линии структуры:

if showStructure
    if not na(lastHL)
        if na(lastHLLine)
            lastHLLine := line.new(bar_index - 50, lastHL, bar_index, lastHL,
                 color=color.new(color.blue, 70), width=1, style=line.style_dashed)
        else
            line.set_xy1(lastHLLine, bar_index - 50, lastHL)
            line.set_xy2(lastHLLine, bar_index, lastHL)

Динамические линии, обновляемые с line.set_xy — они тянутся в реальном времени.

Сигналы BOS/CHoCH — треугольники/круги, смена характера — линия с точками. Инфо-лейбл на последнем баре показывает статус с цветом по тренду.

Алерты:

if hasBOSDown
    alert("BOS DOWN detected at " + str.tostring(close), alert.freq_once_per_bar_close)

Уведомления для алерт-реакции.

Заключение: почему этот индикатор стоит попробовать

Этот индикатор превращает абстрактную SMC в рабочий инструмент. Сделали аналитику структуры и красивую отрисовку. Попробуйте на TradingView, поэкспериментируйте с периодами, и увидите, как он помогает ловить трендовые развороты. Безусловно, тут есть много возможностей для доработок.

Всегда рад услышать ваши мысли и предложения!