OPEN-SOURCE SCRIPT

Confluence AutoEntry (1m/5m/15m) for Alertatron

44
//version=5
indicator("Confluence AutoEntry (1m/5m/15m) for Alertatron", overlay=true, max_lines_count=500, max_labels_count=500)

// =========================
// 参数
// =========================
tf1 = input.timeframe("1", "周期-1")
tf2 = input.timeframe("5", "周期-2")
tf3 = input.timeframe("15", "周期-3")

ema1 = input.int(10, "EMA1")
ema2 = input.int(30, "EMA2")
ema3 = input.int(60, "EMA3")
rLen = input.int(14, "RSI 长度")

thr1 = input.int(60, "阈值-周期1 (0~100)", minval=0, maxval=100)
thr2 = input.int(60, "阈值-周期2 (0~100)", minval=0, maxval=100)
thr3 = input.int(60, "阈值-周期3 (0~100)", minval=0, maxval=100)

// 下单资金(USDT)
usdtPerTrade = input.float(500, "每次下单金额(USDT)", step=5)

// 下单类型(市价/限价)
orderType = input.string("market", "下单类型", options=["market","limit"], tooltip="market=市价;limit=限价(使用 entry 作为限价)")

// 两步延续触发
useTwoStep = input.bool(true, "启用两步延续触发", tooltip="收盘仅“武装”,下一根或后续“延续突破”才真正发单;关闭=收盘即发一次信号")
bufferPct = input.float(0.08, "延续触发缓冲(%)", step=0.01, tooltip="突破需要超过武装价的百分比")
armBars = input.int(1, "武装有效bar数", minval=1, tooltip="超时未触发则自动取消武装")

// 可选:在 JSON 中附带 token
attachToken = input.bool(false, "在 JSON 中附带 token 字段")
longToken = input.string("", "多头 token(可留空)")
shortToken = input.string("", "空头 token(可留空)")

// 图形显示
showShapes = input.bool(true, "图表显示三角/武装/触发标记")
showTriggerLabel = input.bool(true, "触发时显示【入场/TP/SL/Qty】标签")

// 「平衡」展示用 TP/SL 百分比(仅用于标签展示,不发到 Alertatron)
tpPct = input.float(2.0, "展示用:TP百分比(%)")
slPct = input.float(1.0, "展示用:SL百分比(%)")

// =========================
// 工具函数
// =========================
f_score(tf) =>
_c = request.security(syminfo.tickerid, tf, close, barmerge.gaps_off, barmerge.lookahead_off)
_e1 = request.security(syminfo.tickerid, tf, ta.ema(close, ema1), barmerge.gaps_off, barmerge.lookahead_off)
_e2 = request.security(syminfo.tickerid, tf, ta.ema(close, ema2), barmerge.gaps_off, barmerge.lookahead_off)
_e3 = request.security(syminfo.tickerid, tf, ta.ema(close, ema3), barmerge.gaps_off, barmerge.lookahead_off)
_r = request.security(syminfo.tickerid, tf, ta.rsi(close, rLen), barmerge.gaps_off, barmerge.lookahead_off)
_m = request.security(syminfo.tickerid, tf, ta.sma(ta.change(close), 5), barmerge.gaps_off, barmerge.lookahead_off)
_ok1 = _c > _e1 ? 1 : 0
_ok2 = _e1 >= _e2 ? 1 : 0
_ok3 = _e2 >= _e3 ? 1 : 0
_ok4 = _r > 50 ? 1 : 0
_ok5 = _m >= 0 ? 1 : 0
(_ok1 + _ok2 + _ok3 + _ok4 + _ok5) / 5.0 * 100.0

// 价格按最小跳动取整
tickRound(x) =>
syminfo.mintick > 0 ? math.round(x / syminfo.mintick) * syminfo.mintick : x

// 数字 -> 价格串
sPrice(x) =>
str.tostring(x, format.mintick)

