Files
tradingview-pine/参考/rsi.pine
2025-08-02 04:21:30 +00:00

441 lines
26 KiB
Plaintext
Raw 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/
// © BackQuant
//@version=6
indicator('Machine Learning RSI 13', 'ML RSI [13]', overlay = false, max_labels_count = 500)
// Define Groups
const string rsi_g = 'Relative Strength Index'
const string ml_t = 'Machine Learning Thresholds'
const string opt_ = 'ML Optimization'
const string ui = 'UI Settings'
// User Inputs
// RSI Settings
series float src = input.source(close, 'Calculation Source', group = rsi_g, inline = 'X')
simple int rsiLength = input.int(14, 'RSI Length', minval = 2, group = rsi_g, inline = 'X')
simple bool smooth = input.bool(true, 'Smooth RSI?', group = rsi_g, inline = 'smt')
string maType = input.string('Ema', 'Moving Average Type', options = ['SMA', 'Hull', 'Ema', 'Wma', 'DEMA', 'RMA', 'LINREG', 'TEMA', 'ALMA', 'T3'], group = rsi_g, inline = 'smt')
simple int smoothP = input.int(4, 'Smoothing Period', group = rsi_g)
simple int sig = input.int(6, 'Sigma for ALMA', group = rsi_g, tooltip = 'this is only used if the ALMA is choosen in the Moving Average Type Input')
// Machine Learning Thresholding Settings
simple int minThresh = input.int(10, 'Threshold Range Min', inline = 'thresh', group = ml_t)
simple int maxThresh = input.int(90, 'Max', inline = 'thresh', group = ml_t)
simple int step = input.int(5, 'Step', inline = 'thresh', group = ml_t)
// Optimization Settings
simple float perfAlpha = input.float(10, 'Performance Memory', minval = 2, tooltip = 'controls the weighting of past data or the smoothness of calculations.', group = opt_)
simple int maxIter = input.int(1000, 'Max Clustering Steps', group = opt_)
simple int maxData = input.int(3000, 'Max Data Points', group = opt_)
// UI Settings
simple bool showthres = input.bool(true, 'Show Threshold Lines?', group = ui)
simple bool paintCandles = input.bool(false, 'Color Bars According to Trend?', group = ui)
simple bool showSegments = input.bool(true, 'Show RSI Segments?', group = ui)
simple bool showCounting = input.bool(true, 'Show Segment Counting?', group = ui)
int linew = input.int(3, 'Signal Line Width', 1, 4, 1, group = ui)
color longcol = input.color(#00ff00, 'Long Colour', group = ui, inline = 'xxxx')
color shortcol = input.color(#ff0000, 'Short Colour', group = ui, inline = 'xxxx')
color neucol = input.color(color.gray, 'Neutral Colour', group = ui)
color bgCol = input.color(color.new(color.white, 0), 'Pane Background Color', group = ui)
bgcolor(bgCol)
// Custom DEMA function
dema(src, length) =>
ema1 = ta.ema(src, length)
ema2 = ta.ema(ema1, length)
2 * ema1 - ema2
// Custom TEMA function (Triple Exponential Moving Average)
tema(src, length) =>
ema1 = ta.ema(src, length)
ema2 = ta.ema(ema1, length)
ema3 = ta.ema(ema2, length)
3 * ema1 - 3 * ema2 + ema3
// Custom T3 function (Tim Tillson's T3)
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 Function
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)
// Calculate RSI
rsi = ta.rsi(src, rsiLength)
// Smooth?
if smooth == true
rsi := ma(rsi, smoothP, maType, sig)
rsi
// Populate threshold array
var factors = array.new_float(0)
if barstate.isfirst
for i = minThresh to maxThresh by step
array.push(factors, i)
// Clustering for RSI
var rsi_values = array.new_float(0)
if last_bar_index - bar_index <= maxData
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))
// Initialize clusters
var cluster1 = array.new_float(0)
var cluster2 = array.new_float(0)
var cluster3 = array.new_float(0)
// Function to compare two arrays
f_arrays_equal(arr1, arr2) =>
if array.size(arr1) != array.size(arr2)
false
else
all_equal = true
for i = 0 to array.size(arr1) - 1 by 1
if array.get(arr1, i) != array.get(arr2, i)
all_equal := false
break
all_equal
for _ = 0 to maxIter by 1
// Reset clusters at each iteration
cluster1 := array.new_float(0)
cluster2 := array.new_float(0)
cluster3 := array.new_float(0)
for value in rsi_values
distances = array.new_float(3)
for centroid in centroids
array.push(distances, math.abs(value - centroid))
idx = array.indexof(distances, array.min(distances))
if idx == 0
array.push(cluster1, value)
else if idx == 1
array.push(cluster2, value)
else
array.push(cluster3, value)
// Update centroids
new_centroids = array.new_float(3)
array.push(new_centroids, array.avg(cluster1))
array.push(new_centroids, array.avg(cluster2))
array.push(new_centroids, array.avg(cluster3))
if f_arrays_equal(new_centroids, centroids)
break
centroids := new_centroids
centroids
// Dynamic thresholds
long_S = array.get(centroids, 2)
short_S = array.get(centroids, 0)
// RSI Segmentation and Counting Logic
var int rsi_state = 0 // 0 = neutral, 1 = overbought, -1 = oversold
var int prev_rsi_state = 0 // 添加前一个状态的追踪
var int extreme_type = 0 // 1 = overbought type, -1 = oversold type, 0 = no extreme
var int segment_count = 0 // current count for the extreme type
// Alert trigger variables
var bool overbought_repeat_alert = false
var bool oversold_repeat_alert = false
var bool overbought_mark2_alert = false // NEW: specific alert for mark 2
var bool oversold_mark2_alert = false // NEW: specific alert for mark -2
// Determine current RSI state
current_state = rsi > long_S ? 1 : rsi < short_S ? -1 : 0
// Track state changes and update counting
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) // 超卖区内的重复进入 - 深蓝色
// Draw vertical line at state transition points with color coding
if showSegments
line.new(bar_index, 0, bar_index, 100,
color = segment_color,
width = 2, // 增加线宽以便更好地看到颜色
style = line.style_solid,
extend = extend.none)
// 更新前一个状态
prev_rsi_state := rsi_state
// Reset alert triggers
overbought_repeat_alert := false
oversold_repeat_alert := false
overbought_mark2_alert := false // NEW: reset specific mark alerts
oversold_mark2_alert := false // NEW: reset specific mark alerts
// Update counting logic
if current_state == 1 // Entering overbought
if extreme_type != 1 // If previous extreme type was not overbought
extreme_type := 1
segment_count := 1 // Reset to 1
else // Same extreme type, increment
segment_count := segment_count + 1
// Check for repeat alert trigger (count >= 2)
if segment_count >= 2
overbought_repeat_alert := true
// NEW: Check for specific mark 2 alert
if segment_count == 2
overbought_mark2_alert := true
// Display count label
if showCounting
label.new(bar_index, rsi,
text = str.tostring(segment_count),
style = label.style_label_down,
color = longcol,
textcolor = color.white,
size = size.small)
else if current_state == -1 // Entering oversold
if extreme_type != -1 // If previous extreme type was not oversold
extreme_type := -1
segment_count := -1 // Reset to -1
else // Same extreme type, decrement
segment_count := segment_count - 1
// Check for repeat alert trigger (count <= -2)
if segment_count <= -2
oversold_repeat_alert := true
// NEW: Check for specific mark -2 alert
if segment_count == -2
oversold_mark2_alert := true
// Display count label
if showCounting
label.new(bar_index, rsi,
text = str.tostring(segment_count),
style = label.style_label_up,
color = shortcol,
textcolor = color.white,
size = size.small)
// Update state
rsi_state := current_state
// RSI变化计算 - 计算RSI相对于前一个K线的变化
rsi_change = not na(rsi[1]) ? rsi - rsi[1] : 0
rsi_direction = rsi_change > 0 ? "升高" : rsi_change < 0 ? "降低" : "持平"
rsi_change_value = str.tostring(rsi_change, '#.##')
// Create plot outputs for alert messages
plot(segment_count, 'Segment Count', display = display.none)
plot(overbought_repeat_alert ? 1 : 0, 'Overbought Repeat', display = display.none)
plot(oversold_repeat_alert ? 1 : 0, 'Oversold Repeat', display = display.none)
plot(overbought_mark2_alert ? 1 : 0, 'Overbought Mark2', display = display.none) // NEW
plot(oversold_mark2_alert ? 1 : 0, 'Oversold Mark2', display = display.none) // NEW
plot(rsi_change, 'RSI Change', display = display.none) // 添加RSI变化的plot输出
// Cluster plotting arrays
var float cluster1_plot = na
var float cluster2_plot = na
var float cluster3_plot = na
if not na(rsi)
distances = array.new_float(3)
for centroid in centroids
array.push(distances, math.abs(rsi - centroid))
idx = array.indexof(distances, array.min(distances))
cluster1_plot := idx == 0 ? rsi : na
cluster2_plot := idx == 1 ? rsi : na
cluster3_plot := idx == 2 ? rsi : na
cluster3_plot
// --- Visualization ---
col = rsi > long_S ? longcol : rsi < short_S ? shortcol : neucol
plot(rsi, 'RSI', color = col, linewidth = math.max(1, linew)) // Main RSI line
// Dynamic thresholds via Machine Learning
plot(showthres ? long_S : na, 'Long ML Threshold', color = longcol)
plot(showthres ? short_S : na, 'Short ML Threshold', color = shortcol)
// Plot RSI values in clusters
plot(cluster1_plot, 'Cluster 1', color = color.green, linewidth = 2, style = plot.style_circles)
plot(cluster2_plot, 'Cluster 2', color = color.orange, linewidth = 2, style = plot.style_circles)
plot(cluster3_plot, 'Cluster 3', color = color.red, linewidth = 2, style = plot.style_circles)
barcolor(paintCandles ? col : na)
// ===== 统一警报系统 =====
// 统一警报系统 - 只需添加一次警报即可捕获所有信号
// 警报频率限制 - 每分钟只触发一次
var int last_alert_rsi_buy = 0
var int last_alert_rsi_sell = 0
var int last_alert_rsi_neutral_from_ob = 0
var int last_alert_rsi_neutral_from_os = 0
var int last_alert_overbought_repeat = 0
var int last_alert_oversold_repeat = 0
var int last_alert_overbought_mark2 = 0
var int last_alert_oversold_mark2 = 0
var int last_alert_rsi_overbought = 0
var int last_alert_rsi_oversold = 0
var int last_alert_rsi_neutral = 0
// 获取当前时间(分钟级别)
current_minute = math.floor(timenow / 60000)
// 检测所有警报条件并生成对应的JSON消息
alert_message = ""
// 基础穿越信号 - 每分钟限制
if ta.crossover(rsi, long_S) and current_minute > last_alert_rsi_buy
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(long_S, '#.##') + '","事件":"RSI 已上穿多头ML阈值 - 潜在买入信号","信号":"buy"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_rsi_buy := current_minute
if ta.crossunder(rsi, short_S) and current_minute > last_alert_rsi_sell
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(short_S, '#.##') + '","事件":"RSI 已下穿空头ML阈值 - 潜在卖出信号","信号":"sell"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_rsi_sell := current_minute
// 区域转换信号 - 每分钟限制
// if ta.crossunder(rsi, long_S) and current_minute > last_alert_rsi_neutral_from_ob
// alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(long_S, '#.##') + '","事件":"RSI 已下穿多头ML阈值进入中性区","备注":"进入震荡"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_rsi_neutral_from_ob := current_minute
// if ta.crossover(rsi, short_S) and current_minute > last_alert_rsi_neutral_from_os
// alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(short_S, '#.##') + '","事件":"RSI 已上穿空头ML阈值进入中性区","备注":"进入震荡"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_rsi_neutral_from_os := current_minute
// 重复进入信号 - 每分钟限制
if overbought_repeat_alert and current_minute > last_alert_overbought_repeat
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(long_S, '#.##') + '","事件":"RSI 重复进入超买区","标记":"' + str.tostring(segment_count) + '","信号":"overbought_repeat","警告":"大于和等于2次超买信号"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_overbought_repeat := current_minute
if oversold_repeat_alert and current_minute > last_alert_oversold_repeat
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(short_S, '#.##') + '","事件":"RSI 重复进入超卖区","标记":"' + str.tostring(segment_count) + '","信号":"oversold_repeat","警告":"大于和等于2次超卖信号"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_oversold_repeat := current_minute
// 特定标记信号 - 每分钟限制
if overbought_mark2_alert and current_minute > last_alert_overbought_mark2
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(long_S, '#.##') + '","事件":"RSI 第二次进入超买区","标记":"2","信号":"overbought_mark2","警告":"二次超买信号"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_overbought_mark2 := current_minute
if oversold_mark2_alert and current_minute > last_alert_oversold_mark2
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(short_S, '#.##') + '","事件":"RSI 第二次进入超卖区","标记":"-2","信号":"oversold_mark2","警告":"二次超卖信号"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_oversold_mark2 := current_minute
// RSI状态警报 - 每分钟限制
if rsi > long_S and current_minute > last_alert_rsi_overbought
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","超买阈值":"' + str.tostring(long_S, '#.##') + '","事件":"RSI处于超买状态","RSI变化":"' + rsi_direction + '","RSI变化值":"' + rsi_change_value + '","标记":"' + str.tostring(segment_count) + '","信号":"rsi_overbought","备注":"RSI高于超买线"}'
alert(alert_message, alert.freq_once_per_bar)
last_alert_rsi_overbought := current_minute
if rsi < short_S and current_minute > last_alert_rsi_oversold
alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","超卖阈值":"' + str.tostring(short_S, '#.##') + '","事件":"RSI处于超卖状态","RSI变化":"' + rsi_direction + '","RSI变化值":"' + rsi_change_value + '","标记":"' + str.tostring(segment_count) + '","信号":"rsi_oversold","备注":"RSI低于超卖线"}'
alert(alert_message, alert.freq_once_per_bar)
last_alert_rsi_oversold := current_minute
// if rsi < long_S and rsi > short_S and current_minute > last_alert_rsi_neutral
// alert_message := '{"指标名称":"RSI","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(long_S, '#.##') + '","Short ML Threshold":"' + str.tostring(short_S, '#.##') + '","事件":"RSI 处于中性区","备注":"震荡"}'
// alert(alert_message, alert.freq_once_per_bar)
// last_alert_rsi_neutral := current_minute
// ===== 传统警报条件(保留兼容性)=====
// 注意使用统一警报系统时建议只使用上面的alert()函数
// 以下alertcondition保留用于需要单独设置警报的情况
// 创建用于警报的plot变量
plot(rsi, title = 'RSI值', display = display.none)
plot(long_S, title = 'Long ML Threshold', display = display.none)
plot(short_S, title = 'Short ML Threshold', display = display.none)
// 基础穿越信号警报
alertcondition(ta.crossover(rsi, long_S), title = 'RSI上穿多头ML阈值', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","事件":"RSI 已上穿多头ML阈值 - 潜在买入信号","信号":"buy"}')
alertcondition(ta.crossunder(rsi, short_S), title = 'RSI下穿空头ML阈值', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Short ML Threshold":"{{plot("Short ML Threshold")}}","事件":"RSI 已下穿空头ML阈值 - 潜在卖出信号","信号":"sell"}')
// 区域转换信号警报
// alertcondition(ta.crossunder(rsi, long_S), title = 'RSI下穿多头ML阈值进入中性区', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","事件":"RSI 已下穿多头ML阈值进入中性区","备注":"进入震荡","信号":"neutral_from_overbought"}')
// alertcondition(ta.crossover(rsi, short_S), title = 'RSI上穿空头ML阈值进入中性区', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Short ML Threshold":"{{plot("Short ML Threshold")}}","事件":"RSI 已上穿空头ML阈值进入中性区","备注":"进入震荡","信号":"neutral_from_oversold"}')
// 重复进入信号警报
alertcondition(overbought_repeat_alert, title = 'RSI重复进入超买区', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","事件":"RSI 重复进入超买区","标记":"{{plot("Segment Count")}}","信号":"overbought_repeat","警告":"大于和等于2次超买信号"}')
alertcondition(oversold_repeat_alert, title = 'RSI重复进入超卖区', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Short ML Threshold":"{{plot("Short ML Threshold")}}","事件":"RSI 重复进入超卖区","标记":"{{plot("Segment Count")}}","信号":"oversold_repeat","警告":"大于和等于2次超卖信号"}')
// 特定标记信号警报
alertcondition(overbought_mark2_alert, title = 'RSI第二次进入超买区', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","事件":"RSI 第二次进入超买区","标记":"2","信号":"overbought_mark2","警告":"二次超买信号"}')
alertcondition(oversold_mark2_alert, title = 'RSI第二次进入超卖区', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Short ML Threshold":"{{plot("Short ML Threshold")}}","事件":"RSI 第二次进入超卖区","标记":"-2","信号":"oversold_mark2","警告":"二次超卖信号"}')
// RSI状态警报
alertcondition(rsi > long_S, title = 'RSI处于超买状态', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","超买阈值":"{{plot("Long ML Threshold")}}","事件":"RSI处于超买状态","标记":"{{plot("Segment Count")}}","信号":"rsi_overbought","备注":"RSI高于超买线"}')
alertcondition(rsi < short_S, title = 'RSI处于超卖状态', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","超卖阈值":"{{plot("Short ML Threshold")}}","事件":"RSI处于超卖状态","标记":"{{plot("Segment Count")}}","信号":"rsi_oversold","备注":"RSI低于超卖线"}')
alertcondition(rsi < long_S and rsi > short_S, title = 'RSI处于中性区', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","Short ML Threshold":"{{plot("Short ML Threshold")}}","事件":"RSI 处于中性区","备注":"震荡","信号":"rsi_neutral"}')
// 传统RSI阈值警报70/30
// alertcondition(ta.crossover(rsi, 70), title = 'RSI上穿70', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","阈值":"70","事件":"RSI上穿70","信号":"rsi_70_cross_up","备注":"传统超买信号"}')
// alertcondition(ta.crossunder(rsi, 30), title = 'RSI下穿30', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","阈值":"30","事件":"RSI下穿30","信号":"rsi_30_cross_down","备注":"传统超卖信号"}')
// alertcondition(ta.crossunder(rsi, 70), title = 'RSI下穿70', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","阈值":"70","事件":"RSI下穿70","信号":"rsi_70_cross_down","备注":"从超买区回落"}')
// alertcondition(ta.crossover(rsi, 30), title = 'RSI上穿30', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","阈值":"30","事件":"RSI上穿30","信号":"rsi_30_cross_up","备注":"从超卖区反弹"}')
// // RSI中线穿越警报50
// alertcondition(ta.crossover(rsi, 50), title = 'RSI上穿50', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","中线":"50","事件":"RSI上穿50","信号":"rsi_50_cross_up","备注":"多头信号"}')
// alertcondition(ta.crossunder(rsi, 50), title = 'RSI下穿50', message = '{"指标名称":"RSI","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI值")}}","中线":"50","事件":"RSI下穿50","信号":"rsi_50_cross_down","备注":"空头信号"}')