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

Smart Money Concept ML

indicator smart money concept ml for tradingview.pine

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

#__slope direction line.mq5MQL5 / MetaTrader 5 · 1 общих теговrange breakout @meowforex1.pineTradingView · 1 общих тегов#1.mtf_collider_h4_close_13_v18bar.mq4MQL4 / MetaTrader 4 · 1 общих тегов!mth_fasttmaline_edit3.mq4MQL4 / MetaTrader 4 · 1 общих тегов!mth_fasttmaline_edit2.mq4MQL4 / MetaTrader 4 · 1 общих тегов!mth_fasttmaline_edit.mq4MQL4 / MetaTrader 4 · 1 общих тегов
Вы автор или правообладатель?

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

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

О программе

Индикатор «Smart Money Concept ML» отображает на графике карту концепций Smart Money: прорывы свинговой и внутренней структуры (BOS и CHoCH), ордер-блоки, фейр-вэлюэ-гэпы, равные хаи и лоу, сильные и слабые экстремумы, зоны премии и дисконта. Все элементы рисуются на основе фиксированных правил и подтверждаются только при закрытии бара, без использования будущих данных.

Компонент «ML» представляет собой двухпараметрическую байесовскую логистическую регрессию, которая калибрует шансы ретеста и ликвидности на реальных результатах текущего графика. Каждая оценка начинается от вероятности случайного блуждания и корректируется по мере накопления разрешённых прорывов. При недостатке данных таблица показывает режим обучения.

Индикатор генерирует сигналы BUY и SELL исключительно на основе направления свинговой структуры, предлагает три режима входа и управление риском со стопом и двумя целями. Система записывает гипотетические результаты каждого сигнала с указанием среднего R и 95%-ного интервала, а также проводит статистический тест для трёх разбиений прорывов.

Все прорывы, сигналы и записанные оценки фиксируются в момент закрытия бара и не перерисовываются. Поддерживаются оповещения TradingView: отдельные условия для BUY, SELL, закрытия, CHoCH, BOS и снятия ликвидности, а также единый алерт, передающий все входы и выходы с ценами в текстовом или JSON-формате.

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

НастройкаОписание
i_swing Количество баров каждой стороны для подтверждения свинг-хая или свинг-лоу. Свинг-точка становится известной только через столько баров после бара, который она отмечает, поэтому свинги, прорывы и ордер-блоки появляются с задержкой. Свинговые прорывы управляют шансами, тестом на преимущество и сигнала
i_int Количество баров каждой стороны для определения внутренней (минорной) структуры. Внутренние прорывы отображаются, но не влияют на шансы, тест на преимущество и сигналы.
show_int Отображать внутреннюю (минорную) структуру: прорывы внутреннего BOS и CHoCH. Эти прорывы носят исключительно информационный характер.
show_swl Отображать метки свинговых точек: HH (новый максимум), LH (более низкий максимум), HL (более высокий минимум), LL (новый минимум).
show_ob Отображать ордер-блоки: последние свечи противоположного направления на экстремуме ноги. Ордер-блок считается потраченным при закрытии цены через него.
i_ob_keep Максимальное количество отображаемых ордер-блоков одного типа и направления на графике.
show_fvg Отображать фейр-вэлюэ-гэпы. Гэп остаётся на графике до тех пор, пока цена не вернётся через него.
i_fvg_min Минимальный размер фейр-вэлюэ-гэпа в ATR. Гэпы меньшего размера не отображаются, но учитываются в тесте на преимущество.
show_eq Отображать ближайшие два пула равных хаев и лоу на каждой стороне цены.
i_eq_tol Допуск в ATR для формирования пулов равных хаев или лоу: два внутренних свинговых экстремума в пределах этого расстояния без прорыва между ними образуют пул ликвидности.
show_sw Отображать сильный и слабый экстремумы текущего диапазона сделок. Сильный — экстремум, защищённый структурой; слабый — тот, на который структура указывает.
show_pd Закрашивать верхнюю половину текущего диапазона сделок как зону премии, а нижнюю — как зону дисконта. Таблица всегда показывает, где находится цена.
show_draw Отображать шансы ликвидности: метки ближайших незанятых пулов ликвидности выше и ниже цены с вероятностью того, что каждый из них будет пройден первым. Вероятности рассчитываются по формуле случайного блуждания и калибруются на результатах текущего графика.
i_rt_win Количество баров после прорыва свинга, в течение которых возврат к прорывному уровню считается ретестом. Используется для расчёта шансов ретеста и для входов Level Retest и Adaptive.
show_sig Отображать сигналы BUY и SELL на графике. BUY появляется только из бычьей свинговой структуры, SELL — только из медвежьей. Каждый сигнал содержит уровень входа, стоп и две цели.
i_entry Режим входа: Break Close — вход по закрытию бара прорыва; Level Retest — вход по закрытию бара, вернувшегося к прорывному уровню и закрывшемуся за ним; Adaptive — комбинация двух режимов в зависимости от порога i_adapt.
i_adapt Порог калиброванных шансов ретеста для режима Adaptive: при значении на или выше этого порога индикатор ожидает ретест уровня, а при более низком — входит по закрытию прорыва.
i_which Какие типы прорывов генерируют сигналы: CHoCH + BOS — оба типа; CHoCH only — только изменение характера; BOS only — только прорыв структуры.
i_sl_buf Дополнительное расстояние за защищённым свингом для размещения стопа, измеряемое в ATR(14).
i_max_r Максимальное расстояние стопа от входа в ATR. Сигналы с более широким стопом пропускаются.
i_tp1 Первая цель в единицах R (кратность риска). Половина позиции оценивается по достижении TP1.
i_tp2 Вторая цель в единицах R. Оставшаяся часть позиции оценивается по достижении TP2 или стопа. Если TP2 установлен ниже TP1, значения автоматически меняются местами.
i_be При достижении TP1 переносить стоп на уровень входа, фиксируя позицию без убытка.
i_tr_life Количество баров, после которых открытая позиция автоматически закрывается по цене бара. Этот выход оценивается в записи результатов.
i_fmt Формат сообщения алерта: Text — текстовое сообщение с ценами и уровнями; JSON — структурированный JSON с указанием стороны, символа, таймфрейма, цен, стопа и целей.
bull_col Цвет бычьих (покупательных) элементов индикатора: линий, меток и сигналов BUY.
bear_col Цвет медвежьих (продажных) элементов индикатора: линий, меток и сигналов SELL.
show_table Отображать информационную таблицу с направлением структуры, шансами ретеста и ликвидности, записями результатов, тестом на преимущество и калибровкой.
i_tbl_pos Позиция информационной таблицы на графике: верхний правый, нижний правый, верхний левый или нижний левый угол.
zen_mode Скрыть метки и таблицу. Линии структуры, зоны и уровни сигналов остаются видимыми.
Получить полный код

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

