441 lines
26 KiB
Plaintext
441 lines
26 KiB
Plaintext
// 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","备注":"空头信号"}') |