289 lines
12 KiB
Plaintext
289 lines
12 KiB
Plaintext
//@version=6
|
|
indicator("缠论系统 - Chan Theory", shorttitle="缠论", overlay=true, max_lines_count=500, max_labels_count=500)
|
|
|
|
// ================== 参数设置 ==================
|
|
show_merged_klines = input.bool(true, "显示合并K线", group="显示设置")
|
|
show_bi = input.bool(true, "显示笔", group="显示设置")
|
|
show_segment = input.bool(true, "显示线段", group="显示设置")
|
|
show_zhongshu = input.bool(true, "显示中枢", group="显示设置")
|
|
show_signals = input.bool(true, "显示买卖点", group="显示设置")
|
|
|
|
bi_color = input.color(color.blue, "笔的颜色", group="颜色设置")
|
|
segment_color = input.color(color.red, "线段颜色", group="颜色设置")
|
|
zhongshu_color = input.color(color.gray, "中枢颜色", group="颜色设置")
|
|
|
|
// ================== 数据结构定义 ==================
|
|
type KLine
|
|
float high
|
|
float low
|
|
float open
|
|
float close
|
|
int index
|
|
|
|
type FenXing
|
|
int type // 1为顶分型,-1为底分型
|
|
float price
|
|
int index
|
|
bool valid
|
|
|
|
type Bi
|
|
FenXing start_fx
|
|
FenXing end_fx
|
|
bool valid
|
|
|
|
type Segment
|
|
Bi start_bi
|
|
Bi end_bi
|
|
bool valid
|
|
|
|
type ZhongShu
|
|
float high
|
|
float low
|
|
int start_index
|
|
int end_index
|
|
bool valid
|
|
|
|
// ================== K线合并函数 ==================
|
|
merge_klines(prev_kline, curr_kline) =>
|
|
var merged = KLine.new()
|
|
|
|
// 判断包含关系
|
|
prev_includes_curr = prev_kline.high >= curr_kline.high and prev_kline.low <= curr_kline.low
|
|
curr_includes_prev = curr_kline.high >= prev_kline.high and curr_kline.low <= prev_kline.low
|
|
|
|
if prev_includes_curr or curr_includes_prev
|
|
// 有包含关系,进行合并
|
|
if prev_kline.high >= prev_kline.low // 向上趋势中的合并
|
|
merged.high := math.max(prev_kline.high, curr_kline.high)
|
|
merged.low := math.max(prev_kline.low, curr_kline.low)
|
|
else // 向下趋势中的合并
|
|
merged.high := math.min(prev_kline.high, curr_kline.high)
|
|
merged.low := math.min(prev_kline.low, curr_kline.low)
|
|
|
|
merged.open := prev_kline.open
|
|
merged.close := curr_kline.close
|
|
merged.index := curr_kline.index
|
|
[merged, true]
|
|
else
|
|
// 无包含关系,返回当前K线
|
|
[curr_kline, false]
|
|
|
|
// ================== 分型识别函数 ==================
|
|
check_fenxing(k1, k2, k3) =>
|
|
var fx = FenXing.new()
|
|
|
|
// 顶分型:中间K线的高点是最高的,低点也是最高的
|
|
if k2.high > k1.high and k2.high > k3.high and k2.low > k1.low and k2.low > k3.low
|
|
fx.type := 1
|
|
fx.price := k2.high
|
|
fx.index := k2.index
|
|
fx.valid := true
|
|
fx
|
|
// 底分型:中间K线的低点是最低的,高点也是最低的
|
|
else if k2.low < k1.low and k2.low < k3.low and k2.high < k1.high and k2.high < k3.high
|
|
fx.type := -1
|
|
fx.price := k2.low
|
|
fx.index := k2.index
|
|
fx.valid := true
|
|
fx
|
|
else
|
|
fx.valid := false
|
|
fx
|
|
|
|
// ================== 笔的识别函数 ==================
|
|
create_bi(start_fx, end_fx) =>
|
|
var bi = Bi.new()
|
|
|
|
// 检查笔的有效性
|
|
if start_fx.valid and end_fx.valid and start_fx.type != end_fx.type and start_fx.index < end_fx.index
|
|
bi.start_fx := start_fx
|
|
bi.end_fx := end_fx
|
|
bi.valid := true
|
|
bi
|
|
else
|
|
bi.valid := false
|
|
bi
|
|
|
|
// ================== 线段识别函数 ==================
|
|
check_segment_break(bi_array) =>
|
|
// 简化的线段破坏判断逻辑
|
|
// 当新笔突破前面第三根笔的端点时,认为线段被破坏
|
|
bool segment_break = false
|
|
if array.size(bi_array) >= 4
|
|
current_bi = array.get(bi_array, array.size(bi_array) - 1)
|
|
third_bi = array.get(bi_array, array.size(bi_array) - 4)
|
|
|
|
if current_bi.valid and third_bi.valid
|
|
if current_bi.start_fx.type == 1 // 向下笔
|
|
segment_break := current_bi.end_fx.price < third_bi.end_fx.price
|
|
else // 向上笔
|
|
segment_break := current_bi.end_fx.price > third_bi.end_fx.price
|
|
|
|
segment_break
|
|
|
|
// ================== 中枢识别函数 ==================
|
|
find_zhongshu(bi_array) =>
|
|
var zhongshu_array = array.new<ZhongShu>()
|
|
|
|
if array.size(bi_array) >= 3
|
|
for i = 0 to array.size(bi_array) - 3
|
|
bi1 = array.get(bi_array, i)
|
|
bi2 = array.get(bi_array, i + 1)
|
|
bi3 = array.get(bi_array, i + 2)
|
|
|
|
if bi1.valid and bi2.valid and bi3.valid
|
|
// 计算重叠区间
|
|
high1 = math.max(bi1.start_fx.price, bi1.end_fx.price)
|
|
low1 = math.min(bi1.start_fx.price, bi1.end_fx.price)
|
|
high2 = math.max(bi2.start_fx.price, bi2.end_fx.price)
|
|
low2 = math.min(bi2.start_fx.price, bi2.end_fx.price)
|
|
high3 = math.max(bi3.start_fx.price, bi3.end_fx.price)
|
|
low3 = math.min(bi3.start_fx.price, bi3.end_fx.price)
|
|
|
|
overlap_high = math.min(math.min(high1, high2), high3)
|
|
overlap_low = math.max(math.max(low1, low2), low3)
|
|
|
|
if overlap_high > overlap_low
|
|
var zs = ZhongShu.new()
|
|
zs.high := overlap_high
|
|
zs.low := overlap_low
|
|
zs.start_index := bi1.start_fx.index
|
|
zs.end_index := bi3.end_fx.index
|
|
zs.valid := true
|
|
array.push(zhongshu_array, zs)
|
|
|
|
zhongshu_array
|
|
|
|
// ================== 买卖点识别函数 ==================
|
|
identify_trading_signals(bi_array, zhongshu_array) =>
|
|
var buy_signals = array.new<int>()
|
|
var sell_signals = array.new<int>()
|
|
|
|
if array.size(bi_array) >= 2 and array.size(zhongshu_array) >= 1
|
|
current_bi = array.get(bi_array, array.size(bi_array) - 1)
|
|
latest_zs = array.get(zhongshu_array, array.size(zhongshu_array) - 1)
|
|
|
|
if current_bi.valid and latest_zs.valid
|
|
// 一买:价格跌破中枢下沿后的第一次回调
|
|
if current_bi.end_fx.type == -1 and current_bi.end_fx.price < latest_zs.low
|
|
array.push(buy_signals, current_bi.end_fx.index)
|
|
|
|
// 一卖:价格突破中枢上沿后的第一次回调
|
|
if current_bi.end_fx.type == 1 and current_bi.end_fx.price > latest_zs.high
|
|
array.push(sell_signals, current_bi.end_fx.index)
|
|
|
|
[buy_signals, sell_signals]
|
|
|
|
// ================== 主逻辑 ==================
|
|
var merged_klines = array.new<KLine>()
|
|
var fenxing_array = array.new<FenXing>()
|
|
var bi_array = array.new<Bi>()
|
|
var segment_array = array.new<Segment>()
|
|
|
|
// 当前K线数据
|
|
current_kline = KLine.new(high, low, open, close, bar_index)
|
|
|
|
// K线合并处理
|
|
if array.size(merged_klines) > 0
|
|
last_merged = array.get(merged_klines, array.size(merged_klines) - 1)
|
|
[new_kline, is_merged] = merge_klines(last_merged, current_kline)
|
|
|
|
if is_merged
|
|
array.set(merged_klines, array.size(merged_klines) - 1, new_kline)
|
|
else
|
|
array.push(merged_klines, new_kline)
|
|
else
|
|
array.push(merged_klines, current_kline)
|
|
|
|
// 分型识别
|
|
if array.size(merged_klines) >= 3
|
|
k1 = array.get(merged_klines, array.size(merged_klines) - 3)
|
|
k2 = array.get(merged_klines, array.size(merged_klines) - 2)
|
|
k3 = array.get(merged_klines, array.size(merged_klines) - 1)
|
|
|
|
fx = check_fenxing(k1, k2, k3)
|
|
if fx.valid
|
|
array.push(fenxing_array, fx)
|
|
|
|
// 笔的构建
|
|
if array.size(fenxing_array) >= 2
|
|
for i = 0 to array.size(fenxing_array) - 2
|
|
start_fx = array.get(fenxing_array, i)
|
|
end_fx = array.get(fenxing_array, i + 1)
|
|
bi = create_bi(start_fx, end_fx)
|
|
if bi.valid
|
|
array.push(bi_array, bi)
|
|
|
|
// 中枢识别
|
|
zhongshu_array = find_zhongshu(bi_array)
|
|
|
|
// 买卖点识别
|
|
[buy_signals, sell_signals] = identify_trading_signals(bi_array, zhongshu_array)
|
|
|
|
// ================== 绘图显示 ==================
|
|
|
|
// 显示合并K线
|
|
if show_merged_klines and array.size(merged_klines) > 1
|
|
last_kline = array.get(merged_klines, array.size(merged_klines) - 1)
|
|
prev_kline = array.get(merged_klines, array.size(merged_klines) - 2)
|
|
line.new(bar_index[1], prev_kline.close, bar_index, last_kline.close, color=color.yellow, width=1)
|
|
|
|
// 显示笔
|
|
if show_bi and array.size(bi_array) > 0
|
|
for i = 0 to array.size(bi_array) - 1
|
|
bi = array.get(bi_array, i)
|
|
if bi.valid
|
|
line.new(bi.start_fx.index, bi.start_fx.price, bi.end_fx.index, bi.end_fx.price,
|
|
color=bi_color, width=2, style=line.style_solid)
|
|
|
|
// 显示分型
|
|
if array.size(fenxing_array) > 0
|
|
last_fx = array.get(fenxing_array, array.size(fenxing_array) - 1)
|
|
if last_fx.valid and last_fx.index == bar_index
|
|
if last_fx.type == 1
|
|
label.new(bar_index, last_fx.price, "顶", color=color.red, style=label.style_label_down, size=size.small)
|
|
else
|
|
label.new(bar_index, last_fx.price, "底", color=color.green, style=label.style_label_up, size=size.small)
|
|
|
|
// 显示中枢
|
|
if show_zhongshu and array.size(zhongshu_array) > 0
|
|
for i = 0 to array.size(zhongshu_array) - 1
|
|
zs = array.get(zhongshu_array, i)
|
|
if zs.valid
|
|
box.new(zs.start_index, zs.high, zs.end_index, zs.low, border_color=zhongshu_color, bgcolor=color.new(zhongshu_color, 90))
|
|
|
|
// 显示买卖点
|
|
if show_signals
|
|
if array.size(buy_signals) > 0
|
|
for i = 0 to array.size(buy_signals) - 1
|
|
signal_index = array.get(buy_signals, i)
|
|
if signal_index == bar_index
|
|
label.new(bar_index, low, "买", color=color.green, style=label.style_label_up, size=size.normal)
|
|
|
|
if array.size(sell_signals) > 0
|
|
for i = 0 to array.size(sell_signals) - 1
|
|
signal_index = array.get(sell_signals, i)
|
|
if signal_index == bar_index
|
|
label.new(bar_index, high, "卖", color=color.red, style=label.style_label_down, size=size.normal)
|
|
|
|
// ================== 警报设置 ==================
|
|
alertcondition(array.size(buy_signals) > 0 and array.get(buy_signals, array.size(buy_signals) - 1) == bar_index,
|
|
title="缠论买点", message="发现缠论买点信号")
|
|
alertcondition(array.size(sell_signals) > 0 and array.get(sell_signals, array.size(sell_signals) - 1) == bar_index,
|
|
title="缠论卖点", message="发现缠论卖点信号")
|
|
|
|
// ================== 信息面板 ==================
|
|
if barstate.islast
|
|
var table info_table = table.new(position.top_right, 2, 6, bgcolor=color.white, border_width=1)
|
|
table.cell(info_table, 0, 0, "缠论统计", text_color=color.black, text_size=size.normal)
|
|
table.cell(info_table, 1, 0, "", text_color=color.black)
|
|
table.cell(info_table, 0, 1, "合并K线数", text_color=color.black)
|
|
table.cell(info_table, 1, 1, str.tostring(array.size(merged_klines)), text_color=color.black)
|
|
table.cell(info_table, 0, 2, "分型数量", text_color=color.black)
|
|
table.cell(info_table, 1, 2, str.tostring(array.size(fenxing_array)), text_color=color.black)
|
|
table.cell(info_table, 0, 3, "笔数量", text_color=color.black)
|
|
table.cell(info_table, 1, 3, str.tostring(array.size(bi_array)), text_color=color.black)
|
|
table.cell(info_table, 0, 4, "中枢数量", text_color=color.black)
|
|
table.cell(info_table, 1, 4, str.tostring(array.size(zhongshu_array)), text_color=color.black)
|
|
table.cell(info_table, 0, 5, "当前级别", text_color=color.black)
|
|
table.cell(info_table, 1, 5, "1分钟", text_color=color.black) |