indicator("Smart Money Concept ML", "SMC ML",
     overlay = true, max_boxes_count = 50, max_labels_count = 200, max_lines_count = 200, max_bars_back = 500)

var string G_STR = "Structure"
var string G_ZON = "Zones & Liquidity"
var string G_AI  = "Odds Engine"
var string G_SIG = "Signals & Automation"
var string G_VIS = "Display"

i_swing    = input.int  (10,  "Swing Length", group=G_STR, minval=3, maxval=50,
     tooltip="Bars each side that confirm a swing high or low. A swing point is only known this many bars after the bar it marks, so swings, breaks and order blocks appear that many bars late. Swing breaks drive the odds, the edge test and the signals.")
i_int      = input.int  (3,   "Internal Length", group=G_STR, minval=2, maxval=10,
     tooltip="Bars each side for the internal (minor) structure. It forms the equal highs and lows. Internal breaks are display only.")
show_int   = input.bool (false, "Show Internal Structure", group=G_STR)
show_swl   = input.bool (false, "Show Swing Labels (HH · HL · LH · LL)", group=G_STR)

show_ob    = input.bool (true, "Order Blocks", group=G_ZON, inline="ob")
i_ob_keep  = input.int  (2,   "per side", group=G_ZON, inline="ob", minval=1, maxval=10)
show_fvg   = input.bool (false, "Fair Value Gaps", group=G_ZON, inline="fvg")
i_fvg_min  = input.float(0.3, "min ATR", group=G_ZON, inline="fvg", minval=0.0, maxval=3.0, step=0.05,
     tooltip="Smallest gap counted, in ATR. A gap stays drawn until price trades back through it. Gaps are also used by the edge test whether or not they are drawn.")
show_eq    = input.bool (true, "Equal Highs / Lows", group=G_ZON, inline="eq")
i_eq_tol   = input.float(0.15, "tolerance ATR", group=G_ZON, inline="eq", minval=0.02, maxval=1.0, step=0.01,
     tooltip="Two internal swing highs (or lows) within this distance, with no trade beyond them in between, form a liquidity pool. The nearest two on each side are drawn until price trades through them.")
show_sw    = input.bool (true, "Strong / Weak High & Low", group=G_ZON,
     tooltip="The current dealing range. The extreme protected by the structure is Strong; the one the structure is pointing at is Weak.")
show_pd    = input.bool (false, "Premium / Discount Zones", group=G_ZON,
     tooltip="Shades the upper half of the dealing range as premium and the lower half as discount. The table always reports where price sits.")

show_draw  = input.bool (true, "Liquidity Odds", group=G_AI,
     tooltip="Labels the nearest untaken pool above and below price with the chance that it is traded through before the other one: the random-walk first-touch probability, calibrated on this chart's resolved setups.")
