الرئيسية ← المؤشرات ← Gold M15 ← شيفرة القياس
شيفرة القياس

النسخة البحثية من Gold M15

هذه هي الشيفرة التي أنتجت الأرقام المنشورة في صفحة المنهج. ننشرها كاملةً لتتحقّق بنفسك، لا لتصدّقنا على كلمتنا.

ما الفرق بينها وبين المؤشر المنشور

محرّك الإشارات واحد. فبالإعدادات الافتراضية تُطلق هذه النسخة الإشارات نفسها التي يُطلقها المؤشر المنشور على TradingView، ولا تزيد عليه إلا ما يلزم للقياس:

كيف تعيد القياس بنفسك

  1. افتح TradingView، ثم محرّر Pine أسفل الشارت، وأنشئ مؤشراً جديداً.
  2. انسخ الشيفرة أدناه كاملةً، والصقها مكان المحتوى الافتراضي، ثم احفظ واضغط «Add to chart».
  3. افتح شارت الذهب XAUUSD على إطار 15 دقيقة.
  4. من إعدادات المؤشر اضبط «Count signals from» على تاريخ البداية الذي تريده، فتظهر النتيجة في جدول RESEARCH TALLY على الشارت.

قد تختلف أرقامك عن أرقامنا، لأنّها تتوقّف على تاريخ البداية، وعلى عدد الشموع التاريخية التي يحمّلها حسابك في TradingView، وعلى مزوّد السعر. أما المنطق فواحد، وهو أمامك كاملاً.

الشيفرة

تنزيل الملف Pine Script v6 · 635 سطراً
//@version=6
// Gold M15 Signal Engine — research build
// © 2026 Consultya · consultya.co
// Published for reading and verification. This is the code that produced
// the measured figures on the Consultya website. It is not a trading system.
//
// Same engine as the published version, plus what is needed to measure it:
//   1. Partial exits. A third books at TP1, a third at TP2, the rest at
//      TP3 or the stop - which is how the signals are actually traded.
//      Without this, a trade that reached TP1 and came back was recorded
//      as zero, when in reality a third of it was already banked.
//   2. A switch for the breakeven rule, so the same bars can be
//      measured both ways.
//   3. RSI guards on the breakout entry, adjustable rather than fixed.
//   4. An R-multiple tally. Win rate alone cannot tell you whether a
//      system makes money - a 35% win rate can be excellent and a 60%
//      one can be ruinous. R is the distance to the original stop, so
//      every outcome is expressed in units of what was risked.
//   5. A fixed start date and a timeframe override, so the sample can
//      be held still between two readings.

indicator("Gold M15 — Research Build", overlay=true, max_labels_count=500, max_lines_count=100)

// ══════════════════════════════════════════════════════════════
// INPUTS
// ══════════════════════════════════════════════════════════════
i_e20    = input.int(20,    "EMA 20",              group="Moving averages")
i_e50    = input.int(50,    "EMA 50",              group="Moving averages")
i_e200   = input.int(200,   "EMA 200",             group="Moving averages")
i_swing  = input.int(7,     "Swing Length",        group="Support / resistance")
i_maxS   = input.int(2,     "Max Supports",        group="Support / resistance")
i_maxR   = input.int(2,     "Max Resistances",     group="Support / resistance")
i_minD   = input.float(0.4, "Min Gap %",           group="Support / resistance")
i_rsiP   = input.int(14,    "RSI Period",          group="Signal engine")
i_cool   = input.int(5,     "Cooldown Bars",       group="Signal engine")
i_brk    = input.int(10,    "Breakout Bars",       group="Signal engine")
i_exp    = input.int(40,    "Expire Bars (0=off)", group="Signal engine")
// Breakout entries buy strength and sell weakness, which is exactly what
// an exhausted move looks like right before it reverses. These two guards
// block a breakout entry taken into an already-stretched RSI.
i_brkHi  = input.float(70.0, "Breakout: max RSI for a long",  minval=50, maxval=100, group="Signal engine", tooltip="A long breakout is skipped when RSI is above this. 70 reproduces the published behaviour; try 60 to refuse buying into an overbought high.")
i_brkLo  = input.float(30.0, "Breakout: min RSI for a short", minval=0,  maxval=50,  group="Signal engine", tooltip="A short breakout is skipped when RSI is below this. 30 reproduces the published behaviour; try 40 to refuse selling into an oversold low.")
i_atrP   = input.int(14,    "ATR Period",          group="Targets and stop")
i_tp1x   = input.float(0.8, "TP1 × ATR",          group="Targets and stop")
i_tp2x   = input.float(1.8, "TP2 × ATR fallback", group="Targets and stop")
i_tp3x   = input.float(3.0, "TP3 × ATR",          group="Targets and stop")
i_slPct  = input.float(0.35,"Max SL %",            group="Targets and stop")

