Files
tradingview-pine/Swing Failure Pattern.pine
2025-08-02 04:03:35 +00:00

399 lines
21 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © LuxAlgo
//@version=5
indicator( 'SwingFailurePattern [LuxAlgo]', 'LuxAlgo - Swing Failure Pattern', max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500, overlay = true)
//---------------------------------------------------------------------------------------------------------------------}
//Settings
//---------------------------------------------------------------------------------------------------------------------{
sp = '            '
len = input.int ( 5 , 'Swings' , minval= 1 )
bull = input.bool ( true , 'Bullish SFP' )
bear = input.bool ( true , 'Bearish SFP' )
iVal = input.string ('None', 'Validation'+sp+ '   ', group='Volume Validation'
, options = ['Volume outside swing < Threshold' ,'Volume outside swing > Threshold','None'])
percent = input.float (25, 'Volume Threshold %' , inline='valy', group='Volume Validation'
, tooltip = '% of Total Volume' , minval= 0 , maxval=100 )
auto = input.bool (true, '' , inline='auto', group='Volume Validation' )
mlt = input.int (50 , '      Auto' + sp , inline='auto', group='Volume Validation' )
res = input.timeframe('1' , sp +'   LTF' + sp +'  ', inline='ltf' , group='Volume Validation' )
prem = input.bool (false ,'    Premium' , group='Volume Validation'
, tooltip = 'Premium Plan or higher' )
showDash = input.bool (true , 'Show Dashboard' , group= 'Dashboard' )
dashLoc = input.string ( 'Top Right' , 'Location'
, options = ['Top Right', 'Bottom Right', 'Bottom Left'] , group= 'Dashboard' )
textSize = input.string ( 'Normal' , 'Size'
, options = ['Tiny', 'Small', 'Normal'] , group= 'Dashboard' )
dSwingLine = input.bool (true , 'Swing Lines' , group= 'Style' )
dOpposLine = input.bool (true , 'Confirmation Lines' , group= 'Style' )
dSFP_Line = input.bool (true , 'Swing Failure Wick' , group= 'Style' )
dSFP_Label = input.bool (true , 'Swing Failure Label' , group= 'Style' )
showAlerts = input.bool (true , 'Show Alert Markers' , group= 'Style' )
alertSize = input.string ('Normal', 'Alert Marker Size' , group= 'Style'
, options = ['Small', 'Normal', 'Large'] )
colBl = input.color (#089981 , 'Lines / Labels' , inline='lin' , group= 'Style' )
colBr = input.color (#f23645 , '' , inline='lin' , group= 'Style'
, tooltip = 'Bullish/Bearish' )
colBl2 = input.color (#08998180, 'SFP Wicks      ', inline='wic' , group= 'Style' )
colBr2 = input.color (#f2364580, '' , inline='wic' , group= 'Style'
, tooltip = 'Wick outside Swing, Bullish/Bearish' )
//-----------------------------------------------------------------------------}
//UDT
//-----------------------------------------------------------------------------{
type piv
float swing_prc // price
int swing_bix // bar_index
float oppos_prc // price
int oppos_bix // bar_index
bool active
bool confirmed
line swing_line
line oppos_line
line wicky_line
label wicky_label
type swing
int bix
float prc
//-----------------------------------------------------------------------------}
//Variables
//-----------------------------------------------------------------------------{
n = bar_index
INV = color(na)
FGc = chart.fg_color
table_position = dashLoc == 'Bottom Left' ? position.bottom_left
: dashLoc == 'Top Right' ? position.top_right
: position.bottom_right
table_size = textSize == 'Tiny' ? size.tiny
: textSize == 'Small' ? size.small
: size.normal
alert_marker_size = alertSize == 'Small' ? size.small
: alertSize == 'Large' ? size.large
: size.normal
var tb = table.new(table_position, 2, 3
, bgcolor = #1e222d
, border_color = #373a46
, border_width = 1
, frame_color = #373a46
, frame_width = 1)
var swing swingH = swing.new()
var swing swingL = swing.new()
var piv pivH = piv.new()
var piv pivL = piv.new()
validate = iVal != 'None'
valHigher= iVal == 'Volume outside swing > Threshold'
valLower = iVal == 'Volume outside swing < Threshold'
//---------------------------------------------------------------------------------------------------------------------}
//Method
//---------------------------------------------------------------------------------------------------------------------{
method n(float piv) => bool out = not na(piv)
//---------------------------------------------------------------------------------------------------------------------}
//Execution
//---------------------------------------------------------------------------------------------------------------------{
tfS = timeframe.in_seconds( res )
tfC = timeframe.in_seconds(timeframe.period)
rs = auto ? tfC / mlt : tfS
if not validate
res := timeframe.period
else
rs := prem ? rs : math.max(60, rs)
res := timeframe.from_seconds(math.min(tfC, rs))
ph = ta.pivothigh(len, 1)
pl = ta.pivotlow (len, 1)
[ltf_close, ltf_volume] = request.security_lower_tf(syminfo.tickerid, res, [close, volume])
ltf_size = ltf_close.size()
if validate
if ltf_size > 0 and ltf_size[1] == 0
line.new(n, close, n, close + syminfo.mintick, color=color.silver, style=line.style_dotted, extend=extend.both)
//---------------------------------------------------------------------------------------------------------------------}
//Bearish Pattern
//---------------------------------------------------------------------------------------------------------------------{
if bear
if ph.n()
swingH.bix := n-1
swingH.prc := ph
sw = swingH.prc
bx = swingH.bix
if high > sw
and open < sw
and close < sw
valid = true
if validate
if ltf_close.size() > 0
outsideVolume = 0.
totalVolume = ltf_volume.sum()
for j = 0 to ltf_close.size() -1
if ltf_close.get(j) > sw
outsideVolume += ltf_volume.get(j)
if (valHigher ? 100 / totalVolume * outsideVolume < percent
: 100 / totalVolume * outsideVolume > percent
)
valid := false
//if valid
// label.new(n, high, text=str.format("Total Volume: {0}\nWick Volume: {1}", totalVolume, outsideVolume))
if valid
opposL = sw
opposB = n
for i = 1 to n - bx -1
if low [i] < opposL
opposL := low [i]
opposB := n - i
if not pivH.confirmed
pivH.swing_line .delete()
pivH.oppos_line .delete()
pivH.wicky_line .delete()
pivH.wicky_label.delete()
pivH := piv.new(sw, bx, opposL, opposB, true, false)
if dSwingLine
pivH.swing_line := line.new (bx , sw , n, sw , color=colBr)
if dOpposLine
pivH.oppos_line := line.new (opposB, opposL, n, opposL, color=colBr, style=line.style_dotted)
if dSFP_Line
pivH.wicky_line := line.new (n , high , n, sw , color=colBr2, width=3)
if dSFP_Label
pivH.wicky_label := label.new(n , high
, style=label.style_label_down
, text='SFP', textcolor=colBr
, color=INV, size=size.normal
)
if pivH.active and not pivH.confirmed
pivH.swing_line.set_x2(n)
pivH.oppos_line.set_x2(n)
if close < pivH.oppos_prc
pivH.confirmed := true
if pivH.wicky_label.get_x() == n
pivH.wicky_label.set_text('SFP\n▼')
else
label.new(n, high, style=label.style_label_down, text='▼', textcolor=colBr, color=INV, size=size.normal)
if n - pivH.swing_bix > 500
or close > pivH.swing_prc
pivH.active := false
if not pivH.confirmed
pivH.swing_line .delete()
pivH.oppos_line .delete()
pivH.wicky_line .delete()
pivH.wicky_label.delete()
//---------------------------------------------------------------------------------------------------------------------}
//Bullish Pattern
//---------------------------------------------------------------------------------------------------------------------{
if bull
if pl.n()
swingL.bix := n-1
swingL.prc := pl
sw = swingL.prc
bx = swingL.bix
if low < sw
and open > sw
and close > sw
valid = true
if validate
if ltf_close.size() > 0
outsideVolume = 0.
totalVolume = ltf_volume.sum()
for j = 0 to ltf_close.size() -1
if ltf_close.get(j) < sw
outsideVolume += ltf_volume.get(j)
if (valHigher ? 100 / totalVolume * outsideVolume < percent
: 100 / totalVolume * outsideVolume > percent
)
valid := false
if valid
opposH = sw
opposB = n
for i = 1 to n - bx -1
if high[i] > opposH
opposH := high[i]
opposB := n - i
if not pivL.confirmed
pivL.swing_line .delete()
pivL.oppos_line .delete()
pivL.wicky_line .delete()
pivL.wicky_label.delete()
pivL := piv.new(sw, bx, opposH, opposB, true, false)
if dSwingLine
pivL.swing_line := line.new (bx , sw , n, sw , color=colBl )
if dOpposLine
pivL.oppos_line := line.new (opposB, opposH, n, opposH, color=colBl , style=line.style_dotted)
if dSFP_Line
pivL.wicky_line := line.new (n , low , n, sw , color=colBl2, width=3)
if dSFP_Label
pivL.wicky_label := label.new(n , low
, style=label.style_label_up
, text='SFP', textcolor=colBl
, color=INV, size=size.normal
)
if pivL.active and not pivL.confirmed
pivL.swing_line.set_x2(n)
pivL.oppos_line.set_x2(n)
if close > pivL.oppos_prc
pivL.confirmed := true
if pivL.wicky_label.get_x() == n
pivL.wicky_label.set_text('▲\nSFP')
else
label.new(n, low, style=label.style_label_up, text='▲', textcolor=colBl, color=INV, size=size.normal)
if n - pivL.swing_bix > 500
or close < pivL.swing_prc
pivL.active := false
if not pivL.confirmed
pivL.swing_line .delete()
pivL.oppos_line .delete()
pivL.wicky_line .delete()
pivL.wicky_label.delete()
//---------------------------------------------------------------------------------------------------------------------}
//Dashboard
//---------------------------------------------------------------------------------------------------------------------{
if barstate.islast and validate and showDash
tb.cell(0, 0, str.format("LTF: {0}", res), text_color=color.white, text_size=table_size)
//---------------------------------------------------------------------------------------------------------------------}
//Alert System
//---------------------------------------------------------------------------------------------------------------------{
// ===== 统一警报系统 =====
// 统一警报系统 - 只需添加一次警报即可捕获所有信号
// 警报频率限制 - 每分钟只触发一次
var int last_alert_bearish_sfp = 0
var int last_alert_bullish_sfp = 0
var int last_alert_bearish_sfp_confirmed = 0
var int last_alert_bullish_sfp_confirmed = 0
// 获取当前时间(分钟级别)
current_minute = math.floor(timenow / 60000)
// 检测所有警报条件并生成对应的JSON消息
alert_message = ""
// 看跌SFP信号检测 - 每分钟限制
bearish_sfp_signal = bear and high > swingH.prc and open < swingH.prc and close < swingH.prc and not pivH.confirmed
if bearish_sfp_signal and current_minute > last_alert_bearish_sfp
alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动高点":"' + str.tostring(swingH.prc, '#.####') + '","事件":"看跌摆动失败模式触发","信号":"bearish_sfp","备注":"突破高点后看跌回落"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_bearish_sfp := current_minute
// 在K线上绘制看跌SFP信号标记
if showAlerts
label.new(bar_index, high + (high - low) * 0.1,
text='🔻SFP',
style=label.style_label_down,
color=color.new(color.red, 20),
textcolor=color.white,
size=alert_marker_size,
tooltip='突破高点后看跌回落\n摆动高点: ' + str.tostring(swingH.prc, '#.####'))
// 看涨SFP信号检测 - 每分钟限制
bullish_sfp_signal = bull and low < swingL.prc and open > swingL.prc and close > swingL.prc and not pivL.confirmed
if bullish_sfp_signal and current_minute > last_alert_bullish_sfp
alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动低点":"' + str.tostring(swingL.prc, '#.####') + '","事件":"看涨摆动失败模式触发","信号":"bullish_sfp","备注":"跌破低点后看涨反弹"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_bullish_sfp := current_minute
// 在K线上绘制看涨SFP信号标记
if showAlerts
label.new(bar_index, low - (high - low) * 0.1,
text='🔺SFP',
style=label.style_label_up,
color=color.new(color.green, 20),
textcolor=color.white,
size=alert_marker_size,
tooltip='跌破低点后看涨反弹\n摆动低点: ' + str.tostring(swingL.prc, '#.####'))
// 看跌SFP确认信号 - 每分钟限制
bearish_sfp_confirmed = pivH.active and not pivH.confirmed[1] and pivH.confirmed and close < pivH.oppos_prc
if bearish_sfp_confirmed and current_minute > last_alert_bearish_sfp_confirmed
alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动高点":"' + str.tostring(pivH.swing_prc, '#.####') + '","对立价格":"' + str.tostring(pivH.oppos_prc, '#.####') + '","事件":"看跌摆动失败模式确认","信号":"bearish_sfp_confirmed","备注":"跌破对立低点确认看跌"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_bearish_sfp_confirmed := current_minute
// 在K线上绘制看跌SFP确认信号标记
if showAlerts
label.new(bar_index, high + (high - low) * 0.15,
text='✅🔻',
style=label.style_label_down,
color=color.new(color.maroon, 10),
textcolor=color.white,
size=alert_marker_size,
tooltip='跌破对立低点确认看跌\n摆动高点: ' + str.tostring(pivH.swing_prc, '#.####') + '\n对立价格: ' + str.tostring(pivH.oppos_prc, '#.####'))
// 看涨SFP确认信号 - 每分钟限制
bullish_sfp_confirmed = pivL.active and not pivL.confirmed[1] and pivL.confirmed and close > pivL.oppos_prc
if bullish_sfp_confirmed and current_minute > last_alert_bullish_sfp_confirmed
alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动低点":"' + str.tostring(pivL.swing_prc, '#.####') + '","对立价格":"' + str.tostring(pivL.oppos_prc, '#.####') + '","事件":"看涨摆动失败模式确认","信号":"bullish_sfp_confirmed","备注":"突破对立高点确认看涨"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_bullish_sfp_confirmed := current_minute
// 在K线上绘制看涨SFP确认信号标记
if showAlerts
label.new(bar_index, low - (high - low) * 0.15,
text='✅🔺',
style=label.style_label_up,
color=color.new(color.teal, 10),
textcolor=color.white,
size=alert_marker_size,
tooltip='突破对立高点确认看涨\n摆动低点: ' + str.tostring(pivL.swing_prc, '#.####') + '\n对立价格: ' + str.tostring(pivL.oppos_prc, '#.####'))
// ===== 传统警报条件(保留兼容性)=====
// 注意使用统一警报系统时建议只使用上面的alert()函数
// 以下alertcondition保留用于需要单独设置警报的情况
// 创建用于警报的plot变量
plot(swingH.prc, title = '摆动高点', display = display.none)
plot(swingL.prc, title = '摆动低点', display = display.none)
plot(pivH.oppos_prc, title = '看跌对立价格', display = display.none)
plot(pivL.oppos_prc, title = '看涨对立价格', display = display.none)
// SFP信号警报
alertcondition(bearish_sfp_signal, title = '看跌摆动失败模式', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动高点":"{{plot("摆动高点")}}","事件":"看跌摆动失败模式触发","信号":"bearish_sfp","备注":"突破高点后看跌回落"}')
alertcondition(bullish_sfp_signal, title = '看涨摆动失败模式', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动低点":"{{plot("摆动低点")}}","事件":"看涨摆动失败模式触发","信号":"bullish_sfp","备注":"跌破低点后看涨反弹"}')
// SFP确认信号警报
alertcondition(bearish_sfp_confirmed, title = '看跌摆动失败模式确认', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动高点":"{{plot("摆动高点")}}","对立价格":"{{plot("看跌对立价格")}}","事件":"看跌摆动失败模式确认","信号":"bearish_sfp_confirmed","备注":"跌破对立低点确认看跌"}')
alertcondition(bullish_sfp_confirmed, title = '看涨摆动失败模式确认', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动低点":"{{plot("摆动低点")}}","对立价格":"{{plot("看涨对立价格")}}","事件":"看涨摆动失败模式确认","信号":"bullish_sfp_confirmed","备注":"突破对立高点确认看涨"}')
//---------------------------------------------------------------------------------------------------------------------}