Files
tradingview/chan_engine.pine
jackyu66gitandCursor e0b17e05a6 添加缠论画法(chan引擎)指标
移植合并K、分型、笔、线段、中枢和买卖点,并加上均线与 ChanMACD 的 SD/CD 标记。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-09 17:54:16 +08:00

1090 lines
47 KiB
Plaintext
Raw Permalink 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/
// 缠论画法 — 移植自 chan 引擎(chanlun/pipeline/builders
// K线包含 → 分型 → 笔 → 线段 → 中枢 → 买卖点
// 画法颜色与 chan/web 主周期叠层一致
//@version=6
indicator("缠论画法(chan引擎)", shorttitle="缠论chan", overlay=true, max_lines_count=500, max_labels_count=500, max_boxes_count=500, max_bars_back=5000)
// ============================================================================
// 参数
// ============================================================================
show_merged_k = input.bool(false, "显示合并K线", group="显示")
show_fenxing = input.bool(true, "显示分型", group="显示")
show_fx_box = input.bool(true, "显示分型框", group="显示")
show_bi = input.bool(true, "显示笔", group="显示")
show_bi_pending = input.bool(true, "显示未完成笔", group="显示")
show_xianduan = input.bool(true, "显示线段", group="显示")
show_xd_pending = input.bool(true, "显示未完成线段", group="显示")
show_bi_zs = input.bool(true, "显示笔中枢", group="显示")
show_xd_zs = input.bool(true, "显示线段中枢", group="显示")
show_bsp = input.bool(true, "显示买卖点", group="显示")
show_info = input.bool(true, "显示结构计数", group="显示")
show_ema26 = input.bool(true, "显示EMA26", group="均线")
show_ema52 = input.bool(true, "显示EMA52", group="均线")
show_ema104 = input.bool(true, "显示EMA104", group="均线")
show_ema158 = input.bool(true, "显示EMA158", group="均线")
show_ma30 = input.bool(true, "显示MA30", group="均线")
show_ma250 = input.bool(true, "显示MA250", group="均线")
show_macd_div = input.bool(true, "显示MACD背离SD/CD", group="MACD背离")
bi_klc_min = input.int(4, "笔最小合并K线数", group="缠论参数", tooltip="结合律:相邻分型间隔,对应 chan 的 bi_klc_min=4")
color_bi_up = input.color(#dc3545, "上升笔", group="颜色")
color_bi_down = input.color(#28a745, "下降笔", group="颜色")
color_bi_pending = input.color(#FF0000, "未完成笔", group="颜色")
color_xd_up = input.color(#FF6B6B, "上升线段", group="颜色")
color_xd_down = input.color(#4CAF50, "下降线段", group="颜色")
color_xd_pending = input.color(#FF0000, "未完成线段", group="颜色")
color_bi_zs = input.color(#9C27B0, "笔中枢", group="颜色")
color_xd_zs = input.color(#FF9800, "线段中枢", group="颜色")
color_fx_top = input.color(#dc3545, "顶分型", group="颜色")
color_fx_bot = input.color(#28a745, "底分型", group="颜色")
color_ema26 = input.color(color.yellow, "EMA26", group="均线颜色")
color_ema52 = input.color(color.orange, "EMA52", group="均线颜色")
color_ema104 = input.color(color.blue, "EMA104", group="均线颜色")
color_ema158 = input.color(color.purple, "EMA158", group="均线颜色")
color_ma30 = input.color(color.gray, "MA30", group="均线颜色")
color_ma250 = input.color(color.fuchsia, "MA250", group="均线颜色")
[macd_line, signal_line, macd_hist] = ta.macd(close, 12, 26, 9)
ema26 = ta.ema(close, 26)
ema52 = ta.ema(close, 52)
ema104 = ta.ema(close, 104)
ema158 = ta.ema(close, 158)
ma30 = ta.sma(close, 30)
ma250 = ta.sma(close, 250)
plot(show_ema26 ? ema26 : na, "EMA26", color=color_ema26, linewidth=1)
plot(show_ema52 ? ema52 : na, "EMA52", color=color_ema52, linewidth=2)
plot(show_ema104 ? ema104 : na, "EMA104", color=color_ema104, linewidth=2)
plot(show_ema158 ? ema158 : na, "EMA158", color=color_ema158, linewidth=2)
plot(show_ma30 ? ma30 : na, "MA30", color=color_ma30, linewidth=1)
plot(show_ma250 ? ma250 : na, "MA250", color=color_ma250, linewidth=2)
// ChanMACDCD=同柱波内 continue_divSD=同向柱波之间 separate_div
var int wave_dir = 0
var int wave_len = 0
var float wave_peak = na
var int unit_dir = 0
var float unit_peak = na
var int unit_sd_n = 0
sd_n = 0
cd_hit = false
if macd_line != 0 or signal_line != 0 or macd_hist != 0
cur_dir = macd_hist > 0 ? 1 : -1
if wave_dir == 0
wave_dir := cur_dir
wave_len := 1
wave_peak := na
else if cur_dir == wave_dir
wave_len += 1
if wave_len >= 3 and math.abs(macd_hist[1]) > math.abs(macd_hist[2]) and math.abs(macd_hist[1]) > math.abs(macd_hist)
if na(wave_peak) or math.abs(macd_hist[1]) > math.abs(wave_peak)
wave_peak := macd_hist[1]
if unit_dir == wave_dir and not na(unit_peak) and math.abs(wave_peak) < math.abs(unit_peak)
if macd_line[1] * macd_hist[1] > 0
unit_sd_n += 1
sd_n := unit_sd_n
else if macd_line[1] * macd_hist[1] > 0 and signal_line[1] * macd_hist[1] > 0
cd_hit := true
else
if wave_dir == unit_dir and not na(wave_peak)
if na(unit_peak) or math.abs(wave_peak) >= math.abs(unit_peak)
unit_peak := wave_peak
wave_dir := cur_dir
wave_len := 1
wave_peak := na
if signal_line[1] >= 0 and signal_line < 0
unit_dir := -1
unit_peak := wave_dir == -1 ? wave_peak : na
unit_sd_n := 0
else if signal_line[1] <= 0 and signal_line > 0
unit_dir := 1
unit_peak := wave_dir == 1 ? wave_peak : na
unit_sd_n := 0
plotshape(show_macd_div and sd_n == 1 and macd_line[1] > 0, title="SD1上", style=shape.triangleup, location=location.abovebar, color=#03a9f4, offset=-1, text="SD1", textcolor=#03a9f4, size=size.tiny)
plotshape(show_macd_div and sd_n == 1 and macd_line[1] < 0, title="SD1下", style=shape.triangleup, location=location.belowbar, color=#03a9f4, offset=-1, text="SD1", textcolor=#03a9f4, size=size.tiny)
plotshape(show_macd_div and sd_n == 2 and macd_line[1] > 0, title="SD2上", style=shape.triangleup, location=location.abovebar, color=#03a9f4, offset=-1, text="SD2", textcolor=#03a9f4, size=size.tiny)
plotshape(show_macd_div and sd_n == 2 and macd_line[1] < 0, title="SD2下", style=shape.triangleup, location=location.belowbar, color=#03a9f4, offset=-1, text="SD2", textcolor=#03a9f4, size=size.tiny)
plotshape(show_macd_div and sd_n >= 3 and macd_line[1] > 0, title="SD上", style=shape.triangleup, location=location.abovebar, color=#03a9f4, offset=-1, text="SD", textcolor=#03a9f4, size=size.tiny)
plotshape(show_macd_div and sd_n >= 3 and macd_line[1] < 0, title="SD下", style=shape.triangleup, location=location.belowbar, color=#03a9f4, offset=-1, text="SD", textcolor=#03a9f4, size=size.tiny)
plotshape(show_macd_div and cd_hit and macd_line[1] > 0, title="CD上", style=shape.triangledown, location=location.abovebar, color=#ff9800, offset=-1, text="CD", textcolor=#ff9800, size=size.tiny)
plotshape(show_macd_div and cd_hit and macd_line[1] < 0, title="CD下", style=shape.triangledown, location=location.belowbar, color=#ff9800, offset=-1, text="CD", textcolor=#ff9800, size=size.tiny)
// ============================================================================
// 合并K线状态
// ============================================================================
var float[] kh = array.new<float>()
var float[] kl = array.new<float>()
var float[] ko = array.new<float>()
var float[] kc = array.new<float>()
var float[] km = array.new<float>()
var int[] ks = array.new<int>()
var int[] ke = array.new<int>()
var int[] kd = array.new<int>()
// 结构缓存(仅在最后一根重建)
var int[] fx_idx = array.new<int>()
var int[] fx_typ = array.new<int>()
var float[] fx_px = array.new<float>()
var int[] fx_bar = array.new<int>()
var int[] bi_s = array.new<int>()
var int[] bi_e = array.new<int>()
var int[] bi_d = array.new<int>()
var int[] bi_sure = array.new<int>()
var float[] bi_h = array.new<float>()
var float[] bi_l = array.new<float>()
var float[] bi_macd = array.new<float>()
var int[] xd_s = array.new<int>()
var int[] xd_e = array.new<int>()
var int[] xd_d = array.new<int>()
var int[] xd_sure = array.new<int>()
var float[] xd_h = array.new<float>()
var float[] xd_l = array.new<float>()
var float[] feat_hh = array.new<float>()
var float[] feat_ll = array.new<float>()
var int[] feat_bi = array.new<int>()
var float[] us_hh = array.new<float>()
var float[] us_ll = array.new<float>()
var int[] us_bi = array.new<int>()
var int[] zs_s = array.new<int>()
var int[] zs_e = array.new<int>()
var float[] zs_zg = array.new<float>()
var float[] zs_zd = array.new<float>()
var float[] zs_gg = array.new<float>()
var float[] zs_dd = array.new<float>()
var int[] zs_sure = array.new<int>()
var int[] zs_dir = array.new<int>()
var int[] xzs_s = array.new<int>()
var int[] xzs_e = array.new<int>()
var float[] xzs_zg = array.new<float>()
var float[] xzs_zd = array.new<float>()
var int[] xzs_sure = array.new<int>()
var int[] xzs_dir = array.new<int>()
var int[] bsp_bar = array.new<int>()
var float[] bsp_px = array.new<float>()
var int[] bsp_kind = array.new<int>()
var string[] bsp_txt = array.new<string>()
var table info_tbl = table.new(position.top_right, 2, 7, bgcolor=color.new(color.black, 70), border_width=1)
var int last_top = -1
var int last_bot = -1
var line pending_bi_ln = na
var line pending_xd_ln = na
var box[] zs_boxes = array.new<box>()
var line[] zs_gg_lns = array.new<line>()
var line[] zs_dd_lns = array.new<line>()
var box[] xzs_boxes = array.new<box>()
// ============================================================================
// 工具函数(必须在全局)
// ============================================================================
klc_included(ph, pl, ch, cl) =>
bool inc = false
int mode = 0
if ph >= ch
if pl <= cl
inc := true
mode := 1
else if ph == ch
inc := true
mode := 2
else if pl >= cl
inc := true
mode := 3
[inc, mode]
is_top_fx(h1, l1, h2, l2, h3, l3) =>
h2 > h1 and h2 > h3 and l2 > l1 and l2 > l3
is_bot_fx(h1, l1, h2, l2, h3, l3) =>
l2 < l1 and l2 < l3 and h2 < h1 and h2 < h3
sbi_included(sh, sl, bh, bl) =>
sh > bh and sl < bl
in_xwin(x) =>
not na(x) and bar_index - x < 5000
push_fx(ki, typ) =>
array.push(fx_idx, ki)
array.push(fx_typ, typ)
array.push(fx_px, typ == 1 ? array.get(kh, ki) : array.get(kl, ki))
array.push(fx_bar, array.get(ke, ki))
0
fx_has(ki) =>
found = false
n = array.size(fx_idx)
if n > 0
for j = 0 to n - 1
if array.get(fx_idx, j) == ki
found := true
found
sync_xd_hl(idx) =>
// 对齐 ChanSEG:涨段 high=终点笔高 low=起点笔低;跌段 high=起点笔高 low=终点笔低
s = array.get(xd_s, idx)
e = array.get(xd_e, idx)
d = array.get(xd_d, idx)
if d == 1
array.set(xd_h, idx, array.get(bi_h, e))
array.set(xd_l, idx, array.get(bi_l, s))
else
array.set(xd_h, idx, array.get(bi_h, s))
array.set(xd_l, idx, array.get(bi_l, e))
0
push_xd(s, e, d, sure) =>
array.push(xd_s, s)
array.push(xd_e, e)
array.push(xd_d, d)
array.push(xd_sure, sure)
if d == 1
array.push(xd_h, array.get(bi_h, e))
array.push(xd_l, array.get(bi_l, s))
else
array.push(xd_h, array.get(bi_h, s))
array.push(xd_l, array.get(bi_l, e))
0
finish_xd(end_i, sure) =>
xn = array.size(xd_s)
if xn > 0 and end_i >= array.get(xd_s, xn - 1)
array.set(xd_e, xn - 1, end_i)
array.set(xd_sure, xn - 1, sure)
sync_xd_hl(xn - 1)
0
sbi_try_include(hs, ls, bh, bl, dir) =>
inc = false
n = array.size(hs)
if n > 0
if array.get(hs, n - 1) > bh and array.get(ls, n - 1) < bl
if dir == -1
array.set(ls, n - 1, bl)
else
array.set(hs, n - 1, bh)
inc := true
inc
sbi_push(hs, ls, bs, bh, bl, bi) =>
array.push(hs, bh)
array.push(ls, bl)
array.push(bs, bi)
0
sbi_seed(hs, ls, bs, bi) =>
array.clear(hs)
array.clear(ls)
array.clear(bs)
if bi >= 0
array.push(hs, array.get(bi_h, bi))
array.push(ls, array.get(bi_l, bi))
array.push(bs, bi)
0
sbi_fx(hs, ls) =>
int fx = 0
int gap = 0
n = array.size(hs)
if n >= 3
h1 = array.get(hs, n - 3)
l1 = array.get(ls, n - 3)
h2 = array.get(hs, n - 2)
l2 = array.get(ls, n - 2)
h3 = array.get(hs, n - 1)
l3 = array.get(ls, n - 1)
if h2 > h1 and h2 > h3
fx := 1
if l2 > h1
gap := 1
else if l2 < l1 and l2 < l3
fx := -1
if h2 < l1
gap := 1
[fx, gap]
trim_klc() =>
if array.size(kh) > 4000
array.shift(kh)
array.shift(kl)
array.shift(ko)
array.shift(kc)
array.shift(km)
array.shift(ks)
array.shift(ke)
array.shift(kd)
0
add_klc_to_last(ci) =>
// 对齐 ChanBI.add_klc:只推进终点,不改 high/low
bn = array.size(bi_s)
if bn > 0
array.set(bi_e, bn - 1, ci)
acc = array.get(bi_macd, bn - 1)
hv = array.get(km, ci)
if array.get(bi_d, bn - 1) == 1
if hv > 0
acc += hv
else if hv < 0
acc -= hv
array.set(bi_macd, bn - 1, acc)
0
confirm_bi(end_i) =>
// 对齐 ChanBI.set_end_klc:按方向用终点分型延伸 high/low
bn = array.size(bi_s)
if bn > 0 and array.get(bi_sure, bn - 1) == 0
array.set(bi_e, bn - 1, end_i)
array.set(bi_sure, bn - 1, 1)
d = array.get(bi_d, bn - 1)
if d == 1
eh = array.get(kh, end_i)
if eh > array.get(bi_h, bn - 1)
array.set(bi_h, bn - 1, eh)
else
el = array.get(kl, end_i)
if el < array.get(bi_l, bn - 1)
array.set(bi_l, bn - 1, el)
0
new_bi(start_i, dir) =>
array.push(bi_s, start_i)
array.push(bi_e, start_i)
array.push(bi_d, dir)
array.push(bi_sure, 0)
array.push(bi_h, array.get(kh, start_i))
array.push(bi_l, array.get(kl, start_i))
array.push(bi_macd, 0.0)
0
reset_struct() =>
array.clear(fx_idx)
array.clear(fx_typ)
array.clear(fx_px)
array.clear(fx_bar)
array.clear(bi_s)
array.clear(bi_e)
array.clear(bi_d)
array.clear(bi_sure)
array.clear(bi_h)
array.clear(bi_l)
array.clear(bi_macd)
array.clear(xd_s)
array.clear(xd_e)
array.clear(xd_d)
array.clear(xd_sure)
array.clear(xd_h)
array.clear(xd_l)
array.clear(feat_hh)
array.clear(feat_ll)
array.clear(feat_bi)
array.clear(us_hh)
array.clear(us_ll)
array.clear(us_bi)
array.clear(zs_s)
array.clear(zs_e)
array.clear(zs_zg)
array.clear(zs_zd)
array.clear(zs_gg)
array.clear(zs_dd)
array.clear(zs_sure)
array.clear(zs_dir)
array.clear(xzs_s)
array.clear(xzs_e)
array.clear(xzs_zg)
array.clear(xzs_zd)
array.clear(xzs_sure)
array.clear(xzs_dir)
array.clear(bsp_bar)
array.clear(bsp_px)
array.clear(bsp_kind)
array.clear(bsp_txt)
0
bi_overlap(i) =>
ok = false
bn = array.size(bi_s)
if i + 2 < bn
if array.get(bi_sure, i + 2) == 1
if array.get(bi_d, i) == 1
ok := array.get(bi_h, i) > array.get(bi_l, i + 1) and array.get(bi_h, i) < array.get(bi_h, i + 2)
else
ok := array.get(bi_h, i) > array.get(bi_h, i + 1) and array.get(bi_l, i) > array.get(bi_l, i + 2)
ok
// ============================================================================
// 逐K合并
// ============================================================================
nklc = array.size(kh)
if nklc == 0
array.push(kh, high)
array.push(kl, low)
array.push(ko, open)
array.push(kc, close)
array.push(km, macd_hist)
array.push(ks, bar_index)
array.push(ke, bar_index)
array.push(kd, close >= open ? 1 : -1)
else
last = nklc - 1
ph = array.get(kh, last)
pl = array.get(kl, last)
pdir = array.get(kd, last)
[inc, mode] = klc_included(ph, pl, high, low)
if inc
if mode == 1
if pdir == 1
array.set(kl, last, low)
else
array.set(kh, last, high)
else if mode == 2
if pdir == 1
array.set(kh, last, high)
else
array.set(kl, last, low)
else
if pdir == 1
array.set(kh, last, high)
else
array.set(kl, last, low)
array.set(kc, last, close)
array.set(km, last, macd_hist)
array.set(ke, last, bar_index)
nh = array.get(kh, last)
nl = array.get(kl, last)
if array.get(ko, last) > nh
array.set(ko, last, nh)
if array.get(kc, last) > nh
array.set(kc, last, nh)
if array.get(kc, last) < nl
array.set(kc, last, nl)
if array.get(ko, last) < nl
array.set(ko, last, nl)
else
ndir = high > ph ? 1 : -1
array.push(kh, high)
array.push(kl, low)
array.push(ko, open)
array.push(kc, close)
array.push(km, macd_hist)
array.push(ks, bar_index)
array.push(ke, bar_index)
array.push(kd, ndir)
trim_klc()
// ============================================================================
// 结构重建 + 绘制:对齐完整版,全部放在最后一根
// ============================================================================
if barstate.islast
reset_struct()
int N = array.size(kh)
last_top := -1
last_bot := -1
// ---- 笔:cal_bi_list 有效路径(分型按结合律过滤,只画笔端点)----
if N >= 1
for i = 0 to N - 1
int fx = 0
if i >= 1 and i <= N - 3
h1 = array.get(kh, i - 1)
l1 = array.get(kl, i - 1)
h2 = array.get(kh, i)
l2 = array.get(kl, i)
h3 = array.get(kh, i + 1)
l3 = array.get(kl, i + 1)
if is_top_fx(h1, l1, h2, l2, h3, l3)
fx := 1
else if is_bot_fx(h1, l1, h2, l2, h3, l3)
fx := -1
if fx == 1 and last_bot >= 1 and last_bot + 1 < N and i >= 1 and i + 1 < N
bot_h = math.max(array.get(kh, last_bot), math.max(array.get(kh, last_bot - 1), array.get(kh, last_bot + 1)))
if (bot_h > array.get(kl, i - 1) or bot_h > array.get(kl, i + 1)) and (i - last_bot < 100)
fx := 0
if fx == -1 and last_top >= 1 and last_top + 1 < N and i >= 1 and i + 1 < N
top_l = math.min(array.get(kl, last_top), math.min(array.get(kl, last_top - 1), array.get(kl, last_top + 1)))
if (top_l < array.get(kh, i - 1) or top_l < array.get(kh, i + 1)) and (i - last_top < 100)
fx := 0
if fx == 0
add_klc_to_last(i)
else if fx == 1
if last_top >= 0
if last_bot >= 0
if last_bot < last_top
if array.get(kh, last_top) <= array.get(kh, i)
last_top := i
add_klc_to_last(i)
else if last_bot + bi_klc_min > i
add_klc_to_last(i)
else
confirm_bi(last_bot)
new_bi(last_bot, 1)
add_klc_to_last(i)
last_top := i
else
if array.get(kh, last_top) < array.get(kh, i)
bn0 = array.size(bi_s)
if bn0 > 0
array.set(bi_s, bn0 - 1, i)
array.set(bi_d, bn0 - 1, -1)
array.set(bi_h, bn0 - 1, array.get(kh, i))
array.set(bi_l, bn0 - 1, array.get(kl, i))
last_top := i
add_klc_to_last(i)
else if last_bot >= 0
if last_bot + bi_klc_min > i
add_klc_to_last(i)
else
last_top := i
add_klc_to_last(i)
else
last_top := i
new_bi(i, -1)
add_klc_to_last(i)
else
if last_bot >= 0
if last_top >= 0
if last_top < last_bot
if array.get(kl, last_bot) >= array.get(kl, i)
last_bot := i
add_klc_to_last(i)
else if last_top + bi_klc_min > i
add_klc_to_last(i)
else
confirm_bi(last_top)
new_bi(last_top, -1)
add_klc_to_last(i)
last_bot := i
else
if array.get(kl, last_bot) > array.get(kl, i)
bn1 = array.size(bi_s)
if bn1 > 0
array.set(bi_s, bn1 - 1, i)
array.set(bi_d, bn1 - 1, 1)
array.set(bi_h, bn1 - 1, array.get(kh, i))
array.set(bi_l, bn1 - 1, array.get(kl, i))
last_bot := i
add_klc_to_last(i)
else if last_top >= 0
if last_top + bi_klc_min > i
add_klc_to_last(i)
else
last_bot := i
add_klc_to_last(i)
else
last_bot := i
new_bi(i, 1)
add_klc_to_last(i)
int bn = array.size(bi_s)
if bn > 0 and array.get(bi_sure, bn - 1) == 0
array.set(bi_e, bn - 1, N - 1)
// ---- 分型:只画结合律之后的笔端点 + 当前未确认顶底 ----
if bn > 0
for i = 0 to bn - 1
push_fx(array.get(bi_s, i), array.get(bi_d, i) == 1 ? -1 : 1)
if i == bn - 1 and array.get(bi_sure, i) == 1
push_fx(array.get(bi_e, i), array.get(bi_d, i) == 1 ? 1 : -1)
if last_top >= 0 and not fx_has(last_top)
push_fx(last_top, 1)
if last_bot >= 0 and not fx_has(last_bot)
push_fx(last_bot, -1)
// ---- 线段:对齐 get_seg_list / ChanSBI(双特征序列 + 缺口确认)----
int last_up_bi = -1
int last_down_bi = -1
int look_for_top = 0
int look_for_bot = 0
if bn >= 3
for i = 0 to bn - 1
d = array.get(bi_d, i)
bh = array.get(bi_h, i)
bl = array.get(bi_l, i)
xn0 = array.size(xd_s)
if xn0 == 0
if bi_overlap(i)
push_xd(i, i, d, 0)
if d == 1
sbi_seed(us_hh, us_ll, us_bi, i)
else
sbi_seed(feat_hh, feat_ll, feat_bi, i)
else
array.set(xd_e, xn0 - 1, i)
cur = array.get(xd_d, xn0 - 1)
if cur == 1
if d == -1
ds_n = array.size(feat_bi)
if ds_n > 1
if not sbi_try_include(feat_hh, feat_ll, bh, bl, -1)
sbi_push(feat_hh, feat_ll, feat_bi, bh, bl, i)
[fxv, gapv] = sbi_fx(feat_hh, feat_ll)
if fxv == 1
mid_s = array.get(feat_bi, array.size(feat_bi) - 2)
end_i = mid_s - 1
if look_for_top == 1
if xn0 >= 2
array.set(xd_sure, xn0 - 2, 1)
look_for_top := 0
if gapv == 1
look_for_bot := 1
finish_xd(end_i, 0)
push_xd(mid_s, i, -1, 0)
sbi_seed(us_hh, us_ll, us_bi, last_up_bi)
else if look_for_bot == 1
look_for_bot := 0
array.set(xd_s, xn0 - 1, mid_s)
sync_xd_hl(xn0 - 1)
if xn0 >= 2
array.set(xd_e, xn0 - 2, end_i)
array.set(xd_sure, xn0 - 2, 1)
sync_xd_hl(xn0 - 2)
sbi_seed(us_hh, us_ll, us_bi, last_up_bi)
else
finish_xd(end_i, 1)
push_xd(mid_s, i, -1, 0)
sbi_seed(us_hh, us_ll, us_bi, last_up_bi)
else if ds_n == 1
if not sbi_try_include(feat_hh, feat_ll, bh, bl, -1)
sbi_push(feat_hh, feat_ll, feat_bi, bh, bl, i)
else
sbi_push(feat_hh, feat_ll, feat_bi, bh, bl, i)
else if array.size(us_bi) > 0
if not sbi_try_include(us_hh, us_ll, bh, bl, 1)
sbi_push(us_hh, us_ll, us_bi, bh, bl, i)
else
if d == 1
us_n = array.size(us_bi)
if us_n > 1
if not sbi_try_include(us_hh, us_ll, bh, bl, 1)
sbi_push(us_hh, us_ll, us_bi, bh, bl, i)
[fxv2, gapv2] = sbi_fx(us_hh, us_ll)
if fxv2 == -1
mid_s = array.get(us_bi, array.size(us_bi) - 2)
end_i = mid_s - 1
if look_for_bot == 1
if xn0 >= 2
array.set(xd_sure, xn0 - 2, 1)
look_for_bot := 0
if gapv2 == 1
look_for_top := 1
finish_xd(end_i, 0)
push_xd(mid_s, i, 1, 0)
sbi_seed(feat_hh, feat_ll, feat_bi, last_down_bi)
else if look_for_top == 1
look_for_top := 0
array.set(xd_s, xn0 - 1, mid_s)
sync_xd_hl(xn0 - 1)
if xn0 >= 2
array.set(xd_e, xn0 - 2, end_i)
array.set(xd_sure, xn0 - 2, 1)
sync_xd_hl(xn0 - 2)
sbi_seed(feat_hh, feat_ll, feat_bi, last_down_bi)
else
finish_xd(end_i, 1)
push_xd(mid_s, i, 1, 0)
sbi_seed(feat_hh, feat_ll, feat_bi, last_down_bi)
else if us_n == 1
if not sbi_try_include(us_hh, us_ll, bh, bl, 1)
sbi_push(us_hh, us_ll, us_bi, bh, bl, i)
else
sbi_push(us_hh, us_ll, us_bi, bh, bl, i)
else if array.size(feat_bi) > 0
if not sbi_try_include(feat_hh, feat_ll, bh, bl, -1)
sbi_push(feat_hh, feat_ll, feat_bi, bh, bl, i)
if d == 1
last_up_bi := i
else
last_down_bi := i
int xn = array.size(xd_s)
if xn > 0 and array.get(xd_sure, xn - 1) == 0 and bn > 0
array.set(xd_e, xn - 1, bn - 1)
sync_xd_hl(xn - 1)
// ---- 笔中枢:逐字对应 cal_bi_zs_list_pure ----
// zg/zd/gg/dd 只由前三笔钉死;按 leave+back 两笔一组延伸;位置过滤用整段首尾方向
if bn >= 3
int start_idx = 0
float last_zg = na
float last_zdv = na
while start_idx + 2 < bn
if array.get(bi_sure, start_idx) != 1 or array.get(bi_sure, start_idx + 1) != 1 or array.get(bi_sure, start_idx + 2) != 1
start_idx += 1
else
d1 = array.get(bi_d, start_idx)
d2 = array.get(bi_d, start_idx + 1)
d3 = array.get(bi_d, start_idx + 2)
if not (d1 != d2 and d1 == d3)
start_idx += 1
else
h1 = array.get(bi_h, start_idx)
h2 = array.get(bi_h, start_idx + 1)
h3 = array.get(bi_h, start_idx + 2)
l1 = array.get(bi_l, start_idx)
l2 = array.get(bi_l, start_idx + 1)
l3 = array.get(bi_l, start_idx + 2)
zg = math.min(h1, math.min(h2, h3))
zdv = math.max(l1, math.max(l2, l3))
gg = math.max(h1, math.max(h2, h3))
dd = math.min(l1, math.min(l2, l3))
if zg <= zdv
start_idx += 1
else
end_idx = start_idx + 2
ext = start_idx + 3
while ext + 1 < bn
if array.get(bi_sure, ext) != 1 or array.get(bi_sure, ext + 1) != 1
break
else if array.get(bi_h, ext + 1) >= zdv and array.get(bi_l, ext + 1) <= zg
end_idx := ext + 1
ext += 2
else
break
pos_ok = true
if not na(last_zg)
if zg <= last_zdv
pos_ok := d1 == 1 and array.get(bi_d, end_idx) == 1
else if zdv >= last_zg
pos_ok := d1 == -1 and array.get(bi_d, end_idx) == -1
if not pos_ok
start_idx += 1
else
array.push(zs_s, start_idx)
array.push(zs_e, end_idx)
array.push(zs_zg, zg)
array.push(zs_zd, zdv)
array.push(zs_gg, gg)
array.push(zs_dd, dd)
array.push(zs_dir, d1 == -1 ? 1 : -1)
array.push(zs_sure, 1)
last_zg := zg
last_zdv := zdv
start_idx := end_idx + 1
int zn = array.size(zs_s)
if zn > 0 and bn > 0
array.set(zs_sure, zn - 1, array.get(bi_sure, bn - 1))
if array.get(zs_sure, zn - 1) == 0
last_zs_bi = array.get(zs_e, zn - 1)
has_leave = false
if last_zs_bi + 1 < bn
zg0 = array.get(zs_zg, zn - 1)
zd0 = array.get(zs_zd, zn - 1)
for li = last_zs_bi + 1 to bn - 1
if array.get(bi_sure, li) == 1
bh0 = array.get(bi_h, li)
bl0 = array.get(bi_l, li)
if (bl0 > zg0 and bh0 > zg0) or (bh0 < zd0 and bl0 < zd0)
has_leave := true
break
if has_leave and array.get(bi_sure, last_zs_bi) == 1
array.set(zs_sure, zn - 1, 1)
// ---- 线段中枢:从第4根起 ----
if xn >= 6
int si = 3
float lz_zg = na
float lz_zd = na
while si + 2 < xn
if array.get(xd_sure, si) != 1 or array.get(xd_sure, si + 1) != 1 or array.get(xd_sure, si + 2) != 1
si += 1
else
a1 = array.get(xd_d, si)
a2 = array.get(xd_d, si + 1)
a3 = array.get(xd_d, si + 2)
zg = math.min(array.get(xd_h, si), math.min(array.get(xd_h, si + 1), array.get(xd_h, si + 2)))
zdv = math.max(array.get(xd_l, si), math.max(array.get(xd_l, si + 1), array.get(xd_l, si + 2)))
if zg <= zdv
si += 1
else
valid = false
zdir = 0
if na(lz_zg)
if a1 == -1
zdir := 1
valid := a2 == 1 and a3 == -1
else
zdir := -1
valid := a2 == -1 and a3 == 1
else if zdv > lz_zg
zdir := 1
valid := a1 == -1 and a2 == 1 and a3 == -1
else if zg < lz_zd
zdir := -1
valid := a1 == 1 and a2 == -1 and a3 == 1
if not valid
merged_zs = false
if not na(lz_zg)
in_last = (zdv > lz_zd and zdv < lz_zg) or (zg < lz_zg and zg > lz_zd) or (zg > lz_zg and zdv < lz_zd) or (zg < lz_zg and zdv > lz_zd)
if in_last
array.set(xzs_e, array.size(xzs_s) - 1, si + 2)
merged_zs := true
si += 4
if not merged_zs
si += 1
else
base_si = si
eidx = si + 2
li = si + 4
while li < xn
if array.get(xd_sure, li) != 1
break
else
bs = array.get(xd_s, li)
be = array.get(xd_e, li)
sh = math.max(array.get(bi_h, bs), array.get(bi_h, be))
sl = math.min(array.get(bi_l, bs), array.get(bi_l, be))
if sh >= zdv and sl <= zg
eidx := li
li += 2
else
break
array.push(xzs_s, base_si)
array.push(xzs_e, eidx)
array.push(xzs_zg, zg)
array.push(xzs_zd, zdv)
array.push(xzs_dir, zdir)
array.push(xzs_sure, 1)
lz_zg := zg
lz_zd := zdv
si := base_si + 4 + (eidx - (base_si + 2))
// ---- 买卖点 ----
if zn > 0 and bn >= 4
for z = 0 to zn - 1
if array.get(zs_sure, z) != 1
continue
ze = array.get(zs_e, z)
zg = array.get(zs_zg, z)
zdv = array.get(zs_zd, z)
zdir0 = array.get(zs_dir, z)
leave = ze
if leave < bn and array.get(bi_sure, leave) == 1
ek = array.get(bi_e, leave)
if array.get(bi_d, leave) == 1
if array.get(kh, ek) <= zg or (leave + 1 < bn and array.get(bi_sure, leave + 1) == 1 and array.get(kl, array.get(bi_e, leave + 1)) < zdv)
leave += 1
else if array.get(kl, ek) >= zdv or (leave + 1 < bn and array.get(bi_sure, leave + 1) == 1 and array.get(kh, array.get(bi_e, leave + 1)) > zg)
leave += 1
if leave >= bn or array.get(bi_sure, leave) != 1
continue
ek = array.get(bi_e, leave)
ldir = array.get(bi_d, leave)
if (zdir0 == 1 and ldir == 1 and array.get(kh, ek) < zg and array.get(kh, ek) > zdv) or (zdir0 == -1 and ldir == -1 and array.get(kl, ek) < zg and array.get(kl, ek) > zdv)
if leave + 1 < bn
leave += 1
if leave >= bn or array.get(bi_sure, leave) != 1
continue
ldir := array.get(bi_d, leave)
enter = array.get(zs_s, z) - 1
div = false
if enter >= 0 and array.get(bi_d, enter) == ldir
div := array.get(bi_macd, leave) < array.get(bi_macd, enter)
ebar = array.get(ke, array.get(bi_e, leave))
if ldir == 1
if div
array.push(bsp_bar, ebar)
array.push(bsp_px, array.get(bi_h, leave))
array.push(bsp_kind, -1)
array.push(bsp_txt, "S1")
pb = leave + 1
if pb < bn and array.get(bi_sure, pb) == 1 and array.get(bi_d, pb) == -1
if array.get(bi_l, pb) >= zg
array.push(bsp_bar, array.get(ke, array.get(bi_e, pb)))
array.push(bsp_px, array.get(bi_l, pb))
array.push(bsp_kind, 1)
array.push(bsp_txt, "B3")
if div
s2 = pb + 1
if s2 < bn and array.get(bi_sure, s2) == 1 and array.get(bi_h, s2) < array.get(bi_h, leave)
array.push(bsp_bar, array.get(ke, array.get(bi_e, s2)))
array.push(bsp_px, array.get(bi_h, s2))
array.push(bsp_kind, -1)
array.push(bsp_txt, "S2")
else
if div
array.push(bsp_bar, ebar)
array.push(bsp_px, array.get(bi_l, leave))
array.push(bsp_kind, 1)
array.push(bsp_txt, "B1")
pb = leave + 1
if pb < bn and array.get(bi_sure, pb) == 1 and array.get(bi_d, pb) == 1
if array.get(bi_h, pb) <= zdv
array.push(bsp_bar, array.get(ke, array.get(bi_e, pb)))
array.push(bsp_px, array.get(bi_h, pb))
array.push(bsp_kind, -1)
array.push(bsp_txt, "S3")
if div
b2 = pb + 1
if b2 < bn and array.get(bi_sure, b2) == 1 and array.get(bi_l, b2) > array.get(bi_l, leave)
array.push(bsp_bar, array.get(ke, array.get(bi_e, b2)))
array.push(bsp_px, array.get(bi_l, b2))
array.push(bsp_kind, 1)
array.push(bsp_txt, "B2")
// 画法对齐 chan_theory_complete.pine:默认 bar_index,只画最近 5000 根,不指定 xloc.bar_time
if barstate.islast
nK = array.size(kh)
nFx = array.size(fx_idx)
nBi = array.size(bi_s)
nXd = array.size(xd_s)
nZs = array.size(zs_s)
nXz = array.size(xzs_s)
if show_merged_k and nK > 0
for i = 0 to nK - 1
x1 = array.get(ks, i)
x2 = array.get(ke, i)
if in_xwin(x1)
box.new(x1, array.get(kh, i), x2, array.get(kl, i), border_color=color.purple, bgcolor=color.new(color.purple, 88), border_width=1)
if show_fenxing and nFx > 0
for i = 0 to nFx - 1
xb = array.get(fx_bar, i)
if in_xwin(xb)
if array.get(fx_typ, i) == 1
label.new(xb, array.get(fx_px, i), "T", color=color.new(color_fx_top, 100), textcolor=color_fx_top, style=label.style_label_down, size=size.large)
else
label.new(xb, array.get(fx_px, i), "B", color=color.new(color_fx_bot, 100), textcolor=color_fx_bot, style=label.style_label_up, size=size.large)
if show_fx_box and nFx > 0
for i = 0 to nFx - 1
ki = array.get(fx_idx, i)
if ki >= 1 and ki + 1 < nK
x1 = array.get(ke, ki - 1)
x2 = array.get(ke, ki + 1)
if in_xwin(x1)
if array.get(fx_typ, i) == 1
box.new(x1, array.get(kh, ki), x2, math.min(array.get(kl, ki - 1), array.get(kl, ki + 1)), border_color=color.new(color_fx_top, 40), bgcolor=color.new(color_fx_top, 92), border_width=1)
else
box.new(x1, math.max(array.get(kh, ki - 1), array.get(kh, ki + 1)), x2, array.get(kl, ki), border_color=color.new(color_fx_bot, 40), bgcolor=color.new(color_fx_bot, 92), border_width=1)
if not na(pending_bi_ln)
line.delete(pending_bi_ln)
pending_bi_ln := na
if not na(pending_xd_ln)
line.delete(pending_xd_ln)
pending_xd_ln := na
if array.size(zs_boxes) > 0
for zi = 0 to array.size(zs_boxes) - 1
box.delete(array.get(zs_boxes, zi))
line.delete(array.get(zs_gg_lns, zi))
line.delete(array.get(zs_dd_lns, zi))
array.clear(zs_boxes)
array.clear(zs_gg_lns)
array.clear(zs_dd_lns)
if array.size(xzs_boxes) > 0
for zi = 0 to array.size(xzs_boxes) - 1
box.delete(array.get(xzs_boxes, zi))
array.clear(xzs_boxes)
if show_bi and nBi > 0
for i = 0 to nBi - 1
si = array.get(bi_s, i)
ei = array.get(bi_e, i)
d = array.get(bi_d, i)
sure = array.get(bi_sure, i)
x1 = array.get(ke, si)
y1 = d == 1 ? array.get(kl, si) : array.get(kh, si)
if sure == 1
x2 = array.get(ke, ei)
y2 = d == 1 ? array.get(kh, ei) : array.get(kl, ei)
if in_xwin(x1)
line.new(x1, y1, x2, y2, color=d == 1 ? color_bi_up : color_bi_down, width=2)
else if show_bi_pending and i == nBi - 1 and in_xwin(x1)
pending_bi_ln := line.new(x1, y1, bar_index, d == 1 ? array.get(kh, nK - 1) : array.get(kl, nK - 1), color=color_bi_pending, width=2, style=line.style_dashed)
if show_xianduan and nXd > 0
for i = 0 to nXd - 1
si = array.get(xd_s, i)
ei = array.get(xd_e, i)
if si < 0 or ei < 0 or si >= nBi or ei >= nBi
continue
d = array.get(xd_d, i)
sure = array.get(xd_sure, i)
ek = array.get(bi_e, ei)
x1 = array.get(ke, array.get(bi_s, si))
y1 = d == 1 ? array.get(kl, array.get(bi_s, si)) : array.get(kh, array.get(bi_s, si))
if sure == 1
x2 = array.get(ke, ek)
y2 = d == 1 ? array.get(kh, ek) : array.get(kl, ek)
if in_xwin(x1)
line.new(x1, y1, x2, y2, color=d == 1 ? color_xd_up : color_xd_down, width=2)
else if show_xd_pending and i == nXd - 1 and in_xwin(x1)
pending_xd_ln := line.new(x1, y1, bar_index, d == 1 ? array.get(kh, nK - 1) : array.get(kl, nK - 1), color=color_xd_pending, width=2, style=line.style_dashed)
if show_bi_zs and nZs > 0
for i = 0 to nZs - 1
si = array.get(zs_s, i)
ei = array.get(zs_e, i)
x1 = array.get(ke, array.get(bi_s, si))
x2 = array.get(zs_sure, i) == 1 ? array.get(ke, array.get(bi_e, ei)) : bar_index
zg = array.get(zs_zg, i)
zdv = array.get(zs_zd, i)
gg = array.get(zs_gg, i)
dd = array.get(zs_dd, i)
if in_xwin(x1)
array.push(zs_boxes, box.new(x1, zg, x2, zdv, border_color=color_bi_zs, bgcolor=na, border_width=1))
array.push(zs_gg_lns, line.new(x1, gg, x2, gg, color=color_bi_zs, width=1))
array.push(zs_dd_lns, line.new(x1, dd, x2, dd, color=color_bi_zs, width=1))
if show_xd_zs and nXz > 0
for i = 0 to nXz - 1
si = array.get(xzs_s, i)
ei = array.get(xzs_e, i)
if si >= nXd or ei >= nXd
continue
b1 = array.get(xd_s, si)
b2 = array.get(xd_e, ei)
if b1 >= nBi or b2 >= nBi
continue
x1 = array.get(ke, array.get(bi_s, b1))
x2 = array.get(xzs_sure, i) == 1 ? array.get(ke, array.get(bi_e, b2)) : bar_index
if in_xwin(x1)
array.push(xzs_boxes, box.new(x1, array.get(xzs_zg, i), x2, array.get(xzs_zd, i), border_color=color_xd_zs, bgcolor=na, border_width=2))
if show_bsp
bpn = array.size(bsp_bar)
if bpn > 0
for i = 0 to bpn - 1
xb = array.get(bsp_bar, i)
if in_xwin(xb)
if array.get(bsp_kind, i) == 1
label.new(xb, array.get(bsp_px, i), array.get(bsp_txt, i), color=color.lime, style=label.style_label_up, textcolor=color.black, size=size.tiny)
else
label.new(xb, array.get(bsp_px, i), array.get(bsp_txt, i), color=color.red, style=label.style_label_down, textcolor=color.white, size=size.tiny)
if show_info
table.cell(info_tbl, 0, 0, "合并K", text_color=color.gray, text_size=size.small)
table.cell(info_tbl, 1, 0, str.tostring(nK), text_color=color.white, text_size=size.small)
table.cell(info_tbl, 0, 1, "分型", text_color=color.gray, text_size=size.small)
table.cell(info_tbl, 1, 1, str.tostring(nFx), text_color=color.white, text_size=size.small)
table.cell(info_tbl, 0, 2, "笔", text_color=color.gray, text_size=size.small)
table.cell(info_tbl, 1, 2, str.tostring(nBi), text_color=color.white, text_size=size.small)
table.cell(info_tbl, 0, 3, "线段", text_color=color.gray, text_size=size.small)
table.cell(info_tbl, 1, 3, str.tostring(nXd), text_color=color.white, text_size=size.small)
table.cell(info_tbl, 0, 4, "笔中枢", text_color=color.gray, text_size=size.small)
table.cell(info_tbl, 1, 4, str.tostring(nZs), text_color=color.white, text_size=size.small)
table.cell(info_tbl, 0, 5, "段中枢", text_color=color.gray, text_size=size.small)
table.cell(info_tbl, 1, 5, str.tostring(nXz), text_color=color.white, text_size=size.small)
table.cell(info_tbl, 0, 6, "买卖点", text_color=color.gray, text_size=size.small)
table.cell(info_tbl, 1, 6, str.tostring(array.size(bsp_bar)), text_color=color.white, text_size=size.small)
else
table.clear(info_tbl, 0, 0, 1, 6)