294 lines
13 KiB
Plaintext
294 lines
13 KiB
Plaintext
//@version=6
|
||
indicator('RSI 窗口二显示', shorttitle='RSI-W2', overlay=false, max_labels_count=500)
|
||
|
||
//************************************************************************************************************
|
||
// RSI 参数设置
|
||
//************************************************************************************************************
|
||
|
||
// RSI 参数组
|
||
rsi_src = input.source(close, 'RSI Calculation Source', group='RSI')
|
||
rsiLength = input.int(14, 'RSI Length', minval=2, group='RSI')
|
||
smooth = input.bool(true, 'Smooth RSI?', group='RSI')
|
||
maType = input.string('Ema', 'Moving Average Type', options=['SMA', 'Hull', 'Ema', 'Wma', 'DEMA', 'RMA', 'LINREG', 'TEMA', 'ALMA', 'T3'], group='RSI')
|
||
smoothP = input.int(4, 'Smoothing Period', group='RSI')
|
||
sig = input.int(6, 'Sigma for ALMA', group='RSI')
|
||
|
||
// 显示设置
|
||
show_segments = input.bool(true, 'Show RSI Segments?', group='Display')
|
||
show_counting = input.bool(true, 'Show Segment Counting?', group='Display')
|
||
show_rsi_table = input.bool(true, 'Show RSI Info Table?', group='Display')
|
||
show_price_labels = input.bool(true, 'Show Price Labels?', group='Display')
|
||
|
||
//************************************************************************************************************
|
||
// RSI 辅助函数
|
||
//************************************************************************************************************
|
||
|
||
// 自定义移动平均函数
|
||
dema(src, length) =>
|
||
ema1 = ta.ema(src, length)
|
||
ema2 = ta.ema(ema1, length)
|
||
2 * ema1 - ema2
|
||
|
||
tema(src, length) =>
|
||
ema1 = ta.ema(src, length)
|
||
ema2 = ta.ema(ema1, length)
|
||
ema3 = ta.ema(ema2, length)
|
||
3 * ema1 - 3 * ema2 + ema3
|
||
|
||
t3(src, length, vfactor) =>
|
||
ema1 = ta.ema(src, length)
|
||
ema2 = ta.ema(ema1, length)
|
||
ema3 = ta.ema(ema2, length)
|
||
ema4 = ta.ema(ema3, length)
|
||
ema5 = ta.ema(ema4, length)
|
||
ema6 = ta.ema(ema5, length)
|
||
|
||
c1 = -vfactor * vfactor * vfactor
|
||
c2 = 3 * vfactor * vfactor + 3 * vfactor * vfactor * vfactor
|
||
c3 = -6 * vfactor * vfactor - 3 * vfactor - 3 * vfactor * vfactor * vfactor
|
||
c4 = 1 + 3 * vfactor + vfactor * vfactor * vfactor + 3 * vfactor * vfactor
|
||
|
||
c1 * ema6 + c2 * ema5 + c3 * ema4 + c4 * ema3
|
||
|
||
ma(src, len, type, almaSig) =>
|
||
switch type
|
||
'SMA' => ta.sma(src, len)
|
||
'Hull' => ta.hma(src, len)
|
||
'Ema' => ta.ema(src, len)
|
||
'Wma' => ta.wma(src, len)
|
||
'DEMA' => dema(src, len)
|
||
'RMA' => ta.rma(src, len)
|
||
'LINREG' => ta.linreg(src, len, 0)
|
||
'TEMA' => tema(src, len)
|
||
'ALMA' => ta.alma(src, len, 0, almaSig)
|
||
'T3' => t3(src, len, 0.7)
|
||
|
||
//************************************************************************************************************
|
||
// RSI 计算
|
||
//************************************************************************************************************
|
||
|
||
// 计算RSI
|
||
rsi = ta.rsi(rsi_src, rsiLength)
|
||
if smooth
|
||
rsi := ma(rsi, smoothP, maType, sig)
|
||
|
||
// RSI 机器学习阈值计算
|
||
var rsi_values = array.new_float(0)
|
||
if last_bar_index - bar_index <= 1000
|
||
array.push(rsi_values, rsi)
|
||
|
||
var centroids = array.new_float(3)
|
||
if array.size(rsi_values) > 3
|
||
array.set(centroids, 0, array.percentile_linear_interpolation(rsi_values, 25))
|
||
array.set(centroids, 1, array.percentile_linear_interpolation(rsi_values, 50))
|
||
array.set(centroids, 2, array.percentile_linear_interpolation(rsi_values, 75))
|
||
|
||
long_S = array.get(centroids, 2)
|
||
short_S = array.get(centroids, 0)
|
||
|
||
// RSI 状态和计数逻辑
|
||
var int rsi_state = 0
|
||
var int prev_rsi_state = 0
|
||
var int extreme_type = 0
|
||
var int segment_count = 0
|
||
|
||
// 计数标签变量 - 不再使用全局变量,每次状态变化时创建新标签以保留历史
|
||
|
||
current_state = rsi > long_S ? 1 : rsi < short_S ? -1 : 0
|
||
|
||
if current_state != rsi_state and not na(rsi)
|
||
// 确定分段线颜色
|
||
segment_color = color.black
|
||
|
||
if prev_rsi_state == 0 and current_state == 1
|
||
segment_color := color.new(color.red, 0) // 从中性区进入超买区
|
||
else if prev_rsi_state == 0 and current_state == -1
|
||
segment_color := color.new(color.blue, 0) // 从中性区进入超卖区
|
||
else if prev_rsi_state == 1 and current_state == 0
|
||
segment_color := color.new(color.orange, 0) // 从超买区进入中性区
|
||
else if prev_rsi_state == -1 and current_state == 0
|
||
segment_color := color.new(color.green, 0) // 从超卖区进入中性区
|
||
else if prev_rsi_state == 1 and current_state == -1
|
||
segment_color := color.new(color.purple, 0) // 从超买区直接到超卖区
|
||
else if prev_rsi_state == -1 and current_state == 1
|
||
segment_color := color.new(color.yellow, 0) // 从超卖区直接到超买区
|
||
else if prev_rsi_state == 1 and current_state == 1
|
||
segment_color := color.new(color.maroon, 0) // 超买区内的重复进入
|
||
else if prev_rsi_state == -1 and current_state == -1
|
||
segment_color := color.new(color.navy, 0) // 超卖区内的重复进入
|
||
|
||
// 绘制分段线
|
||
if show_segments
|
||
line.new(bar_index, 0, bar_index, 100,
|
||
color = segment_color,
|
||
width = 2,
|
||
style = line.style_solid,
|
||
extend = extend.none)
|
||
|
||
// 更新计数逻辑
|
||
if current_state == 1
|
||
if extreme_type != 1
|
||
extreme_type := 1
|
||
segment_count := 1
|
||
else
|
||
segment_count := segment_count + 1
|
||
|
||
// 显示超买计数标签(保留历史标签)
|
||
if show_counting
|
||
label.new(bar_index, rsi,
|
||
text = str.tostring(segment_count),
|
||
style = label.style_label_down,
|
||
color = color.red,
|
||
textcolor = color.white,
|
||
size = size.small)
|
||
|
||
else if current_state == -1
|
||
if extreme_type != -1
|
||
extreme_type := -1
|
||
segment_count := -1
|
||
else
|
||
segment_count := segment_count - 1
|
||
|
||
// 显示超卖计数标签(保留历史标签)
|
||
if show_counting
|
||
label.new(bar_index, rsi,
|
||
text = str.tostring(segment_count),
|
||
style = label.style_label_up,
|
||
color = color.blue,
|
||
textcolor = color.white,
|
||
size = size.small)
|
||
|
||
// 更新前一个状态
|
||
prev_rsi_state := rsi_state
|
||
rsi_state := current_state
|
||
|
||
//************************************************************************************************************
|
||
// RSI 绘图
|
||
//************************************************************************************************************
|
||
|
||
// RSI 主线
|
||
rsi_color = rsi > long_S ? color.red : rsi < short_S ? color.blue : color.gray
|
||
plot(rsi, 'RSI', color=rsi_color, linewidth=2)
|
||
|
||
// 动态阈值线
|
||
plot(long_S, 'Long ML Threshold', color=color.red, linewidth=1)
|
||
plot(short_S, 'Short ML Threshold', color=color.blue, linewidth=1)
|
||
|
||
// 50中线
|
||
hline(50, 'RSI 50', color=color.gray, linestyle=hline.style_dashed)
|
||
|
||
//************************************************************************************************************
|
||
// RSI 变化方向计算
|
||
//************************************************************************************************************
|
||
|
||
// 计算RSI变化
|
||
rsi_change = not na(rsi[1]) ? rsi - rsi[1] : 0
|
||
rsi_direction = rsi_change > 0.1 ? "↗" : rsi_change < -0.1 ? "↘" : "→"
|
||
rsi_direction_text = rsi_change > 0.1 ? "上升" : rsi_change < -0.1 ? "下降" : "平稳"
|
||
rsi_change_value = str.tostring(math.abs(rsi_change), '#.##')
|
||
|
||
//************************************************************************************************************
|
||
// RSI 价格标签显示(参考MRC原始实现)
|
||
//************************************************************************************************************
|
||
|
||
// 价格格式化函数
|
||
format_rsi_value(value) =>
|
||
str.tostring(value, '#.##')
|
||
|
||
// 声明RSI标签变量
|
||
var label rsi_label = na
|
||
var label long_threshold_label = na
|
||
var label short_threshold_label = na
|
||
var label midline_label = na
|
||
|
||
if show_price_labels and barstate.islast
|
||
// RSI 主线标签(包含变化方向)
|
||
rsi_label_text = "RSI: " + format_rsi_value(rsi) + " " + rsi_direction + " (" + rsi_direction_text + " " + rsi_change_value + ")"
|
||
if na(rsi_label)
|
||
rsi_label := label.new(bar_index, rsi, rsi_label_text, xloc=xloc.bar_index, style=label.style_label_left, color=rsi_color, textcolor=color.white)
|
||
else
|
||
label.set_xy(rsi_label, bar_index, rsi)
|
||
label.set_text(rsi_label, rsi_label_text)
|
||
label.set_color(rsi_label, rsi_color)
|
||
|
||
// 超买阈值标签
|
||
if na(long_threshold_label)
|
||
long_threshold_label := label.new(bar_index, long_S, "超买: " + format_rsi_value(long_S), xloc=xloc.bar_index, style=label.style_label_left, color=color.red, textcolor=color.white)
|
||
else
|
||
label.set_xy(long_threshold_label, bar_index, long_S)
|
||
label.set_text(long_threshold_label, "超买: " + format_rsi_value(long_S))
|
||
|
||
// 超卖阈值标签
|
||
if na(short_threshold_label)
|
||
short_threshold_label := label.new(bar_index, short_S, "超卖: " + format_rsi_value(short_S), xloc=xloc.bar_index, style=label.style_label_left, color=color.blue, textcolor=color.white)
|
||
else
|
||
label.set_xy(short_threshold_label, bar_index, short_S)
|
||
label.set_text(short_threshold_label, "超卖: " + format_rsi_value(short_S))
|
||
|
||
// 50中线标签
|
||
if na(midline_label)
|
||
midline_label := label.new(bar_index, 50, "中线: 50.00", xloc=xloc.bar_index, style=label.style_label_left, color=color.gray, textcolor=color.white)
|
||
else
|
||
label.set_xy(midline_label, bar_index, 50)
|
||
label.set_text(midline_label, "中线: 50.00")
|
||
|
||
//************************************************************************************************************
|
||
// RSI 信息表格
|
||
//************************************************************************************************************
|
||
|
||
if show_rsi_table and barstate.islast
|
||
// RSI 状态
|
||
rsi_position = rsi > long_S ? "超买区" : rsi < short_S ? "超卖区" : "中性区"
|
||
rsi_cross_count = math.abs(segment_count)
|
||
cross_type = segment_count > 0 ? "超买穿越" : segment_count < 0 ? "超卖穿越" : "无穿越"
|
||
|
||
// 创建RSI表格(扩展为10行)
|
||
var table rsi_table = table.new(
|
||
position = position.bottom_right,
|
||
columns = 2,
|
||
rows = 10,
|
||
bgcolor = color.new(color.yellow, 80),
|
||
border_width = 1)
|
||
|
||
// 清空表格
|
||
table.clear(rsi_table, 0, 0, 1, 9)
|
||
|
||
// 添加RSI标题
|
||
table.cell(rsi_table, 0, 0, "RSI指标", text_color=color.black, bgcolor=color.new(color.orange, 70), text_size=size.small)
|
||
table.cell(rsi_table, 1, 0, "实时数值", text_color=color.black, bgcolor=color.new(color.orange, 70), text_size=size.small)
|
||
|
||
// RSI 值
|
||
table.cell(rsi_table, 0, 1, "RSI值", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 1, str.tostring(rsi, '#.##'), text_color=color.black, text_size=size.tiny)
|
||
|
||
// RSI 位置
|
||
table.cell(rsi_table, 0, 2, "RSI位置", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 2, rsi_position, text_color=color.black, text_size=size.tiny)
|
||
|
||
// RSI 阈值
|
||
table.cell(rsi_table, 0, 3, "超买阈值", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 3, str.tostring(long_S, '#.##'), text_color=color.black, text_size=size.tiny)
|
||
|
||
table.cell(rsi_table, 0, 4, "超卖阈值", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 4, str.tostring(short_S, '#.##'), text_color=color.black, text_size=size.tiny)
|
||
|
||
// RSI 穿越次数
|
||
table.cell(rsi_table, 0, 5, "穿越次数", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 5, str.tostring(rsi_cross_count), text_color=color.black, text_size=size.tiny)
|
||
|
||
// RSI 穿越类型
|
||
table.cell(rsi_table, 0, 6, "穿越类型", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 6, cross_type, text_color=color.black, text_size=size.tiny)
|
||
|
||
// RSI 状态标记
|
||
table.cell(rsi_table, 0, 7, "状态标记", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 7, str.tostring(segment_count), text_color=color.black, text_size=size.tiny)
|
||
|
||
// RSI 变化方向
|
||
table.cell(rsi_table, 0, 8, "变化方向", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 8, rsi_direction_text + " " + rsi_direction, text_color=color.black, text_size=size.tiny)
|
||
|
||
// RSI 变化幅度
|
||
table.cell(rsi_table, 0, 9, "变化幅度", text_color=color.black, text_size=size.tiny)
|
||
table.cell(rsi_table, 1, 9, rsi_change_value, text_color=color.black, text_size=size.tiny)
|