diff --git a/chan_engine.pine b/chan_engine.pine new file mode 100644 index 0000000..faafd4c --- /dev/null +++ b/chan_engine.pine @@ -0,0 +1,1089 @@ +// 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)