// 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) // ChanMACD:CD=同柱波内 continue_div;SD=同向柱波之间 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() var float[] kl = array.new() var float[] ko = array.new() var float[] kc = array.new() var float[] km = array.new() var int[] ks = array.new() var int[] ke = array.new() var int[] kd = array.new() // 结构缓存(仅在最后一根重建) var int[] fx_idx = array.new() var int[] fx_typ = array.new() var float[] fx_px = array.new() var int[] fx_bar = array.new() var int[] bi_s = array.new() var int[] bi_e = array.new() var int[] bi_d = array.new() var int[] bi_sure = array.new() var float[] bi_h = array.new() var float[] bi_l = array.new() var float[] bi_macd = array.new() var int[] xd_s = array.new() var int[] xd_e = array.new() var int[] xd_d = array.new() var int[] xd_sure = array.new() var float[] xd_h = array.new() var float[] xd_l = array.new() var float[] feat_hh = array.new() var float[] feat_ll = array.new() var int[] feat_bi = array.new() var float[] us_hh = array.new() var float[] us_ll = array.new() var int[] us_bi = array.new() var int[] zs_s = array.new() var int[] zs_e = array.new() var float[] zs_zg = array.new() var float[] zs_zd = array.new() var float[] zs_gg = array.new() var float[] zs_dd = array.new() var int[] zs_sure = array.new() var int[] zs_dir = array.new() var int[] xzs_s = array.new() var int[] xzs_e = array.new() var float[] xzs_zg = array.new() var float[] xzs_zd = array.new() var int[] xzs_sure = array.new() var int[] xzs_dir = array.new() var int[] bsp_bar = array.new() var float[] bsp_px = array.new() var int[] bsp_kind = array.new() var string[] bsp_txt = array.new() 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() var line[] zs_gg_lns = array.new() var line[] zs_dd_lns = array.new() var box[] xzs_boxes = array.new() // ============================================================================ // 工具函数(必须在全局) // ============================================================================ 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)