Files
tradingview-pine/Swing Failure Pattern.pine

399 lines
21 KiB
Plaintext
Raw Normal View History

2025-08-02 04:03:35 +00:00
// 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","备注":"突破对立高点确认看涨"}')
//---------------------------------------------------------------------------------------------------------------------}