// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © 缠论完整实现 - Pine Script v6 //@version=6 indicator("缠论完整版", shorttitle="缠论", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500, max_bars_back=5000) // ============================================================================ // 参数设置 // ============================================================================ show_merged_k = input.bool(true, "显示处理后K线", group="显示设置") show_fenxing = input.bool(true, "显示分型", group="显示设置") show_bi = input.bool(true, "显示笔", group="显示设置") show_xianduan = input.bool(true, "显示线段", group="显示设置") show_bi_zhongshu = input.bool(true, "显示笔中枢", group="显示设置") show_xd_zhongshu = input.bool(true, "显示线段中枢", group="显示设置") show_buy_sell = input.bool(true, "显示买卖点", group="显示设置") show_ema24 = input.bool(true, "显示EMA24", group="显示设置") show_ema52 = input.bool(true, "显示EMA52", group="显示设置") show_ema104 = input.bool(true, "显示EMA104", group="显示设置") show_ema156 = input.bool(true, "显示EMA156", group="显示设置") show_ema_trend = input.bool(true, "显示均线趋势", group="显示设置") show_tp_signals = input.bool(true, "显示止盈信号", group="显示设置") min_bi_bars = input.int(4, "笔最小K线数", minval=3, maxval=10, group="缠论参数") use_macd_filter = input.bool(false, "MACD过滤分型", tooltip="顶分型要求MACD>0,底分型要求MACD<0", group="缠论参数") // EMA参数设置 ema1_len = input.int(24, "EMA1周期", minval=5, maxval=100, group="均线参数", tooltip="最快均线,默认24") ema2_len = input.int(52, "EMA2周期", minval=10, maxval=200, group="均线参数", tooltip="次快均线,默认52") ema3_len = input.int(104, "EMA3周期", minval=20, maxval=300, group="均线参数", tooltip="次慢均线,默认104") ema4_len = input.int(156, "EMA4周期", minval=30, maxval=500, group="均线参数", tooltip="EMA156仅用于显示,不参与三均线排列/交叉") trend_confirm_bars = input.int(3, "趋势确认K线数", minval=1, maxval=20, group="均线参数", tooltip="排列持续N根K线才确认趋势,增大可减少震荡假信号") color_bi_up = input.color(color.red, "上升笔颜色", group="颜色设置") color_bi_down = input.color(color.green, "下降笔颜色", group="颜色设置") color_xd = input.color(color.blue, "线段颜色", group="颜色设置") color_bi_zs = input.color(color.new(color.yellow, 80), "笔中枢颜色", group="颜色设置") color_xd_zs = input.color(color.new(color.orange, 70), "线段中枢颜色", group="颜色设置") color_ema24 = input.color(color.yellow, "EMA24颜色", group="颜色设置") color_ema52 = input.color(color.orange, "EMA52颜色", group="颜色设置") color_ema104 = input.color(color.blue, "EMA104颜色", group="颜色设置") color_ema156 = input.color(color.purple, "EMA156颜色", group="颜色设置") // ============================================================================ // MACD计算 // ============================================================================ [macd_line, signal_line, macd_hist] = ta.macd(close, 12, 26, 9) // ============================================================================ // EMA均线计算 // ============================================================================ ema24 = ta.ema(close, ema1_len) ema52 = ta.ema(close, ema2_len) ema104 = ta.ema(close, ema3_len) ema156 = ta.ema(close, ema4_len) // 绘制EMA均线 plot(show_ema24 ? ema24 : na, "EMA24", color=color_ema24, linewidth=2) plot(show_ema52 ? ema52 : na, "EMA52", color=color_ema52, linewidth=2) plot(show_ema104 ? ema104 : na, "EMA104", color=color_ema104, linewidth=2) plot(show_ema156 ? ema156 : na, "EMA156", color=color_ema156, linewidth=2) // 趋势/排列与交叉仅使用三条均线:EMA24, EMA52, EMA104(EMA156仅显示) // ============================================================================ // 三均线趋势排列检测 // ============================================================================ // 多头排列:EMA1 > EMA2 > EMA3(从上到下依次排列) // 空头排列:EMA1 < EMA2 < EMA3(从下到上依次排列) is_bullish_aligned = ema24 > ema52 and ema52 > ema104 is_bearish_aligned = ema24 < ema52 and ema52 < ema104 // 中枢震荡区间:均线未形成多头或空头排列(均线纠缠状态) is_in_oscillation = not is_bullish_aligned and not is_bearish_aligned // EMA交叉检测(52和104) ema_golden_cross = ta.crossover(ema52, ema104) // 金叉:EMA52上穿EMA104 ema_death_cross = ta.crossunder(ema52, ema104) // 死叉:EMA52下穿EMA104 // 绘制交叉点(中枢震荡区间不显示) plotshape(show_ema52 and show_ema104 and ema_golden_cross and not is_in_oscillation, title="EMA金叉", style=shape.triangleup, location=location.belowbar, color=color.lime, size=size.small, text="金叉") plotshape(show_ema52 and show_ema104 and ema_death_cross and not is_in_oscillation, title="EMA死叉", style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, text="死叉") // 计算排列持续的K线数 var int bullish_bars = 0 var int bearish_bars = 0 if is_bullish_aligned bullish_bars := bullish_bars + 1 else bullish_bars := 0 if is_bearish_aligned bearish_bars := bearish_bars + 1 else bearish_bars := 0 // 确认的多头/空头排列(持续N根K线后确认) is_bullish_confirmed = bullish_bars >= trend_confirm_bars is_bearish_confirmed = bearish_bars >= trend_confirm_bars was_bullish_confirmed = bullish_bars[1] >= trend_confirm_bars was_bearish_confirmed = bearish_bars[1] >= trend_confirm_bars // 多头趋势起始:刚好达到确认K线数 bullish_trend_start = bullish_bars == trend_confirm_bars // 多头趋势结束:从已确认变为未确认 bullish_trend_end = not is_bullish_confirmed and was_bullish_confirmed // 空头趋势起始:刚好达到确认K线数 bearish_trend_start = bearish_bars == trend_confirm_bars // 空头趋势结束:从已确认变为未确认 bearish_trend_end = not is_bearish_confirmed and was_bearish_confirmed // 绘制趋势起始和结束点(中枢震荡区间不显示) plotshape(show_ema_trend and bullish_trend_start and not is_in_oscillation, title="多头趋势开始", style=shape.diamond, location=location.belowbar, color=color.lime, size=size.normal, text="多头↑") plotshape(show_ema_trend and bullish_trend_end and not is_in_oscillation, title="多头趋势结束", style=shape.xcross, location=location.abovebar, color=color.orange, size=size.small, text="多头结束") plotshape(show_ema_trend and bearish_trend_start and not is_in_oscillation, title="空头趋势开始", style=shape.diamond, location=location.abovebar, color=color.red, size=size.normal, text="空头↓") plotshape(show_ema_trend and bearish_trend_end and not is_in_oscillation, title="空头趋势结束", style=shape.xcross, location=location.belowbar, color=color.orange, size=size.small, text="空头结束") // 背景色标识当前趋势状态(只有确认后才显示,中枢震荡区间不显示) bgcolor(show_ema_trend and is_bullish_confirmed and not is_in_oscillation ? color.new(color.lime, 90) : na, title="多头背景") bgcolor(show_ema_trend and is_bearish_confirmed and not is_in_oscillation ? color.new(color.red, 90) : na, title="空头背景") // ============================================================================ // 交叉 + 排列 组合信号 // ============================================================================ // 记录最近的金叉/死叉位置 var int last_golden_cross_bar = na var int last_death_cross_bar = na if ema_golden_cross last_golden_cross_bar := bar_index if ema_death_cross last_death_cross_bar := bar_index // 计算距离上次交叉的K线数 bars_since_golden = na(last_golden_cross_bar) ? 9999 : bar_index - last_golden_cross_bar bars_since_death = na(last_death_cross_bar) ? 9999 : bar_index - last_death_cross_bar // 组合信号1:金叉后N根K线内出现多头排列确认 = 强势做多 // 组合信号2:死叉后N根K线内出现空头排列确认 = 强势做空 combo_lookback = 30 // 交叉后30根K线内排列确认有效 // 强势做多:金叉后多头排列刚确认(且金叉在死叉之后) strong_long_signal = bullish_trend_start and bars_since_golden <= combo_lookback and bars_since_golden < bars_since_death // 强势做空:死叉后空头排列刚确认(且死叉在金叉之后) strong_short_signal = bearish_trend_start and bars_since_death <= combo_lookback and bars_since_death < bars_since_golden // 组合信号3:已处于多头排列中出现金叉 = 趋势加速 // 组合信号4:已处于空头排列中出现死叉 = 趋势加速 trend_accelerate_long = ema_golden_cross and is_bullish_confirmed trend_accelerate_short = ema_death_cross and is_bearish_confirmed // 组合信号5:已处于多头排列中出现死叉 = 趋势可能反转预警 // 组合信号6:已处于空头排列中出现金叉 = 趋势可能反转预警 trend_reverse_warning_long = ema_death_cross and is_bullish_confirmed trend_reverse_warning_short = ema_golden_cross and is_bearish_confirmed // 绘制组合信号(中枢震荡区间不显示) plotshape(show_ema_trend and strong_long_signal and not is_in_oscillation, title="强势做多", style=shape.labelup, location=location.belowbar, color=color.new(color.lime, 0), size=size.normal, text="强多", textcolor=color.white) plotshape(show_ema_trend and strong_short_signal and not is_in_oscillation, title="强势做空", style=shape.labeldown, location=location.abovebar, color=color.new(color.red, 0), size=size.normal, text="强空", textcolor=color.white) plotshape(show_ema_trend and trend_accelerate_long and not is_in_oscillation, title="多头加速", style=shape.triangleup, location=location.belowbar, color=color.aqua, size=size.small, text="加速↑") plotshape(show_ema_trend and trend_accelerate_short and not is_in_oscillation, title="空头加速", style=shape.triangledown, location=location.abovebar, color=color.fuchsia, size=size.small, text="加速↓") plotshape(show_ema_trend and trend_reverse_warning_long and not is_in_oscillation, title="多头预警", style=shape.flag, location=location.abovebar, color=color.yellow, size=size.small, text="预警") plotshape(show_ema_trend and trend_reverse_warning_short and not is_in_oscillation, title="空头预警", style=shape.flag, location=location.belowbar, color=color.yellow, size=size.small, text="预警") // ============================================================================ // MACD背驰止盈系统 // ============================================================================ // 原理:趋势运行中,通过MACD能量柱(柱状图)的峰值变化检测动量衰减 // 以MACD零轴穿越作为"波段"分界,比较相邻两波的MACD峰值与对应价格极值: // 背驰止盈:价格创新高/低,但MACD能量柱峰值未能同步创新高/低(经典顶/底背驰) // 衰竭止盈:价格未能创新高/低,且MACD动量同步减弱(力量衰竭,趋势将尽) // MACD零轴穿越检测 macd_cross_below_zero = macd_hist[1] > 0 and macd_hist <= 0 // MACD能量柱由正转负(一波上涨动能结束) macd_cross_above_zero = macd_hist[1] < 0 and macd_hist >= 0 // MACD能量柱由负转正(一波下跌动能结束) // --- 多头趋势止盈追踪变量 --- var float bull_prev_wave_price = na // 上一波价格高点 var float bull_prev_wave_macd = na // 上一波MACD正柱峰值 var float bull_curr_wave_price = na // 当前波价格高点 var float bull_curr_wave_macd = 0.0 // 当前波MACD正柱峰值 var int bull_wave_count = 0 // 已完成的MACD正柱波段计数 // --- 空头趋势止盈追踪变量 --- var float bear_prev_wave_price = na // 上一波价格低点 var float bear_prev_wave_macd = na // 上一波MACD负柱谷值 var float bear_curr_wave_price = na // 当前波价格低点 var float bear_curr_wave_macd = 0.0 // 当前波MACD负柱谷值 var int bear_wave_count = 0 // 已完成的MACD负柱波段计数 // 多头趋势开始:重置所有追踪变量 if bullish_trend_start bull_prev_wave_price := na bull_prev_wave_macd := na bull_curr_wave_price := high bull_curr_wave_macd := math.max(macd_hist, 0) bull_wave_count := 0 // 空头趋势开始:重置所有追踪变量 if bearish_trend_start bear_prev_wave_price := na bear_prev_wave_macd := na bear_curr_wave_price := low bear_curr_wave_macd := math.min(macd_hist, 0) bear_wave_count := 0 // === 多头趋势中的止盈检测 === // 在确认的多头趋势中持续追踪MACD能量柱波段 bool tp_long_divergence = false // 顶背驰止盈信号 bool tp_long_exhaustion = false // 多头力量衰竭止盈信号 if is_bullish_confirmed // 持续追踪当前波段的价格高点 bull_curr_wave_price := math.max(nz(bull_curr_wave_price, high), high) // 持续追踪当前波段的MACD正柱峰值(只在MACD>0时更新) if macd_hist > 0 bull_curr_wave_macd := math.max(nz(bull_curr_wave_macd), macd_hist) // MACD能量柱由正转负:一波上涨动能结束,检查背驰 if macd_cross_below_zero and nz(bull_curr_wave_macd) > 0 bull_wave_count += 1 // 至少第二波才能与前一波比较 if bull_wave_count >= 2 and not na(bull_prev_wave_macd) and bull_prev_wave_macd > 0 // 顶背驰:价格创新高(或持平),但MACD能量柱峰值更低 if bull_curr_wave_price >= bull_prev_wave_price and bull_curr_wave_macd < bull_prev_wave_macd tp_long_divergence := true // 力量衰竭:价格未创新高,MACD能量柱峰值也更低 else if bull_curr_wave_price < bull_prev_wave_price and bull_curr_wave_macd < bull_prev_wave_macd tp_long_exhaustion := true // 保存当前波为前一波参考 bull_prev_wave_price := bull_curr_wave_price bull_prev_wave_macd := bull_curr_wave_macd // 重置当前波段追踪 bull_curr_wave_price := high bull_curr_wave_macd := 0.0 // === 空头趋势中的止盈检测 === // 在确认的空头趋势中持续追踪MACD能量柱波段 bool tp_short_divergence = false // 底背驰止盈信号 bool tp_short_exhaustion = false // 空头力量衰竭止盈信号 if is_bearish_confirmed // 持续追踪当前波段的价格低点 bear_curr_wave_price := math.min(nz(bear_curr_wave_price, low), low) // 持续追踪当前波段的MACD负柱谷值(只在MACD<0时更新) if macd_hist < 0 bear_curr_wave_macd := math.min(nz(bear_curr_wave_macd), macd_hist) // MACD能量柱由负转正:一波下跌动能结束,检查背驰 if macd_cross_above_zero and nz(bear_curr_wave_macd) < 0 bear_wave_count += 1 // 至少第二波才能与前一波比较 if bear_wave_count >= 2 and not na(bear_prev_wave_macd) and bear_prev_wave_macd < 0 // 底背驰:价格创新低(或持平),但MACD能量柱谷值绝对值更小(负值回升) if bear_curr_wave_price <= bear_prev_wave_price and bear_curr_wave_macd > bear_prev_wave_macd tp_short_divergence := true // 力量衰竭:价格未创新低,MACD能量柱谷值也回升 else if bear_curr_wave_price > bear_prev_wave_price and bear_curr_wave_macd > bear_prev_wave_macd tp_short_exhaustion := true // 保存当前波为前一波参考 bear_prev_wave_price := bear_curr_wave_price bear_prev_wave_macd := bear_curr_wave_macd // 重置当前波段追踪 bear_curr_wave_price := low bear_curr_wave_macd := 0.0 // 绘制止盈信号 // 多头背驰止盈(品红色标签,显眼,标在K线上方) plotshape(show_tp_signals and show_ema_trend and tp_long_divergence, title="多头背驰止盈", style=shape.labeldown, location=location.abovebar, color=color.new(color.fuchsia, 0), size=size.normal, text="背驰止盈", textcolor=color.white) // 多头衰竭止盈(橙色标签,标在K线上方) plotshape(show_tp_signals and show_ema_trend and tp_long_exhaustion, title="多头衰竭止盈", style=shape.labeldown, location=location.abovebar, color=color.new(color.orange, 0), size=size.small, text="衰竭止盈", textcolor=color.white) // 空头背驰止盈(品红色标签,显眼,标在K线下方) plotshape(show_tp_signals and show_ema_trend and tp_short_divergence, title="空头背驰止盈", style=shape.labelup, location=location.belowbar, color=color.new(color.fuchsia, 0), size=size.normal, text="背驰止盈", textcolor=color.white) // 空头衰竭止盈(橙色标签,标在K线下方) plotshape(show_tp_signals and show_ema_trend and tp_short_exhaustion, title="空头衰竭止盈", style=shape.labelup, location=location.belowbar, color=color.new(color.orange, 0), size=size.small, text="衰竭止盈", textcolor=color.white) // ============================================================================ // K线包含处理(缠论标准) // ============================================================================ // 存储处理后的K线 var float[] mk_high = array.new() var float[] mk_low = array.new() var int[] mk_idx = array.new() // 合并K线的结束bar_index var int[] mk_start_idx = array.new() // 合并K线的起始bar_index var float[] mk_macd = array.new() // 每根合并K线对应的MACD值 // 当前趋势方向(用于包含处理) var int mk_trend = 0 // 1=向上,-1=向下 // 检测包含关系:两根K线互相包含 is_contain(h1, l1, h2, l2) => (h1 >= h2 and l1 <= l2) or (h2 >= h1 and l2 <= l1) // K线包含处理逻辑 mk_size = array.size(mk_high) if mk_size == 0 // 第一根K线 array.push(mk_high, high) array.push(mk_low, low) array.push(mk_idx, bar_index) array.push(mk_start_idx, bar_index) array.push(mk_macd, macd_hist) else prev_h = array.get(mk_high, mk_size - 1) prev_l = array.get(mk_low, mk_size - 1) prev_start = array.get(mk_start_idx, mk_size - 1) if is_contain(prev_h, prev_l, high, low) // 有包含关系,需要合并 // 确定处理方向 if mk_size >= 2 prev2_h = array.get(mk_high, mk_size - 2) prev2_l = array.get(mk_low, mk_size - 2) mk_trend := prev_h > prev2_h ? 1 : (prev_l < prev2_l ? -1 : mk_trend) // 根据方向合并:向上取高高低低,向下取低高低低 new_h = mk_trend >= 0 ? math.max(prev_h, high) : math.min(prev_h, high) new_l = mk_trend >= 0 ? math.max(prev_l, low) : math.min(prev_l, low) array.set(mk_high, mk_size - 1, new_h) array.set(mk_low, mk_size - 1, new_l) array.set(mk_idx, mk_size - 1, bar_index) array.set(mk_macd, mk_size - 1, macd_hist) // 更新MACD为最新值 // mk_start_idx 保持不变(保留合并起始位置) else // 无包含关系 // 更新趋势方向 mk_trend := high > prev_h ? 1 : -1 // 添加新K线 array.push(mk_high, high) array.push(mk_low, low) array.push(mk_idx, bar_index) array.push(mk_start_idx, bar_index) array.push(mk_macd, macd_hist) // 限制大小 if array.size(mk_high) > 5000 array.shift(mk_high) array.shift(mk_low) array.shift(mk_idx) array.shift(mk_start_idx) array.shift(mk_macd) // ============================================================================ // 基于处理后K线的分型识别 // ============================================================================ var int[] fx_bars = array.new() var float[] fx_prices = array.new() var int[] fx_types = array.new() // 1=顶, -1=底 var float[] fx_macds = array.new() // 分型对应的MACD值 // 分型识别(使用处理后的K线) curr_mk_size = array.size(mk_high) if curr_mk_size >= 3 // 获取最近三根处理后的K线 h1 = array.get(mk_high, curr_mk_size - 3) h2 = array.get(mk_high, curr_mk_size - 2) h3 = array.get(mk_high, curr_mk_size - 1) l1 = array.get(mk_low, curr_mk_size - 3) l2 = array.get(mk_low, curr_mk_size - 2) l3 = array.get(mk_low, curr_mk_size - 1) idx2 = array.get(mk_idx, curr_mk_size - 2) // 顶分型:中间K线高点最高 is_top = h2 > h1 and h2 > h3 // 底分型:中间K线低点最低 is_bottom = l2 < l1 and l2 < l3 // MACD过滤:顶分型要求MACD>0,底分型要求MACD<0 macd_at_fx = array.get(mk_macd, curr_mk_size - 2) // 中间K线的MACD值 macd_ok = not use_macd_filter or (is_top and macd_at_fx > 0) or (is_bottom and macd_at_fx < 0) if (is_top or is_bottom) and macd_ok fx_arr_size = array.size(fx_bars) fx_type = is_top ? 1 : -1 fx_price = is_top ? h2 : l2 should_add = true if fx_arr_size > 0 last_type = array.get(fx_types, fx_arr_size - 1) last_bar = array.get(fx_bars, fx_arr_size - 1) last_price = array.get(fx_prices, fx_arr_size - 1) if last_type == fx_type // 同类型:保留更极端的(且必须满足MACD条件) if is_top and fx_price > last_price and (not use_macd_filter or macd_at_fx > 0) array.set(fx_bars, fx_arr_size - 1, idx2) array.set(fx_prices, fx_arr_size - 1, fx_price) array.set(fx_macds, fx_arr_size - 1, macd_at_fx) else if is_bottom and fx_price < last_price and (not use_macd_filter or macd_at_fx < 0) array.set(fx_bars, fx_arr_size - 1, idx2) array.set(fx_prices, fx_arr_size - 1, fx_price) array.set(fx_macds, fx_arr_size - 1, macd_at_fx) should_add := false else // 不同类型:检查距离 if idx2 - last_bar < min_bi_bars should_add := false // 检查笔的有效性 if fx_type == 1 and fx_price <= last_price should_add := false if fx_type == -1 and fx_price >= last_price should_add := false if should_add array.push(fx_bars, idx2) array.push(fx_prices, fx_price) array.push(fx_types, fx_type) array.push(fx_macds, macd_at_fx) if array.size(fx_bars) > 2000 array.shift(fx_bars) array.shift(fx_prices) array.shift(fx_types) array.shift(fx_macds) // ============================================================================ // 绘制 // ============================================================================ if barstate.islast fx_count = array.size(fx_bars) merged_k_count = array.size(mk_high) // ==================== 绘制处理后的K线 ==================== if show_merged_k and merged_k_count > 0 for i = 0 to merged_k_count - 1 m_start = array.get(mk_start_idx, i) m_end = array.get(mk_idx, i) m_h = array.get(mk_high, i) m_l = array.get(mk_low, i) if bar_index - m_end <= 5000 // 用矩形显示合并后的K线范围 box.new(m_start, m_h, m_end, m_l, border_color=color.purple, bgcolor=color.new(color.purple, 90), border_width=1) // ==================== 绘制分型 ==================== if show_fenxing and fx_count > 0 for i = 0 to fx_count - 1 fx_bar = array.get(fx_bars, i) fx_price = array.get(fx_prices, i) fx_type = array.get(fx_types, i) if bar_index - fx_bar <= 5000 if fx_type == 1 label.new(fx_bar, fx_price, "顶", color=color.red, style=label.style_label_down, textcolor=color.white, size=size.tiny) else label.new(fx_bar, fx_price, "底", color=color.green, style=label.style_label_up, textcolor=color.white, size=size.tiny) // ==================== 构建笔 ==================== var int[] bi_start_bars = array.new() var float[] bi_start_prices = array.new() var int[] bi_end_bars = array.new() var float[] bi_end_prices = array.new() var int[] bi_dirs = array.new() array.clear(bi_start_bars) array.clear(bi_start_prices) array.clear(bi_end_bars) array.clear(bi_end_prices) array.clear(bi_dirs) if fx_count >= 2 for i = 0 to fx_count - 2 fx1_bar = array.get(fx_bars, i) fx1_price = array.get(fx_prices, i) fx1_type = array.get(fx_types, i) fx2_bar = array.get(fx_bars, i + 1) fx2_price = array.get(fx_prices, i + 1) fx2_type = array.get(fx_types, i + 1) if fx1_type != fx2_type bi_dir = fx1_type == -1 ? 1 : -1 array.push(bi_start_bars, fx1_bar) array.push(bi_start_prices, fx1_price) array.push(bi_end_bars, fx2_bar) array.push(bi_end_prices, fx2_price) array.push(bi_dirs, bi_dir) bi_count = array.size(bi_start_bars) // ==================== 绘制笔 ==================== if show_bi and bi_count > 0 for i = 0 to bi_count - 1 s_bar = array.get(bi_start_bars, i) s_price = array.get(bi_start_prices, i) e_bar = array.get(bi_end_bars, i) e_price = array.get(bi_end_prices, i) bi_dir = array.get(bi_dirs, i) if bar_index - s_bar <= 5000 bi_color = bi_dir == 1 ? color_bi_up : color_bi_down line.new(s_bar, s_price, e_bar, e_price, color=bi_color, width=2) // ==================== 构建线段(标准特征序列法) ==================== // 缠论线段标准规则: // 1. 线段至少包含3笔 // 2. 特征序列由与线段方向相反的笔组成 // 3. 特征序列要先做包含处理(与K线包含处理同理) // 4. 包含处理后的特征序列出现分型,线段才结束 // - 上升线段:特征序列(向下笔)出现顶分型 → 线段结束(第一种情况) // - 下降线段:特征序列(向上笔)出现底分型 → 线段结束(第一种情况) // 5. 第二种情况:特征序列无分型,但出现反向缺口(gap)确认反转 var int[] xd_start_bars = array.new() var float[] xd_start_prices = array.new() var int[] xd_end_bars = array.new() var float[] xd_end_prices = array.new() var int[] xd_dirs = array.new() array.clear(xd_start_bars) array.clear(xd_start_prices) array.clear(xd_end_bars) array.clear(xd_end_prices) array.clear(xd_dirs) if bi_count >= 3 // 特征序列存储(包含处理前的原始序列) var float[] feat_raw_highs = array.new() var float[] feat_raw_lows = array.new() var int[] feat_raw_end_bars = array.new() var float[] feat_raw_end_prices = array.new() array.clear(feat_raw_highs) array.clear(feat_raw_lows) array.clear(feat_raw_end_bars) array.clear(feat_raw_end_prices) // 特征序列包含处理后的序列 var float[] feat_merged_highs = array.new() var float[] feat_merged_lows = array.new() var int[] feat_merged_end_bars = array.new() var float[] feat_merged_end_prices = array.new() array.clear(feat_merged_highs) array.clear(feat_merged_lows) array.clear(feat_merged_end_bars) array.clear(feat_merged_end_prices) // 线段状态 xd_start_bar = array.get(bi_start_bars, 0) xd_start_price = array.get(bi_start_prices, 0) xd_dir = array.get(bi_dirs, 0) xd_bi_count = 1 // 极值追踪 xd_high = math.max(array.get(bi_start_prices, 0), array.get(bi_end_prices, 0)) xd_low = math.min(array.get(bi_start_prices, 0), array.get(bi_end_prices, 0)) xd_high_bar = xd_dir == 1 ? array.get(bi_end_bars, 0) : array.get(bi_start_bars, 0) xd_low_bar = xd_dir == 1 ? array.get(bi_start_bars, 0) : array.get(bi_end_bars, 0) i = 1 while i < bi_count curr_bi_dir = array.get(bi_dirs, i) curr_bi_start_bar = array.get(bi_start_bars, i) curr_bi_start_price = array.get(bi_start_prices, i) curr_bi_end_bar = array.get(bi_end_bars, i) curr_bi_end_price = array.get(bi_end_prices, i) curr_bi_high = math.max(curr_bi_start_price, curr_bi_end_price) curr_bi_low = math.min(curr_bi_start_price, curr_bi_end_price) xd_bi_count += 1 // 更新线段极值 if curr_bi_high > xd_high xd_high := curr_bi_high xd_high_bar := curr_bi_dir == 1 ? curr_bi_end_bar : curr_bi_start_bar if curr_bi_low < xd_low xd_low := curr_bi_low xd_low_bar := curr_bi_dir == -1 ? curr_bi_end_bar : curr_bi_start_bar // 反向笔加入特征序列(上升线段收集向下笔,下降线段收集向上笔) if curr_bi_dir != xd_dir array.push(feat_raw_highs, curr_bi_high) array.push(feat_raw_lows, curr_bi_low) array.push(feat_raw_end_bars, curr_bi_end_bar) array.push(feat_raw_end_prices, curr_bi_end_price) // === 特征序列包含处理 === // 与K线包含处理原理相同: // 上升线段的特征序列(向下笔):包含处理方向看前一个特征元素 // 高点升→向上处理(取高高低低);低点降→向下处理(取低低高高) fm_size = array.size(feat_merged_highs) if fm_size == 0 array.push(feat_merged_highs, curr_bi_high) array.push(feat_merged_lows, curr_bi_low) array.push(feat_merged_end_bars, curr_bi_end_bar) array.push(feat_merged_end_prices, curr_bi_end_price) else fm_prev_h = array.get(feat_merged_highs, fm_size - 1) fm_prev_l = array.get(feat_merged_lows, fm_size - 1) // 检查是否存在包含关系 has_contain = (fm_prev_h >= curr_bi_high and fm_prev_l <= curr_bi_low) or (curr_bi_high >= fm_prev_h and curr_bi_low <= fm_prev_l) if has_contain // 确定包含处理方向 fm_trend = 0 if fm_size >= 2 fm_prev2_h = array.get(feat_merged_highs, fm_size - 2) fm_prev2_l = array.get(feat_merged_lows, fm_size - 2) fm_trend := fm_prev_h > fm_prev2_h ? 1 : (fm_prev_l < fm_prev2_l ? -1 : 0) // 向上处理:取高高低低;向下处理:取低低高高 if fm_trend >= 0 array.set(feat_merged_highs, fm_size - 1, math.max(fm_prev_h, curr_bi_high)) array.set(feat_merged_lows, fm_size - 1, math.max(fm_prev_l, curr_bi_low)) else array.set(feat_merged_highs, fm_size - 1, math.min(fm_prev_h, curr_bi_high)) array.set(feat_merged_lows, fm_size - 1, math.min(fm_prev_l, curr_bi_low)) // 更新end信息为最新的笔 array.set(feat_merged_end_bars, fm_size - 1, curr_bi_end_bar) array.set(feat_merged_end_prices, fm_size - 1, curr_bi_end_price) else // 无包含关系,直接加入 array.push(feat_merged_highs, curr_bi_high) array.push(feat_merged_lows, curr_bi_low) array.push(feat_merged_end_bars, curr_bi_end_bar) array.push(feat_merged_end_prices, curr_bi_end_price) // 至少3笔后检查线段是否结束 xd_broken = false xd_break_bar = 0 xd_break_price = 0.0 if xd_bi_count >= 3 fm_size = array.size(feat_merged_highs) // === 第一种情况:特征序列包含处理后出现分型 === if fm_size >= 3 fm_h1 = array.get(feat_merged_highs, fm_size - 3) fm_h2 = array.get(feat_merged_highs, fm_size - 2) fm_h3 = array.get(feat_merged_highs, fm_size - 1) fm_l1 = array.get(feat_merged_lows, fm_size - 3) fm_l2 = array.get(feat_merged_lows, fm_size - 2) fm_l3 = array.get(feat_merged_lows, fm_size - 1) if xd_dir == 1 // 上升线段:特征序列(向下笔)出现顶分型 → 线段结束 // 顶分型:中间元素高点最高 if fm_h2 > fm_h1 and fm_h2 > fm_h3 xd_broken := true else // 下降线段:特征序列(向上笔)出现底分型 → 线段结束 // 底分型:中间元素低点最低 if fm_l2 < fm_l1 and fm_l2 < fm_l3 xd_broken := true // === 第二种情况:特征序列不满足分型条件,但出现缺口确认反转 === // 上升线段:如果某向下笔的低点直接跌破前一向上笔的高点(缺口),确认反转 // 下降线段:如果某向上笔的高点直接突破前一向下笔的低点(缺口),确认反转 if not xd_broken and fm_size >= 2 if xd_dir == 1 // 上升线段第二种情况:最近两个特征元素(向下笔), // 后者低点 < 前者低点,且后者高点 < 前者低点(缺口) fm_prev_h2 = array.get(feat_merged_highs, fm_size - 2) fm_prev_l2 = array.get(feat_merged_lows, fm_size - 2) fm_curr_h2 = array.get(feat_merged_highs, fm_size - 1) fm_curr_l2 = array.get(feat_merged_lows, fm_size - 1) if fm_curr_h2 < fm_prev_l2 xd_broken := true else // 下降线段第二种情况: // 后者高点 > 前者高点,且后者低点 > 前者高点(缺口) fm_prev_h2 = array.get(feat_merged_highs, fm_size - 2) fm_prev_l2 = array.get(feat_merged_lows, fm_size - 2) fm_curr_h2 = array.get(feat_merged_highs, fm_size - 1) fm_curr_l2 = array.get(feat_merged_lows, fm_size - 1) if fm_curr_l2 > fm_prev_h2 xd_broken := true if xd_broken // 保存线段:上升线段终点取最高点,下降线段终点取最低点 xd_end_bar = xd_dir == 1 ? xd_high_bar : xd_low_bar xd_end_price = xd_dir == 1 ? xd_high : xd_low array.push(xd_start_bars, xd_start_bar) array.push(xd_start_prices, xd_start_price) array.push(xd_end_bars, xd_end_bar) array.push(xd_end_prices, xd_end_price) array.push(xd_dirs, xd_dir) // 新线段开始 xd_start_bar := xd_end_bar xd_start_price := xd_end_price xd_dir := xd_dir == 1 ? -1 : 1 xd_bi_count := 1 // 重置极值 xd_high := curr_bi_high xd_low := curr_bi_low xd_high_bar := curr_bi_dir == 1 ? curr_bi_end_bar : curr_bi_start_bar xd_low_bar := curr_bi_dir == -1 ? curr_bi_end_bar : curr_bi_start_bar // 清空特征序列 array.clear(feat_raw_highs) array.clear(feat_raw_lows) array.clear(feat_raw_end_bars) array.clear(feat_raw_end_prices) array.clear(feat_merged_highs) array.clear(feat_merged_lows) array.clear(feat_merged_end_bars) array.clear(feat_merged_end_prices) i += 1 // 最后一个未完成线段:特征序列包含处理后至少有2个元素才保存 feat_final_size = array.size(feat_merged_highs) if xd_bi_count >= 3 and feat_final_size >= 2 xd_end_bar = xd_dir == 1 ? xd_high_bar : xd_low_bar xd_end_price = xd_dir == 1 ? xd_high : xd_low array.push(xd_start_bars, xd_start_bar) array.push(xd_start_prices, xd_start_price) array.push(xd_end_bars, xd_end_bar) array.push(xd_end_prices, xd_end_price) array.push(xd_dirs, xd_dir) xd_count = array.size(xd_start_bars) // ==================== 绘制线段 ==================== if show_xianduan and xd_count > 0 for i = 0 to xd_count - 1 s_bar = array.get(xd_start_bars, i) s_price = array.get(xd_start_prices, i) e_bar = array.get(xd_end_bars, i) e_price = array.get(xd_end_prices, i) xd_dir_draw = array.get(xd_dirs, i) if bar_index - s_bar <= 5000 // 上涨线段(1)=绿色,下跌线段(-1)=红色 xd_color = xd_dir_draw == 1 ? color.green : color.red line.new(s_bar, s_price, e_bar, e_price, color=xd_color, width=4) // ==================== 构建笔中枢(线段内部) ==================== // 缠论标准:笔中枢必须在同一条线段内部构建 // 中枢方向 = 所在线段的方向(上涨线段内 → 上涨中枢,下跌线段内 → 下跌中枢) // 中枢由线段内任意连续3笔的价格重叠区间构成 // ZG = min(3笔高点),ZD = max(3笔低点),ZG > ZD 则构成中枢 // 中枢延伸:同一线段内后续笔与中枢[ZD, ZG]有重叠,则时间扩展,区间不变 var int[] bi_zs_start_bars = array.new() var int[] bi_zs_end_bars = array.new() var float[] bi_zs_highs = array.new() var float[] bi_zs_lows = array.new() var int[] bi_zs_types = array.new() // 1=上涨中枢, -1=下跌中枢 array.clear(bi_zs_start_bars) array.clear(bi_zs_end_bars) array.clear(bi_zs_highs) array.clear(bi_zs_lows) array.clear(bi_zs_types) if xd_count > 0 and bi_count >= 3 // 遍历每条线段,在线段内部寻找笔中枢 for xd_idx = 0 to xd_count - 1 xd_s_bar = array.get(xd_start_bars, xd_idx) xd_e_bar = array.get(xd_end_bars, xd_idx) xd_direction = array.get(xd_dirs, xd_idx) // 收集该线段范围内的笔索引 var int[] xd_bi_indices = array.new() array.clear(xd_bi_indices) for bi_idx = 0 to bi_count - 1 bi_s = array.get(bi_start_bars, bi_idx) bi_e = array.get(bi_end_bars, bi_idx) // 笔的起点和终点都在线段范围内 if bi_s >= xd_s_bar and bi_e <= xd_e_bar array.push(xd_bi_indices, bi_idx) local_bi_count = array.size(xd_bi_indices) if local_bi_count >= 3 j = 0 while j <= local_bi_count - 3 idx1 = array.get(xd_bi_indices, j) idx2 = array.get(xd_bi_indices, j + 1) idx3 = array.get(xd_bi_indices, j + 2) bi1_h = math.max(array.get(bi_start_prices, idx1), array.get(bi_end_prices, idx1)) bi1_l = math.min(array.get(bi_start_prices, idx1), array.get(bi_end_prices, idx1)) bi2_h = math.max(array.get(bi_start_prices, idx2), array.get(bi_end_prices, idx2)) bi2_l = math.min(array.get(bi_start_prices, idx2), array.get(bi_end_prices, idx2)) bi3_h = math.max(array.get(bi_start_prices, idx3), array.get(bi_end_prices, idx3)) bi3_l = math.min(array.get(bi_start_prices, idx3), array.get(bi_end_prices, idx3)) zg = math.min(bi1_h, math.min(bi2_h, bi3_h)) zd = math.max(bi1_l, math.max(bi2_l, bi3_l)) if zg > zd zs_start = array.get(bi_start_bars, idx1) zs_end = array.get(bi_end_bars, idx3) zs_type = xd_direction // 中枢方向 = 线段方向 // 中枢延伸:同一线段内后续笔与中枢有重叠 last_ext = j + 2 if j + 3 < local_bi_count for ext_j = j + 3 to local_bi_count - 1 ext_idx = array.get(xd_bi_indices, ext_j) ext_h = math.max(array.get(bi_start_prices, ext_idx), array.get(bi_end_prices, ext_idx)) ext_l = math.min(array.get(bi_start_prices, ext_idx), array.get(bi_end_prices, ext_idx)) if ext_l < zg and ext_h > zd zs_end := array.get(bi_end_bars, ext_idx) last_ext := ext_j else break // 避免与上一个中枢重叠 should_add_zs = true zs_arr_size = array.size(bi_zs_start_bars) if zs_arr_size > 0 last_zs_end = array.get(bi_zs_end_bars, zs_arr_size - 1) if zs_start <= last_zs_end should_add_zs := false if should_add_zs array.push(bi_zs_start_bars, zs_start) array.push(bi_zs_end_bars, zs_end) array.push(bi_zs_highs, zg) array.push(bi_zs_lows, zd) array.push(bi_zs_types, zs_type) j := last_ext + 1 else j += 1 else j += 1 bi_zs_count = array.size(bi_zs_start_bars) // ==================== 构建线段中枢 ==================== // 上涨中枢:以下跌线段开始、下跌线段结束 (-1, 1, -1) // 下跌中枢:以上涨线段开始、上涨线段结束 (1, -1, 1) // ZG = min(各线段高点),ZD = max(各线段低点),ZG > ZD 则构成中枢 // 中枢延伸:后续线段与中枢有重叠,则时间扩展,区间不变 var int[] xd_zs_start_bars = array.new() var int[] xd_zs_end_bars = array.new() var float[] xd_zs_highs = array.new() var float[] xd_zs_lows = array.new() var int[] xd_zs_types = array.new() // 1=上涨中枢, -1=下跌中枢 array.clear(xd_zs_start_bars) array.clear(xd_zs_end_bars) array.clear(xd_zs_highs) array.clear(xd_zs_lows) array.clear(xd_zs_types) if xd_count >= 3 i = 0 while i <= xd_count - 3 // 检查线段方向模式 xd1_dir = array.get(xd_dirs, i) xd2_dir = array.get(xd_dirs, i + 1) xd3_dir = array.get(xd_dirs, i + 2) // 上涨中枢: (-1, 1, -1) 下跌线段开始下跌线段结束 is_up_xd_zs = xd1_dir == -1 and xd2_dir == 1 and xd3_dir == -1 // 下跌中枢: (1, -1, 1) 上涨线段开始上涨线段结束 is_down_xd_zs = xd1_dir == 1 and xd2_dir == -1 and xd3_dir == 1 if is_up_xd_zs or is_down_xd_zs xd1_s_p = array.get(xd_start_prices, i) xd1_e_p = array.get(xd_end_prices, i) xd2_s_p = array.get(xd_start_prices, i + 1) xd2_e_p = array.get(xd_end_prices, i + 1) xd3_s_p = array.get(xd_start_prices, i + 2) xd3_e_p = array.get(xd_end_prices, i + 2) xd1_h = math.max(xd1_s_p, xd1_e_p) xd1_l = math.min(xd1_s_p, xd1_e_p) xd2_h = math.max(xd2_s_p, xd2_e_p) xd2_l = math.min(xd2_s_p, xd2_e_p) xd3_h = math.max(xd3_s_p, xd3_e_p) xd3_l = math.min(xd3_s_p, xd3_e_p) zg_xd = math.min(xd1_h, math.min(xd2_h, xd3_h)) zd_xd = math.max(xd1_l, math.max(xd2_l, xd3_l)) if zg_xd > zd_xd zs_start_xd = array.get(xd_start_bars, i) zs_end_xd = array.get(xd_end_bars, i + 2) xd_zs_type = is_up_xd_zs ? 1 : -1 // 中枢延伸:后续线段与中枢有重叠,则延伸时间范围 last_ext_xd = i + 2 if i + 3 < xd_count for ext_idx = i + 3 to xd_count - 1 ext_s_p = array.get(xd_start_prices, ext_idx) ext_e_p = array.get(xd_end_prices, ext_idx) ext_h = math.max(ext_s_p, ext_e_p) ext_l = math.min(ext_s_p, ext_e_p) if ext_l < zg_xd and ext_h > zd_xd zs_end_xd := array.get(xd_end_bars, ext_idx) last_ext_xd := ext_idx else break should_add_xd_zs = true xd_zs_arr_size = array.size(xd_zs_start_bars) if xd_zs_arr_size > 0 last_xd_zs_end = array.get(xd_zs_end_bars, xd_zs_arr_size - 1) if zs_start_xd <= last_xd_zs_end should_add_xd_zs := false if should_add_xd_zs array.push(xd_zs_start_bars, zs_start_xd) array.push(xd_zs_end_bars, zs_end_xd) array.push(xd_zs_highs, zg_xd) array.push(xd_zs_lows, zd_xd) array.push(xd_zs_types, xd_zs_type) i := last_ext_xd + 1 else i += 1 else i += 1 else i += 1 xd_zs_count = array.size(xd_zs_start_bars) // ==================== 绘制笔中枢 ==================== // 上涨中枢(1)=绿色,下跌中枢(-1)=红色 if show_bi_zhongshu and bi_zs_count > 0 for i = 0 to bi_zs_count - 1 zs_start = array.get(bi_zs_start_bars, i) zs_end = array.get(bi_zs_end_bars, i) zs_h = array.get(bi_zs_highs, i) zs_l = array.get(bi_zs_lows, i) zs_type = array.get(bi_zs_types, i) if bar_index - zs_start <= 5000 zs_border = zs_type == 1 ? color.green : color.red zs_bg = zs_type == 1 ? color.new(color.green, 85) : color.new(color.red, 85) box.new(zs_start, zs_h, zs_end, zs_l, border_color=zs_border, bgcolor=zs_bg, border_width=1) // ==================== 绘制线段中枢 ==================== // 上涨中枢(1)=绿色,下跌中枢(-1)=红色 if show_xd_zhongshu and xd_zs_count > 0 for i = 0 to xd_zs_count - 1 zs_start = array.get(xd_zs_start_bars, i) zs_end = array.get(xd_zs_end_bars, i) zs_h = array.get(xd_zs_highs, i) zs_l = array.get(xd_zs_lows, i) xd_zs_type = array.get(xd_zs_types, i) if bar_index - zs_start <= 5000 xd_zs_border = xd_zs_type == 1 ? color.lime : color.maroon xd_zs_bg = xd_zs_type == 1 ? color.new(color.lime, 80) : color.new(color.maroon, 80) box.new(zs_start, zs_h, zs_end, zs_l, border_color=xd_zs_border, bgcolor=xd_zs_bg, border_width=2) // ==================== 买卖点(缠论标准三类买卖点 - 与笔中枢方向对应) ==================== // 笔中枢方向 = 线段方向 // 下跌中枢(-1):在中枢下方找一买(底背驰) // 上涨中枢(1):在中枢上方找一卖(顶背驰) // 三买三卖:离开中枢后回调/反弹不回到中枢内部 var int[] bs_bars = array.new() var int[] bs_types = array.new() var float[] bs_prices = array.new() array.clear(bs_bars) array.clear(bs_types) array.clear(bs_prices) max_zs_to_process = math.min(bi_zs_count, 15) zs_start_idx = bi_zs_count - max_zs_to_process if show_buy_sell and bi_zs_count > 0 and fx_count >= 2 for j = zs_start_idx to bi_zs_count - 1 zs_start = array.get(bi_zs_start_bars, j) zs_end = array.get(bi_zs_end_bars, j) zs_h = array.get(bi_zs_highs, j) zs_l = array.get(bi_zs_lows, j) zs_type = array.get(bi_zs_types, j) // 1=上涨中枢, -1=下跌中枢 // 找中枢前最后一个底分型(用于一买背驰比较) float prev_bottom_price = na float prev_bottom_macd = na // 找中枢前最后一个顶分型(用于一卖背驰比较) float prev_top_price = na float prev_top_macd = na for i = fx_count - 1 to 0 fx_b = array.get(fx_bars, i) if fx_b >= zs_start continue fx_t = array.get(fx_types, i) fx_p = array.get(fx_prices, i) fx_m = array.get(fx_macds, i) if fx_t == -1 and na(prev_bottom_price) prev_bottom_price := fx_p prev_bottom_macd := fx_m if fx_t == 1 and na(prev_top_price) prev_top_price := fx_p prev_top_macd := fx_m if not na(prev_bottom_price) and not na(prev_top_price) break // 找中枢后的分型 float leave_price = na int leave_bar = na bool has_left_zs = false bool found_1st = false for i = 0 to fx_count - 1 if found_1st and has_left_zs break fx_bar = array.get(fx_bars, i) if fx_bar <= zs_end continue fx_price = array.get(fx_prices, i) fx_type = array.get(fx_types, i) fx_macd = array.get(fx_macds, i) macd_valid = not use_macd_filter or (fx_type == 1 and fx_macd > 0) or (fx_type == -1 and fx_macd < 0) // 一买:下跌中枢(-1)下方的底分型背驰 if not found_1st and zs_type == -1 and fx_type == -1 and fx_price < zs_l and macd_valid if not na(prev_bottom_macd) and not na(prev_bottom_price) and fx_price < prev_bottom_price and fx_macd > prev_bottom_macd label.new(fx_bar, fx_price * 0.998, "1买", color=color.lime, style=label.style_label_up, textcolor=color.black, size=size.tiny) array.push(bs_bars, fx_bar) array.push(bs_types, 1) array.push(bs_prices, fx_price) found_1st := true // 一卖:上涨中枢(1)上方的顶分型背驰 if not found_1st and zs_type == 1 and fx_type == 1 and fx_price > zs_h and macd_valid if not na(prev_top_macd) and not na(prev_top_price) and fx_price > prev_top_price and fx_macd < prev_top_macd label.new(fx_bar, fx_price * 1.002, "1卖", color=color.red, style=label.style_label_down, textcolor=color.white, size=size.tiny) array.push(bs_bars, fx_bar) array.push(bs_types, -1) array.push(bs_prices, fx_price) found_1st := true // 记录离开中枢点 if not has_left_zs if zs_type == -1 and fx_type == 1 and fx_price > zs_h // 下跌中枢向上离开 leave_price := fx_price leave_bar := fx_bar has_left_zs := true if zs_type == 1 and fx_type == -1 and fx_price < zs_l // 上涨中枢向下离开 leave_price := fx_price leave_bar := fx_bar has_left_zs := true // 三买三卖 if has_left_zs and not na(leave_bar) for i = 0 to fx_count - 1 fx_bar = array.get(fx_bars, i) if fx_bar <= leave_bar continue fx_price = array.get(fx_prices, i) fx_type = array.get(fx_types, i) fx_macd = array.get(fx_macds, i) macd_valid = not use_macd_filter or (fx_type == 1 and fx_macd > 0) or (fx_type == -1 and fx_macd < 0) // 三买:下跌中枢向上离开后,回调底分型不跌破中枢上沿 if zs_type == -1 and leave_price > zs_h and fx_type == -1 and fx_price > zs_h and macd_valid label.new(fx_bar, fx_price * 0.998, "3买", color=color.new(color.lime, 30), style=label.style_label_up, textcolor=color.black, size=size.tiny) array.push(bs_bars, fx_bar) array.push(bs_types, 3) array.push(bs_prices, fx_price) break // 三卖:上涨中枢向下离开后,反弹顶分型不涨过中枢下沿 if zs_type == 1 and leave_price < zs_l and fx_type == 1 and fx_price < zs_l and macd_valid label.new(fx_bar, fx_price * 1.002, "3卖", color=color.new(color.red, 30), style=label.style_label_down, textcolor=color.white, size=size.tiny) array.push(bs_bars, fx_bar) array.push(bs_types, -3) array.push(bs_prices, fx_price) break // 二买二卖 bs_count = array.size(bs_bars) if bs_count > 0 for k = 0 to bs_count - 1 bs_bar = array.get(bs_bars, k) bs_type = array.get(bs_types, k) bs_price = array.get(bs_prices, k) if bs_type == 1 // 一买后找二买 int bounce_bar = na for i = 0 to fx_count - 1 fx_b = array.get(fx_bars, i) if fx_b <= bs_bar continue fx_t = array.get(fx_types, i) if na(bounce_bar) and fx_t == 1 bounce_bar := fx_b continue if not na(bounce_bar) and fx_t == -1 fx_p = array.get(fx_prices, i) if fx_p > bs_price fx_m = array.get(fx_macds, i) macd_valid = not use_macd_filter or fx_m < 0 if macd_valid label.new(fx_b, fx_p * 0.998, "2买", color=color.new(color.lime, 50), style=label.style_label_up, textcolor=color.black, size=size.tiny) array.push(bs_bars, fx_b) array.push(bs_types, 2) array.push(bs_prices, fx_p) break if bs_type == -1 // 一卖后找二卖 int pullback_bar = na for i = 0 to fx_count - 1 fx_b = array.get(fx_bars, i) if fx_b <= bs_bar continue fx_t = array.get(fx_types, i) if na(pullback_bar) and fx_t == -1 pullback_bar := fx_b continue if not na(pullback_bar) and fx_t == 1 fx_p = array.get(fx_prices, i) if fx_p < bs_price fx_m = array.get(fx_macds, i) macd_valid = not use_macd_filter or fx_m > 0 if macd_valid label.new(fx_b, fx_p * 1.002, "2卖", color=color.new(color.red, 50), style=label.style_label_down, textcolor=color.white, size=size.tiny) array.push(bs_bars, fx_b) array.push(bs_types, -2) array.push(bs_prices, fx_p) break // ==================== 信息面板 ==================== var table info_panel = table.new(position.top_right, 2, 8, bgcolor=color.new(color.black, 80), border_width=1, border_color=color.gray) table.cell(info_panel, 0, 0, "缠论分析", text_color=color.white, text_size=size.normal) table.cell(info_panel, 1, 0, "", text_color=color.white) table.cell(info_panel, 0, 1, "处理后K线", text_color=color.gray, text_size=size.small) table.cell(info_panel, 1, 1, str.tostring(merged_k_count), text_color=color.white, text_size=size.small) table.cell(info_panel, 0, 2, "分型数量", text_color=color.gray, text_size=size.small) table.cell(info_panel, 1, 2, str.tostring(fx_count), text_color=color.white, text_size=size.small) table.cell(info_panel, 0, 3, "笔数量", text_color=color.gray, text_size=size.small) table.cell(info_panel, 1, 3, str.tostring(bi_count), text_color=color.white, text_size=size.small) table.cell(info_panel, 0, 4, "线段数量", text_color=color.gray, text_size=size.small) table.cell(info_panel, 1, 4, str.tostring(xd_count), text_color=color.white, text_size=size.small) table.cell(info_panel, 0, 5, "笔中枢", text_color=color.yellow, text_size=size.small) table.cell(info_panel, 1, 5, str.tostring(bi_zs_count), text_color=color.white, text_size=size.small) table.cell(info_panel, 0, 6, "线段中枢", text_color=color.orange, text_size=size.small) table.cell(info_panel, 1, 6, str.tostring(xd_zs_count), text_color=color.white, text_size=size.small) table.cell(info_panel, 0, 7, "当前K线", text_color=color.gray, text_size=size.small) table.cell(info_panel, 1, 7, str.tostring(bar_index), text_color=color.white, text_size=size.small)