//@version=6 strategy("XAUUSD LR-Adaptive AI 911 TEAM)", overlay=true, initial_capital=20000, default_qty_type=strategy.percent_of_equity, default_qty_value=10, pyramiding=0, calc_on_order_fills=true, calc_on_every_tick=true, max_labels_count=500, max_lines_count=500) // ===== Inputs ===== groupSig = "Signals" channelLen = input.int(5, "Channel Length", group=groupSig) lrLen = input.int(90, "LR Filter Length", group=groupSig) groupATR = "ATR / Risk" atrLen = input.int(9, "ATR Length", group=groupATR) atrMultSL = input.float(0.6, "ATR x for Stop Loss", step=0.1, group=groupATR) atrMultTP = input.float(0.9, "ATR x for Take Profit", step=0.1, group=groupATR) groupTrail = "Trailing" useTrailing= input.bool(true, "Use Trailing Stop (ATR-based)", group=groupTrail) trailMult = input.float(0.8, "ATR x for Trailing Step", step=0.1, group=groupTrail) groupSafe = "Safety Exits" maxAdverseATR = input.float(2.5, "Force Close if Adverse > X*ATR", step=0.1, group=groupSafe) maxBarsHold = input.int(180, "Time Stop: Max Bars In Trade", group=groupSafe) beATR = input.float(0.8, "Breakeven when Gain >= X*ATR", step=0.1, group=groupSafe) groupQuick = "Quick ATR Exits" takeATR = input.float(0.6, "Quick TP (ATRx)", step=0.1, group=groupQuick) stopATR = input.float(1.0, "Quick SL (ATRx)", step=0.1, group=groupQuick) groupExec = "Execution Control" cooldownBars = input.int(0, "Cooldown bars after CLOSE (0 = flip same bar)", minval=0, group=groupExec) oneActionPerBar = input.bool(true, "One action per bar (alerts)", group=groupExec) coolCloseSec = input.int(3, "HARD cooldown for CLOSE (seconds)", minval=0, group=groupExec) groupAuto = "Auto-Trade (Webhook JSON)" keyInp = input.string("MY_SECRET_911", "Webhook Key", group=groupAuto) lotInp = input.float(0.01, "Lot to send", step=0.01, group=groupAuto) // ทอง: เริ่ม 150–300 จุด (ขึ้นกับ point/โบรก) devInp = input.int(200, "Deviation (points)", group=groupAuto) groupUSD = "USD Display" // ส่วนใหญ่ 1 lot XAUUSD, 1 point = $0.01 → $1 ต่อ lot/point usdPerPointPerLot = input.float(1.0, "USD per 1 point per 1 lot", step=0.1, group=groupUSD) // ===== Signals ===== upBound = ta.highest(high, channelLen)[1] downBound = ta.lowest(low, channelLen)[1] lrLine = ta.linreg(close, lrLen, 0) atr = ta.atr(atrLen) isBullishTrend = lrLine > lrLine[1] and close > lrLine isBearishTrend = lrLine < lrLine[1] and close < lrLine plot(lrLine, "LR Trend", color=color.rgb(0,150,255), linewidth=2) plot(upBound, "Upper", color=color.new(color.lime, 0)) plot(downBound,"Lower", color=color.new(color.red, 0)) // ===== Helpers ===== f_clamp_long(sl, tp) => _sl = math.min(sl, close) _tp = math.max(tp, close) _sl := _sl >= _tp ? _tp - atr : _sl [_sl, _tp] f_clamp_short(sl, tp) => _sl = math.max(sl, close) _tp = math.min(tp, close) _tp := _tp >= _sl ? _sl - atr : _tp [_sl, _tp] // ===== JSON (one-line builder) ===== f_json(action) => '{"key":"' + keyInp + '","attach":true,"symbol":"' + syminfo.ticker + '","action":"' + action + '","lot":' + str.tostring(lotInp) + ',"deviation":' + str.tostring(devInp) + '}' json_open_long = f_json("LONG") json_open_short = f_json("SHORT") // ===== Alert throttle (TV limit) ===== groupAlert = "Alert Throttle" maxAlertsPer3min = input.int(12, "Max alerts / 3 min (<=15)", minval=1, maxval=15, group=groupAlert) minAlertSec = input.int(5, "Min seconds between alerts", minval=0, group=groupAlert) onePerBarLimit = input.bool(true, "At most 1 alert per bar", group=groupAlert) // state (แก้เฉพาะนอกฟังก์ชัน) var int __bucket3m = na var int __bucketCount = 0 var int __lastAlertMs = na var int __lastAlertBar = na if barstate.isrealtime _now = timenow _bk = na(_now) ? __bucket3m : int(math.floor(_now / 180000)) // 180000 ms = 3 นาที if na(__bucket3m) or (not na(_bk) and _bk != __bucket3m) __bucket3m := _bk __bucketCount := 0 f_can_alert() => if not barstate.isrealtime true else _now = timenow _coolOk = na(_now) or na(__lastAlertMs) or (_now - __lastAlertMs) >= minAlertSec * 1000 _quotaOk = __bucketCount < maxAlertsPer3min _barOk = not onePerBarLimit or na(__lastAlertBar) or __lastAlertBar != bar_index _coolOk and _quotaOk and _barOk f_alert(json) => _ok = f_can_alert() if _ok alert(json, alert.freq_once_per_bar) _ok // ===== Trade State ===== var int lastCloseBar = na var bool sentCloseThisFlat = false var bool wasFlat = strategy.position_size == 0 var int lastCloseMs = na flatNow = strategy.position_size == 0 canHardCooldownClose() => na(timenow) or na(lastCloseMs) or (timenow - lastCloseMs) >= coolCloseSec * 1000 canSendCloseNow() => barstate.isrealtime and canHardCooldownClose() and (na(lastCloseBar) or lastCloseBar != bar_index) and not sentCloseThisFlat // ===== Runtime ===== var float entryATR = na var float currentSL = na var float currentTP = na var int lastActionBar = na var string pendingSide = na // "L" or "S" newPos = strategy.position_size != 0 and strategy.position_size[1] == 0 lostLevels = strategy.position_size != 0 and (na(currentSL) or na(currentTP)) if (newPos or lostLevels) entryATR := atr if strategy.position_size > 0 currentSL := strategy.position_avg_price - (entryATR * atrMultSL) currentTP := strategy.position_avg_price + (entryATR * atrMultTP) else currentSL := strategy.position_avg_price + (entryATR * atrMultSL) currentTP := strategy.position_avg_price - (entryATR * atrMultTP) if newPos sentCloseThisFlat := false canActThisBar = not oneActionPerBar or na(lastActionBar) or bar_index > lastActionBar notCooldown = na(lastActionBar) or (bar_index - lastActionBar >= cooldownBars) // ===== Close conditions (mark & flip) ===== closeSignal = false closedThisBar = false if canActThisBar if strategy.position_size > 0 and isBearishTrend strategy.close("L", comment="ReverseClose") closeSignal := true closedThisBar := true pendingSide := "S" lastActionBar := bar_index else if strategy.position_size < 0 and isBullishTrend strategy.close("S", comment="ReverseClose") closeSignal := true closedThisBar := true pendingSide := "L" lastActionBar := bar_index adverse = strategy.position_size > 0 ? (strategy.position_avg_price - close) / atr : strategy.position_size < 0 ? (close - strategy.position_avg_price) / atr : 0.0 barsInPos = ta.barssince(strategy.position_size == 0) if strategy.position_size > 0 and adverse > maxAdverseATR strategy.close("L", comment="ATR-Kill") closeSignal := true closedThisBar := true lastActionBar := bar_index if strategy.position_size < 0 and adverse > maxAdverseATR strategy.close("S", comment="ATR-Kill") closeSignal := true closedThisBar := true lastActionBar := bar_index if strategy.position_size != 0 and barsInPos >= maxBarsHold strategy.close(strategy.position_size > 0 ? "L" : "S", comment="TimeStop") closeSignal := true closedThisBar := true lastActionBar := bar_index gain = strategy.position_size > 0 ? (close - strategy.position_avg_price) / atr : strategy.position_size < 0 ? (strategy.position_avg_price - close) / atr : 0.0 if strategy.position_size > 0 and gain >= beATR currentSL := math.max(currentSL, strategy.position_avg_price) if strategy.position_size < 0 and gain >= beATR currentSL := math.min(currentSL, strategy.position_avg_price) gainATR = strategy.position_size > 0 ? (close - strategy.position_avg_price) / atr : strategy.position_size < 0 ? (strategy.position_avg_price - close) / atr : 0.0 if strategy.position_size != 0 and gainATR >= takeATR strategy.close(strategy.position_size > 0 ? "L" : "S", comment="QuickTP") closeSignal := true closedThisBar := true lastActionBar := bar_index if strategy.position_size != 0 and (-gainATR) >= stopATR strategy.close(strategy.position_size > 0 ? "L" : "S", comment="QuickSL") closeSignal := true closedThisBar := true lastActionBar := bar_index // ===== Manage exits (SL/TP + trailing) ===== if strategy.position_size > 0 if useTrailing and not na(currentSL) trailStop = close - (atr * trailMult) currentSL := math.max(currentSL, trailStop) [lsl, ltp] = f_clamp_long(currentSL, currentTP) strategy.exit("XL_Exit", from_entry="L", stop=lsl, limit=ltp) else if strategy.position_size < 0 if useTrailing and not na(currentSL) trailStop = close + (atr * trailMult) currentSL := math.min(currentSL, trailStop) [ssl, stp] = f_clamp_short(currentSL, currentTP) strategy.exit("XS_Exit", from_entry="S", stop=ssl, limit=stp) // ===== OPEN (allow flip same bar) ===== allowRealtimeOpen = barstate.isrealtime okBarConfirm = barstate.isconfirmed or allowRealtimeOpen canOpenNow = flatNow and (notCooldown or closedThisBar or not na(pendingSide)) and canActThisBar and okBarConfirm if canOpenNow strategy.cancel_all() if (pendingSide == "L") and isBullishTrend and not na(upBound) strategy.entry("L", strategy.long, stop=upBound + syminfo.mintick) if f_alert(json_open_long) __lastAlertMs := timenow, __bucketCount += 1, __lastAlertBar := bar_index pendingSide := na lastActionBar := bar_index else if (pendingSide == "S") and isBearishTrend and not na(downBound) strategy.entry("S", strategy.short, stop=downBound - syminfo.mintick) if f_alert(json_open_short) __lastAlertMs := timenow, __bucketCount += 1, __lastAlertBar := bar_index pendingSide := na lastActionBar := bar_index else if isBullishTrend and not na(upBound) strategy.entry("L", strategy.long, stop=upBound + syminfo.mintick) if f_alert(json_open_long) __lastAlertMs := timenow, __bucketCount += 1, __lastAlertBar := bar_index lastActionBar := bar_index else if isBearishTrend and not na(downBound) strategy.entry("S", strategy.short, stop=downBound - syminfo.mintick) if f_alert(json_open_short) __lastAlertMs := timenow, __bucketCount += 1, __lastAlertBar := bar_index lastActionBar := bar_index // ===== Transition: position -> flat (mark CLOSE once) ===== justClosed = (not wasFlat) and strategy.position_size == 0 and strategy.position_size[1] != 0 wasFlat := flatNow if justClosed closeSignal := true // ===== SINGLE dispatch for CLOSE (hard cooldown + anti-dup + throttle) ===== if closeSignal and canSendCloseNow() if f_alert(f_json("CLOSE")) __lastAlertMs := timenow, __bucketCount += 1, __lastAlertBar := bar_index lastCloseBar := bar_index sentCloseThisFlat := true if barstate.isrealtime and not na(timenow) lastCloseMs := timenow // ===== USD HUD ===== inPos = strategy.position_size != 0 dir = strategy.position_size > 0 ? 1.0 : strategy.position_size < 0 ? -1.0 : 0.0 avgPrice = strategy.position_avg_price pnl_points = inPos ? (close - avgPrice) * dir : 0.0 pnl_usd = pnl_points * usdPerPointPerLot * lotInp atr_usd = atr * usdPerPointPerLot * lotInp tp_usd = takeATR * atr_usd sl_usd = stopATR * atr_usd to_tp_usd = inPos ? math.max(0, (takeATR - gainATR) * atr_usd) : na to_sl_usd = inPos ? math.max(0, (stopATR + gainATR) * atr_usd) : na gain_now = inPos ? (dir > 0 ? (close - avgPrice) / atr : (avgPrice - close) / atr) : 0.0 adverse_now = inPos ? (dir > 0 ? (avgPrice - close) / atr : (close - avgPrice) / atr) : 0.0 var label _hud = na if barstate.islast if not na(_hud) label.delete(_hud) dirTxt = strategy.position_size > 0 ? "LONG" : strategy.position_size < 0 ? "SHORT" : "FLAT" hud = "Dir: " + dirTxt + " | CooldownOK: " + (notCooldown ? "YES" : "NO") hud += "\nATR ≈ $" + str.tostring(atr_usd, format.mintick) hud += "\nQuick TP/SL: +" + str.tostring(tp_usd, format.mintick) + " / -" + str.tostring(sl_usd, format.mintick) hud += "\nPnL$ now: " + str.tostring(pnl_usd, format.mintick) if inPos hud += "\nTo TP: $" + str.tostring(to_tp_usd, format.mintick) + " To SL: $" + str.tostring(to_sl_usd, format.mintick) hud += "\nGain ATR: " + str.tostring(gain_now, format.mintick) hud += "\nAdverse ATR: " + str.tostring(adverse_now, format.mintick) if not na(pendingSide) hud += "\nPending: " + pendingSide _hud := label.new(bar_index, high, hud, yloc=yloc.abovebar, style=label.style_label_down)