i_rt_win   = input.int  (20,  "Retest Window (bars)", group=G_AI, minval=3, maxval=200,
     tooltip="Bars after a swing break in which a return to the broken level counts as a retest. Used by the retest odds and by the Level Retest and Adaptive entries.")

show_sig   = input.bool  (true, "Show Buy / Sell Signals", group=G_SIG,
     tooltip="BUY only from bullish swing structure, SELL only from bearish. Each signal carries its entry, stop and two targets, drawn on the chart and sent in the alert message.")
i_entry    = input.string("Break Close", "Entry", group=G_SIG, options=["Break Close", "Level Retest", "Adaptive"],
     tooltip="Break Close (default): enter on the close of the break bar. Its signals do not depend on how much history is loaded.\n\nLevel Retest: wait up to the Retest Window for a bar that trades back to the broken level and closes beyond it again; enter on that close. No trade if it does not come, or if the protected swing is closed through first.\n\nAdaptive: Level Retest when the break's retest odds are at or above the threshold below, Break Close otherwise.\n\nThe stop is beyond the protected swing in every mode.")
i_adapt    = input.float (0.80, "Adaptive: wait for the retest at odds ≥", group=G_SIG, minval=0.50, maxval=0.99, step=0.01,
     tooltip="Adaptive entry only. When a break's calibrated retest odds are at or above this value, wait for a Level Retest; below it, enter on the break close.")
i_which    = input.string("CHoCH + BOS", "Breaks That Signal", group=G_SIG, options=["CHoCH + BOS", "CHoCH only", "BOS only"])
i_sl_buf   = input.float (0.20, "Stop Buffer (ATR x)", group=G_SIG, minval=0.0, maxval=3.0, step=0.05,
     tooltip="Extra distance beyond the protected swing for the stop, in ATR(14).")
i_max_r    = input.float (6.0, "Max Risk (ATR x)", group=G_SIG, minval=0.5, maxval=30.0, step=0.5,
     tooltip="A signal whose stop would sit further than this from entry is skipped.")
i_tp1      = input.float (1.0, "TP1 (R)", group=G_SIG, minval=0.25, maxval=10.0, step=0.25, inline="tp")
i_tp2      = input.float (2.0, "TP2 (R)", group=G_SIG, minval=0.5, maxval=20.0, step=0.25, inline="tp",
     tooltip="Targets as multiples of the risk (entry to stop). Half the position is scored at TP1, the rest at TP2 or the stop. If TP2 is set below TP1 the two are swapped.")
i_be       = input.bool  (true, "Stop to Entry After TP1", group=G_SIG)
i_tr_life  = input.int   (150, "Trade Timeout (bars)", group=G_SIG, minval=10, maxval=2000,
     tooltip="A signal still open after this many bars is closed at that bar's close, and that exit is scored in the Record.")
i_fmt      = input.string("Text", "Alert Message Format", group=G_SIG, options=["Text", "JSON"],
     tooltip="For automation, create ONE alert with the condition 'Any alert() function call'. Every entry and exit is then sent with side, symbol, timeframe, price, stop and targets.")

float TP1R = math.min(i_tp1, i_tp2)
float TP2R = math.max(i_tp1, i_tp2)

