Files
tradingview/chan_theory_complete.pine
T

1263 lines
63 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// This 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="最慢均线,默认156")
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)
// ============================================================================
// 四均线趋势排列检测
// ============================================================================
// 多头排列:EMA1 > EMA2 > EMA3 > EMA4(从上到下依次排列)
// 空头排列:EMA1 < EMA2 < EMA3 < EMA4(从下到上依次排列)
is_bullish_aligned = ema24 > ema52 and ema52 > ema104 and ema104 > ema156
is_bearish_aligned = ema24 < ema52 and ema52 < ema104 and ema104 < ema156
// 中枢震荡区间:均线未形成多头或空头排列(均线纠缠状态)
is_in_oscillation = not is_bullish_aligned and not is_bearish_aligned
// EMA交叉检测(52和156
ema_golden_cross = ta.crossover(ema52, ema156) // 金叉:EMA52上穿EMA156
ema_death_cross = ta.crossunder(ema52, ema156) // 死叉:EMA52下穿EMA156
// 绘制交叉点(中枢震荡区间不显示)
plotshape(show_ema52 and show_ema156 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_ema156 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<float>()
var float[] mk_low = array.new<float>()
var int[] mk_idx = array.new<int>() // 合并K线的结束bar_index
var int[] mk_start_idx = array.new<int>() // 合并K线的起始bar_index
var float[] mk_macd = array.new<float>() // 每根合并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<int>()
var float[] fx_prices = array.new<float>()
var int[] fx_types = array.new<int>() // 1=顶, -1=底
var float[] fx_macds = array.new<float>() // 分型对应的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<int>()
var float[] bi_start_prices = array.new<float>()
var int[] bi_end_bars = array.new<int>()
var float[] bi_end_prices = array.new<float>()
var int[] bi_dirs = array.new<int>()
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. 特征序列出现分型且确认,线段结束
var int[] xd_start_bars = array.new<int>()
var float[] xd_start_prices = array.new<float>()
var int[] xd_end_bars = array.new<int>()
var float[] xd_end_prices = array.new<float>()
var int[] xd_dirs = array.new<int>()
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_highs = array.new<float>()
var float[] feat_lows = array.new<float>()
array.clear(feat_highs)
array.clear(feat_lows)
// 线段状态
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_highs, curr_bi_high)
array.push(feat_lows, curr_bi_low)
// 至少3笔后检查线段是否结束
if xd_bi_count >= 3
feat_size = array.size(feat_highs)
xd_broken = false
if feat_size >= 2
// 简化判断:反向笔突破前高/前低
if xd_dir == 1
// 上升线段:向下笔低点创新低
if curr_bi_dir == -1 and curr_bi_end_price < xd_start_price
xd_broken := true
// 或者连续两个特征序列元素低点下移
if feat_size >= 2
prev_feat_l = array.get(feat_lows, feat_size - 2)
curr_feat_l = array.get(feat_lows, feat_size - 1)
if curr_feat_l < prev_feat_l and curr_bi_end_price < array.get(bi_start_prices, i - 1)
xd_broken := true
else
// 下降线段:向上笔高点创新高
if curr_bi_dir == 1 and curr_bi_end_price > xd_start_price
xd_broken := true
// 或者连续两个特征序列元素高点上移
if feat_size >= 2
prev_feat_h = array.get(feat_highs, feat_size - 2)
curr_feat_h = array.get(feat_highs, feat_size - 1)
if curr_feat_h > prev_feat_h and curr_bi_end_price > array.get(bi_start_prices, i - 1)
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_highs)
array.clear(feat_lows)
i += 1
// 最后一个线段:只有在特征序列有至少2个元素时才保存(说明有足够的反向笔)
// 未确认的线段不画出,避免误导
feat_final_size = array.size(feat_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)
// ==================== 构建笔中枢 ====================
// 笔中枢必须在同一线段内,不能跨线段
// 下跌线段中只有下跌笔中枢(上-下-上)
// 上涨线段中只有上涨笔中枢(下-上-下)
var int[] bi_zs_start_bars = array.new<int>()
var int[] bi_zs_end_bars = array.new<int>()
var float[] bi_zs_highs = array.new<float>()
var float[] bi_zs_lows = array.new<float>()
var int[] bi_zs_types = array.new<int>() // 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)
// 找出该线段范围内的笔索引
int[] xd_bi_indices = array.new<int>()
for bi_idx = 0 to bi_count - 1
bi_s_bar = array.get(bi_start_bars, bi_idx)
bi_e_bar = array.get(bi_end_bars, bi_idx)
// 笔在线段范围内
if bi_s_bar >= xd_s_bar and bi_e_bar <= xd_e_bar
array.push(xd_bi_indices, bi_idx)
xd_bi_count_local = array.size(xd_bi_indices)
// 在线段内寻找符合条件的三笔组合
if xd_bi_count_local >= 3
j = 0
while j <= xd_bi_count_local - 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_dir = array.get(bi_dirs, idx1)
bi2_dir = array.get(bi_dirs, idx2)
bi3_dir = array.get(bi_dirs, idx3)
// 根据线段方向确定要找的中枢类型
// 下跌线段(-1):找下跌笔中枢 (1, -1, 1)
// 上涨线段(1):找上涨笔中枢 (-1, 1, -1)
bool is_valid_zs = false
if xd_direction == -1
// 下跌线段,找下跌笔中枢(以上升笔开始)
is_valid_zs := bi1_dir == 1 and bi2_dir == -1 and bi3_dir == 1
else
// 上涨线段,找上涨笔中枢(以下降笔开始)
is_valid_zs := bi1_dir == -1 and bi2_dir == 1 and bi3_dir == -1
if is_valid_zs
bi1_s_p = array.get(bi_start_prices, idx1)
bi1_e_p = array.get(bi_end_prices, idx1)
bi2_s_p = array.get(bi_start_prices, idx2)
bi2_e_p = array.get(bi_end_prices, idx2)
bi3_s_p = array.get(bi_start_prices, idx3)
bi3_e_p = array.get(bi_end_prices, idx3)
bi1_h = math.max(bi1_s_p, bi1_e_p)
bi1_l = math.min(bi1_s_p, bi1_e_p)
bi2_h = math.max(bi2_s_p, bi2_e_p)
bi2_l = math.min(bi2_s_p, bi2_e_p)
bi3_h = math.max(bi3_s_p, bi3_e_p)
bi3_l = math.min(bi3_s_p, bi3_e_p)
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 // 中枢类型与线段方向一致
should_add_zs = true
zs_arr_size = array.size(bi_zs_start_bars)
if zs_arr_size > 0
last_zs_start = array.get(bi_zs_start_bars, zs_arr_size - 1)
last_zs_end = array.get(bi_zs_end_bars, zs_arr_size - 1)
last_zs_h = array.get(bi_zs_highs, zs_arr_size - 1)
last_zs_l = array.get(bi_zs_lows, zs_arr_size - 1)
// 检查上一个中枢是否在同一线段内
last_zs_in_same_xd = last_zs_start >= xd_s_bar and last_zs_end <= xd_e_bar
if zs_start <= last_zs_end and last_zs_in_same_xd
should_add_zs := false
// 中枢扩展:只有在同一线段内才能扩展
if last_zs_in_same_xd and zg >= last_zs_l and zd <= last_zs_h and zs_start <= last_zs_end + 50
array.set(bi_zs_end_bars, zs_arr_size - 1, 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 += 3
else
j += 1
else
j += 1
else
j += 1
bi_zs_count = array.size(bi_zs_start_bars)
// ==================== 构建线段中枢 ====================
// 跌出上一个中枢 → 新的下跌中枢以上涨线段开始 (1, -1, 1)
// 涨出上一个中枢 → 新的上涨中枢以下降线段开始 (-1, 1, -1)
var int[] xd_zs_start_bars = array.new<int>()
var int[] xd_zs_end_bars = array.new<int>()
var float[] xd_zs_highs = array.new<float>()
var float[] xd_zs_lows = array.new<float>()
var int[] xd_zs_types = array.new<int>() // 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
// 跟踪上一个中枢信息
last_zs_high = 0.0
last_zs_low = 0.0
last_zs_end_idx = -1 // 上一个中枢结束的线段索引
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) - 以上升线段开始和结束
// 上涨中枢:下降-上升-下降 (-1, 1, -1) - 以下降线段开始和结束
is_down_xd_zs = xd1_dir == 1 and xd2_dir == -1 and xd3_dir == 1
is_up_xd_zs = xd1_dir == -1 and xd2_dir == 1 and xd3_dir == -1
// 检查是否符合离开方向的要求
bool type_matches = false
if last_zs_end_idx < 0
// 没有上一个中枢,任意类型都可以
type_matches := is_down_xd_zs or is_up_xd_zs
else
// 检查从上一个中枢到当前位置之间,是否有线段离开了上一个中枢
// 找到离开上一个中枢的方向
leave_direction = 0 // 0=未离开, 1=向上离开, -1=向下离开
for check_idx = last_zs_end_idx to i
if check_idx < xd_count
check_price = array.get(xd_end_prices, check_idx)
if check_price < last_zs_low and leave_direction != -1
leave_direction := -1 // 向下离开
else if check_price > last_zs_high and leave_direction != 1
leave_direction := 1 // 向上离开
if leave_direction == -1
// 向下离开,期望下跌中枢
type_matches := is_down_xd_zs
else if leave_direction == 1
// 向上离开,期望上涨中枢
type_matches := is_up_xd_zs
else
// 未离开中枢,不形成新中枢
type_matches := false
if type_matches and (is_down_xd_zs or is_up_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
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
// 中枢扩展(同类型中枢且有重叠)
last_xd_zs_type = array.get(xd_zs_types, xd_zs_arr_size - 1)
if xd_zs_type == last_xd_zs_type and zg_xd >= last_zs_low and zd_xd <= last_zs_high and zs_start_xd <= last_xd_zs_end + 100
array.set(xd_zs_end_bars, xd_zs_arr_size - 1, zs_end_xd)
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)
// 更新上一个中枢信息
last_zs_high := zg_xd
last_zs_low := zd_xd
last_zs_end_idx := i + 2
i += 3
else
i += 1
else
i += 1
else
i += 1
xd_zs_count = array.size(xd_zs_start_bars)
// ==================== 绘制笔中枢 ====================
// 上涨中枢用绿色,下跌中枢用红色
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
// 上涨中枢(1)=绿色边框,下跌中枢(-1)=红色边框
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)
// ==================== 绘制线段中枢 ====================
// 上涨中枢用绿色,下跌中枢用红色
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
// 上涨中枢(1)=绿色边框,下跌中枢(-1)=红色边框
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)
// ==================== 买卖点(缠论标准三类买卖点) ====================
// 第一类:趋势背驰点(离开中枢后与进入中枢前的走势发生MACD背驰)
// 第二类:第一类之后回调/反弹不破第一类
// 第三类:离开中枢后回调/反弹不回到中枢内部
// 存储已标记的买卖点,避免重复
var int[] bs_bars = array.new<int>()
var int[] bs_types = array.new<int>() // 正数=买点,负数=卖点,绝对值=类型
var float[] bs_prices = array.new<float>()
array.clear(bs_bars)
array.clear(bs_types)
array.clear(bs_prices)
if show_buy_sell and bi_zs_count > 0 and fx_count >= 2
// 遍历笔中枢,寻找买卖点
for j = 0 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_extreme_price = na
float prev_extreme_macd = na
for i = 0 to fx_count - 1
fx_b = array.get(fx_bars, i)
fx_p = array.get(fx_prices, i)
fx_t = array.get(fx_types, i)
fx_m = array.get(fx_macds, i)
if fx_b < zs_start
// 下跌中枢(需要一买):找之前的底分型
if zs_type == -1 and fx_t == -1
prev_extreme_price := fx_p
prev_extreme_macd := fx_m
// 上涨中枢(需要一卖):找之前的顶分型
else if zs_type == 1 and fx_t == 1
prev_extreme_price := fx_p
prev_extreme_macd := fx_m
// 找中枢后的极值分型(离开中枢的点)
float leave_price = na
float leave_macd = na
int leave_bar = na
bool has_left_zs = false // 是否已经离开中枢
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)
fx_macd = array.get(fx_macds, i)
if fx_bar <= zs_end
continue
if bar_index - fx_bar > 5000
continue
// MACD过滤
macd_valid = not use_macd_filter or (fx_type == 1 and fx_macd > 0) or (fx_type == -1 and fx_macd < 0)
// ========== 第一类买点(一买)==========
// 下跌中枢 + 底分型跌破中枢下沿 + MACD背驰
if zs_type == -1 and fx_type == -1 and fx_price < zs_l and macd_valid
// 背驰:价格更低,但MACD柱状图绝对值更小(负值更大)
is_divergence = not na(prev_extreme_macd) and not na(prev_extreme_price) and fx_price < prev_extreme_price and fx_macd > prev_extreme_macd
if is_divergence
already_marked = false
if array.size(bs_bars) > 0
for k = 0 to array.size(bs_bars) - 1
if array.get(bs_bars, k) == fx_bar
already_marked := true
break
if not already_marked
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)
// ========== 第一类卖点(一卖)==========
// 上涨中枢 + 顶分型突破中枢上沿 + MACD背驰
if zs_type == 1 and fx_type == 1 and fx_price > zs_h and macd_valid
// 背驰:价格更高,但MACD柱状图更小
is_divergence = not na(prev_extreme_macd) and not na(prev_extreme_price) and fx_price > prev_extreme_price and fx_macd < prev_extreme_macd
if is_divergence
already_marked = false
if array.size(bs_bars) > 0
for k = 0 to array.size(bs_bars) - 1
if array.get(bs_bars, k) == fx_bar
already_marked := true
break
if not already_marked
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)
// 记录离开中枢的极值点(用于判断三买三卖)
if fx_type == 1 and fx_price > zs_h and not has_left_zs
// 顶分型突破中枢上沿 = 向上离开
leave_price := fx_price
leave_bar := fx_bar
has_left_zs := true
if fx_type == -1 and fx_price < zs_l and not has_left_zs
// 底分型跌破中枢下沿 = 向下离开
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)
fx_price = array.get(fx_prices, i)
fx_type = array.get(fx_types, i)
fx_macd = array.get(fx_macds, i)
if fx_bar <= leave_bar
continue
if bar_index - fx_bar > 5000
continue
macd_valid = not use_macd_filter or (fx_type == 1 and fx_macd > 0) or (fx_type == -1 and fx_macd < 0)
// 三买:向上离开后,回调底分型不跌破中枢上沿
if leave_price > zs_h and fx_type == -1 and fx_price > zs_h and macd_valid
already_marked = false
if array.size(bs_bars) > 0
for k = 0 to array.size(bs_bars) - 1
if array.get(bs_bars, k) == fx_bar
already_marked := true
break
if not already_marked
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 leave_price < zs_l and fx_type == 1 and fx_price < zs_l and macd_valid
already_marked = false
if array.size(bs_bars) > 0
for k = 0 to array.size(bs_bars) - 1
if array.get(bs_bars, k) == fx_bar
already_marked := true
break
if not already_marked
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)
fx_t = array.get(fx_types, i)
if fx_b > bs_bar and fx_t == 1
bounce_bar := fx_b
break
// 找反弹后的底分型(二买候选),且必须不破一买
if not na(bounce_bar)
for i = 0 to fx_count - 1
fx_b = array.get(fx_bars, i)
fx_p = array.get(fx_prices, i)
fx_t = array.get(fx_types, i)
fx_m = array.get(fx_macds, i)
if fx_b > bounce_bar and fx_t == -1 and fx_p > bs_price
already_marked = false
if array.size(bs_bars) > 0
for m = 0 to array.size(bs_bars) - 1
if array.get(bs_bars, m) == fx_b
already_marked := true
break
macd_valid = not use_macd_filter or fx_m < 0
if not already_marked and macd_valid and bar_index - fx_b <= 5000
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)
fx_t = array.get(fx_types, i)
if fx_b > bs_bar and fx_t == -1
pullback_bar := fx_b
break
// 找回调后的顶分型(二卖候选),且必须不过一卖
if not na(pullback_bar)
for i = 0 to fx_count - 1
fx_b = array.get(fx_bars, i)
fx_p = array.get(fx_prices, i)
fx_t = array.get(fx_types, i)
fx_m = array.get(fx_macds, i)
if fx_b > pullback_bar and fx_t == 1 and fx_p < bs_price
already_marked = false
if array.size(bs_bars) > 0
for m = 0 to array.size(bs_bars) - 1
if array.get(bs_bars, m) == fx_b
already_marked := true
break
macd_valid = not use_macd_filter or fx_m > 0
if not already_marked and macd_valid and bar_index - fx_b <= 5000
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)