tradingview-bollinger-wicks.pine
Copy
//@version=6
indicator("布林带插针标记 - 多时间框架", shorttitle="BB-Wicks", 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="警报设置")
// RSI 预警设置
enable_rsi_alert = input.bool(true, title="启用RSI预警", tooltip="启用RSI超买超卖预警", group="RSI预警设置")
rsi_length = input.int(14, title="RSI周期", minval=1, group="RSI预警设置")
rsi_overbought = input.int(70, title="超买阈值", minval=50, maxval=100, group="RSI预警设置")
rsi_oversold = input.int(30, title="超卖阈值", minval=0, maxval=50, group="RSI预警设置")
// RSI 背离设置
enable_rsi_divergence = input.bool(true, title="启用RSI背离预警", tooltip="检测价格与RSI的背离信号", group="RSI背离设置")
divergence_lookback = input.int(9, title="历史K线周期", minval=5, maxval=50, tooltip="用于计算平均K线长度的历史周期", group="RSI背离设置")
// 连续插针检测设置
enable_continuous_wick = input.bool(true, title="启用连续插针预警", tooltip="检测上下轨附近连续插针信号", group="连续插针设置")
continuous_wick_count = input.int(2, title="插针数量", minval=2, maxval=5, tooltip="触发信号所需的连续插针数量", group="连续插针设置")
// K线形态设置(射击之星、吞没)
enable_candle_pattern = input.bool(true, title="启用K线形态预警", tooltip="检测射击之星和吞没形态", group="K线形态设置")
enable_shooting_star = input.bool(true, title="射击之星", tooltip="上影线长、实体小、下影线短,看跌信号", group="K线形态设置")
enable_engulfing = input.bool(true, title="吞没形态", tooltip="当前K线实体完全包住前一根K线实体", group="K线形态设置")
pattern_upper_wick_ratio = input.float(2.0, title="射击之星上影线/实体比", minval=1.5, maxval=5.0, step=0.1, tooltip="上影线至少为实体的倍数", group="K线形态设置")
// 定时日线推送设置
enable_daily_line_push = input.bool(true, title="启用定时日线推送", tooltip="按设定时间推送指定交易对的日线数据", group="定时日线推送")
daily_push_symbol = input.symbol("BINANCE:BTCUSDT", title="推送交易对", group="定时日线推送")
daily_push_hour = input.int(8, title="推送小时(北京时间)", minval=0, maxval=23, group="定时日线推送")
daily_push_minute = input.int(1, title="推送分钟(北京时间)", minval=0, maxval=59, 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]
// ==================== 函数:检测射击之星 ====================
// 射击之星:上影线长、实体小,下方可有插针但下影线长度 < 上影线1/3
detect_shooting_star(h_price, l_price, o_price, c_price, ratio) =>
body = math.abs(c_price - o_price)
upper_w = h_price - math.max(o_price, c_price)
lower_w = math.min(o_price, c_price) - l_price
// 实体不能为0,上影线>=实体*ratio,下影线 < 上影线/3(允许有下影线但须远短于上影线)
body > 0 and upper_w >= body * ratio and lower_w < upper_w / 3
// ==================== 函数:检测倒锤子(射击之星倒过来的形态)====================
// 倒锤子:下影线长、实体小,上方可有插针但上影线 < 下影线1/3,看涨 reversal 信号
detect_inverted_shooting_star(h_price, l_price, o_price, c_price, ratio) =>
body = math.abs(c_price - o_price)
upper_w = h_price - math.max(o_price, c_price)
lower_w = math.min(o_price, c_price) - l_price
// 实体不能为0,下影线>=实体*ratio,上影线 < 下影线/3(允许有上影线但须远短于下影线)
body > 0 and lower_w >= body * ratio and upper_w < lower_w / 3
// ==================== 函数:检测吞没形态 ====================
// 看涨吞没:当前阳线实体包住前一根阴线实体
// 看跌吞没:当前阴线实体包住前一根阳线实体
detect_bullish_engulfing(o1, c1, o0, c0) =>
prev_bearish = c1 < o1 // 前一根阴线
curr_bullish = c0 > o0 // 当前阳线
curr_open_below_prev_close = o0 <= c1
curr_close_above_prev_open = c0 >= o1
prev_bearish and curr_bullish and curr_open_below_prev_close and curr_close_above_prev_open
detect_bearish_engulfing(o1, c1, o0, c0) =>
prev_bullish = c1 > o1 // 前一根阳线
curr_bearish = c0 < o0 // 当前阴线
curr_open_above_prev_close = o0 >= c1
curr_close_below_prev_open = c0 <= o1
prev_bullish and curr_bearish and curr_open_above_prev_close and curr_close_below_prev_open
// ==================== 辅助函数:获取标签大小 ====================
get_label_size() =>
switch label_size
"tiny" => size.tiny
"small" => size.small
"normal" => size.normal
"large" => size.large
"huge" => size.huge
=> size.small
// ==================== 变量:警报条件(合并后)====================
wick_alert = false // 合并的上插针和下插针警报
rsi_alert = false // 合并的RSI超买超卖警报
rsi_divergence_alert = false // 合并的RSI背离警报
continuous_wick_alert = false // 连续插针警报
candle_pattern_alert = false // K线形态警报(射击之星、吞没)
// 警报消息
wick_alert_message = ""
rsi_alert_message = ""
rsi_divergence_alert_message = ""
continuous_wick_alert_message = ""
candle_pattern_alert_message = ""
// 连续插针计数(用于检测连续插针信号)
var int continuous_upper_wick_counter = 0 // 连续上插针计数(上下轨附近)
var int continuous_lower_wick_counter = 0 // 连续下插针计数(上下轨附近)
// 非上下轨附近连续插针检测(用于存储历史插针位置,允许跳过最多2根K线)
var array<int> non_zone_upper_wick_bars = array.new<int>() // 非上轨附近的上插针K线索引
var array<int> non_zone_lower_wick_bars = array.new<int>() // 非下轨附近的下插针K线索引
// ==================== 主逻辑 ====================
label_size_value = get_label_size()
// 初始化警报变量
wick_alert := false
wick_alert_message := ""
// 循环处理所有配置的时间框架 - 忠实记录每个插针,不合并不忽略
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
wick_alert := true
if wick_alert_message == ""
wick_alert_message := "上插针:" + tf_name + "@" + str.tostring(high_tf)
else
wick_alert_message := wick_alert_message + ",上插针:" + tf_name + "@" + str.tostring(high_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
wick_alert := true
if wick_alert_message == ""
wick_alert_message := "下插针:" + tf_name + "@" + str.tostring(low_tf)
else
wick_alert_message := wick_alert_message + ",下插针:" + tf_name + "@" + str.tostring(low_tf)
// ==================== RSI 计算和预警(合并)====================
rsi_value = ta.rsi(close, rsi_length)
// 初始化RSI警报
rsi_alert := false
rsi_alert_message := ""
// 检测 RSI 超买超卖条件(合并)
if enable_rsi_alert
rsi_overbought_trigger = rsi_value > rsi_overbought and rsi_value[1] <= rsi_overbought
rsi_oversold_trigger = rsi_value < rsi_oversold and rsi_value[1] >= rsi_oversold
if rsi_overbought_trigger
rsi_alert := true
rsi_alert_message := "RSI超买:" + str.tostring(rsi_value, "#.##")
else if rsi_oversold_trigger
rsi_alert := true
rsi_alert_message := "RSI超卖:" + str.tostring(rsi_value, "#.##")
// ==================== 可选:绘制布林带(当前时间框架) ====================
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
// ==================== RSI 背离检测(实时,合并)====================
// 初始化背离警报
rsi_divergence_alert := false
rsi_divergence_alert_message := ""
if enable_rsi_divergence and not na(rsi_value)
// 计算历史9根K线的平均长度(实体长度:|close - open|)
avg_candle_length = 0.0
for i = 1 to divergence_lookback
candle_length = math.abs(close[i] - open[i])
avg_candle_length := avg_candle_length + candle_length
avg_candle_length := avg_candle_length / divergence_lookback
// 当前K线的长度
current_candle_length = math.abs(close - open)
// 价格变化阈值:平均长度的一半
price_threshold = avg_candle_length / 2.0
// 查找左侧历史最高价和对应的RSI(不包括当前K线)
// 至少相隔3根K线,所以需要检查索引
min_distance = 3 // 至少相隔3根K线
highest_price = ta.highest(high[1], divergence_lookback)
highest_price_idx = ta.highestbars(high[1], divergence_lookback)
// highest_price_idx 是负数,表示距离 high[1] 的偏移量
// 例如:highest_price_idx = -1 表示在 high[2],距离当前K线2根(不满足)
// highest_price_idx = -2 表示在 high[3],距离当前K线3根(满足)
// highest_price_idx = -3 表示在 high[4],距离当前K线4根(满足)
// 需要确保至少相隔3根K线,即 highest_price_idx <= -min_distance
// 计算实际索引:如果 highest_price_idx = -2,实际位置是 high[1-2] = high[-1],但这是无效的
// 正确理解:highest_price_idx = -2 表示在 high[1+(-2)] = high[-1],但应该理解为 high[3]
// 实际上:highest_price_idx = -2 时,最高价在 high[3],对应的RSI是 rsi_value[3]
// 所以:actual_idx = 1 + highest_price_idx + 1 = 2 + highest_price_idx
// 但更简单:如果 highest_price_idx = -2,那么实际K线位置是 1 + (-2) + 1 = 0,不对
// 重新理解:highest_price_idx 是相对于 high[1] 的偏移,所以实际位置是 1 + highest_price_idx
// 如果 highest_price_idx = -2,实际位置是 1 + (-2) = -1,这是无效的
// 实际上 ta.highestbars 返回的索引:-1 表示在 high[2],-2 表示在 high[3]
// 所以实际K线位置是 1 + abs(highest_price_idx) = 1 + 2 = 3
// 对应的RSI是 rsi_value[3]
actual_highest_bar = 1 + math.abs(highest_price_idx)
highest_rsi = (highest_price_idx <= -min_distance and actual_highest_bar <= divergence_lookback) ? rsi_value[actual_highest_bar] : na
// 查找左侧历史最低价和对应的RSI(不包括当前K线)
lowest_price = ta.lowest(low[1], divergence_lookback)
lowest_price_idx = ta.lowestbars(low[1], divergence_lookback)
// lowest_price_idx 是负数,表示距离 low[1] 的偏移量
// 需要确保至少相隔3根K线,即 lowest_price_idx <= -min_distance
actual_lowest_bar = 1 + math.abs(lowest_price_idx)
lowest_rsi = (lowest_price_idx <= -min_distance and actual_lowest_bar <= divergence_lookback) ? rsi_value[actual_lowest_bar] : na
// 实时检测看跌背离:价格创新高但RSI未创新高
bearish_divergence = false
if not na(highest_price) and not na(highest_rsi)
price_new_high = high > highest_price
rsi_not_new_high = rsi_value < highest_rsi
// 条件:价格创新高 + RSI未创新高 + 当前K线长度 > 平均长度的一半 + 至少相隔3根K线
if price_new_high and rsi_not_new_high and current_candle_length > price_threshold
bearish_divergence := true
rsi_divergence_alert := true
rsi_divergence_alert_message := "看跌背离"
if show_labels
label.new(bar_index, high, "看跌背离", color=color.new(color.orange, 0), textcolor=color.white, style=label.style_label_down, size=label_size_value)
// 实时检测看涨背离:价格创新低但RSI未创新低
bullish_divergence = false
if not na(lowest_price) and not na(lowest_rsi)
price_new_low = low < lowest_price
rsi_not_new_low = rsi_value > lowest_rsi
// 条件:价格创新低 + RSI未创新低 + 当前K线长度 > 平均长度的一半 + 至少相隔3根K线
if price_new_low and rsi_not_new_low and current_candle_length > price_threshold
bullish_divergence := true
rsi_divergence_alert := true
if rsi_divergence_alert_message == ""
rsi_divergence_alert_message := "看涨背离"
else
rsi_divergence_alert_message := rsi_divergence_alert_message + ",看涨背离"
if show_labels
label.new(bar_index, low, "看涨背离", color=color.new(color.aqua, 0), textcolor=color.white, style=label.style_label_up, size=label_size_value)
// ==================== 绘制布林带(当前时间框架) ====================
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)
// ==================== 连续插针检测(上下轨附近)====================
// 初始化连续插针警报
continuous_wick_alert := false
continuous_wick_alert_message := ""
if enable_continuous_wick
// 使用当前时间框架的数据
body_size = math.abs(close - open)
// 检测当前K线是否有插针(使用detect_wick函数)
[upper_wick_current, lower_wick_current, uw_size_current, lw_size_current] = detect_wick(upper_current, lower_current, high, low, open, close)
// ========== 上轨附近连续插针检测 ==========
// 计算中轨到上轨的距离
mid_to_upper = upper_current - basis_current
// 85%位置:中轨 + 85% * (上轨 - 中轨)
upper_zone_threshold = basis_current + mid_to_upper * 0.85
// 检查当前K线是否在上轨附近(85%以上)
is_in_upper_zone = high >= upper_zone_threshold
// 计算上影线大小
upper_wick_size = high - math.max(open, close)
// 检测有效上插针:在上轨附近且插针大于实体
valid_upper_wick = is_in_upper_zone and upper_wick_size > body_size and upper_wick_size > 0
// 连续上插针计数逻辑(上下轨附近:2次即可报警)
if valid_upper_wick
// 检测到有效上插针,增加计数
continuous_upper_wick_counter := continuous_upper_wick_counter + 1
// 如果达到指定数量,触发信号
if continuous_upper_wick_counter == continuous_wick_count
continuous_wick_alert := true
if continuous_wick_alert_message == ""
continuous_wick_alert_message := "连续上插针(" + str.tostring(continuous_upper_wick_counter) + "次)"
else
continuous_wick_alert_message := continuous_wick_alert_message + ",连续上插针(" + str.tostring(continuous_upper_wick_counter) + "次)"
if show_labels
label.new(bar_index, high, "连续上插针", color=color.new(color.purple, 0), textcolor=color.white, style=label.style_label_down, size=label_size_value)
else
// 没有检测到上插针,清空计数
continuous_upper_wick_counter := 0
// ========== 非上轨附近连续插针检测(允许跳过最多2根K线,需要3根以上插针)==========
// 检测非上轨附近的上插针:突破上轨但不在85%区域
non_zone_upper_wick = upper_wick_current and not is_in_upper_zone
if non_zone_upper_wick
// 记录当前K线索引
array.push(non_zone_upper_wick_bars, bar_index)
// 只保留最近20根K线的记录(避免数组过大)
if array.size(non_zone_upper_wick_bars) > 20
array.shift(non_zone_upper_wick_bars)
// 检查是否有连续插针(允许跳过最多2根K线,需要至少3根插针)
if array.size(non_zone_upper_wick_bars) >= 3
// 从最新的插针开始向前检查
recent_count = 1 // 当前K线已经有插针
last_wick_bar = bar_index
gap_allowed = 2 // 允许跳过的K线数量
// 检查最近的插针序列(从倒数第二个开始向前检查)
for i = array.size(non_zone_upper_wick_bars) - 2 to 0
wick_bar = array.get(non_zone_upper_wick_bars, i)
// 检查与前一个插针的间隔
gap = last_wick_bar - wick_bar - 1
if gap <= gap_allowed
recent_count := recent_count + 1
last_wick_bar := wick_bar
else
// 间隔太大,停止检查
break
// 如果找到至少3根连续插针(允许跳过最多2根K线),触发信号
if recent_count >= 3
continuous_wick_alert := true
if continuous_wick_alert_message == ""
continuous_wick_alert_message := "非上轨连续上插针(" + str.tostring(recent_count) + "次)"
else
continuous_wick_alert_message := continuous_wick_alert_message + ",非上轨连续上插针(" + str.tostring(recent_count) + "次)"
if show_labels
label.new(bar_index, high, "非上轨连续上插针", color=color.new(color.purple, 0), textcolor=color.white, style=label.style_label_down, size=label_size_value)
// ========== 下轨附近连续插针检测 ==========
// 计算中轨到下轨的距离
mid_to_lower = basis_current - lower_current
// 15%位置:下轨 + 15% * (中轨 - 下轨) = 下轨 + 0.15 * (中轨 - 下轨)
lower_zone_threshold = lower_current + mid_to_lower * 0.15
// 检查当前K线是否在下轨附近(15%以内)
is_in_lower_zone = low <= lower_zone_threshold
// 计算下影线大小
lower_wick_size = math.min(open, close) - low
// 检测有效下插针:在下轨附近且插针大于实体
valid_lower_wick = is_in_lower_zone and lower_wick_size > body_size and lower_wick_size > 0
// 连续下插针计数逻辑(上下轨附近:2次即可报警)
if valid_lower_wick
// 检测到有效下插针,增加计数
continuous_lower_wick_counter := continuous_lower_wick_counter + 1
// 如果达到指定数量,触发信号
if continuous_lower_wick_counter == continuous_wick_count
continuous_wick_alert := true
if continuous_wick_alert_message == ""
continuous_wick_alert_message := "连续下插针(" + str.tostring(continuous_lower_wick_counter) + "次)"
else
continuous_wick_alert_message := continuous_wick_alert_message + ",连续下插针(" + str.tostring(continuous_lower_wick_counter) + "次)"
if show_labels
label.new(bar_index, low, "连续下插针", color=color.new(color.purple, 0), textcolor=color.white, style=label.style_label_up, size=label_size_value)
else
// 没有检测到下插针,清空计数
continuous_lower_wick_counter := 0
// ========== 非下轨附近连续插针检测(允许跳过最多2根K线,需要3根以上插针)==========
// 检测非下轨附近的下插针:突破下轨但不在15%区域
non_zone_lower_wick = lower_wick_current and not is_in_lower_zone
if non_zone_lower_wick
// 记录当前K线索引
array.push(non_zone_lower_wick_bars, bar_index)
// 只保留最近20根K线的记录(避免数组过大)
if array.size(non_zone_lower_wick_bars) > 20
array.shift(non_zone_lower_wick_bars)
// 检查是否有连续插针(允许跳过最多2根K线,需要至少3根插针)
if array.size(non_zone_lower_wick_bars) >= 3
// 从最新的插针开始向前检查
recent_count = 1 // 当前K线已经有插针
last_wick_bar = bar_index
gap_allowed = 2 // 允许跳过的K线数量
// 检查最近的插针序列(从倒数第二个开始向前检查)
for i = array.size(non_zone_lower_wick_bars) - 2 to 0
wick_bar = array.get(non_zone_lower_wick_bars, i)
// 检查与前一个插针的间隔
gap = last_wick_bar - wick_bar - 1
if gap <= gap_allowed
recent_count := recent_count + 1
last_wick_bar := wick_bar
else
// 间隔太大,停止检查
break
// 如果找到至少3根连续插针(允许跳过最多2根K线),触发信号
if recent_count >= 3
continuous_wick_alert := true
if continuous_wick_alert_message == ""
continuous_wick_alert_message := "非下轨连续下插针(" + str.tostring(recent_count) + "次)"
else
continuous_wick_alert_message := continuous_wick_alert_message + ",非下轨连续下插针(" + str.tostring(recent_count) + "次)"
if show_labels
label.new(bar_index, low, "非下轨连续下插针", color=color.new(color.purple, 0), textcolor=color.white, style=label.style_label_up, size=label_size_value)
// ==================== K线形态检测(射击之星、吞没,需在布林带上下轨附近)====================
candle_pattern_alert := false
candle_pattern_alert_message := ""
if enable_candle_pattern
// 布林带位置判断:上轨附近(85%以上)、下轨附近(15%以内)
mid_to_upper_bb = upper_current - basis_current
upper_zone_bb = basis_current + mid_to_upper_bb * 0.85
mid_to_lower_bb = basis_current - lower_current
lower_zone_bb = lower_current + mid_to_lower_bb * 0.15
at_upper_band = high >= upper_zone_bb
at_lower_band = low <= lower_zone_bb
above_mid_band = high >= basis_current // 中轨之上
below_mid_band = low <= basis_current // 中轨之下
// 射击之星:看跌信号,需在中轨之上
if enable_shooting_star and above_mid_band and detect_shooting_star(high, low, open, close, pattern_upper_wick_ratio)
candle_pattern_alert := true
candle_pattern_alert_message := "射击之星"
if show_labels
label.new(bar_index, high, "射击之星", color=color.new(color.orange, 0), textcolor=color.white, style=label.style_label_down, size=label_size_value)
// 倒锤子:看涨信号,射击之星倒过来的形态,需在中轨之下,上影线 < 下影线/3
if enable_shooting_star and below_mid_band and detect_inverted_shooting_star(high, low, open, close, pattern_upper_wick_ratio)
candle_pattern_alert := true
if candle_pattern_alert_message != ""
candle_pattern_alert_message := candle_pattern_alert_message + ",倒锤子"
else
candle_pattern_alert_message := "倒锤子"
if show_labels
label.new(bar_index, low, "倒锤子", color=color.new(color.aqua, 0), textcolor=color.white, style=label.style_label_up, size=label_size_value)
// 看涨吞没:当前阳线包住前一根阴线,需在布林带下轨附近
if enable_engulfing and at_lower_band and detect_bullish_engulfing(open[1], close[1], open, close)
candle_pattern_alert := true
if candle_pattern_alert_message != ""
candle_pattern_alert_message := candle_pattern_alert_message + ",看涨吞没"
else
candle_pattern_alert_message := "看涨吞没"
if show_labels
label.new(bar_index, low, "看涨吞没", color=color.new(color.green, 0), textcolor=color.white, style=label.style_label_up, size=label_size_value)
// 看跌吞没:当前阴线包住前一根阳线,需在布林带上轨附近
if enable_engulfing and at_upper_band and detect_bearish_engulfing(open[1], close[1], open, close)
candle_pattern_alert := true
if candle_pattern_alert_message != ""
candle_pattern_alert_message := candle_pattern_alert_message + ",看跌吞没"
else
candle_pattern_alert_message := "看跌吞没"
if show_labels
label.new(bar_index, high, "看跌吞没", color=color.new(color.red, 0), textcolor=color.white, style=label.style_label_down, size=label_size_value)
// ==================== 实时检测当前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 or lower_wick_realtime
wick_alert := true
if upper_wick_realtime and lower_wick_realtime
wick_alert_message := "实时上插针@" + str.tostring(high) + ",下插针@" + str.tostring(low) + " 价格:" + str.tostring(close)
else if upper_wick_realtime
wick_alert_message := "实时上插针@" + str.tostring(high) + " 价格:" + str.tostring(close)
else
wick_alert_message := "实时下插针@" + str.tostring(low) + " 价格:" + str.tostring(close)
// ==================== 警报:动态消息携带信号类型(合并为单一日志)====================
// 格式:ticker | 信号中文描述 | time | close | high | low ; 触发信号【周期】
// 插针:仅连续插针预警;K线形态:具体到射击之星/倒锤子/看涨吞没/看跌吞没
get_alert_time() =>
str.format("{0,date,yyyy-MM-dd'T'HH:mm:ss'Z'}", time)
// 当前图表周期转可读标签(兜底不为空)
get_timeframe_label() =>
tf = timeframe.period
// 分钟周期:1/3/5/15/30/45/60/120/240...
n = str.tonumber(tf)
if not na(n)
n >= 60 and n % 60 == 0 ? str.tostring(int(n / 60)) + "h" : str.tostring(int(n)) + "m"
else
// 非分钟周期:D/W/M 等
tf == "D" or tf == "1D" ? "1D" :
tf == "W" or tf == "1W" ? "1W" :
tf == "M" or tf == "1M" ? "1M" :
(str.length(tf) > 0 ? tf : "unknown_tf")
// 生成北京时间日期键,用于限制每天只推送一次
get_bj_day_key() =>
y = year(time, "Asia/Shanghai")
m = month(time, "Asia/Shanghai")
d = dayofmonth(time, "Asia/Shanghai")
y * 10000 + m * 100 + d
// 记录最近一次日线推送日期
var int last_daily_line_push_day = na
// 定时推送指定交易对上一根完整日线(北京时间)
if enable_daily_line_push and barstate.isconfirmed
is_push_time = hour(time, "Asia/Shanghai") == daily_push_hour and minute(time, "Asia/Shanghai") == daily_push_minute
day_key = get_bj_day_key()
if is_push_time and day_key != last_daily_line_push_day
d_open = request.security(daily_push_symbol, "1D", open[1], lookahead=barmerge.lookahead_off)
d_high = request.security(daily_push_symbol, "1D", high[1], lookahead=barmerge.lookahead_off)
d_low = request.security(daily_push_symbol, "1D", low[1], lookahead=barmerge.lookahead_off)
d_close = request.security(daily_push_symbol, "1D", close[1], lookahead=barmerge.lookahead_off)
d_time = request.security(daily_push_symbol, "1D", time[1], lookahead=barmerge.lookahead_off)
d_time_str = str.format("{0,date,yyyy-MM-dd}", d_time)
daily_msg = str.format("{0} | 日线播报 | {1} | O:{2} H:{3} L:{4} C:{5}; 定时推送", daily_push_symbol, d_time_str, str.tostring(d_open), str.tostring(d_high), str.tostring(d_low), str.tostring(d_close))
alert(daily_msg, alert.freq_once_per_bar_close)
last_daily_line_push_day := day_key
all_signals = continuous_wick_alert or rsi_alert or rsi_divergence_alert or candle_pattern_alert
if all_signals
sig_desc = ""
if continuous_wick_alert
sig_desc := continuous_wick_alert_message != "" ? continuous_wick_alert_message : "连续插针"
else if rsi_alert
sig_desc := rsi_alert_message != "" ? rsi_alert_message : "RSI超买超卖"
else if rsi_divergence_alert
sig_desc := rsi_divergence_alert_message != "" ? rsi_divergence_alert_message : "RSI背离"
else if candle_pattern_alert
sig_desc := candle_pattern_alert_message != "" ? candle_pattern_alert_message : "K线形态"
else
sig_desc := "未知信号"
t_str = get_alert_time()
tf_label = get_timeframe_label()
// 触发信号【15m】/【1h】/【4h】等,与警报所选周期一致
final_msg = str.format("{0} | {1} | {2} | {3} | {4} | {5} | {6}; 触发信号", syminfo.ticker, sig_desc, t_str, str.tostring(close), str.tostring(high), str.tostring(low), tf_label)
alert(final_msg, alert.freq_once_per_bar_close)
Show all 611 lines