638 lines
38 KiB
Plaintext
638 lines
38 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 - 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)
|