bull_col   = input.color(color.new(#2962FF, 0), "Bullish Colour", group=G_VIS)
bear_col   = input.color(color.new(#FF6D00, 0), "Bearish Colour", group=G_VIS)
show_table = input.bool (true, "Show Info Table", group=G_VIS)
i_tbl_pos  = input.string("Top Right", "Info Table Position", group=G_VIS, options=["Top Right", "Bottom Right", "Top Left", "Bottom Left"])
zen_mode   = input.bool (false, "Zen Mode", group=G_VIS,
     tooltip="Hides labels and the table. Structure lines, zones and signal levels remain.")

int   CAL_CAP    = 500     // resolved samples kept for calibration
float CAL_LAMBDA = 50.0    // prior strength pulling the calibration to the formula
int   CAL_MIN    = 30      // resolved samples before the odds read LIVE
int   FT_WIN     = 150     // bars a break has to follow through, for the edge test
int   DRAW_WIN   = 1000    // bars a liquidity setup has to resolve; only setups a random walk resolves inside it are taken
float Z_BONF     = 2.39    // two-sided 5% over three splits
int   MARKS_KEEP = 6       // swing breaks drawn
float OB_MAX_ATR = 3.0     // order blocks taller than this many ATR are not drawn
float LBL_GAP    = 0.45    // right-edge labels closer than this many ATR are merged

float atr       = ta.atr(14)
float sd1       = ta.stdev(ta.change(close), 100)
bool  confirmed = barstate.isconfirmed
bool  atrOk     = not na(atr) and atr > 0
int   bar_ms    = math.max(nz(timeframe.in_seconds(timeframe.period), 60), 1) * 1000
float ph_sw     = ta.pivothigh(high, i_swing, i_swing)
float pl_sw     = ta.pivotlow (low,  i_swing, i_swing)
float ph_in     = ta.pivothigh(high, i_int, i_int)
float pl_in     = ta.pivotlow (low,  i_int, i_int)

bool fvgBull = atrOk and low > high[2] and close[1] > high[2] and (low - high[2]) >= i_fvg_min * atr
bool fvgBear = atrOk and high < low[2] and close[1] < low[2] and (low[2] - high) >= i_fvg_min * atr
int  lastFvgBullBar = ta.valuewhen(fvgBull, bar_index, 0)
int  lastFvgBearBar = ta.valuewhen(fvgBear, bar_index, 0)

f_role(int dir) =>
    dir == 1 ? bull_col : bear_col

f_px(float p) =>
    na(p) ? "—" : str.tostring(math.round_to_mintick(p))

f_pct(float v) =>
    na(v) ? "—" : str.tostring(math.round(v * 100)) + "%"

f_sgn(float v, string fmt) =>
    na(v) ? "—" : (v >= 0 ? "+" : "−") + str.tostring(math.abs(v), fmt)

f_phi(float x) =>
    float z = math.abs(x)
    float t = 1.0 / (1.0 + 0.2316419 * z)
    float d = 0.3989422804014327 * math.exp(-0.5 * z * z)
    float p = 1.0 - d * t * (0.319381530 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))))
    x >= 0 ? p : 1.0 - p

f_clampP(float p) =>
    math.max(0.005, math.min(0.995, p))

f_logit(float p) =>
    float q = f_clampP(p)
    math.log(q / (1.0 - q))

f_touch(float d, float sd, int w) =>
    na(sd) or sd <= 0 or na(d) ? na : f_clampP(2.0 * (1.0 - f_phi(d / (sd * math.sqrt(w)))))

type Cal
    array<float> x
    array<int>   y
    bool  fixA = false   // slope-only fit, a pinned at 0
    float a   = 0.0
    float b   = 1.0
    int   n   = 0      // scored samples
    int   hit = 0      // samples where the event happened
    float sp  = 0.0    // sum of the odds shown at print
    float bsM = 0.0    // Brier score of the odds shown
    float bsF = 0.0    // Brier score of the formula alone
    float bsB = 0.0    // Brier score of the running base rate
    float cut1 = 0.70  // reliability bands: below cut1, cut1 to cut2, cut2 and above
    float cut2 = 0.90
    float n1  = 0.0    // per band: samples, events, sum of odds shown
    float h1  = 0.0
    float p1  = 0.0
    float n2  = 0.0
    float h2  = 0.0
    float p2  = 0.0
    float n3  = 0.0
    float h3  = 0.0
    float p3  = 0.0

var Cal c_rt = Cal.new(array.new_float(), array.new_int(), cut1 = 0.70, cut2 = 0.90)          // retest odds
var Cal c_dr = Cal.new(array.new_float(), array.new_int(), true, cut1 = 0.65, cut2 = 0.80)    // liquidity odds, intercept pinned

f_calP(Cal c, float p0) =>
    na(p0) ? na : 1.0 / (1.0 + math.exp(-(c.a + c.b * f_logit(p0))))

f_calFit(Cal c) =>
    int n = array.size(c.x)
    if n > 0
        float a = c.a
        float b = c.b
        for iter = 0 to 3
            float ga  = CAL_LAMBDA * a
            float gb  = CAL_LAMBDA * (b - 1.0)
            float haa = CAL_LAMBDA
            float hab = 0.0
            float hbb = CAL_LAMBDA
            for i = 0 to n - 1
                float x = array.get(c.x, i)
                float p = 1.0 / (1.0 + math.exp(-(a + b * x)))
                float e = p - array.get(c.y, i)
                float w = p * (1.0 - p)
                ga  += e
                gb  += e * x
                haa += w
                hab += w * x
                hbb += w * x * x
            float det = haa * hbb - hab * hab
            if c.fixA
                b -= gb / hbb
            else if det > 0
                a -= (hbb * ga - hab * gb) / det
                b -= (haa * gb - hab * ga) / det
        c.a := a
        c.b := b
    true

f_calScore(Cal c, float pm, float p0, int y) =>
    float base = c.n > 0 ? float(c.hit) / c.n : 0.5
    c.bsB += math.pow(base - y, 2)
    c.bsM += math.pow(pm - y, 2)
    c.bsF += math.pow(p0 - y, 2)
    c.sp  += pm
    c.n   += 1
    c.hit += y
    if pm < c.cut1
        c.n1 += 1
        c.h1 += y
        c.p1 += pm
    else if pm < c.cut2
        c.n2 += 1
        c.h2 += y
        c.p2 += pm
    else
        c.n3 += 1
        c.h3 += y
        c.p3 += pm
    array.push(c.x, f_logit(p0))
    array.push(c.y, y)
    if array.size(c.x) > CAL_CAP
        array.shift(c.x)
        array.shift(c.y)
    f_calFit(c)

f_bandOk(float n, float h, float p, float z) =>
    bool ok = true
    if n >= 20
        float pr = p / n
        ok := math.abs(pr - h / n) <= z * math.sqrt(math.max(pr * (1.0 - pr), 0.0025) / n)
    ok

f_calOk(Cal c) =>
    bool ok = false
    if c.n >= CAL_MIN
        ok := f_bandOk(c.n, c.hit, c.sp, 1.96) and f_bandOk(c.n1, c.h1, c.p1, 2.39) and f_bandOk(c.n2, c.h2, c.p2, 2.39) and f_bandOk(c.n3, c.h3, c.p3, 2.39)
    ok

f_calText(Cal c) =>
    string s = "—"
    if c.n > 0
        s := str.tostring(math.round(c.sp / c.n * 100)) + "→" + f_pct(float(c.hit) / c.n) + (c.n >= CAL_MIN ? (f_calOk(c) ? " ✓" : " ✗") : " (n " + str.tostring(c.n) + ")")
    s

f_band(string lbl, float n, float h, float p) =>
    n > 0 ? lbl + " said " + str.tostring(math.round(p / n * 100)) + " → " + f_pct(h / n) + " (n " + str.tostring(n, "#") + ")" : lbl + " —"

f_bands(Cal c) =>
    string a = str.tostring(math.round(c.cut1 * 100))
    string b = str.tostring(math.round(c.cut2 * 100))
    f_band("under " + a + "%:", c.n1, c.h1, c.p1) + "\n   " + f_band(a + "–" + b + "%:", c.n2, c.h2, c.p2) + "\n   " + f_band(b + "%+:", c.n3, c.h3, c.p3)

f_skill(float bs, float ref) =>
    ref > 0 ? f_sgn(1.0 - bs / ref, "0.000") : "—"

type Struct
    int   len     = 10
    float hi      = na
    int   hiT     = 0
    int   hiB     = 0
    bool  hiOpen  = false
    float lo      = na
    int   loT     = 0
    int   loB     = 0
    bool  loOpen  = false
    float prevHi  = na
    float prevLo  = na
    int   prevHiT = 0
    int   prevLoT = 0
    int   prevHiB = 0
    int   prevLoB = 0
    float legLo   = na     // lowest low since the latest swing high printed
    int   legLoB  = 0
    int   legLoT  = 0
    float legHi   = na     // highest high since the latest swing low printed
    int   legHiB  = 0
    int   legHiT  = 0
    int   trend   = 0
    bool  newHi   = false
    bool  newLo   = false
    int   ev      = 0      // 1 bullish break, -1 bearish break, 0 none
    bool  evChoch = false
    float evLvl   = na
    int   evLvlT  = 0
    float evProt  = na
    int   evProtB = 0
    int   evProtT = 0
    bool  evSweep = false

f_step(Struct s, float ph, float pl) =>
    s.ev    := 0
    s.newHi := false
    s.newLo := false
    if not na(s.legLo) and low < s.legLo
        s.legLo  := low
        s.legLoB := bar_index
        s.legLoT := time
    if not na(s.legHi) and high > s.legHi
        s.legHi  := high
        s.legHiB := bar_index
        s.legHiT := time

    if s.hiOpen and close > s.hi
        s.ev      := 1
        s.evChoch := s.trend == -1
        s.trend   := 1
        s.evLvl   := s.hi
        s.evLvlT  := s.hiT
        s.evProt  := na(s.legLo) ? low : s.legLo
        s.evProtB := na(s.legLo) ? bar_index : s.legLoB
        s.evProtT := na(s.legLo) ? time : s.legLoT

        float rf = s.loB < s.evProtB ? s.lo : s.prevLoB < s.evProtB ? s.prevLo : na
        s.evSweep := not na(rf) and s.evProt < rf
        s.hiOpen  := false
    else if s.loOpen and close < s.lo
        s.ev      := -1
        s.evChoch := s.trend == 1
        s.trend   := -1
        s.evLvl   := s.lo
        s.evLvlT  := s.loT
        s.evProt  := na(s.legHi) ? high : s.legHi
        s.evProtB := na(s.legHi) ? bar_index : s.legHiB
        s.evProtT := na(s.legHi) ? time : s.legHiT
        float rf = s.hiB < s.evProtB ? s.hi : s.prevHiB < s.evProtB ? s.prevHi : na
        s.evSweep := not na(rf) and s.evProt > rf
        s.loOpen  := false
    if not na(ph)
        s.prevHi  := s.hi
        s.prevHiT := s.hiT
        s.prevHiB := s.hiB
        s.hi      := ph
        s.hiT    := time[s.len]
        s.hiB    := bar_index - s.len
        s.hiOpen := true
        s.newHi  := true

        float m  = low
        int   mb = 0
        for k = 0 to s.len - 1
            if low[k] < m
                m  := low[k]
                mb := k
        s.legLo  := m
        s.legLoB := bar_index - mb
        s.legLoT := time[mb]
    if not na(pl)
        s.prevLo  := s.lo
        s.prevLoT := s.loT
        s.prevLoB := s.loB
        s.lo      := pl
        s.loT    := time[s.len]
        s.loB    := bar_index - s.len
        s.loOpen := true
        s.newLo  := true
        float m  = high
        int   mb = 0
        for k = 0 to s.len - 1
            if high[k] > m
                m  := high[k]
                mb := k
        s.legHi  := m
        s.legHiB := bar_index - mb
        s.legHiT := time[mb]
    s

var Struct sw = Struct.new(i_swing)
var Struct it = Struct.new(i_int)

if confirmed
    f_step(sw, ph_sw, pl_sw)
    f_step(it, ph_in, pl_in)

float rng_hi  = na(sw.legHi) ? sw.hi : na(sw.hi) ? sw.legHi : math.max(sw.hi, sw.legHi)
float rng_lo  = na(sw.legLo) ? sw.lo : na(sw.lo) ? sw.legLo : math.min(sw.lo, sw.legLo)
float rng_pos = na(rng_hi) or na(rng_lo) or rng_hi <= rng_lo ? na : (close - rng_lo) / (rng_hi - rng_lo)

type Mark
    int    t0
    int    t1
    float  lvl
    int    dir
    bool   choch
    float  pr        // retest odds when the break printed
    int    ret = 0   // 1 retested within the window, -1 not, 0 open
    string tip = ""

var array<Mark> swMarks = array.new<Mark>()
var array<Mark> inMarks = array.new<Mark>()

type SwPt
    int    t
    float  p
    string txt
    int    dir

var array<SwPt> swPts = array.new<SwPt>()

type Brk
    int   dir
    float lvl
    float en
    float prot
    float r
    int   bar0
    bool  choch
    bool  fvg
    bool  sweep
    float p0       // retest formula at print
    float pm       // retest odds shown at print
    bool  rtDone = false
    bool  ftDone = false
    Mark  mk

var array<Brk> pend = array.new<Brk>()

type Drw
    float up
    float dn
    bool  nUp       // the nearer pool is the one above
    int   bar0
    float p0        // formula: nearer pool first
    float pm        // odds shown at print

var array<Drw> d_pend = array.new<Drw>()

type Zone
    int   dir
    float top
    float bot
    int   t0
    int   kind      // 0 order block, 1 fair value gap

var array<Zone> zones = array.new<Zone>()

type Liq
    float lvl
    int   kind      // 0 swing point, 1 equal highs / lows
    int   tLine
    int   tLabel

var array<Liq> liqUp = array.new<Liq>()
var array<Liq> liqDn = array.new<Liq>()

var array<int> et = array.new_int(12, 0)

f_etAdd(int k, int y) =>
    array.set(et, k, array.get(et, k) + 1)
    array.set(et, k + 1, array.get(et, k + 1) + y)
    true

f_z(int n1, int w1, int n2, int w2) =>
    float z = na
    if n1 >= 10 and n2 >= 10
        float p  = float(w1 + w2) / (n1 + n2)
        float se = math.sqrt(p * (1.0 - p) * (1.0 / n1 + 1.0 / n2))
        z := se > 0 ? (float(w1) / n1 - float(w2) / n2) / se : 0.0
    z
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 Trade
    int    dir    = 0
    float  entry  = na
    float  sl0    = na
    float  sl     = na
    float  tp1    = na
    float  tp2    = na
    int    bar0   = 0
    int    t0     = 0
    int    tBE    = 0
    int    tEnd   = 0
    float  px     = na
    bool   tp1hit = false
    bool   open   = true
    string res    = ""
    string how    = ""

var array<Trade> trades = array.new<Trade>()

var array<float> rec = array.from(0.0, 0.0, 0.0, 0.0, 0.0)

f_openDir() =>
    int d = 0
    if array.size(trades) > 0
        Trade t = array.get(trades, array.size(trades) - 1)
        if t.open
            d := t.dir
    d

f_msg(string kind, Trade t, float px) =>
    string side = t.dir == 1 ? "buy" : "sell"
    string pos  = t.dir == 1 ? "long" : "short"
    string act  = kind == "entry" ? side : kind == "tp1" ? "partial_close" : "close"
    i_fmt == "JSON" ? '{"source":"SMC ML","event":"' + kind + '","action":"' + act + '","position":"' + pos + '","side":"' + side + '","symbol":"' + syminfo.ticker + '","timeframe":"' + timeframe.period + '","price":' + f_px(px) + ',"entry":' + f_px(t.entry) + ',"sl":' + f_px(t.sl) + ',"tp1":' + f_px(t.tp1) + ',"tp2":' + f_px(t.tp2) + '}' : kind == "entry" ? "SMC ML: " + str.upper(side) + " " + syminfo.ticker + " @ " + f_px(px) + "  |  SL " + f_px(t.sl) + "  |  TP1 " + f_px(t.tp1) + "  |  TP2 " + f_px(t.tp2) : "SMC ML: " + syminfo.ticker + " " + str.upper(pos) + " " + str.upper(kind) + " @ " + f_px(px) + (kind == "tp1" ? "  |  SL now " + f_px(t.sl) : "")

f_R(Trade t, float px) =>
    float rk = math.abs(t.entry - t.sl0)
    float m  = rk > 0 ? t.dir * (px - t.entry) / rk : 0.0
    t.tp1hit ? 0.5 * TP1R + 0.5 * m : m

f_close(Trade t, string res, float px) =>
    t.open := false
    t.res  := res
    t.tEnd := time
    t.px   := px
    float R = f_R(t, px)
    array.set(rec, 0, array.get(rec, 0) + 1)
    array.set(rec, 1, array.get(rec, 1) + (t.tp1hit ? 1 : 0))
    array.set(rec, 2, array.get(rec, 2) + (res == "SL" ? 1 : 0))
    array.set(rec, 3, array.get(rec, 3) + R)
    array.set(rec, 4, array.get(rec, 4) + R * R)
    while array.size(trades) > 1
        array.shift(trades)
    true

f_addZone(Zone z) =>
    int hit = -1
    if array.size(zones) > 0
        for i = 0 to array.size(zones) - 1
            Zone e = array.get(zones, i)
            bool fits = z.kind == 1 or na(atr) or math.max(e.top, z.top) - math.min(e.bot, z.bot) <= OB_MAX_ATR * atr
            if hit < 0 and e.kind == z.kind and e.dir == z.dir and z.bot <= e.top and z.top >= e.bot and fits
                hit := i
    if hit >= 0
        Zone e = array.remove(zones, hit)
        e.top := math.max(e.top, z.top)
        e.bot := math.min(e.bot, z.bot)
        e.t0  := math.min(e.t0, z.t0)
        array.push(zones, e)
    else
        array.push(zones, z)
    true

f_capZones(int kind, int dir, int keep) =>
    int n = 0
    if array.size(zones) > 0
        for i = array.size(zones) - 1 to 0
            Zone z = array.get(zones, i)
            if z.kind == kind and z.dir == dir
                n += 1
                if n > keep
                    array.remove(zones, i)
    true

f_addLiq(array<Liq> a, int side, float lvl, int kind, int tLine, int tLabel) =>
    int pos = array.size(a)
    if array.size(a) > 0
        for i = 0 to array.size(a) - 1
            float v = array.get(a, i).lvl
            if side == 1 ? lvl < v : lvl > v
                pos := i
                break
    array.insert(a, pos, Liq.new(lvl, kind, tLine, tLabel))
    while array.size(a) > 20
        array.pop(a)
    true

f_sweep(array<Liq> a, int side) =>
    bool eq   = false
    bool more = array.size(a) > 0
    while more
        Liq l = array.get(a, 0)
        if side == 1 ? high > l.lvl : low < l.lvl
            eq := eq or l.kind == 1
            array.shift(a)
            more := array.size(a) > 0
        else
            more := false
    eq

f_eqClean(int side, float lv, int fromB) =>
    int  span = math.min(bar_index - fromB - 1, 300)
    bool ok   = true
    if span >= 0
        for k = 0 to span
            if side == 1 ? high[k] > lv : low[k] < lv
                ok := false
                break
    ok

f_ring(array<Mark> a, Mark m, int cap) =>
    array.push(a, m)
    while array.size(a) > cap
        array.shift(a)
    true

bool   ev_bchoch = false
bool   ev_schoch = false
bool   ev_bbos   = false
bool   ev_sbos   = false
bool   ev_buy    = false
bool   ev_sell   = false
bool   ev_exit   = false
bool   ev_sweep  = false
string al_exit   = ""
string al_entry  = ""

var int   rt_dir  = 0
var int   rt_bar  = 0
var float rt_lvl  = na
var float rt_prot = na
var float rt_p    = na

if confirmed and array.size(trades) > 0
    Trade t = array.get(trades, array.size(trades) - 1)
    if t.open and bar_index > t.bar0
        bool L     = t.dir == 1
        bool hitSL = L ? low <= t.sl : high >= t.sl
        bool hitT2 = L ? high >= t.tp2 : low <= t.tp2
        bool hitT1 = not t.tp1hit and (L ? high >= t.tp1 : low <= t.tp1)
        if hitSL
            string how  = not t.tp1hit ? "SL" : t.tBE > 0 ? "TP1, then stop at entry" : "TP1, then SL"

            float  slPx = L ? math.min(open, t.sl) : math.max(open, t.sl)
            f_close(t, how, slPx)
            ev_exit := true
            al_exit := f_msg(t.tBE > 0 ? "stop at entry" : "sl", t, slPx)
        else if hitT2
            float t2Px = L ? math.max(open, t.tp2) : math.min(open, t.tp2)
            t.tp1hit := true
            f_close(t, "TP2", t2Px)
            ev_exit := true
            al_exit := f_msg("tp2", t, t2Px)
        else
            if hitT1
                t.tp1hit := true
                if i_be
                    t.sl  := t.entry
                    t.tBE := time
                al_exit := f_msg("tp1", t, t.tp1)
            if bar_index - t.bar0 >= i_tr_life
                f_close(t, "timeout", close)
                ev_exit := true
                al_exit := f_msg("timeout", t, close)

if confirmed and array.size(pend) > 0
    for i = array.size(pend) - 1 to 0
        Brk b = array.get(pend, i)
        if bar_index > b.bar0
            bool L = b.dir == 1
            if not b.rtDone
                bool touch = L ? low <= b.lvl : high >= b.lvl
                if touch or bar_index - b.bar0 >= i_rt_win
                    b.rtDone := true
                    b.mk.ret := touch ? 1 : -1
                    if not na(b.p0)
                        f_calScore(c_rt, b.pm, b.p0, touch ? 1 : 0)
            if not b.ftDone
                bool fail = L ? close < b.prot : close > b.prot
                bool win  = L ? close >= b.en + b.r : close <= b.en - b.r
                if fail or win or bar_index - b.bar0 >= FT_WIN
                    b.ftDone := true
                    if fail or win
                        int y = fail ? 0 : 1
                        f_etAdd(b.choch ? 2 : 0, y)
                        f_etAdd(b.fvg ? 4 : 6, y)
                        f_etAdd(b.sweep ? 8 : 10, y)
            if b.rtDone and b.ftDone
                array.remove(pend, i)

if confirmed and array.size(d_pend) > 0
    for i = array.size(d_pend) - 1 to 0
        Drw w = array.get(d_pend, i)
        if bar_index > w.bar0
            bool hu = high > w.up
            bool hd = low < w.dn
            if hu or hd or bar_index - w.bar0 >= DRAW_WIN
                if hu != hd
                    f_calScore(c_dr, w.pm, w.p0, hu == w.nUp ? 1 : 0)
                array.remove(d_pend, i)

if confirmed and array.size(zones) > 0
    for i = array.size(zones) - 1 to 0
        Zone z = array.get(zones, i)
        bool gone = z.kind == 0 ? (z.dir == 1 ? close < z.bot : close > z.top) : (z.dir == 1 ? low <= z.bot : high >= z.top)
        if gone
            array.remove(zones, i)

if confirmed and show_fvg and (fvgBull or fvgBear)
    int d = fvgBull ? 1 : -1
    f_addZone(Zone.new(d, fvgBull ? low : low[2], fvgBull ? high[2] : high, time[2], 1))
    f_capZones(1, d, 4)

if confirmed
    bool sU = f_sweep(liqUp, 1)
    bool sD = f_sweep(liqDn, -1)
    ev_sweep := sU or sD

if confirmed and atrOk
    if sw.newHi
        f_addLiq(liqUp, 1, sw.hi, 0, sw.hiT, sw.hiT)
    if sw.newLo
        f_addLiq(liqDn, -1, sw.lo, 0, sw.loT, sw.loT)
    if it.newHi and not na(it.prevHi) and math.abs(it.hi - it.prevHi) <= i_eq_tol * atr
        float lv = math.max(it.hi, it.prevHi)
        if f_eqClean(1, lv, it.prevHiB)
            f_addLiq(liqUp, 1, lv, 1, it.prevHiT, it.hiT)
    if it.newLo and not na(it.prevLo) and math.abs(it.lo - it.prevLo) <= i_eq_tol * atr
        float lv = math.min(it.lo, it.prevLo)
        if f_eqClean(-1, lv, it.prevLoB)
            f_addLiq(liqDn, -1, lv, 1, it.prevLoT, it.loT)

if confirmed and show_swl
    if sw.newHi
        array.push(swPts, SwPt.new(sw.hiT, sw.hi, na(sw.prevHi) ? "H" : sw.hi > sw.prevHi ? "HH" : "LH", 1))
    if sw.newLo
        array.push(swPts, SwPt.new(sw.loT, sw.lo, na(sw.prevLo) ? "L" : sw.lo < sw.prevLo ? "LL" : "HL", -1))
    while array.size(swPts) > 30
        array.shift(swPts)

if confirmed and show_int and it.ev != 0 and not (sw.ev == it.ev and sw.evLvl == it.evLvl)
    f_ring(inMarks, Mark.new(it.evLvlT, t

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

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

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

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

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

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

Стоимость полного кода
3 845,20₽ ≈ $47.00