i_partial = input.bool(true, "Book a third at each target", group="Stop management", tooltip="Models how the signals are actually traded: a third closes at TP1, a third at TP2, the remainder at TP3 or the stop. Turn it off to treat every signal as one all-or-nothing position, which records a trade that reached TP1 and came back as exactly zero.")
i_beTP1   = input.bool(true, "Move stop to breakeven at TP1", group="Stop management", tooltip="ON reproduces the published behaviour. OFF leaves the stop where it started until TP2.")

i_start  = input.time(1788220800000, "Count signals from", group="Research", tooltip="Signals before this moment are ignored entirely - not drawn and not counted. Pin it to a fixed date so two settings can be compared over exactly the same period.")
i_stats  = input.bool(true, "Show tally", group="Research", tooltip="Counts the outcomes the indicator drew inside the window. Not a backtest: no spread, no slippage, no sizing, and when a single bar touches both the stop and a target the stop is assumed to come first.")
i_anyTF  = input.bool(false, "Allow any timeframe (research only)", group="Research", tooltip="The engine was tuned on 15-minute bars. Turning this on gives a larger sample but measures a differently-behaving system.")
i_h4     = input.bool(true, "H4 trend filter", group="Higher-timeframe filter", tooltip="Only allow signals that agree with the 4-hour trend.")

// ══════════════════════════════════════════════════════════════
// CORE INDICATORS
// ══════════════════════════════════════════════════════════════
ema20  = ta.ema(close, i_e20)
ema50  = ta.ema(close, i_e50)
ema200 = ta.ema(close, i_e200)
rsiVal = ta.rsi(close, i_rsiP)
atrVal = ta.atr(i_atrP)