// 小数点四舍五入(用于数量展示)
roundN(x, n) =>
_f = math.pow(10.0, n)
math.round(x * _f) / _f

sQty(x) =>
str.tostring(roundN(x, 6))

// 去掉交易所前缀和 .P 后缀,得到 BASEQUOTE(如 ETHUSDC)
cleanSymbol() =>
_s = syminfo.ticker
_arr = str.split(_s, ":")
_last = array.get(_arr, array.size(_arr) - 1)
str.replace_all(_last, ".P", "")

// =========================
// 多周期评分 -> 一致方向
// =========================
score1 = f_score(tf1)
score2 = f_score(tf2)
score3 = f_score(tf3)

dir1 = score1 >= thr1 ? 1 : -1
dir2 = score2 >= thr2 ? 1 : -1
dir3 = score3 >= thr3 ? 1 : -1

conf_long = dir1 == 1 and dir2 == 1 and dir3 == 1
conf_short = dir1 == -1 and dir2 == -1 and dir3 == -1

// =========================
// 两步法:收盘“武装” + 下一根/后续“延续触发”
// =========================
var bool armLong = false
var float armLongPx = na
var int armLongUntil = na

var bool armShort = false
var float armShortPx = na
var int armShortUntil = na

if barstate.isconfirmed
if conf_long
armLong := true
armLongPx := close
armLongUntil := bar_index + armBars
if conf_short
armShort := true
armShortPx := close
armShortUntil := bar_index + armBars

longTrigPrice = armLong ? armLongPx * (1 + bufferPct/100.0) : na
shortTrigPrice = armShort ? armShortPx * (1 - bufferPct/100.0) : na

triggerLong = useTwoStep ? (armLong and high >= longTrigPrice) : (barstate.isconfirmed and conf_long)
triggerShort = useTwoStep ? (armShort and low <= shortTrigPrice) : (barstate.isconfirmed and conf_short)

// 触发后立刻卸载武装;超时未触发亦卸载
if triggerLong
armLong := false
if triggerShort
armShort := false

if bar_index > armLongUntil
armLong := false
if bar_index > armShortUntil
armShort := false

// =========================
// 发单用价位(在触发时会被使用)
// =========================
float entryL = tickRound(useTwoStep ? longTrigPrice : close)
float entryS = tickRound(useTwoStep ? shortTrigPrice : close)
float tpL = tickRound(entryL * (1 + tpPct/100.0))
float slL = tickRound(entryL * (1 - slPct/100.0))
float tpS = tickRound(entryS * (1 - tpPct/100.0))
float slS = tickRound(entryS * (1 + slPct/100.0))
// —— 展示标签直接使用上面算好的 entryL/entryS/tpL/slL/tpS/slS
if showTriggerLabel and barstate.isconfirmed
if triggerLong
_txt = "LONG Entry: " + sPrice(entryL) + "\nTP: " + sPrice(tpL) + "\nSL: " + sPrice(slL) + "\nEstQty: " + sQty(usdtPerTrade/entryL)
label.new(bar_index, low, text=_txt, style=label.style_label_up, textcolor=color.white, color=color.new(color.lime, 0))
if triggerShort
_txt = "SHORT Entry: " + sPrice(entryS) + "\nTP: " + sPrice(tpS) + "\nSL: " + sPrice(slS) + "\nEstQty: " + sQty(usdtPerTrade/entryS)
label.new(bar_index, high, text=_txt, style=label.style_label_down, textcolor=color.white, color=color.new(color.red, 0))


// ============ 构造 JSON(按方向用不同 signal 名,所有数值写入) ============
buildMsg(_side, _entry, _tp, _sl) =>
_sig = _side == "buy" ? "open_long" : "open_short"
_token = attachToken ? ',"token":"' + (_side == "buy" ? longToken : shortToken) + '"' : ""
_msg = '{"signal":"' + _sig + '"'
_msg := _msg + ',"side":"' + _side + '"'
_msg := _msg + ',"symbol":"' + cleanSymbol() + '"'
_msg := _msg + ',"order_type":"market"'
_msg := _msg + ',"usdt_per_trade":' + str.tostring(usdtPerTrade)
_msg := _msg + ',"entry":' + sPrice(_entry)
_msg := _msg + ',"tp":' + sPrice(_tp)
_msg := _msg + ',"sl":' + sPrice(_sl)
_msg := _msg + _token + "}"
_msg

