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

638 lines
38 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 - Optimized Version
//@version=6
indicator('Optimized ML RSI Pro', 'ML RSI Pro', overlay = false, max_labels_count = 500)
// ===== PARAMETER GROUPS =====
const string rsi_g = 'RSI Settings'
const string ml_g = 'Machine Learning'
const string opt_g = 'Optimization'
const string filter_g = 'Signal Filters'
const string ui_g = 'UI & Alerts'
const string preset_g = 'Preset Modes'
// ===== PRESET MODES =====
string preset_mode = input.string('Custom', 'Preset Mode',
options = ['Conservative', 'Balanced', 'Aggressive', 'Custom'],
group = preset_g, tooltip = 'Choose a preset or use Custom for manual settings')
// ===== RSI SETTINGS =====
series float src = input.source(close, 'Source', group = rsi_g, inline = 'rsi1')
simple int rsi_length = input.int(14, 'RSI Length', minval = 2, maxval = 50, group = rsi_g, inline = 'rsi1')
simple bool enable_smooth = input.bool(true, 'Enable Smoothing', group = rsi_g, inline = 'smooth1')
string ma_type = input.string('EMA', 'MA Type',
options = ['SMA', 'EMA', 'RMA', 'WMA', 'HMA'], group = rsi_g, inline = 'smooth1')
simple int smooth_length = input.int(3, 'Smooth Length', minval = 2, maxval = 10, group = rsi_g)
// ===== MACHINE LEARNING SETTINGS =====
simple int lookback_bars = input.int(500, 'Lookback Period', minval = 100, maxval = 2000,
group = ml_g, tooltip = 'Number of bars for ML analysis (reduced from 3000 for better performance)')
simple int update_frequency = input.int(5, 'Update Every N Bars', minval = 1, maxval = 20,
group = ml_g, tooltip = 'Update ML thresholds every N bars to improve performance')
simple int max_iterations = input.int(50, 'Max Iterations', minval = 10, maxval = 200,
group = ml_g, tooltip = 'Maximum clustering iterations (reduced from 1000)')
simple float convergence_threshold = input.float(0.1, 'Convergence Threshold', minval = 0.01, maxval = 1.0,
group = ml_g, tooltip = 'Stop clustering when centroids change less than this value')
// ===== OPTIMIZATION SETTINGS =====
simple bool enable_outlier_filter = input.bool(true, 'Filter Outliers', group = opt_g)
simple float outlier_threshold = input.float(2.0, 'Outlier Threshold (Std Dev)', minval = 1.0, maxval = 3.0, group = opt_g)
simple bool enable_weighted_data = input.bool(true, 'Weight Recent Data', group = opt_g)
simple float weight_decay = input.float(0.95, 'Weight Decay Factor', minval = 0.8, maxval = 0.99, group = opt_g)
// ===== SIGNAL FILTERS =====
simple bool enable_trend_filter = input.bool(true, 'Enable Trend Filter', group = filter_g)
simple int trend_length = input.int(50, 'Trend MA Length', minval = 20, maxval = 200, group = filter_g)
simple bool enable_volume_filter = input.bool(false, 'Enable Volume Filter', group = filter_g)
simple float volume_threshold = input.float(1.2, 'Volume Threshold (x Average)', minval = 0.5, maxval = 3.0, group = filter_g)
// ===== UI & ALERT SETTINGS =====
simple bool show_thresholds = input.bool(true, 'Show Threshold Lines', group = ui_g)
simple bool show_signals = input.bool(true, 'Show Signal Markers', group = ui_g)
simple bool show_statistics = input.bool(false, 'Show Statistics Table', group = ui_g)
simple bool enable_alerts = input.bool(true, 'Enable Alerts', group = ui_g)
color bull_color = input.color(#00ff88, 'Bull Color', group = ui_g, inline = 'colors')
color bear_color = input.color(#ff4444, 'Bear Color', group = ui_g, inline = 'colors')
color neutral_color = input.color(#888888, 'Neutral Color', group = ui_g, inline = 'colors')
// ===== PRESET CONFIGURATIONS =====
[preset_lookback, preset_update_freq, preset_max_iter, preset_conv_thresh] = switch preset_mode
'Conservative' => [800, 10, 30, 0.2]
'Balanced' => [500, 5, 50, 0.1]
'Aggressive' => [300, 3, 80, 0.05]
=> [lookback_bars, update_frequency, max_iterations, convergence_threshold]
// Apply preset values if not custom mode
final_lookback = preset_mode != 'Custom' ? preset_lookback : lookback_bars
final_update_freq = preset_mode != 'Custom' ? preset_update_freq : update_frequency
final_max_iter = preset_mode != 'Custom' ? preset_max_iter : max_iterations
final_conv_thresh = preset_mode != 'Custom' ? preset_conv_thresh : convergence_threshold
// ===== HELPER FUNCTIONS =====
// Enhanced Moving Average Function
f_ma(src, length, ma_type) =>
switch ma_type
'SMA' => ta.sma(src, length)
'EMA' => ta.ema(src, length)
'RMA' => ta.rma(src, length)
'WMA' => ta.wma(src, length)
'HMA' => ta.hma(src, length)
=> ta.ema(src, length)
// Outlier Detection Function
f_is_outlier(value, data_array, threshold) =>
if array.size(data_array) < 10
false
else
mean_val = array.avg(data_array)
std_dev = array.stdev(data_array)
math.abs(value - mean_val) > threshold * std_dev
// Weighted Average Function
f_weighted_avg(data_array, weight_decay) =>
if array.size(data_array) == 0
na
else
weighted_sum = 0.0
weight_sum = 0.0
size = array.size(data_array)
for i = 0 to size - 1
weight = math.pow(weight_decay, size - 1 - i)
weighted_sum := weighted_sum + array.get(data_array, i) * weight
weight_sum := weight_sum + weight
weighted_sum / weight_sum
// Enhanced K-means Clustering Function
f_enhanced_kmeans(data_array, max_iter, conv_thresh) =>
size = array.size(data_array)
if size < 10
[na, na, na]
else
// Initialize centroids using percentiles
c1 = array.percentile_linear_interpolation(data_array, 20)
c2 = array.percentile_linear_interpolation(data_array, 50)
c3 = array.percentile_linear_interpolation(data_array, 80)
// Iterative clustering with early convergence
for iteration = 0 to max_iter - 1
cluster1 = array.new_float()
cluster2 = array.new_float()
cluster3 = array.new_float()
// Assign points to clusters
for i = 0 to size - 1
value = array.get(data_array, i)
d1 = math.abs(value - c1)
d2 = math.abs(value - c2)
d3 = math.abs(value - c3)
if d1 <= d2 and d1 <= d3
array.push(cluster1, value)
else if d2 <= d3
array.push(cluster2, value)
else
array.push(cluster3, value)
// Calculate new centroids
new_c1 = array.size(cluster1) > 0 ?
(enable_weighted_data ? f_weighted_avg(cluster1, weight_decay) : array.avg(cluster1)) : c1
new_c2 = array.size(cluster2) > 0 ?
(enable_weighted_data ? f_weighted_avg(cluster2, weight_decay) : array.avg(cluster2)) : c2
new_c3 = array.size(cluster3) > 0 ?
(enable_weighted_data ? f_weighted_avg(cluster3, weight_decay) : array.avg(cluster3)) : c3
// Check convergence
if math.abs(new_c1 - c1) < conv_thresh and
math.abs(new_c2 - c2) < conv_thresh and
math.abs(new_c3 - c3) < conv_thresh
break
c1 := new_c1
c2 := new_c2
c3 := new_c3
[c1, c2, c3]
// ===== MAIN CALCULATIONS =====
// Calculate RSI
rsi_raw = ta.rsi(src, rsi_length)
rsi = enable_smooth ? f_ma(rsi_raw, smooth_length, ma_type) : rsi_raw
// Data collection with performance optimization
var rsi_data = array.new_float()
var int last_update_bar = 0
// Update data array periodically
if bar_index % final_update_freq == 0 or barstate.islast
// Add current RSI value
if not na(rsi)
// Apply outlier filter if enabled
if enable_outlier_filter and f_is_outlier(rsi, rsi_data, outlier_threshold)
// Skip outlier
na
else
array.push(rsi_data, rsi)
// Maintain data size
while array.size(rsi_data) > final_lookback
array.shift(rsi_data)
last_update_bar := bar_index
// Calculate dynamic thresholds using enhanced clustering
[oversold_threshold, neutral_center, overbought_threshold] = f_enhanced_kmeans(rsi_data, final_max_iter, final_conv_thresh)
// Fallback to traditional levels if clustering fails
final_oversold = na(oversold_threshold) ? 30 : oversold_threshold
final_overbought = na(overbought_threshold) ? 70 : overbought_threshold
// ===== SIGNAL GENERATION =====
// Basic signals
rsi_oversold = rsi < final_oversold
rsi_overbought = rsi > final_overbought
rsi_neutral = not rsi_oversold and not rsi_overbought
// ===== RSI SEGMENTATION AND COUNTING LOGIC (from original) =====
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
var bool oversold_mark2_alert = false
// Determine current RSI state
current_state = rsi > final_overbought ? 1 : rsi < final_oversold ? -1 : 0
// Track state changes and update counting
if current_state != rsi_state and not na(rsi)
// Update previous state
prev_rsi_state := rsi_state
// Reset alert triggers
overbought_repeat_alert := false
oversold_repeat_alert := false
overbought_mark2_alert := false
oversold_mark2_alert := false
// 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
// Check for specific mark 2 alert
if segment_count == 2
overbought_mark2_alert := true
// Display count label
if show_signals
label.new(bar_index, rsi,
text = str.tostring(segment_count),
style = label.style_label_down,
color = bear_color,
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
// Check for specific mark -2 alert
if segment_count == -2
oversold_mark2_alert := true
// Display count label
if show_signals
label.new(bar_index, rsi,
text = str.tostring(segment_count),
style = label.style_label_up,
color = bull_color,
textcolor = color.white,
size = size.small)
// Update state
rsi_state := current_state
// Enhanced signals with filters
trend_ma = f_ma(close, trend_length, 'EMA')
is_uptrend = enable_trend_filter ? close > trend_ma : true
is_downtrend = enable_trend_filter ? close < trend_ma : true
volume_avg = ta.sma(volume, 20)
is_high_volume = enable_volume_filter ? volume > volume_threshold * volume_avg : true
// Final filtered signals
buy_signal = ta.crossover(rsi, final_oversold) and is_uptrend and is_high_volume
sell_signal = ta.crossunder(rsi, final_overbought) and is_downtrend and is_high_volume
// ===== VISUALIZATION =====
// RSI line with dynamic coloring
rsi_color = rsi_overbought ? bear_color : rsi_oversold ? bull_color : neutral_color
plot(rsi, 'RSI', color = rsi_color, linewidth = 2)
// Dynamic threshold lines
plot(show_thresholds ? final_overbought : na, 'Overbought', color = bear_color, style = plot.style_line)
plot(show_thresholds ? final_oversold : na, 'Oversold', color = bull_color, style = plot.style_line)
plot(show_thresholds and not na(neutral_center) ? neutral_center : na, 'Neutral', color = neutral_color, style = plot.style_line)
// Signal markers
plotshape(show_signals and buy_signal, 'Buy Signal', shape.triangleup, location.bottom, bull_color, size = size.small)
plotshape(show_signals and sell_signal, 'Sell Signal', shape.triangledown, location.top, bear_color, size = size.small)
// Reference lines
hline(50, 'Midline', color = color.gray, linestyle = hline.style_solid)
hline(70, 'Traditional OB', color = color.new(color.red, 50), linestyle = hline.style_dotted)
hline(30, 'Traditional OS', color = color.new(color.green, 50), linestyle = hline.style_dotted)
// ===== STATISTICS TABLE =====
if show_statistics and barstate.islast
var table stats_table = table.new(position.top_right, 2, 6, bgcolor = color.white, border_width = 1)
table.cell(stats_table, 0, 0, 'Metric', text_color = color.black, bgcolor = color.gray)
table.cell(stats_table, 1, 0, 'Value', text_color = color.black, bgcolor = color.gray)
table.cell(stats_table, 0, 1, 'Current RSI', text_color = color.black)
table.cell(stats_table, 1, 1, str.tostring(rsi, '#.##'), text_color = color.black)
table.cell(stats_table, 0, 2, 'Oversold Level', text_color = color.black)
table.cell(stats_table, 1, 2, str.tostring(final_oversold, '#.##'), text_color = color.black)
table.cell(stats_table, 0, 3, 'Overbought Level', text_color = color.black)
table.cell(stats_table, 1, 3, str.tostring(final_overbought, '#.##'), text_color = color.black)
table.cell(stats_table, 0, 4, 'Data Points', text_color = color.black)
table.cell(stats_table, 1, 4, str.tostring(array.size(rsi_data)), text_color = color.black)
table.cell(stats_table, 0, 5, 'Mode', text_color = color.black)
table.cell(stats_table, 1, 5, preset_mode, text_color = color.black)
// ===== ADVANCED FEATURES (moved before alerts) =====
// Multi-timeframe RSI analysis
rsi_htf = request.security(syminfo.tickerid, '1D', rsi, lookahead = barmerge.lookahead_off)
htf_trend = rsi_htf > 50 ? 1 : rsi_htf < 50 ? -1 : 0
// Divergence Detection Functions
f_find_pivot_high(src, length) =>
ph = ta.pivothigh(src, length, length)
ph
f_find_pivot_low(src, length) =>
pl = ta.pivotlow(src, length, length)
pl
// Divergence variables
var float last_high_price = na
var float last_high_rsi = na
var float last_low_price = na
var float last_low_rsi = na
var int last_high_bar = na
var int last_low_bar = na
// Calculate pivot points on each bar for consistency
pivot_high = f_find_pivot_high(close, 5)
pivot_low = f_find_pivot_low(close, 5)
// Update pivot points
if not na(pivot_high)
last_high_price := pivot_high
last_high_rsi := rsi[5]
last_high_bar := bar_index[5]
if not na(pivot_low)
last_low_price := pivot_low
last_low_rsi := rsi[5]
last_low_bar := bar_index[5]
// Detect divergences
bullish_divergence = not na(last_low_price) and not na(last_low_rsi) and close < last_low_price and rsi > last_low_rsi and bar_index - last_low_bar < 50
bearish_divergence = not na(last_high_price) and not na(last_high_rsi) and close > last_high_price and rsi < last_high_rsi and bar_index - last_high_bar < 50
// RSI Momentum and Velocity
rsi_momentum = rsi - rsi[1]
rsi_velocity = rsi_momentum - rsi_momentum[1]
rsi_acceleration = rsi_velocity - rsi_velocity[1]
// Dynamic position sizing based on RSI strength
rsi_strength = math.abs(rsi - 50) / 50
position_size_factor = rsi_strength > 0.4 ? 1.5 : rsi_strength > 0.2 ? 1.0 : 0.5
// Enhanced signal scoring system
signal_score = 0.0
if buy_signal
signal_score := signal_score + 3.0 // Base signal
signal_score := htf_trend == 1 ? signal_score + 1.0 : signal_score // HTF alignment
signal_score := bullish_divergence ? signal_score + 2.0 : signal_score // Divergence
signal_score := rsi_momentum > 0 ? signal_score + 0.5 : signal_score // Momentum
signal_score := rsi_velocity > 0 ? signal_score + 0.5 : signal_score // Acceleration
if sell_signal
signal_score := signal_score - 3.0 // Base signal
signal_score := htf_trend == -1 ? signal_score - 1.0 : signal_score // HTF alignment
signal_score := bearish_divergence ? signal_score - 2.0 : signal_score // Divergence
signal_score := rsi_momentum < 0 ? signal_score - 0.5 : signal_score // Momentum
signal_score := rsi_velocity < 0 ? signal_score - 0.5 : signal_score // Acceleration
// Signal quality classification
signal_quality = math.abs(signal_score) > 5 ? 'Strong' : math.abs(signal_score) > 3 ? 'Medium' : math.abs(signal_score) > 1 ? 'Weak' : 'None'
// ===== UNIFIED ALERT SYSTEM =====
// 统一警报系统 - 只需添加一次警报即可捕获所有信号
// 警报频率限制 - 每分钟只触发一次
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_high_quality_buy = 0
var int last_alert_high_quality_sell = 0
var int last_alert_bullish_divergence = 0
var int last_alert_bearish_divergence = 0
var int last_alert_rsi_overbought = 0
var int last_alert_rsi_oversold = 0
// 获取当前时间(分钟级别)
current_minute = math.floor(timenow / 60000)
// 检测所有警报条件并生成对应的JSON消息
alert_message = ""
// 基础穿越信号 - 每分钟限制
if ta.crossover(rsi, final_overbought) and enable_alerts and current_minute > last_alert_rsi_buy
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(final_overbought, '#.##') + '","事件":"RSI 已上穿多头ML阈值 - 潜在买入信号","信号":"buy"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_rsi_buy := current_minute
if ta.crossunder(rsi, final_oversold) and enable_alerts and current_minute > last_alert_rsi_sell
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(final_oversold, '#.##') + '","事件":"RSI 已下穿空头ML阈值 - 潜在卖出信号","信号":"sell"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_rsi_sell := current_minute
// 区域转换信号 - 每分钟限制
// if ta.crossunder(rsi, final_overbought) and enable_alerts and current_minute > last_alert_rsi_neutral_from_ob
// alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(final_overbought, '#.##') + '","事件":"RSI 已下穿多头ML阈值进入中性区","备注":"进入震荡"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_rsi_neutral_from_ob := current_minute
// if ta.crossover(rsi, final_oversold) and enable_alerts and current_minute > last_alert_rsi_neutral_from_os
// alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(final_oversold, '#.##') + '","事件":"RSI 已上穿空头ML阈值进入中性区","备注":"进入震荡"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_rsi_neutral_from_os := current_minute
// 重复进入信号 - 每分钟限制
if overbought_repeat_alert and enable_alerts and current_minute > last_alert_overbought_repeat
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(final_overbought, '#.##') + '","事件":"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 enable_alerts and current_minute > last_alert_oversold_repeat
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(final_oversold, '#.##') + '","事件":"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 enable_alerts and current_minute > last_alert_overbought_mark2
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Long ML Threshold":"' + str.tostring(final_overbought, '#.##') + '","事件":"RSI 第二次进入超买区","标记":"2","信号":"overbought_mark2","警告":"二次超买信号"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_overbought_mark2 := current_minute
if oversold_mark2_alert and enable_alerts and current_minute > last_alert_oversold_mark2
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","Short ML Threshold":"' + str.tostring(final_oversold, '#.##') + '","事件":"RSI 第二次进入超卖区","标记":"-2","信号":"oversold_mark2","警告":"二次超卖信号"}'
alert(alert_message, alert.freq_once_per_bar_close)
last_alert_oversold_mark2 := current_minute
// 高级信号 - 每分钟限制
// if buy_signal and signal_score > 4 and enable_alerts and current_minute > last_alert_high_quality_buy
// alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","事件":"高质量买入信号","信号":"high_quality_buy","评分":"' + str.tostring(signal_score, '#.#') + '","备注":"强烈看涨条件"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_high_quality_buy := current_minute
// if sell_signal and signal_score < -4 and enable_alerts and current_minute > last_alert_high_quality_sell
// alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","事件":"高质量卖出信号","信号":"high_quality_sell","评分":"' + str.tostring(signal_score, '#.#') + '","备注":"强烈看跌条件"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_high_quality_sell := current_minute
// 背离信号 - 每分钟限制
// if bullish_divergence and enable_alerts and current_minute > last_alert_bullish_divergence
// alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","事件":"牛市背离","信号":"bullish_divergence","备注":"价格新低但RSI更高"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_bullish_divergence := current_minute
// if bearish_divergence and enable_alerts and current_minute > last_alert_bearish_divergence
// alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","事件":"熊市背离","信号":"bearish_divergence","备注":"价格新高但RSI更低"}'
// alert(alert_message, alert.freq_once_per_bar_close)
// last_alert_bearish_divergence := current_minute
// RSI状态警报 - 计算RSI变化方向
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, '#.##')
// RSI处于超买状态警报 - 每分钟限制
if rsi_overbought and enable_alerts and current_minute > last_alert_rsi_overbought
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","超买阈值":"' + str.tostring(final_overbought, '#.##') + '","事件":"RSI处于超买状态","RSI变化":"' + rsi_direction + '","RSI变化值":"' + rsi_change_value + '","信号":"rsi_overbought","备注":"RSI高于超买线"}'
alert(alert_message, alert.freq_once_per_bar)
last_alert_rsi_overbought := current_minute
// RSI处于超卖状态警报 - 每分钟限制
if rsi_oversold and enable_alerts and current_minute > last_alert_rsi_oversold
alert_message := '{"指标名称":"ML_RSI_Pro","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","RSI":"' + str.tostring(rsi, '#.##') + '","超卖阈值":"' + str.tostring(final_oversold, '#.##') + '","事件":"RSI处于超卖状态","RSI变化":"' + rsi_direction + '","RSI变化值":"' + rsi_change_value + '","信号":"rsi_oversold","备注":"RSI低于超卖线"}'
alert(alert_message, alert.freq_once_per_bar)
last_alert_rsi_oversold := current_minute
// (Advanced features moved above alerts section to avoid duplication)
// ===== ENHANCED VISUALIZATION =====
// Divergence markers
plotshape(bullish_divergence and show_signals, 'Bullish Divergence', shape.diamond,
location.bottom, color.lime, size = size.normal, text = 'BD')
plotshape(bearish_divergence and show_signals, 'Bearish Divergence', shape.diamond,
location.top, color.orange, size = size.normal, text = 'BD')
// RSI momentum histogram
plot(rsi_momentum * 10, 'RSI Momentum', color = rsi_momentum > 0 ? color.green : color.red,
style = plot.style_histogram, histbase = 0, display = display.none)
// Background coloring for trend alignment
bgcolor(enable_trend_filter and htf_trend == 1 ? color.new(color.green, 95) : enable_trend_filter and htf_trend == -1 ? color.new(color.red, 95) : na, title = 'HTF Trend Background')
// ===== PERFORMANCE METRICS =====
var int total_signals = 0
var int correct_signals = 0
var float total_return = 0.0
var float entry_price = na
// Track signal performance (simplified)
if buy_signal or sell_signal
total_signals := total_signals + 1
entry_price := close
// Calculate approximate return after 10 bars
if not na(entry_price) and bar_index % 10 == 0
return_pct = (close - entry_price) / entry_price * 100
if (buy_signal[10] and return_pct > 0) or (sell_signal[10] and return_pct < 0)
correct_signals := correct_signals + 1
total_return := total_return + math.abs(return_pct)
entry_price := na
// Calculate success rate
success_rate = total_signals > 0 ? correct_signals / total_signals * 100 : 0
// ===== ENHANCED STATISTICS TABLE =====
if show_statistics and barstate.islast
var table enhanced_stats = table.new(position.top_left, 2, 10, bgcolor = color.white, border_width = 1)
table.cell(enhanced_stats, 0, 0, 'Advanced Metrics', text_color = color.white, bgcolor = color.blue)
table.cell(enhanced_stats, 1, 0, 'Value', text_color = color.white, bgcolor = color.blue)
table.cell(enhanced_stats, 0, 1, 'Signal Quality', text_color = color.black)
table.cell(enhanced_stats, 1, 1, signal_quality, text_color = color.black)
table.cell(enhanced_stats, 0, 2, 'Signal Score', text_color = color.black)
table.cell(enhanced_stats, 1, 2, str.tostring(signal_score, '#.#'), text_color = color.black)
table.cell(enhanced_stats, 0, 3, 'HTF Trend', text_color = color.black)
table.cell(enhanced_stats, 1, 3, htf_trend == 1 ? 'Bullish' : htf_trend == -1 ? 'Bearish' : 'Neutral', text_color = color.black)
table.cell(enhanced_stats, 0, 4, 'RSI Momentum', text_color = color.black)
table.cell(enhanced_stats, 1, 4, str.tostring(rsi_momentum, '#.##'), text_color = color.black)
table.cell(enhanced_stats, 0, 5, 'Position Size', text_color = color.black)
table.cell(enhanced_stats, 1, 5, str.tostring(position_size_factor, '#.#') + 'x', text_color = color.black)
table.cell(enhanced_stats, 0, 6, 'Total Signals', text_color = color.black)
table.cell(enhanced_stats, 1, 6, str.tostring(total_signals), text_color = color.black)
table.cell(enhanced_stats, 0, 7, 'Success Rate', text_color = color.black)
table.cell(enhanced_stats, 1, 7, str.tostring(success_rate, '#.#') + '%', text_color = color.black)
table.cell(enhanced_stats, 0, 8, 'Bullish Div', text_color = color.black)
table.cell(enhanced_stats, 1, 8, bullish_divergence ? 'YES' : 'NO', text_color = color.black)
table.cell(enhanced_stats, 0, 9, 'Bearish Div', text_color = color.black)
table.cell(enhanced_stats, 1, 9, bearish_divergence ? 'YES' : 'NO', text_color = color.black)
// ===== 传统警报条件(保留兼容性)=====
// 注意使用统一警报系统时建议只使用上面的alert()函数
// 以下alertcondition保留用于需要单独设置警报的情况
// 基础穿越信号警报
alertcondition(ta.crossover(rsi, final_overbought), title = 'RSI上穿多头ML阈值', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","事件":"RSI 已上穿多头ML阈值 - 潜在买入信号","信号":"buy"}')
alertcondition(ta.crossunder(rsi, final_oversold), title = 'RSI下穿空头ML阈值', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","Short ML Threshold":"{{plot("Short ML Threshold")}}","事件":"RSI 已下穿空头ML阈值 - 潜在卖出信号","信号":"sell"}')
// 区域转换信号警报
// alertcondition(ta.crossunder(rsi, final_overbought), title = 'RSI下穿多头ML阈值进入中性区', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","事件":"RSI 已下穿多头ML阈值进入中性区","备注":"进入震荡","信号":"neutral_from_overbought"}')
// alertcondition(ta.crossover(rsi, final_oversold), title = 'RSI上穿空头ML阈值进入中性区', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{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 = '{"指标名称":"ML_RSI_Pro","交易对":"{{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 = '{"指标名称":"ML_RSI_Pro","交易对":"{{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 = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","Long ML Threshold":"{{plot("Long ML Threshold")}}","事件":"RSI 第二次进入超买区","标记":"2","信号":"overbought_mark2","警告":"二次超买信号"}')
alertcondition(oversold_mark2_alert, title = 'RSI第二次进入超卖区', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","Short ML Threshold":"{{plot("Short ML Threshold")}}","事件":"RSI 第二次进入超卖区","标记":"-2","信号":"oversold_mark2","警告":"二次超卖信号"}')
// 高级信号警报
// alertcondition(buy_signal and signal_score > 4, title = '高质量买入信号', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","事件":"高质量买入信号","信号":"high_quality_buy","评分":"{{plot("Signal Score")}}","备注":"强烈看涨条件"}')
// alertcondition(sell_signal and signal_score < -4, title = '高质量卖出信号', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","事件":"高质量卖出信号","信号":"high_quality_sell","评分":"{{plot("Signal Score")}}","备注":"强烈看跌条件"}')
// 背离信号警报
// alertcondition(bullish_divergence, title = '牛市背离', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","事件":"牛市背离","信号":"bullish_divergence","备注":"价格新低但RSI更高"}')
// alertcondition(bearish_divergence, title = '熊市背离', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","事件":"熊市背离","信号":"bearish_divergence","备注":"价格新高但RSI更低"}')
// RSI状态警报
alertcondition(rsi_overbought, title = 'RSI处于超买状态', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","超买阈值":"{{plot("Long ML Threshold")}}","事件":"RSI处于超买状态","信号":"rsi_overbought","备注":"RSI高于超买线"}')
alertcondition(rsi_oversold, title = 'RSI处于超卖状态', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","超卖阈值":"{{plot("Short ML Threshold")}}","事件":"RSI处于超卖状态","信号":"rsi_oversold","备注":"RSI低于超卖线"}')
// 基础买卖信号警报
// alertcondition(buy_signal, title = '买入信号', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","事件":"买入信号","信号":"buy_signal","备注":"过滤后的买入信号"}')
// alertcondition(sell_signal, title = '卖出信号', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","事件":"卖出信号","信号":"sell_signal","备注":"过滤后的卖出信号"}')
// 传统RSI阈值警报70/30
// alertcondition(ta.crossover(rsi, 70), title = 'RSI上穿70', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","阈值":"70","事件":"RSI上穿70","信号":"rsi_70_cross_up","备注":"传统超买信号"}')
// alertcondition(ta.crossunder(rsi, 30), title = 'RSI下穿30', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","阈值":"30","事件":"RSI下穿30","信号":"rsi_30_cross_down","备注":"传统超卖信号"}')
// RSI中线穿越警报50
// alertcondition(ta.crossover(rsi, 50), title = 'RSI上穿50', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","中线":"50","事件":"RSI上穿50","信号":"rsi_50_cross_up","备注":"多头信号"}')
// alertcondition(ta.crossunder(rsi, 50), title = 'RSI下穿50', message = '{"指标名称":"ML_RSI_Pro","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","RSI":"{{plot("RSI")}}","中线":"50","事件":"RSI下穿50","信号":"rsi_50_cross_down","备注":"空头信号"}')
// ===== PLOTS FOR EXTERNAL ACCESS =====
plot(buy_signal ? 1 : 0, 'Buy Signal Plot', display = display.none)
plot(sell_signal ? 1 : 0, 'Sell Signal Plot', display = display.none)
plot(final_oversold, 'Short ML Threshold', display = display.none) // For alert reference
plot(final_overbought, 'Long ML Threshold', display = display.none) // For alert reference
plot(signal_score, 'Signal Score', display = display.none)
plot(bullish_divergence ? 1 : 0, 'Bullish Divergence Plot', display = display.none)
plot(bearish_divergence ? 1 : 0, 'Bearish Divergence Plot', display = display.none)
plot(htf_trend, 'HTF Trend', display = display.none)
plot(segment_count, 'Segment Count', display = display.none) // For alert reference
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)
plot(oversold_mark2_alert ? 1 : 0, 'Oversold Mark2', display = display.none)
plot(rsi_overbought ? 1 : 0, 'RSI Overbought Status', display = display.none)
plot(rsi_oversold ? 1 : 0, 'RSI Oversold Status', display = display.none)
plot(rsi_change, 'RSI Change', display = display.none)