Strong Reversal Signals

ProjectSyndicate · study · 417 行 · 点赞 3,350 · TradingView 原页

本页源码来自 TradingView 公开发布的开源脚本,版权归原作者所有, 请遵循其原始许可(Pine 脚本常见 CC BY-NC-SA / MPL-2.0 / MIT)。 本项目仅用于研究检索与许可范围内的移植。

Pine Script

//@version=6
indicator("Strong Reversal Signals", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500)
 
gSrc = "Liquidity Source"
src_mode = input.string("Macro Pivots", "Source", options=["Macro Pivots", "HTF Previous Bar", "Session High/Low"], group=gSrc)
pivLen   = input.int(20, "Pivot Length", minval=3, maxval=100, group=gSrc)
htfRes   = input.timeframe("240", "HTF Resolution", group=gSrc)
sessTz   = input.string("America/New_York", "Session Timezone", group=gSrc)
sAsia    = input.session("2000-0000", "Asian Session", group=gSrc)
sLon     = input.session("0300-0800", "London Session", group=gSrc)
sNy      = input.session("0900-1600", "New York Session", group=gSrc)
grabWick = input.float(20.0, "Min Reversal Wick (%)", minval=1, maxval=100, tooltip="Minimum rejection-wick size on the candle that sweeps liquidity.", group=gSrc)
 
gT = "Targets"
tp1Pct = input.float(10.0, "TP1 % of ADR10", minval=0.5, step=0.5, group=gT)
tp2Pct = input.float(25.0, "TP2 % of ADR10", minval=0.5, step=0.5, group=gT)
slPct  = input.float(35.0, "SL % of ADR10",  minval=0.5, step=0.5, group=gT)
expBars= input.int(120, "Trade Expiry (bars)", minval=10, group=gT)
zoneW  = input.int(30, "Zone Width (bars)", minval=10, maxval=500, group=gT)
zoneH  = input.float(2.0, "Zone Height % of ADR10", minval=0.1, step=0.1, group=gT)
keptN  = input.int(100, "Max Signals Kept", minval=1, maxval=120, group=gT)
 
gR = "Strength Ranking"
minStars = input.int(0, "Minimum Stars To Plot", minval=0, maxval=4, tooltip="0 = show every signal. Raise to show only stronger setups; the dashboard still tracks every tier.", group=gR)
wTrend = input.float(0.30, "Weight · Trend Context",   minval=0, maxval=1, step=0.05, group=gR)
wWick  = input.float(0.25, "Weight · Rejection Wick",  minval=0, maxval=1, step=0.05, group=gR)
wCalm  = input.float(0.20, "Weight · Volatility Regime",minval=0, maxval=1, step=0.05, group=gR)
wNear  = input.float(0.15, "Weight · Mean Proximity",  minval=0, maxval=1, step=0.05, group=gR)
wSweep = input.float(0.10, "Weight · Sweep Depth",     minval=0, maxval=1, step=0.05, group=gR)
 
