tradingview-bollinger-wicks.pine
Copy
//@version=6
indicator("布林带插针标记 - 多时间框架", shorttitle="BB Wicks MTF", overlay=true, max_bars_back=500)
// ==================== 输入参数 ====================
// 布林带参数
bb_length = input.int(20, title="布林带周期", minval=1)
bb_mult = input.float(2.0, title="布林带倍数", minval=0.1, step=0.1)
// ==================== 时间框架配置(可扩展)====================
// 配置说明:
// - timeframes_code: 存储时间框架代码(TradingView格式)
// - timeframes_name: 存储显示名称(对应位置)
// 时间框架代码说明:
// "15" = 15分钟, "30" = 30分钟, "60" = 1小时
// "240" = 4小时, "1D" = 1天, "1W" = 1周
// 要添加新时间框架,在两个数组中对应位置添加即可
var timeframes_code = array.new<string>()
var timeframes_name = array.new<string>()
if barstate.isfirst
// 默认配置:15分钟、1小时、4小时
array.push(timeframes_code, "15")
array.push(timeframes_name, "15M")
array.push(timeframes_code, "60")
array.push(timeframes_name, "1H")
array.push(timeframes_code, "240")
array.push(timeframes_name, "4H")
// 要添加新时间框架,取消下面的注释并修改:
// array.push(timeframes_code, "30")
// array.push(timeframes_name, "30M") // 30分钟
// array.push(timeframes_code, "1D")
// array.push(timeframes_name, "1D") // 1天
// array.push(timeframes_code, "1W")
// array.push(timeframes_name, "1W") // 1周
// 插针检测参数
wick_ratio = input.float(0.3, title="插针比例(实体/影线)", minval=0.1, maxval=1.0, step=0.1, tooltip="影线长度与实体长度的比例,值越小越严格")
// 显示选项
show_labels = input.bool(true, title="显示标签", group="显示设置")
show_plots = input.bool(true, title="显示布林带", group="显示设置")
label_size = input.string("small", title="标签大小", options=["tiny", "small", "normal", "large", "huge"], group="显示设置")
// 警报设置
enable_alert = input.bool(true, title="启用警报", tooltip="启用后可在TradingView中设置警报,通过webhook调用API", group="警报设置")
alert_on_realtime = input.bool(true, title="实时K线警报", tooltip="在当前时间框架检测到插针时触发警报", group="警报设置")
alert_on_mtf = input.bool(false, title="多时间框架警报", tooltip="在其他时间框架检测到插针时触发警报", group="警报设置")
// 柱体边沿走平检测设置
enable_wick_flat = input.bool(true, title="启用边沿走平预警", tooltip="检测上下轨附近柱体边沿走平信号", group="边沿走平设置")
wick_flat_count = input.int(4, title="连续K线数量", minval=3, maxval=10, tooltip="触发走平信号所需的连续K线数量", group="边沿走平设置")
wick_flat_price_tolerance = input.float(5.0, title="价格相近阈值(美元)", minval=0.1, maxval=50.0, step=0.1, tooltip="边沿价格差异的绝对值(美元),0-5刀视为走平。注意:需要根据币种价格调整,高价币种可能需要更大的值", group="边沿走平设置")
band_distance_threshold = input.float(0.15, title="轨距阈值", minval=0.05, maxval=0.5, step=0.05, tooltip="K线距离上下轨的距离阈值(15%表示在轨距的15%范围内)", group="边沿走平设置")
// 颜色设置
color_upper_wick = input.color(color.new(color.red, 0), title="上插针颜色", group="颜色设置")
color_lower_wick = input.color(color.new(color.green, 0), title="下插针颜色", group="颜色设置")
// ==================== 函数:计算布林带 ====================
calc_bb(tf) =>
[high_tf, low_tf, close_tf, open_tf] = request.security(syminfo.tickerid, tf, [high, low, close, open], lookahead=barmerge.lookahead_off)
// 计算布林带
basis = ta.sma(close_tf, bb_length)
dev = bb_mult * ta.stdev(close_tf, bb_length)
upper = basis + dev
lower = basis - dev
[upper, lower, basis, high_tf, low_tf, close_tf, open_tf]
// ==================== 函数:检测插针 ====================
detect_wick(upper_band, lower_band, high_price, low_price, open_price, close_price) =>
body_size = math.abs(close_price - open_price)
upper_wick = high_price - math.max(open_price, close_price)
lower_wick = math.min(open_price, close_price) - low_price
// 上插针:上影线突破上轨,且上影线长度满足条件
upper_wick_condition = high_price > upper_band and upper_wick > 0 and (body_size == 0 or upper_wick / math.max(body_size, 0.0001) >= wick_ratio)
// 下插针:下影线突破下轨,且下影线长度满足条件
lower_wick_condition = low_price < lower_band and lower_wick > 0 and (body_size == 0 or lower_wick / math.max(body_size, 0.0001) >= wick_ratio)
[upper_wick_condition, lower_wick_condition, upper_wick, lower_wick]
// ==================== 辅助函数:获取标签大小 ====================
get_label_size() =>
switch label_size
"tiny" => size.tiny
"small" => size.small
"normal" => size.normal
"large" => size.large
"huge" => size.huge
=> size.small
// ==================== 变量:警报条件 ====================
upper_wick_alert = false
lower_wick_alert = false
upper_alert_message = ""
lower_alert_message = ""
wick_flat_alert = false // 插针走平警报
wick_flat_alert_message = ""
// 插针历史数据存储(用于走平检测)
// 使用多个独立数组存储,相同索引位置的元素组成一条完整记录
// 存储结构:[bar_index, close_price, open_price, is_upper_wick, upper_band, lower_band, high, low]
var array<float> wick_bar_index = array.new<float>()
var array<float> wick_close = array.new<float>()
var array<float> wick_open = array.new<float>()
var array<float> wick_is_upper = array.new<float>()
var array<float> wick_upper_band = array.new<float>()
var array<float> wick_lower_band = array.new<float>()
var array<float> wick_high = array.new<float>()
var array<float> wick_low = array.new<float>()
// 插针走平信号计数(用于控制显示频率)
var int wick_flat_upper_count = 0 // 上轨普通走平连续计数
var int wick_flat_lower_count = 0 // 下轨普通走平连续计数
var int wick_golden_flat_upper_count = 0 // 上轨黄金走平连续计数
var int wick_golden_flat_lower_count = 0 // 下轨黄金走平连续计数
var bool wick_flat_upper_last = false // 上次是否检测到上轨普通走平
var bool wick_flat_lower_last = false // 上次是否检测到下轨普通走平
var bool wick_golden_flat_upper_last = false // 上次是否检测到上轨黄金走平
var bool wick_golden_flat_lower_last = false // 上次是否检测到下轨黄金走平
var int wick_flat_upper_miss_count = 0 // 上轨普通走平连续未检测次数
var int wick_flat_lower_miss_count = 0 // 下轨普通走平连续未检测次数
var int wick_golden_flat_upper_miss_count = 0 // 上轨黄金走平连续未检测次数
var int wick_golden_flat_lower_miss_count = 0 // 下轨黄金走平连续未检测次数
// ==================== 主逻辑 ====================
label_size_value = get_label_size()
// 循环处理所有配置的时间框架 - 忠实记录每个插针,不合并不忽略
for i = 0 to array.size(timeframes_code) - 1
tf_code = array.get(timeframes_code, i)
tf_name = array.get(timeframes_name, i)
[upper_tf, lower_tf, basis_tf, high_tf, low_tf, close_tf, open_tf] = calc_bb(tf_code)
[upper_wick_tf, lower_wick_tf, uw_size_tf, lw_size_tf] = detect_wick(upper_tf, lower_tf, high_tf, low_tf, open_tf, close_tf)
// 上插针:直接显示和记录,不合并
if upper_wick_tf
if show_labels
label.new(bar_index, high_tf, tf_name + "↑", color=color_upper_wick, textcolor=color.white, style=label.style_label_down, size=label_size_value)
// 设置多时间框架警报
if enable_alert and alert_on_mtf
upper_wick_alert := true
if upper_alert_message == ""
upper_alert_message := tf_name + "@" + str.tostring(high_tf)
else
upper_alert_message := upper_alert_message + "," + tf_name + "@" + str.tostring(high_tf)
// 记录插针历史(用于走平检测)
if enable_wick_flat
array.push(wick_bar_index, bar_index)
array.push(wick_close, close_tf)
array.push(wick_open, open_tf)
array.push(wick_is_upper, 1.0) // 1表示上插针
array.push(wick_upper_band, upper_tf)
array.push(wick_lower_band, lower_tf)
array.push(wick_high, high_tf)
array.push(wick_low, low_tf)
// 下插针:直接显示和记录,不合并
if lower_wick_tf
if show_labels
label.new(bar_index, low_tf, tf_name + "↓", color=color_lower_wick, textcolor=color.white, style=label.style_label_up, size=label_size_value)
// 设置多时间框架警报
if enable_alert and alert_on_mtf
lower_wick_alert := true
if lower_alert_message == ""
lower_alert_message := tf_name + "@" + str.tostring(low_tf)
else
lower_alert_message := lower_alert_message + "," + tf_name + "@" + str.tostring(low_tf)
// 记录插针历史(用于走平检测)
if enable_wick_flat
array.push(wick_bar_index, bar_index)
array.push(wick_close, close_tf)
array.push(wick_open, open_tf)
array.push(wick_is_upper, 0.0) // 0表示下插针
array.push(wick_upper_band, upper_tf)
array.push(wick_lower_band, lower_tf)
array.push(wick_high, high_tf)
array.push(wick_low, low_tf)
// ==================== 可选:绘制布林带(当前时间框架) ====================
basis_current = ta.sma(close, bb_length)
dev_current = bb_mult * ta.stdev(close, bb_length)
upper_current = basis_current + dev_current
lower_current = basis_current - dev_current
plot(show_plots ? upper_current : na, "上轨", color=color.new(color.blue, 50), linewidth=1)
plot(show_plots ? lower_current : na, "下轨", color=color.new(color.blue, 50), linewidth=1)
plot(show_plots ? basis_current : na, "中轨", color=color.new(color.blue, 70), linewidth=1)
// ==================== 柱体边沿走平检测 ====================
// 初始化走平警报
wick_flat_alert := false
wick_flat_alert_message := ""
// 检查当前交易品种是否为ETH
is_eth = str.contains(syminfo.ticker, "ETH") or str.contains(syminfo.ticker, "eth")
if enable_wick_flat and is_eth
// 计算当前布林带轨距
band_distance = upper_current - lower_current
upper_threshold = band_distance * band_distance_threshold
lower_threshold = band_distance * band_distance_threshold
// 检查最近N根连续K线的柱体边沿是否走平
// 上轨走平:检查柱体上边沿(阳线=收盘价,阴线=开盘价)
// 下轨走平:检查柱体下边沿(阳线=开盘价,阴线=收盘价)
// 收集最近N根连续K线的柱体边沿价格
// 普通走平:所有K线的边沿价格
upper_edge_list_all = array.new<float>()
lower_edge_list_all = array.new<float>()
// 黄金走平:在轨附近的K线边沿价格
upper_edge_list_golden = array.new<float>()
lower_edge_list_golden = array.new<float>()
// 检查最近N根连续K线(包括当前K线)
for i = 0 to wick_flat_count - 1
if bar_index >= i
// 获取历史K线的数据
hist_close = close[i]
hist_open = open[i]
hist_high = high[i]
hist_low = low[i]
// 计算该K线时的布林带(使用历史数据)
hist_basis = ta.sma(close[i], bb_length)
hist_dev = bb_mult * ta.stdev(close[i], bb_length)
hist_upper = hist_basis + hist_dev
hist_lower = hist_basis - hist_dev
hist_band_distance = hist_upper - hist_lower
hist_upper_threshold = hist_band_distance * band_distance_threshold
hist_lower_threshold = hist_band_distance * band_distance_threshold
// 判断K线是阳线还是阴线
is_bullish = hist_close >= hist_open
// 计算柱体边沿价格(用于普通走平检测)
upper_edge_price = is_bullish ? hist_close : hist_open
lower_edge_price = is_bullish ? hist_open : hist_close
// 普通走平:存储所有K线的边沿价格
array.push(upper_edge_list_all, upper_edge_price)
array.push(lower_edge_list_all, lower_edge_price)
// 检查是否在上轨附近(15%范围内)- 用于黄金走平
distance_from_upper = hist_high - hist_upper
if distance_from_upper >= -hist_upper_threshold and distance_from_upper <= hist_upper_threshold
// 黄金走平:存储柱体上边沿
array.push(upper_edge_list_golden, upper_edge_price)
// 检查是否在下轨附近(15%范围内)- 用于黄金走平
distance_from_lower = hist_lower - hist_low
if distance_from_lower >= -hist_lower_threshold and distance_from_lower <= hist_lower_threshold
// 黄金走平:存储柱体下边沿
array.push(lower_edge_list_golden, lower_edge_price)
// 检测上轨走平(检查连续N根K线的柱体上边沿是否走平)
upper_flat_detected = false // 普通走平
upper_golden_flat_detected = false // 黄金走平
// 普通走平检测:检查所有K线的边沿价格
if array.size(upper_edge_list_all) >= wick_flat_count
max_edge = array.get(upper_edge_list_all, 0)
min_edge = array.get(upper_edge_list_all, 0)
for k = 0 to array.size(upper_edge_list_all) - 1
edge_price = array.get(upper_edge_list_all, k)
max_edge := math.max(max_edge, edge_price)
min_edge := math.min(min_edge, edge_price)
price_diff = max_edge - min_edge
if price_diff <= wick_flat_price_tolerance
upper_flat_detected := true
// 黄金走平检测:检查在轨附近的K线边沿价格
if array.size(upper_edge_list_golden) >= wick_flat_count
max_edge = array.get(upper_edge_list_golden, 0)
min_edge = array.get(upper_edge_list_golden, 0)
for k = 0 to array.size(upper_edge_list_golden) - 1
edge_price = array.get(upper_edge_list_golden, k)
max_edge := math.max(max_edge, edge_price)
min_edge := math.min(min_edge, edge_price)
price_diff = max_edge - min_edge
if price_diff <= wick_flat_price_tolerance
upper_golden_flat_detected := true
// 检测下轨走平(检查连续N根K线的柱体下边沿是否走平)
lower_flat_detected = false // 普通走平
lower_golden_flat_detected = false // 黄金走平
// 普通走平检测:检查所有K线的边沿价格
if array.size(lower_edge_list_all) >= wick_flat_count
max_edge = array.get(lower_edge_list_all, 0)
min_edge = array.get(lower_edge_list_all, 0)
for k = 0 to array.size(lower_edge_list_all) - 1
edge_price = array.get(lower_edge_list_all, k)
max_edge := math.max(max_edge, edge_price)
min_edge := math.min(min_edge, edge_price)
price_diff = max_edge - min_edge
if price_diff <= wick_flat_price_tolerance
lower_flat_detected := true
// 黄金走平检测:检查在轨附近的K线边沿价格
if array.size(lower_edge_list_golden) >= wick_flat_count
max_edge = array.get(lower_edge_list_golden, 0)
min_edge = array.get(lower_edge_list_golden, 0)
for k = 0 to array.size(lower_edge_list_golden) - 1
edge_price = array.get(lower_edge_list_golden, k)
max_edge := math.max(max_edge, edge_price)
min_edge := math.min(min_edge, edge_price)
price_diff = max_edge - min_edge
if price_diff <= wick_flat_price_tolerance
lower_golden_flat_detected := true
// 上轨普通走平计数逻辑
if upper_flat_detected
if wick_flat_upper_last
wick_flat_upper_count := wick_flat_upper_count + 1
else
wick_flat_upper_count := 1
wick_flat_upper_last := true
wick_flat_upper_miss_count := 0
else
if wick_flat_upper_last
wick_flat_upper_miss_count := wick_flat_upper_miss_count + 1
if wick_flat_upper_miss_count >= 2
wick_flat_upper_count := 0
wick_flat_upper_miss_count := 0
else
wick_flat_upper_miss_count := wick_flat_upper_miss_count + 1
if wick_flat_upper_miss_count >= 2
wick_flat_upper_count := 0
wick_flat_upper_miss_count := 0
wick_flat_upper_last := false
// 上轨黄金走平计数逻辑
if upper_golden_flat_detected
if wick_golden_flat_upper_last
wick_golden_flat_upper_count := wick_golden_flat_upper_count + 1
else
wick_golden_flat_upper_count := 1
wick_golden_flat_upper_last := true
wick_golden_flat_upper_miss_count := 0
else
if wick_golden_flat_upper_last
wick_golden_flat_upper_miss_count := wick_golden_flat_upper_miss_count + 1
if wick_golden_flat_upper_miss_count >= 2
wick_golden_flat_upper_count := 0
wick_golden_flat_upper_miss_count := 0
else
wick_golden_flat_upper_miss_count := wick_golden_flat_upper_miss_count + 1
if wick_golden_flat_upper_miss_count >= 2
wick_golden_flat_upper_count := 0
wick_golden_flat_upper_miss_count := 0
wick_golden_flat_upper_last := false
// 下轨普通走平计数逻辑
if lower_flat_detected
if wick_flat_lower_last
wick_flat_lower_count := wick_flat_lower_count + 1
else
wick_flat_lower_count := 1
wick_flat_lower_last := true
wick_flat_lower_miss_count := 0
else
if wick_flat_lower_last
wick_flat_lower_miss_count := wick_flat_lower_miss_count + 1
if wick_flat_lower_miss_count >= 2
wick_flat_lower_count := 0
wick_flat_lower_miss_count := 0
else
wick_flat_lower_miss_count := wick_flat_lower_miss_count + 1
if wick_flat_lower_miss_count >= 2
wick_flat_lower_count := 0
wick_flat_lower_miss_count := 0
wick_flat_lower_last := false
// 下轨黄金走平计数逻辑
if lower_golden_flat_detected
if wick_golden_flat_lower_last
wick_golden_flat_lower_count := wick_golden_flat_lower_count + 1
else
wick_golden_flat_lower_count := 1
wick_golden_flat_lower_last := true
wick_golden_flat_lower_miss_count := 0
else
if wick_golden_flat_lower_last
wick_golden_flat_lower_miss_count := wick_golden_flat_lower_miss_count + 1
if wick_golden_flat_lower_miss_count >= 2
wick_golden_flat_lower_count := 0
wick_golden_flat_lower_miss_count := 0
else
wick_golden_flat_lower_miss_count := wick_golden_flat_lower_miss_count + 1
if wick_golden_flat_lower_miss_count >= 2
wick_golden_flat_lower_count := 0
wick_golden_flat_lower_miss_count := 0
wick_golden_flat_lower_last := false
// 只有当连续检测到3次走平信号时才显示和报警(每3次显示一次,显示后重置计数)
upper_flat_should_show = wick_flat_upper_count > 0 and wick_flat_upper_count % 3 == 0
lower_flat_should_show = wick_flat_lower_count > 0 and wick_flat_lower_count % 3 == 0
upper_golden_flat_should_show = wick_golden_flat_upper_count > 0 and wick_golden_flat_upper_count % 3 == 0
lower_golden_flat_should_show = wick_golden_flat_lower_count > 0 and wick_golden_flat_lower_count % 3 == 0
// 合并走平信号(优先显示黄金走平)
if upper_golden_flat_should_show or lower_golden_flat_should_show or upper_flat_should_show or lower_flat_should_show
wick_flat_alert := true
message_parts = array.new<string>()
if upper_golden_flat_should_show
array.push(message_parts, "上轨黄金走平(" + str.tostring(array.size(upper_edge_list_golden)) + "个)")
else if upper_flat_should_show
array.push(message_parts, "上轨走平(" + str.tostring(array.size(upper_edge_list_all)) + "个)")
if lower_golden_flat_should_show
array.push(message_parts, "下轨黄金走平(" + str.tostring(array.size(lower_edge_list_golden)) + "个)")
else if lower_flat_should_show
array.push(message_parts, "下轨走平(" + str.tostring(array.size(lower_edge_list_all)) + "个)")
wick_flat_alert_message := ""
for j = 0 to array.size(message_parts) - 1
if wick_flat_alert_message == ""
wick_flat_alert_message := array.get(message_parts, j)
else
wick_flat_alert_message := wick_flat_alert_message + "," + array.get(message_parts, j)
if show_labels
if upper_golden_flat_should_show
label.new(bar_index, upper_current, "上轨黄金走平", color=color.new(color.yellow, 0), textcolor=color.black, style=label.style_label_down, size=label_size_value)
else if upper_flat_should_show
label.new(bar_index, upper_current, "上轨走平", color=color.new(color.purple, 0), textcolor=color.white, style=label.style_label_down, size=label_size_value)
if lower_golden_flat_should_show
label.new(bar_index, lower_current, "下轨黄金走平", color=color.new(color.yellow, 0), textcolor=color.black, style=label.style_label_up, size=label_size_value)
else if lower_flat_should_show
label.new(bar_index, lower_current, "下轨走平", color=color.new(color.purple, 0), textcolor=color.white, style=label.style_label_up, size=label_size_value)
// 显示后重置计数,实现真正的"三次显示一次"
if upper_golden_flat_should_show
wick_golden_flat_upper_count := 0
if upper_flat_should_show
wick_flat_upper_count := 0
if lower_golden_flat_should_show
wick_golden_flat_lower_count := 0
if lower_flat_should_show
wick_flat_lower_count := 0
// ==================== 实时检测当前K线的插针 ====================
if barstate.islast
// 使用当前图表的数据计算布林带
basis_realtime = ta.sma(close, bb_length)
dev_realtime = bb_mult * ta.stdev(close, bb_length)
upper_realtime = basis_realtime + dev_realtime
lower_realtime = basis_realtime - dev_realtime
[upper_wick_realtime, lower_wick_realtime, uw_size_realtime, lw_size_realtime] = detect_wick(upper_realtime, lower_realtime, high, low, open, close)
// 实时标记(仅在最后一根K线)
if upper_wick_realtime and show_labels
label.new(bar_index, high, "实时↑", color=color.new(color.red, 20), textcolor=color.white, style=label.style_label_down, size=size.normal)
if lower_wick_realtime and show_labels
label.new(bar_index, low, "实时↓", color=color.new(color.green, 20), textcolor=color.white, style=label.style_label_up, size=size.normal)
// 设置实时警报
if enable_alert and alert_on_realtime
if upper_wick_realtime
upper_wick_alert := true
upper_alert_message := "实时@" + str.tostring(high) + " 价格:" + str.tostring(close)
if lower_wick_realtime
lower_wick_alert := true
lower_alert_message := "实时@" + str.tostring(low) + " 价格:" + str.tostring(close)
// ==================== 警报条件定义 ====================
// 创建用于警报的plot(隐藏显示)
plot_upper_alert = plot(upper_wick_alert ? 1 : 0, title="上插针警报", display=display.none)
plot_lower_alert = plot(lower_wick_alert ? 1 : 0, title="下插针警报", display=display.none)
plot_wick_flat_alert = plot(wick_flat_alert ? 1 : 0, title="插针走平警报", display=display.none)
// 上插针警报条件 - 只能使用 TradingView 模板变量,不能使用字符串连接
alertcondition(upper_wick_alert, title="上插针预警", message="{{ticker}} 上插针 | 时间:{{time}} | 价格:{{close}} | 最高:{{high}} | 最低:{{low}}")
// 下插针警报条件 - 只能使用 TradingView 模板变量,不能使用字符串连接
alertcondition(lower_wick_alert, title="下插针预警", message="{{ticker}} 下插针 | 时间:{{time}} | 价格:{{close}} | 最高:{{high}} | 最低:{{low}}")
// 插针走平警报条件(合并上插针走平和下插针走平)
alertcondition(wick_flat_alert, title="插针走平预警", message="{{ticker}} | wick_flat_alert | {{time}} | {{close}} | {{high}} | {{low}} ; 插针走平")
Show all 475 lines