Smart Money Concept ML
Похожие материалы по тегам
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «indicator smart money concept ml for tradingview.pine» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
Индикатор «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? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.