gC = "Colors"
colBuy  = input.color(#1f9e6f, "Buy / Long",  inline="c", group=gC)
colSell = input.color(#7e2330, "Sell / Short", inline="c", group=gC)
colTP   = input.color(#1f9e6f, "Target Zones", group=gC)
colSL   = input.color(#7e2330, "Stop Zone", group=gC)
zoneAlpha   = input.int(82, "Zone Transparency", minval=0, maxval=100, group=gC)
borderAlpha = input.int(45, "Border Transparency", minval=0, maxval=100, group=gC)
showTags = input.bool(true, "Show SL/TP Tags", group=gC)
lblSize  = input.string("Normal", "Signal Label Size", options=["Tiny","Small","Normal","Large","Huge"], group=gC)
tagSize  = input.string("Normal", "SL/TP Tag Size", options=["Tiny","Small","Normal","Large","Huge"], group=gC)
lblOff   = input.float(6.0, "Signal Label Offset (% of ADR10)", minval=0, step=0.5, tooltip="Pushes the BUY/SELL label clear of the candles (below the bar for buys, above for sells).", group=gC)
 
gP = "Dashboard"
showPanel = input.bool(true, "Show Dashboard", group=gP)
panelPos  = input.string("Top Right", "Position", options=["Top Right","Top Left","Bottom Right","Bottom Left","Middle Right"], group=gP)
panelSize = input.string("Normal", "Text Size", options=["Tiny","Small","Normal","Large","Huge"], group=gP)
 
gA = "Alerts"
alertsOn      = input.bool(true, "Enable Alerts", group=gA)
alertMinStars = input.int(0, "Alert Minimum Stars", minval=0, maxval=4, tooltip="Only fire alerts for setups at or above this rank. 4 = strongest only.", group=gA)
 
f_clamp(float v) => math.min(math.max(v, 0.0), 1.0)
f_stars(float s) => s >= 7.5 ? "★★★★" : s >= 6.5 ? "★★★" : s >= 5.5 ? "★★" : s >= 4.5 ? "★" : "·"
f_tier(float s)  => s >= 7.5 ? 4 : s >= 6.5 ? 3 : s >= 5.5 ? 2 : s >= 4.5 ? 1 : 0
f_pos() =>
    switch panelPos
        "Top Right"    => position.top_right
        "Top Left"     => position.top_left
        "Bottom Right" => position.bottom_right
        "Bottom Left"  => position.bottom_left
        "Middle Right" => position.middle_right
        => position.top_right
f_sz(string s, bool head) => head ? (s=="Tiny"?size.tiny:s=="Small"?size.small:s=="Normal"?size.normal:s=="Large"?size.large:size.huge) : (s=="Tiny"?size.tiny:s=="Small"?size.small:s=="Normal"?size.small:s=="Large"?size.normal:size.large)
f_lblsz(string s) => s=="Tiny"?size.tiny:s=="Small"?size.small:s=="Normal"?size.normal:s=="Large"?size.large:size.huge
f_tname(float s) => s>=7.5?"ELITE":s>=6.5?"STRONG":s>=5.5?"MODERATE":s>=4.5?"WEAK":"MINIMAL"
f_amsg(int dir, float sc, float e, float sl, float tp1, float tp2) =>
    (dir==1?"BUY Reversal":"SELL Reversal") + "  " + f_stars(sc) + " (" + f_tname(sc) + ")\n" + syminfo.ticker + " " + timeframe.period + "\nStrength: " + str.tostring(sc,"#.0") + "/10\nEntry: " + str.tostring(e,format.mintick) + "\nSL: " + str.tostring(sl,format.mintick) + "\nTP1: " + str.tostring(tp1,format.mintick) + "\nTP2: " + str.tostring(tp2,format.mintick)
 
float atr14  = nz(ta.atr(14), ta.cum(high - low) / (bar_index + 1))
float ema50  = ta.ema(close, 50)
float ema200 = ta.ema(close, 200)
float atrPct = ta.percentrank(atr14, 100)
int   L = (pivLen % 2 == 0) ? (pivLen + 1) : pivLen
int   pv = int(L / 2)
 
bool useHtf  = src_mode == "HTF Previous Bar"
bool useSess = src_mode == "Session High/Low"
 
[hHtf, lHtf] = request.security(syminfo.tickerid, htfRes, [high[1], low[1]], lookahead=barmerge.lookahead_on)
bool newHtf = ta.change(time(htfRes)) != 0
 
bool inAsia = not na(time(timeframe.period, sAsia, sessTz))
bool inLon  = not na(time(timeframe.period, sLon,  sessTz))
bool inNy   = not na(time(timeframe.period, sNy,   sessTz))
var float aH = na
var float aL = na
var float lH = na
var float lL = na
var float nH = na
var float nL = na
if inAsia
    aH := na(aH) ? high : math.max(aH, high)
    aL := na(aL) ? low  : math.min(aL, low)
if inLon
    lH := na(lH) ? high : math.max(lH, high)
    lL := na(lL) ? low  : math.min(lL, low)
if inNy
    nH := na(nH) ? high : math.max(nH, high)
    nL := na(nL) ? low  : math.min(nL, low)
bool aEnd = not inAsia and inAsia[1]
bool lEnd = not inLon  and inLon[1]
bool nEnd = not inNy   and inNy[1]
 
float rngBar = (high - low) == 0 ? syminfo.mintick : (high - low)
float upWick = (high - math.max(open, close)) / rngBar
float dnWick = (math.min(open, close) - low) / rngBar
 
float adr10 = request.security(syminfo.tickerid, "D", (math.sum(high[1] - low[1], 10)) / 10, lookahead=barmerge.lookahead_off)
float adrA  = nz(adr10) > 0.0 ? adr10 : close * 0.04
float tp1D  = adrA * (tp1Pct / 100)
float tp2D  = adrA * (tp2Pct / 100)
float slD   = adrA * (slPct  / 100)
float zh    = adrA * (zoneH / 100) / 2
bool  adrOk = nz(adr10) > 0.0
float rTP1  = slPct > 0 ? tp1Pct / slPct : 0.0
float rTP2  = slPct > 0 ? tp2Pct / slPct : 0.0
 
f_score(int dir, float entry, float gExt, float gLvl, float gWick) =>
    float fTrend = (dir == -1 ? (ema50 - ema200) : (ema200 - ema50)) / atr14
    float fAlign = (dir == -1 ? (ema200 - entry) : (entry - ema200)) / atr14
    float trendN = f_clamp((fTrend + 2.0) / 4.0)
    float wickN  = f_clamp(gWick / 0.6)
    float calmN  = f_clamp(1.0 - atrPct / 100.0)
    float nearN  = f_clamp(1.0 - math.max(fAlign, 0.0) / 3.0)
    float sweepN = f_clamp(math.abs(gExt - gLvl) / (atr14 * 1.5))
    float wsum = wTrend + wWick + wCalm + wNear + wSweep
    float raw = wsum > 0 ? (trendN*wTrend + wickN*wWick + calmN*wCalm + nearN*wNear + sweepN*wSweep) / wsum : 0.0
    math.min(math.max(raw * 10.0, 0.0), 10.0)
 
type Trade
    int   dir        = 0
    float entry      = na
    float sl         = na
    float tp1        = na
    float tp2        = na
    float score      = 0.0
    int   tier       = 0
    int   entryBar   = na
    bool  reachedTP1 = false
    bool  resolved   = false
    box   bSL        = na
    box   bTP1       = na
    box   bTP2       = na
    label lEntry     = na
    label lSL        = na
    label lTP1       = na
    label lTP2       = na
 
var array<Trade> trades = array.new<Trade>()
 
var int totalSignals = 0
var int wins = 0
var int losses = 0
var int tp2Hit = 0
var int expired = 0
var float totalR = 0.0
var array<int> tierWin  = array.new_int(5, 0)
var array<int> tierLoss = array.new_int(5, 0)
var array<string> recent = array.new<string>()
 
f_logRecent(string s) =>
    array.unshift(recent, s)
    if array.size(recent) > 10
        array.pop(recent)
 
f_zone(float lvl, int x1, int x2, color c) =>
    box.new(x1, lvl + zh, x2, lvl - zh, bgcolor=color.new(c, zoneAlpha), border_color=color.new(c, borderAlpha), border_width=1)
 
f_tag(float lvl, int x1, string t, color c) =>
    showTags ? label.new(x1, lvl, t, style=label.style_label_lower_left, color=color.new(c, 25), textcolor=color.white, size=f_lblsz(tagSize)) : na
 
var float topVal = na
var bool  topBrk = true
var float btmVal = na
var bool  btmBrk = true
var int   gState = 0
var float gExt   = na
var float gLvl   = na
var float gWick  = na
 
if useSess
    if aEnd or lEnd or nEnd
        float fh = na
        float fl = na
        if aEnd
            fh := aH
            fl := aL
            aH := na
            aL := na
        else if lEnd
            fh := lH
            fl := lL
            lH := na
            lL := na
        else if nEnd
            fh := nH
            fl := nL
            nH := na
            nL := na
        topVal := fh
        btmVal := fl
        topBrk := false
        btmBrk := false
        gState := 0
else if useHtf
    if newHtf
        topVal := hHtf
        btmVal := lHtf
        topBrk := false
        btmBrk := false
        gState := 0
else
    float ph = ta.pivothigh(high, pv, pv)
    float pl = ta.pivotlow(low, pv, pv)
    if not na(ph)
        topVal := ph
        topBrk := false
    if not na(pl)
        btmVal := pl
        btmBrk := false
 
if (not topBrk) and close > topVal and upWick * 100 >= grabWick
    topBrk := true
    gState := 1
    gExt := high
    gLvl := topVal
    gWick := upWick
if (not btmBrk) and close < btmVal and dnWick * 100 >= grabWick
    btmBrk := true
    gState := -1
    gExt := low
    gLvl := btmVal
    gWick := dnWick
 
var bool firedLong  = false
var bool firedShort = false
firedLong := false
firedShort := false
 
f_open(int dir, float sc) =>
    float e = close
    Trade t = Trade.new(dir, e, dir == -1 ? e + slD : e - slD, dir == -1 ? e - tp1D : e + tp1D, dir == -1 ? e - tp2D : e + tp2D, sc, f_tier(sc), bar_index, false, false, na, na, na, na, na, na, na)
    int x1 = bar_index
    int x2 = bar_index + zoneW
    t.bSL  := f_zone(t.sl,  x1, x2, colSL)
    t.bTP1 := f_zone(t.tp1, x1, x2, colTP)
    t.bTP2 := f_zone(t.tp2, x1, x2, colTP)
    t.lSL  := f_tag(t.sl,  x1, "SL",  colSL)
    t.lTP1 := f_tag(t.tp1, x1, "TP1", colTP)
    t.lTP2 := f_tag(t.tp2, x1, "TP2", colTP)
    color ec = dir == 1 ? colBuy : colSell
    string txt = (dir == 1 ? "BUY Reversal " : "SELL Reversal ") + f_stars(sc)
    float off = adrA * (lblOff / 100.0)
    float ly  = dir == 1 ? low - off : high + off
    t.lEntry := label.new(x1, ly, txt, style=dir == 1 ? label.style_label_up : label.style_label_down, color=color.new(ec, 0), textcolor=color.white, size=f_lblsz(lblSize))
    array.push(trades, t)
 
if gState == 1
    if high >= gExt
        gExt := high
    else if barstate.isconfirmed
        gState := 2
        if adrOk
            float sc = f_score(-1, close, gExt, gLvl, gWick)
            if f_tier(sc) >= minStars
                f_open(-1, sc)
                totalSignals += 1
                firedShort := true
                if alertsOn and f_tier(sc) >= alertMinStars
                    alert(f_amsg(-1, sc, close, close + slD, close - tp1D, close - tp2D), alert.freq_once_per_bar_close)
 
if gState == -1
    if low <= gExt
        gExt := low
    else if barstate.isconfirmed
        gState := -2
        if adrOk
            float sc = f_score(1, close, gExt, gLvl, gWick)
            if f_tier(sc) >= minStars
                f_open(1, sc)
                totalSignals += 1
                firedLong := true
                if alertsOn and f_tier(sc) >= alertMinStars
                    alert(f_amsg(1, sc, close, close - slD, close + tp1D, close + tp2D), alert.freq_once_per_bar_close)
 
if barstate.isconfirmed and array.size(trades) > 0
    for i = 0 to array.size(trades) - 1
        if i < array.size(trades)
            Trade t = array.get(trades, i)
            if not t.resolved and bar_index > t.entryBar
                bool done = false
                string res = ""
                float r = 0.0
                bool hSL = t.dir == 1 ? low <= t.sl : high >= t.sl
                bool hT1 = t.dir == 1 ? high >= t.tp1 : low <= t.tp1
                bool hT2 = t.dir == 1 ? high >= t.tp2 : low <= t.tp2
                if hSL and not t.reachedTP1
                    done := true
                    res := "SL"
                    r := -1.0
                else
                    if hT1
                        t.reachedTP1 := true
                    if t.reachedTP1 and hT2
                        done := true
                        res := "TP2"
                        r := rTP2
                    else if t.reachedTP1 and hSL
                        done := true
                        res := "TP1"
                        r := rTP1
                if not done and (bar_index - t.entryBar) >= expBars
                    done := true
                    if t.reachedTP1
                        res := "TP1"
                        r := rTP1
                    else
                        res := "EXP"
                if done
                    t.resolved := true
                    if res == "SL"
                        losses += 1
                        totalR += r
                        array.set(tierLoss, t.tier, array.get(tierLoss, t.tier) + 1)
                        f_logRecent("SL")
                    else if res == "TP1"
                        wins += 1
                        totalR += r
                        array.set(tierWin, t.tier, array.get(tierWin, t.tier) + 1)
                        f_logRecent("TP1")
                    else if res == "TP2"
                        wins += 1
                        tp2Hit += 1
                        totalR += r
                        array.set(tierWin, t.tier, array.get(tierWin, t.tier) + 1)
                        f_logRecent("TP2")
                    else
                        expired += 1
 
int openCount = 0
if array.size(trades) > 0
    for i = 0 to array.size(trades) - 1
        if not array.get(trades, i).resolved
            openCount += 1
 
while array.size(trades) > keptN
    Trade old = array.shift(trades)
    box.delete(old.bSL)
    box.delete(old.bTP1)
    box.delete(old.bTP2)
    label.delete(old.lEntry)
    label.delete(old.lSL)
    label.delete(old.lTP1)
    label.delete(old.lTP2)
 
f_tierWR(int k) =>
    int w = array.get(tierWin, k)
    int l = array.get(tierLoss, k)
    int n = w + l
    [n > 0 ? w / n * 100 : 0.0, n]
 
if showPanel and barstate.islast
    string th = f_sz(panelSize, true)
    string tb = f_sz(panelSize, false)
    int resolved = wins + losses
    float wr  = resolved > 0 ? wins / resolved * 100 : 0.0
    float exp = resolved > 0 ? totalR / resolved : 0.0
 
    var table d = table.new(f_pos(), 3, 24, bgcolor=color.new(#101418, 8), frame_color=color.new(#39414b, 40), frame_width=2, border_color=color.new(#39414b, 60), border_width=1)
    table.cell(d, 0, 0, "STRONG REVERSAL SIGNALS", text_color=color.white, text_size=th, bgcolor=color.new(#1f9e6f, 25))
    table.merge_cells(d, 0, 0, 2, 0)
 
    table.cell(d, 0, 1, "Trades", text_color=color.silver, text_size=tb, text_halign=text.align_left)
    table.cell(d, 1, 1, str.tostring(totalSignals), text_color=color.white, text_size=tb)
    table.cell(d, 2, 1, "open " + str.tostring(openCount), text_color=color.new(color.orange, 0), text_size=tb, text_halign=text.align_right)
 
    table.cell(d, 0, 2, "Wins / Losses", text_color=color.silver, text_size=tb, text_halign=text.align_left)
    table.cell(d, 1, 2, str.tostring(wins), text_color=color.new(colBuy, 0), text_size=tb)
    table.cell(d, 2, 2, str.tostring(losses), text_color=color.new(colSell, 0), text_size=tb, text_halign=text.align_right)
 
    color wrc = wr >= 50 ? color.new(colBuy, 0) : color.new(colSell, 0)
    table.cell(d, 0, 3, "Win Rate", text_color=color.white, text_size=tb, text_halign=text.align_left)
    table.cell(d, 1, 3, resolved > 0 ? str.tostring(wr, "#.#") + "%" : "—", text_color=wrc, text_size=tb)
    table.cell(d, 2, 3, (totalR >= 0 ? "+" : "") + str.tostring(totalR, "#.#") + "R", text_color=wrc, text_size=tb, text_halign=text.align_right)
 
    table.cell(d, 0, 4, "Expectancy", text_color=color.silver, text_size=tb, text_halign=text.align_left)
    table.cell(d, 1, 4, resolved > 0 ? (exp >= 0 ? "+" : "") + str.tostring(exp, "#.##") + "R" : "—", text_color=wrc, text_size=tb)
    table.cell(d, 2, 4, "TP2 " + str.tostring(tp2Hit), text_color=color.new(colTP, 0), text_size=tb, text_halign=text.align_right)
 
    table.cell(d, 0, 5, "WIN RATE BY STRENGTH", text_color=color.new(color.white, 20), text_size=tb, bgcolor=color.new(#39414b, 70), text_halign=text.align_center)
    table.merge_cells(d, 0, 5, 2, 5)
    table.cell(d, 0, 6, "Tier", text_color=color.new(color.white,40), text_size=tb, text_halign=text.align_left)
    table.cell(d, 1, 6, "WR", text_color=color.new(color.white,40), text_size=tb)
    table.cell(d, 2, 6, "n", text_color=color.new(color.white,40), text_size=tb, text_halign=text.align_right)
    string[] lab = array.from("★★★★", "★★★", "★★", "★")
    int rowi = 7
    for k = 4 to 1
        [twr, tn] = f_tierWR(k)
        color tc = twr >= 80 ? color.new(colBuy, 0) : twr >= 70 ? color.new(#b6c47a, 0) : color.new(colSell, 0)
        table.cell(d, 0, rowi, array.get(lab, 4 - k), text_color=color.yellow, text_size=tb, text_halign=text.align_left)
        table.cell(d, 1, rowi, tn > 0 ? str.tostring(twr, "#.#") + "%" : "—", text_color=tc, text_size=tb)
        table.cell(d, 2, rowi, str.tostring(tn), text_color=color.white, text_size=tb, text_halign=text.align_right)
        rowi += 1
 
    table.cell(d, 0, 11, "LAST TRADES", text_color=color.new(color.white, 20), text_size=tb, bgcolor=color.new(#39414b, 70), text_halign=text.align_center)
    table.merge_cells(d, 0, 11, 2, 11)
    int hn = array.size(recent)
    if hn > 0
        for i = 0 to math.min(hn - 1, 9)
            string o = array.get(recent, i)
            color oc = o == "SL" ? color.new(colSell, 0) : color.new(colTP, 0)
            table.cell(d, 0, 12 + i, "#" + str.tostring(i + 1), text_color=color.new(color.white, 35), text_size=tb, text_halign=text.align_left)
            table.cell(d, 1, 12 + i, o, text_color=oc, text_size=tb)
            table.cell(d, 2, 12 + i, "", text_color=color.white, text_size=tb)
 
alertcondition(firedLong,  "Buy Reversal",  "Bullish reversal signal formed.")
alertcondition(firedShort, "Sell Reversal", "Bearish reversal signal formed.")

← 返回列表