plot(ema20,  "EMA 20",  color=color.yellow, linewidth=1)
plot(ema50,  "EMA 50",  color=#1E90FF,      linewidth=2)
plot(ema200, "EMA 200", color=color.red,    linewidth=2)

// ══════════════════════════════════════════════════════════════
// H4 TREND FILTER
// ══════════════════════════════════════════════════════════════
h4_ema200    = request.security(syminfo.tickerid, "240", ta.ema(close, 200), lookahead=barmerge.lookahead_off)
h4_close     = request.security(syminfo.tickerid, "240", close,              lookahead=barmerge.lookahead_off)
h4_uptrend   = h4_close > h4_ema200
h4_downtrend = h4_close < h4_ema200
h4_buy_ok    = not i_h4 or h4_uptrend
h4_sell_ok   = not i_h4 or h4_downtrend
plot(h4_ema200, "H4 EMA 200", color=color.new(color.purple, 50), linewidth=1, style=plot.style_stepline)

// ══════════════════════════════════════════════════════════════
// TIMEFRAME GUARD
// ══════════════════════════════════════════════════════════════
is_m15 = i_anyTF or timeframe.period == "15"
if not is_m15 and barstate.islast
    label.new(bar_index, high, "Consultya: this script runs on the 15-minute timeframe only", color=color.yellow,
              textcolor=color.black, style=label.style_label_down, size=size.normal)

// ══════════════════════════════════════════════════════════════
// SUPPORT / RESISTANCE DETECTION
// ══════════════════════════════════════════════════════════════
var array<float> supArr  = array.new_float(0)
var array<float> resArr  = array.new_float(0)
var array<line>  supLns  = array.new_line(0)
var array<line>  resLns  = array.new_line(0)

f_near(arr, lvl, gap) =>
    found = false
    n = array.size(arr)
    if n > 0
        for j = 0 to n - 1
            if math.abs(array.get(arr, j) - lvl) < gap
                found := true
                break
    found

ph = ta.pivothigh(high, i_swing, i_swing)
pl = ta.pivotlow(low,   i_swing, i_swing)

if not na(ph) and is_m15
    gap = close * i_minD / 100.0
    if ph > close and not f_near(resArr, ph, gap)
        if array.size(resArr) >= i_maxR
            line.delete(array.shift(resLns))
            array.shift(resArr)
        array.push(resArr, ph)
        ln = line.new(bar_index - i_swing, ph, bar_index, ph,
             extend=extend.right, color=color.new(#FF4500, 20), style=line.style_dashed, width=1)
        array.push(resLns, ln)

if not na(pl) and is_m15
    gap = close * i_minD / 100.0
    if pl < close and not f_near(supArr, pl, gap)
        if array.size(supArr) >= i_maxS
            line.delete(array.shift(supLns))
            array.shift(supArr)
        array.push(supArr, pl)
        ln = line.new(bar_index - i_swing, pl, bar_index, pl,
             extend=extend.right, color=color.new(color.lime, 20), style=line.style_solid, width=1)
        array.push(supLns, ln)

// ══════════════════════════════════════════════════════════════
// SIGNAL STATE
// ══════════════════════════════════════════════════════════════
var int   lastSig  = -999
var bool  sigOn    = false
var bool  sigBuy   = true
var float sigEntry = na
var float sigSL    = na
var float sigSL0   = na    // the ORIGINAL stop - this is what defines 1R
var float sigTP1   = na
var float sigTP2   = na
var float sigTP3   = na
var int   sigBar   = -1
var bool  sigDone  = false
var bool  t1Hit    = false
var bool  t2Hit    = false

// Partial-exit bookkeeping for the signal currently running.
var float bookedR = 0.0   // R already banked from closed portions
var float openFr  = 1.0   // fraction of the position still open

// ══════════════════════════════════════════════════════════════
// OUTCOME COUNTERS
// ══════════════════════════════════════════════════════════════
var int   cTotal  = 0
var int   cTP3    = 0
var int   cTP2    = 0
var int   cT1Stop = 0
var int   cLoss   = 0
var int   cExp    = 0
var int   cWin    = 0   // finished above breakeven
var int   cFlat   = 0   // finished at breakeven
var int   cDown   = 0   // finished below breakeven
var float sumR    = 0.0
var float bestR   = 0.0
var float worstR  = 0.0
var float sumPts  = 0.0   // same results, in price points instead of R

// Return a price expressed in R. Positive means it sits beyond the entry
// in the trade's favour. Pine forbids a function writing to a global, so
// this only computes - the totals are updated at the call site.
f_R(float px) =>
    risk = math.abs(sigEntry - sigSL0)
    risk > 0 and not na(px) ? (sigBuy ? (px - sigEntry) / risk : (sigEntry - px) / risk) : na

// ══════════════════════════════════════════════════════════════
// INFO PANEL
// ══════════════════════════════════════════════════════════════
var table pnl = table.new(position.top_right, 2, 10, bgcolor=#0D1117, border_width=1, border_color=#2A2D4A, frame_width=2, frame_color=#2A2D4A)

// ══════════════════════════════════════════════════════════════
// HELPERS
// ══════════════════════════════════════════════════════════════
f_nearestAbove(tp) =>
    float best = na
    n = array.size(resArr)
    if n > 0
        for j = 0 to n - 1
            v = array.get(resArr, j)
            if v > tp and (na(best) or v < best)
                best := v
    best

f_nearestBelow(tp) =>
    float best = na
    n = array.size(supArr)
    if n > 0
        for j = 0 to n - 1
            v = array.get(supArr, j)
            if v < tp and (na(best) or v > best)
                best := v
    best

f_tps(isBuy, price, base, rng, atr) =>
    maxSL = price * (i_slPct / 100.0)
    sl_   = isBuy ? base - math.min(rng * 0.5, maxSL)
                  : base + math.min(rng * 0.5, maxSL)
    tp1_  = isBuy ? price + atr * i_tp1x
                  : price - atr * i_tp1x
    nr    = f_nearestAbove(tp1_)
    ns    = f_nearestBelow(tp1_)
    tp2_  = isBuy ? (not na(nr) and nr > tp1_ ? nr : price + atr * i_tp2x)
                  : (not na(ns) and ns < tp1_ ? ns : price - atr * i_tp2x)
    tp3r  = isBuy ? price + atr * i_tp3x : price - atr * i_tp3x
    tp3_  = isBuy ? (tp3r <= tp2_ ? tp2_ + (tp2_ - price) * 0.5 : tp3r)
                  : (tp3r >= tp2_ ? tp2_ - (price - tp2_) * 0.5 : tp3r)
    [sl_, tp1_, tp2_, tp3_]

// ══════════════════════════════════════════════════════════════
// MAIN LOGIC — evaluated on confirmed bars only (no repainting)
// ══════════════════════════════════════════════════════════════
if is_m15 and barstate.isconfirmed

    rng = high - low
    third = 1.0 / 3.0

    // Holds this bar's closed-trade result, if a signal closed on it.
    // At most one signal can close per bar, so one slot is enough.
    float barR = na

    // ── Track the outcome of the active signal ───────────────────────────
    if sigOn and not sigDone and sigBar >= 0 and bar_index > sigBar

        if sigBuy
            if low <= sigSL
                label.new(bar_index, sigSL, "✕ SL",
                          color=#C83C3C, textcolor=color.white,
                          style=label.style_label_up, size=size.tiny)
                barR := bookedR + openFr * f_R(sigSL)
                sigOn   := false
                sigDone := true
                if t2Hit
                    cTP2 += 1
                else if t1Hit
                    cT1Stop += 1
                else
                    cLoss += 1

            else if high >= sigTP3
                if not t1Hit
                    label.new(bar_index, sigTP1, ">> TP1",
                              color=#00C8A0, textcolor=color.black,
                              style=label.style_label_down, size=size.tiny)
                    t1Hit := true
                    if i_partial
                        bookedR += third * f_R(sigTP1)
                        openFr  -= third
                if not t2Hit
                    label.new(bar_index, sigTP2, ">> TP2",
                              color=#00D0A8, textcolor=color.black,
                              style=label.style_label_down, size=size.tiny)
                    t2Hit := true
                    if i_partial
                        bookedR += third * f_R(sigTP2)
                        openFr  -= third
                label.new(bar_index, sigTP3, ">> TP3",
                          color=#00EB78, textcolor=color.black,
                          style=label.style_label_down, size=size.tiny)
                barR := bookedR + openFr * f_R(sigTP3)
                bookedR := barR
                openFr  := 0.0
                sigDone := true
                cTP3 += 1

            else if not t2Hit and high >= sigTP2
                if not t1Hit
                    label.new(bar_index, sigTP1, ">> TP1",
                              color=#00C8A0, textcolor=color.black,
                              style=label.style_label_down, size=size.tiny)
                    t1Hit := true
                    if i_partial
                        bookedR += third * f_R(sigTP1)
                        openFr  -= third
                label.new(bar_index, sigTP2, ">> TP2",
                          color=#00D0A8, textcolor=color.black,
                          style=label.style_label_down, size=size.tiny)
                t2Hit := true
                if i_partial
                    bookedR += third * f_R(sigTP2)
                    openFr  -= third
                sigSL := sigTP1

            else if not t1Hit and high >= sigTP1
                label.new(bar_index, sigTP1, ">> TP1",
                          color=#00C8A0, textcolor=color.black,
                          style=label.style_label_down, size=size.tiny)
                t1Hit := true
                if i_partial
                    bookedR += third * f_R(sigTP1)
                    openFr  -= third
                if i_beTP1
                    sigSL := sigEntry

        else  // SELL
            if high >= sigSL
                label.new(bar_index, sigSL, "✕ SL",
                          color=#C83C3C, textcolor=color.white,
                          style=label.style_label_down, size=size.tiny)
                barR := bookedR + openFr * f_R(sigSL)
                sigOn   := false
                sigDone := true
                if t2Hit
                    cTP2 += 1
                else if t1Hit
                    cT1Stop += 1
                else
                    cLoss += 1

            else if low <= sigTP3
                if not t1Hit
                    label.new(bar_index, sigTP1, ">> TP1",
                              color=#00C8A0, textcolor=color.black,
                              style=label.style_label_up, size=size.tiny)
                    t1Hit := true
                    if i_partial
                        bookedR += third * f_R(sigTP1)
                        openFr  -= third
                if not t2Hit
                    label.new(bar_index, sigTP2, ">> TP2",
                              color=#00D0A8, textcolor=color.black,
                              style=label.style_label_up, size=size.tiny)
                    t2Hit := true
                    if i_partial
                        bookedR += third * f_R(sigTP2)
                        openFr  -= third
                label.new(bar_index, sigTP3, ">> TP3",
                          color=#00EB78, textcolor=color.black,
                          style=label.style_label_up, size=size.tiny)
                barR := bookedR + openFr * f_R(sigTP3)
                bookedR := barR
                openFr  := 0.0
                sigDone := true
                cTP3 += 1

            else if not t2Hit and low <= sigTP2
                if not t1Hit
                    label.new(bar_index, sigTP1, ">> TP1",
                              color=#00C8A0, textcolor=color.black,
                              style=label.style_label_up, size=size.tiny)
                    t1Hit := true
                    if i_partial
                        bookedR += third * f_R(sigTP1)
                        openFr  -= third
                label.new(bar_index, sigTP2, ">> TP2",
                          color=#00D0A8, textcolor=color.black,
                          style=label.style_label_up, size=size.tiny)
                t2Hit := true
                if i_partial
                    bookedR += third * f_R(sigTP2)
                    openFr  -= third
                sigSL := sigTP1

            else if not t1Hit and low <= sigTP1
                label.new(bar_index, sigTP1, ">> TP1",
                          color=#00C8A0, textcolor=color.black,
                          style=label.style_label_up, size=size.tiny)
                t1Hit := true
                if i_partial
                    bookedR += third * f_R(sigTP1)
                    openFr  -= third
                if i_beTP1
                    sigSL := sigEntry

    // ── Close the signal once it has run out of time ─────────────────────
    if sigOn and not sigDone and i_exp > 0 and bar_index - sigBar >= i_exp
        label.new(bar_index, sigBuy ? low : high, "⏳",
                  color=#808080, textcolor=color.white,
                  style=sigBuy ? label.style_label_up : label.style_label_down, size=size.tiny)
        barR := bookedR + openFr * f_R(close)
        sigOn   := false
        sigDone := true
        if t2Hit
            cTP2 += 1
        else if t1Hit
            cT1Stop += 1
        else
            cExp += 1

    // ── Fold this bar's result into the running totals ───────────────────
    if not na(barR)
        sumR   += barR
        sumPts += barR * math.abs(sigEntry - sigSL0)
        bestR  := math.max(bestR, barR)
        worstR := math.min(worstR, barR)
        if barR > 0.001
            cWin += 1
        else if barR < -0.001
            cDown += 1
        else
            cFlat += 1

    // ── Signal detection ─────────────────────────────────────────────────
    // Nothing fires before the research start date, so the tally always
    // covers the same window regardless of how many bars loaded.
    inWindow = time >= i_start
    canSig   = inWindow and bar_index - lastSig >= i_cool
    actBuy   = sigOn and not sigDone and     sigBuy
    actSell  = sigOn and not sigDone and not sigBuy

    fire_buy  = false
    fire_sell = false
    buy_base  = low
    sell_base = high
    buy_clr   = color.lime
    sell_clr  = color.orange

    if canSig
        upTrd   = ema20 > ema50
        dnTrd   = ema20 < ema50
        abv200  = close > ema200
        blw200  = close < ema200
        strongU = rng > 0 and (close - low)  >= rng * 0.55
        strongD = rng > 0 and (high - close) >= rng * 0.55
        rsiOk   = rsiVal >= 35.0 and rsiVal <= 65.0

        // ── Signal 1: EMA Bounce ─────────────────────────────────────────
        if upTrd and abv200 and low <= ema20 and close > ema20 and strongU and rsiOk and h4_buy_ok and not actBuy
            fire_buy := true
            buy_base := low
            buy_clr  := color.lime

        else if dnTrd and blw200 and high >= ema20 and close < ema20 and strongD and rsiOk and h4_sell_ok and not actSell
            fire_sell := true
            sell_base := high
            sell_clr  := color.orange

        // ── Signal 2: SR Bounce ──────────────────────────────────────────
        if not fire_buy and not fire_sell
            tol = close * 0.0015

            n_s = array.size(supArr)
            if n_s > 0
                for j = 0 to n_s - 1
                    lvl = array.get(supArr, j)
                    if lvl < close and low <= lvl + tol and low >= lvl - tol * 2.0 and
                         (close - low) >= rng * 0.55 and rsiVal < 60.0 and h4_buy_ok and not actBuy
                        fire_buy := true
                        buy_base := lvl
                        buy_clr  := color.aqua
                        break

            if not fire_buy
                n_r = array.size(resArr)
                if n_r > 0
                    for j = 0 to n_r - 1
                        lvl = array.get(resArr, j)
                        if lvl > close and high >= lvl - tol and high <= lvl + tol * 2.0 and
                             (high - close) >= rng * 0.55 and rsiVal > 40.0 and h4_sell_ok and not actSell
                            fire_sell := true
                            sell_base := lvl
                            sell_clr  := color.yellow
                            break

        // ── Signal 3: N-Bar Breakout ─────────────────────────────────────
        if not fire_buy and not fire_sell and bar_index >= i_brk + 1
            hh = ta.highest(high, i_brk)[1]
            ll = ta.lowest(low,   i_brk)[1]

            if close > hh and rng > 0 and (close - low) >= rng * 0.5 and rsiVal < i_brkHi and h4_buy_ok and not actBuy
                fire_buy  := true
                buy_base  := low
                buy_clr   := color.white
            else if close < ll and rng > 0 and (high - close) >= rng * 0.5 and rsiVal > i_brkLo and h4_sell_ok and not actSell
                fire_sell := true
                sell_base := high
                sell_clr  := color.fuchsia

    // ── Emit the signal and store its levels ─────────────────────────────
    if fire_buy
        [sl_, tp1_, tp2_, tp3_] = f_tps(true, close, buy_base, rng, atrVal)
        off = close * 0.002
        tip = "Entry: " + str.tostring(close, format.mintick) +
              "  SL: "  + str.tostring(sl_,   format.mintick) +
              "  TP1: " + str.tostring(tp1_,  format.mintick)
        label.new(bar_index, buy_base - off, "",
                  color=buy_clr, style=label.style_arrowup, size=size.small, tooltip=tip)
        sigOn    := true
        sigBuy   := true
        sigEntry := close
        sigSL    := sl_
        sigSL0   := sl_
        sigTP1   := tp1_
        sigTP2   := tp2_
        sigTP3   := tp3_
        sigBar   := bar_index
        sigDone  := false
        t1Hit    := false
        t2Hit    := false
        bookedR  := 0.0
        openFr   := 1.0
        lastSig  := bar_index
        cTotal   += 1

    if fire_sell and not fire_buy
        [sl_, tp1_, tp2_, tp3_] = f_tps(false, close, sell_base, rng, atrVal)
        off = close * 0.002
        tip = "Entry: " + str.tostring(close,  format.mintick) +
              "  SL: "  + str.tostring(sl_,    format.mintick) +
              "  TP1: " + str.tostring(tp1_,   format.mintick)
        label.new(bar_index, sell_base + off, "",
                  color=sell_clr, style=label.style_arrowdown, size=size.small, tooltip=tip)
        sigOn    := true
        sigBuy   := false
        sigEntry := close
        sigSL    := sl_
        sigSL0   := sl_
        sigTP1   := tp1_
        sigTP2   := tp2_
        sigTP3   := tp3_
        sigBar   := bar_index
        sigDone  := false
        t1Hit    := false
        t2Hit    := false
        bookedR  := 0.0
        openFr   := 1.0
        lastSig  := bar_index
        cTotal   += 1

// ══════════════════════════════════════════════════════════════
// PANEL UPDATE — refreshes on the last bar
// ══════════════════════════════════════════════════════════════
if is_m15 and barstate.islast and sigOn
    barsEl   = bar_index - sigBar
    expired  = not sigDone and i_exp > 0 and barsEl >= i_exp

    stat_txt = expired ? "-- Expired" :
               sigDone ? ">> TP3 hit — signal complete" :
               t2Hit   ? ">> TP2 hit — SL moved to TP1" :
               t1Hit   ? (i_beTP1 ? ">> TP1 hit — SL at entry" : ">> TP1 hit — stop unchanged") :
                         "● Active"
    stat_clr = expired ? #808080 : sigDone ? #00EB78 : t2Hit ? #00D0A0 : t1Hit ? #00C0B0 : #50B450

    dir_txt = sigBuy ? "BUY  +" : "SELL  -"
    dir_clr = sigBuy ? #00D25A  : #DC3C3C

    sl_lbl  = str.tostring(sigSL, format.mintick) + (t2Hit ? " [TP1]" : t1Hit and i_beTP1 ? " [BE]" : "")

    hdr_bg  = #1A1E3A
    row_bg  = #0D1117
    dir_bg  = sigBuy ? #003A18 : #3A0010

    table.cell(pnl, 0, 0, "▣ CONSULTYA", text_color=#8AB4FF, text_size=size.normal, bgcolor=hdr_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 0, "",            text_color=#8AB4FF, text_size=size.normal, bgcolor=hdr_bg)
    table.cell(pnl, 0, 1, dir_txt,       text_color=dir_clr, text_size=size.large, bgcolor=dir_bg, text_halign=text.align_center)
    table.cell(pnl, 1, 1, "",            text_color=dir_clr, text_size=size.large, bgcolor=dir_bg)
    table.cell(pnl, 0, 2, "Entry",       text_color=#BBBBBB, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 2, str.tostring(sigEntry, format.mintick), text_color=color.white, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_right)
    table.cell(pnl, 0, 3, "SL",          text_color=#FF6060, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 3, sl_lbl,        text_color=#FF8080, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_right)
    table.cell(pnl, 0, 4, "TP1",         text_color=#BBBBBB, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 4, str.tostring(sigTP1, format.mintick), text_color=#64C8FF, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_right)
    table.cell(pnl, 0, 5, "TP2",         text_color=#BBBBBB, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 5, str.tostring(sigTP2, format.mintick), text_color=#00E5CC, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_right)
    table.cell(pnl, 0, 6, "TP3",         text_color=#BBBBBB, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 6, str.tostring(sigTP3, format.mintick), text_color=#00FF88, text_size=size.normal, bgcolor=row_bg, text_halign=text.align_right)
    table.cell(pnl, 0, 7, "Booked",      text_color=#555577, text_size=size.small,  bgcolor=row_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 7, str.tostring(bookedR, "#.##") + "R  ·  " + str.tostring(openFr * 100, "#") + "% open", text_color=#8899AA, text_size=size.small, bgcolor=row_bg, text_halign=text.align_right)
    table.cell(pnl, 0, 8, stat_txt,      text_color=stat_clr, text_size=size.normal, bgcolor=hdr_bg, text_halign=text.align_left)
    table.cell(pnl, 1, 8, "",            text_color=stat_clr, text_size=size.normal, bgcolor=hdr_bg)
    h4_lbl = not i_h4 ? "H4 OFF" : h4_uptrend ? "H4 uptrend" : "H4 downtrend"
    h4_clr = not i_h4 ? #555555 : h4_uptrend  ? #00C853     : #FF1744
    table.cell(pnl, 0, 9, h4_lbl,        text_color=h4_clr,   text_size=size.small,  bgcolor=#0A0D15, text_halign=text.align_left)
    table.cell(pnl, 1, 9, "",            text_color=h4_clr,   text_size=size.small,  bgcolor=#0A0D15)

// ══════════════════════════════════════════════════════════════
// RESEARCH TALLY
// ══════════════════════════════════════════════════════════════
var table stats = table.new(position.bottom_right, 2, 12, bgcolor=#0D1117, border_width=1, border_color=#2A2D4A, frame_width=2, frame_color=#2A2D4A)

if is_m15 and barstate.islast and i_stats
    closed = cWin + cFlat + cDown
    avgR   = closed > 0 ? sumR / closed : 0.0

    // With partial exits on, "profitable" means the trade finished with
    // money banked - which is what a subscriber actually experiences.
    wrTxt  = closed > 0 ? str.tostring(cWin * 100.0 / closed, "#.#") + "%" : "-"
    t1Txt  = closed > 0 ? str.tostring((cTP3 + cTP2 + cT1Stop) * 100.0 / closed, "#.#") + "%" : "-"

    rClr   = sumR > 0 ? #00E58A : sumR < 0 ? #FF6060 : #9AAABB
    modeTxt = (i_partial ? "thirds" : "all-or-nothing") + (i_beTP1 ? " · BE at TP1" : " · stop to TP2")

    // Under roughly 200 closed trades the numbers are mostly noise, so
    // the sample size is flagged rather than left for the eye to judge.
    nTxt   = closed >= 300 ? "" : closed >= 200 ? "  (thin)" : "  (TOO FEW)"
    nClr   = closed >= 300 ? #8899AA : closed >= 200 ? #F0C040 : #FF6060

    sHdr = #1A1E3A
    sRow = #0D1117

    table.cell(stats, 0, 0, "RESEARCH TALLY", text_color=#8AB4FF, text_size=size.normal, bgcolor=sHdr, text_halign=text.align_left)
    table.cell(stats, 1, 0, str.tostring(cTotal) + " fired", text_color=color.white, text_size=size.normal, bgcolor=sHdr, text_halign=text.align_right)

    table.cell(stats, 0, 1, "Total R", text_color=#BBBBBB, text_size=size.large, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 1, str.tostring(sumR, "#.#") + "R", text_color=rClr, text_size=size.large, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 2, "Avg R per trade", text_color=#BBBBBB, text_size=size.normal, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 2, str.tostring(avgR, "#.###") + "R", text_color=rClr, text_size=size.normal, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 3, "Closed in profit", text_color=#BBBBBB, text_size=size.normal, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 3, wrTxt, text_color=#00E58A, text_size=size.normal, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 4, "Reached TP1", text_color=#BBBBBB, text_size=size.small, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 4, t1Txt, text_color=#9AAABB, text_size=size.small, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 5, "Best / worst", text_color=#BBBBBB, text_size=size.small, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 5, str.tostring(bestR, "#.#") + "R / " + str.tostring(worstR, "#.#") + "R", text_color=#9AAABB, text_size=size.small, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 6, "Profit / flat / loss", text_color=#BBBBBB, text_size=size.small, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 6, str.tostring(cWin) + " / " + str.tostring(cFlat) + " / " + str.tostring(cDown), text_color=#9AAABB, text_size=size.small, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 7, "TP3 reached", text_color=#BBBBBB, text_size=size.normal, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 7, str.tostring(cTP3), text_color=#00FF88, text_size=size.normal, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 8, "TP2, closed at TP1", text_color=#BBBBBB, text_size=size.normal, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 8, str.tostring(cTP2), text_color=#00E5CC, text_size=size.normal, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 9, "TP1, then stopped", text_color=#BBBBBB, text_size=size.normal, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 9, str.tostring(cT1Stop), text_color=#64C8FF, text_size=size.normal, bgcolor=sRow, text_halign=text.align_right)

    table.cell(stats, 0, 10, modeTxt, text_color=#8899AA, text_size=size.small, bgcolor=#0A0D15, text_halign=text.align_left)
    table.cell(stats, 1, 10, str.tostring(closed) + " closed" + nTxt, text_color=nClr, text_size=size.small, bgcolor=#0A0D15, text_halign=text.align_right)

    table.cell(stats, 0, 11, "Total points", text_color=#BBBBBB, text_size=size.normal, bgcolor=sRow, text_halign=text.align_left)
    table.cell(stats, 1, 11, str.tostring(sumPts, "#.#"), text_color=rClr, text_size=size.normal, bgcolor=sRow, text_halign=text.align_right)

أداة قياس، لا نظام تداول. لا تحتسب فرق السعر ولا الانزلاق ولا عمولة الوسيط.

© 2026 Consultya — منشورة للاطّلاع والتحقّق.

اقرأ منهجية القياس والأرقام بحدودها ←