msgLong = buildMsg("buy", entryL, tpL, slL)
msgShort = buildMsg("sell", entryS, tpS, slS)




// =========================
// 报警 & 发单(在 TradingView 里选择 Any alert() function call;Webhook 填机器人 URL;Message 留空)
// =========================
alertcondition(triggerLong, title="多仓开仓(…)", message="LONG")
alertcondition(triggerShort, title="空仓开仓(…)", message="SHORT")


if barstate.isconfirmed
if triggerLong
alert(message = msgLong, freq = alert.freq_once_per_bar_close)
if triggerShort
alert(message = msgShort, freq = alert.freq_once_per_bar_close)


// =========================
// 可视化:形态+触发线
// =========================
plotshape(showShapes and conf_long and barstate.isconfirmed, title="收盘-多武装", style=shape.triangleup, color=color.new(color.green, 0), size=size.tiny, text="ARM L", location=location.belowbar)
plotshape(showShapes and conf_short and barstate.isconfirmed, title="收盘-空武装", style=shape.triangledown, color=color.new(color.red, 0), size=size.tiny, text="ARM S", location=location.abovebar)
plotshape(showShapes and triggerLong, title="触发-开多", style=shape.labelup, color=color.new(color.lime, 0), text="▶ LONG", location=location.belowbar, size=size.tiny)
plotshape(showShapes and triggerShort, title="触发-开空", style=shape.labeldown, color=color.new(color.maroon,0), text="▶ SHORT", location=location.abovebar, size=size.tiny)

plot(useTwoStep and armLong ? armLongPx : na, "武装价-L", color=color.new(color.green, 70), style=plot.style_circles, linewidth=1)
plot(useTwoStep and armShort ? armShortPx : na, "武装价-S", color=color.new(color.red, 70), style=plot.style_circles, linewidth=1)
plot(useTwoStep ? longTrigPrice : na, "触发线-L", color=color.new(color.lime, 0), style=plot.style_linebr, linewidth=1)
plot(useTwoStep ? shortTrigPrice : na, "触发线-S", color=color.new(color.maroon, 0), style=plot.style_linebr, linewidth=1)

// =========================
// 可视化:触发时弹出【入场/TP/SL/Qty】标签(仅展示用)
// =========================
if showTriggerLabel and barstate.isconfirmed
if triggerLong
_qtyL = usdtPerTrade > 0 and entryL > 0 ? (usdtPerTrade / entryL) : na
_txtL = "LONG Entry: " + sPrice(entryL) + "\nTP: " + sPrice(tpL) + "\nSL: " + sPrice(slL) + "\nEstQty: " + sQty(_qtyL)
label.new(bar_index, low, text=_txtL, style=label.style_label_up, textcolor=color.white, color=color.new(color.lime, 0))
if triggerShort
_qtyS = usdtPerTrade > 0 and entryS > 0 ? (usdtPerTrade / entryS) : na
_txtS = "SHORT Entry: " + sPrice(entryS) + "\nTP: " + sPrice(tpS) + "\nSL: " + sPrice(slS) + "\nEstQty: " + sQty(_qtyS)
label.new(bar_index, high, text=_txtS, style=label.style_label_down, textcolor=color.white, color=color.new(color.red, 0))

Wyłączenie odpowiedzialności

Informacje i publikacje przygotowane przez TradingView lub jego użytkowników, prezentowane na tej stronie, nie stanowią rekomendacji ani porad handlowych, inwestycyjnych i finansowych i nie powinny być w ten sposób traktowane ani wykorzystywane. Więcej informacji na ten temat znajdziesz w naszym Regulaminie.