Files
tradingview/chan_theory_core.pine
2026-05-14 14:17:09 +08:00

697 lines
31 KiB
Plaintext

// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © 缠论核心版 - 从 chan_theory_complete 提取:K线包含处理、分型、笔、线段、中枢 + EMA均线趋势
//@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="K线显示")
show_fenxing = input.bool(true, "显示分型", group="分型显示")
show_bi = input.bool(true, "显示笔", group="笔显示")
show_bi_zhongshu = input.bool(true, "显示笔中枢", group="笔显示")
show_xianduan = input.bool(true, "显示线段", group="线段显示")
show_xd_zhongshu = 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="均线显示")
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_bi_zs = input.color(color.new(color.yellow, 80), "笔中枢颜色", group="笔颜色")
color_xd = input.color(color.blue, "线段颜色", 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)
// ============================================================================
// 三均线趋势排列检测
// ============================================================================
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_golden_cross = ta.crossover(ema52, ema104)
ema_death_cross = ta.crossunder(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="死叉")
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
is_bullish_confirmed = bullish_bars >= trend_confirm_bars
is_bearish_confirmed = bearish_bars >= trend_confirm_bars
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="空头背景")
// ============================================================================
// K线包含处理(缠论标准)
// ============================================================================
var float[] mk_high = array.new<float>()
var float[] mk_low = array.new<float>()
var int[] mk_idx = array.new<int>()
var int[] mk_start_idx = array.new<int>()
var float[] mk_macd = array.new<float>()
var int mk_trend = 0
is_contain(h1, l1, h2, l2) =>
(h1 >= h2 and l1 <= l2) or (h2 >= h1 and l2 <= l1)
mk_size = array.size(mk_high)
if mk_size == 0
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)
else
mk_trend := high > prev_h ? 1 : -1
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>()
var float[] fx_macds = array.new<float>()
curr_mk_size = array.size(mk_high)
if curr_mk_size >= 3
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)
is_top = h2 > h1 and h2 > h3
is_bottom = l2 < l1 and l2 < l3
macd_at_fx = array.get(mk_macd, curr_mk_size - 2)
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
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
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, "T",
color=color.new(color.red, 100),
style=label.style_none,
textcolor=color.red,
size=size.normal)
else
label.new(fx_bar, fx_price, "B",
color=color.new(color.green, 100),
style=label.style_none,
textcolor=color.green,
size=size.normal)
// ==================== 构建笔 ====================
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)
// ==================== 构建线段(标准特征序列法) ====================
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_raw_highs = array.new<float>()
var float[] feat_raw_lows = array.new<float>()
var int[] feat_raw_end_bars = array.new<int>()
var float[] feat_raw_end_prices = array.new<float>()
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<float>()
var float[] feat_merged_lows = array.new<float>()
var int[] feat_merged_end_bars = array.new<int>()
var float[] feat_merged_end_prices = array.new<float>()
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)
// === 特征序列包含处理 ===
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))
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
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
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>()
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<int>()
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)
// ==================== 构建线段中枢 ====================
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>()
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)
is_up_xd_zs = xd1_dir == -1 and xd2_dir == 1 and xd3_dir == -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)
// ==================== 绘制笔中枢 ====================
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)
// ==================== 绘制线段中枢 ====================
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)