diff --git a/MaxTrendPoints.pine b/MaxTrendPoints.pine new file mode 100644 index 0000000..a6bb5e9 --- /dev/null +++ b/MaxTrendPoints.pine @@ -0,0 +1,307 @@ +// This work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International +// https://creativecommons.org/licenses/by-nc-sa/4.0/ +// © BigBeluga + +//@version=6 +indicator("Max Trend Points [BigBeluga]", overlay = true, max_lines_count = 500, max_labels_count = 500) + +// INPUTS ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――{ +var line_up = line(na) +var line_dn = line(na) +var lbl_up = label(na) +var lbl_dn = label(na) + +var highest = array.new_float(0) +var lowest = array.new_float(0) + +var int start = na +var int last_trend_change = 0 +var float last_trend_price = 0.0 + +// Trend Factor Inputs +factor_default = input.float(2.5, step = 0.1, title="默认趋势因子") +factor_btc = input.float(3.0, step = 0.1, title="BTCUSD 趋势因子") +factor_xau = input.float(2.5, step = 0.1, title="XAUUSD 趋势因子") +factor_eth = input.float(2.8, step = 0.1, title="ETHUSD 趋势因子") +factor_gbp = input.float(2.5, step = 0.1, title="GBPJPY 趋势因子") + +// Consolidation Bars Inputs +consolidation_bars_default = input.int(20, title="默认震荡检测周期", minval=10) +consolidation_bars_btc = input.int(25, title="BTCUSD 震荡检测周期", minval=10) +consolidation_bars_xau = input.int(25, title="XAUUSD 震荡检测周期", minval=10) +consolidation_bars_eth = input.int(20, title="ETHUSD 震荡检测周期", minval=10) +consolidation_bars_gbp = input.int(18, title="GBPJPY 震荡检测周期", minval=10) + +// Consolidation Threshold Inputs +consolidation_threshold_default = input.float(0.1, title="默认震荡阈值(%)", minval=0.1, step=0.02) +consolidation_threshold_btc = input.float(0.2, title="BTCUSD 震荡阈值(%)", minval=0.1, step=0.02) +consolidation_threshold_xau = input.float(0.3, title="XAUUSD 震荡阈值(%)", minval=0.1, step=0.02) +consolidation_threshold_eth = input.float(0.15, title="ETHUSD 震荡阈值(%)", minval=0.1, step=0.02) +consolidation_threshold_gbp = input.float(0.16, title="GBPJPY 震荡阈值(%)", minval=0.1, step=0.02) + +// Activity Period Inputs +activity_period_default = input.int(14, title="默认活跃度检测周期", minval=5) +activity_period_btc = input.int(20, title="BTCUSD 活跃度检测周期", minval=5) +activity_period_xau = input.int(20, title="XAUUSD 活跃度检测周期", minval=5) +activity_period_eth = input.int(20, title="ETHUSD 活跃度检测周期", minval=5) +activity_period_gbp = input.int(12, title="GBPJPY 活跃度检测周期", minval=5) + +// Activity Threshold Inputs +activity_threshold_default = input.float(1.3, title="默认活跃度阈值(ATR倍数)", minval=0.1, step=0.1) +activity_threshold_btc = input.float(1.5, title="BTCUSD 活跃度阈值(ATR倍数)", minval=0.1, step=0.1) +activity_threshold_xau = input.float(1.4, title="XAUUSD 活跃度阈值(ATR倍数)", minval=0.1, step=0.1) +activity_threshold_eth = input.float(1.4, title="ETHUSD 活跃度阈值(ATR倍数)", minval=0.1, step=0.1) +activity_threshold_gbp = input.float(1.3, title="GBPJPY 活跃度阈值(ATR倍数)", minval=0.1, step=0.1) + +// Colors +col_up = input.color(color.rgb(28, 194, 216), "上涨颜色") +col_dn = input.color(color.rgb(228, 144, 19), "下跌颜色") + +// Alert Settings +alert_group = "警报设置" +enable_trend_alert = input.bool(true, "启用趋势变化警报", group=alert_group) +enable_consolidation_alert = input.bool(true, "启用震荡警报", group=alert_group) +enable_activity_alert = input.bool(true, "启用市场活跃度警报", group=alert_group) + +// Select Parameters Based on Trading Pair +sym = syminfo.tickerid +factor = + sym == "TICKMILL:BTCUSD" ? factor_btc : + sym == "TICKMILL:XAUUSD" ? factor_xau : + sym == "TICKMILL:ETHUSD" ? factor_eth : + sym == "TICKMILL:GBPJPY" ? factor_gbp : + factor_default + +consolidation_bars = + sym == "TICKMILL:BTCUSD" ? consolidation_bars_btc : + sym == "TICKMILL:XAUUSD" ? consolidation_bars_xau : + sym == "TICKMILL:ETHUSD" ? consolidation_bars_eth : + sym == "TICKMILL:GBPJPY" ? consolidation_bars_gbp : + consolidation_bars_default + +consolidation_threshold = + sym == "TICKMILL:BTCUSD" ? consolidation_threshold_btc : + sym == "TICKMILL:XAUUSD" ? consolidation_threshold_xau : + sym == "TICKMILL:ETHUSD" ? consolidation_threshold_eth : + sym == "TICKMILL:GBPJPY" ? consolidation_threshold_gbp : + consolidation_threshold_default + +activity_period = + sym == "TICKMILL:BTCUSD" ? activity_period_btc : + sym == "TICKMILL:XAUUSD" ? activity_period_xau : + sym == "TICKMILL:ETHUSD" ? activity_period_eth : + sym == "TICKMILL:GBPJPY" ? activity_period_gbp : + activity_period_default + +activity_threshold = + sym == "TICKMILL:BTCUSD" ? activity_threshold_btc : + sym == "TICKMILL:XAUUSD" ? activity_threshold_xau : + sym == "TICKMILL:ETHUSD" ? activity_threshold_eth : + sym == "TICKMILL:GBPJPY" ? activity_threshold_gbp : + activity_threshold_default +// } + +// CALCULATIONS――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――{ +trend_line(factor)=> + src = hl2 + dist = ta.hma(high-low, 200) + upperBand = src + factor * dist + lowerBand = src - factor * dist + prevLowerBand = nz(lowerBand[1]) + prevUpperBand = nz(upperBand[1]) + + lowerBand := lowerBand > prevLowerBand or close[1] < prevLowerBand ? lowerBand : prevLowerBand + upperBand := upperBand < prevUpperBand or close[1] > prevUpperBand ? upperBand : prevUpperBand + + int _direction = na + float trend_line = na + prevTrendLine = nz(trend_line[1]) + if na(dist[1]) + _direction := 1 + else if prevTrendLine == prevUpperBand + _direction := close > upperBand ? -1 : 1 + else + _direction := close < lowerBand ? 1 : -1 + + trend_line := _direction == -1 ? lowerBand : upperBand + + [trend_line, _direction] + +[trend_line, _direction] = trend_line(factor) + +t_change = ta.cross(_direction, 0) + +// Oscillation Detection +highest_recent = ta.highest(high, consolidation_bars) +lowest_recent = ta.lowest(low, consolidation_bars) +price_range = (highest_recent - lowest_recent) / lowest_recent * 100 +is_consolidating = price_range < consolidation_threshold + +// Market Activity Detection +atr_current = ta.atr(activity_period) +atr_avg = ta.sma(atr_current, activity_period * 2) +is_active = atr_current > atr_avg * activity_threshold + +// Calculate Trend Strength +trend_strength = math.abs(close - trend_line) / trend_line * 100 + +if t_change + array.clear(highest) + array.clear(lowest) + start := bar_index + last_trend_change := bar_index + last_trend_price := close + +if t_change and _direction == 1 + line_dn := line.new(bar_index, close, na, na, color = chart.fg_color, style = line.style_dashed) + lbl_dn := label.new(bar_index, low, color = color.new(col_dn, 50), textcolor = chart.fg_color, size = size.small, style = label.style_label_up) + +if t_change and _direction == -1 + lbl_up := label.new(bar_index, high, color = color.new(col_up, 50), textcolor = chart.fg_color, size = size.small, style = label.style_label_down) + line_up := line.new(bar_index, close, na, na, color = chart.fg_color, style = line.style_dashed) + +if not t_change and _direction == -1 + array.push(highest, high) + label.set_xy(lbl_up, start + array.indexof(highest, array.max(highest))+1, array.max(highest)) + line.set_xy2(line_up, start + array.indexof(highest, array.max(highest))+1, array.max(highest)) + +if not t_change and _direction == 1 + array.push(lowest, low) + label.set_xy(lbl_dn, start + array.indexof(lowest, array.min(lowest))+1, array.min(lowest)) + line.set_xy2(line_dn, start + array.indexof(lowest, array.min(lowest))+1, array.min(lowest)) + +if line.get_y2(line_up) > line.get_y1(line_up) + label.set_text(lbl_up, str.tostring((line.get_y2(line_up) - line.get_y1(line_up)) / line.get_y1(line_up) * 100, format.percent)) +if line.get_y2(line_dn) < line.get_y1(line_dn) + label.set_text(lbl_dn, str.tostring((line.get_y2(line_dn) - line.get_y1(line_dn)) / line.get_y1(line_dn) * 100, format.percent)) + +t_color = _direction == -1 ? col_up : col_dn + +// Track previous states for state change detection +var bool was_consolidating = false +var bool was_active = false +was_consolidating := is_consolidating +was_active := is_active +// } + +// ===== 统一警报系统 ===== +// 统一警报系统 - 只需添加一次警报即可捕获所有信号 + +// 检测所有警报条件并生成对应的JSON消息 +alert_message = "" + +// 趋势变化警报 +if enable_trend_alert and t_change and _direction == -1 + alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格转为上涨趋势","方向":"上涨趋势","信号":"trend_up"}' + alert(alert_message, alert.freq_once_per_bar_close) + +if enable_trend_alert and t_change and _direction == 1 + alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格转为下跌趋势","方向":"下跌趋势","信号":"trend_down"}' + alert(alert_message, alert.freq_once_per_bar_close) + +// 震荡状态变化警报 +// if enable_consolidation_alert and is_consolidating and not was_consolidating[1] +// alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"市场进入震荡状态","状态":"震荡","信号":"consolidation_start"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if enable_consolidation_alert and not is_consolidating and was_consolidating[1] +// direction_text = _direction == -1 ? "上涨趋势" : "下跌趋势" +// alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"市场突破震荡状态","方向":"' + direction_text + '","信号":"consolidation_break"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// // 活跃度变化警报 +// if enable_activity_alert and is_active and not was_active[1] +// alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"市场活跃度增加","活跃状态":"活跃","信号":"activity_increase"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if enable_activity_alert and not is_active and was_active[1] +// alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"市场活跃度降低","活跃状态":"不活跃","信号":"activity_decrease"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// 综合市场分析警报 +// if t_change and (enable_trend_alert or enable_consolidation_alert or enable_activity_alert) +// direction_text = _direction == -1 ? "上涨趋势" : "下跌趋势" +// status_text = is_consolidating ? "震荡" : (is_active ? "活跃" : "正常") +// alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"综合市场分析","方向":"' + direction_text + '","状态":"' + status_text + '","信号":"market_analysis"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// 状态监控警报(频率较低) +if enable_trend_alert and _direction == -1 + alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于上涨趋势","方向":"上涨趋势","信号":"trend_up_status"}' + alert(alert_message, alert.freq_once_per_bar) + +if enable_trend_alert and _direction == 1 + alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于下跌趋势","方向":"下跌趋势","信号":"trend_down_status"}' + alert(alert_message, alert.freq_once_per_bar) + +if enable_consolidation_alert and is_consolidating + alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于震荡状态","状态":"震荡","信号":"consolidation_status"}' + alert(alert_message, alert.freq_once_per_bar) + +if enable_activity_alert and is_active + alert_message := '{"指标名称":"MaxTrendPoints","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","趋势线":"' + str.tostring(trend_line, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.##') + '","价格范围":"' + str.tostring(price_range, '#.####') + '","当前ATR":"' + str.tostring(atr_current, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于活跃状态","活跃状态":"活跃","信号":"activity_status"}' + alert(alert_message, alert.freq_once_per_bar) + +// ===== 传统警报条件(保留兼容性)===== +// 注意:使用统一警报系统时,建议只使用上面的alert()函数 +// 以下alertcondition保留用于需要单独设置警报的情况 + +// 创建用于警报的plot变量 +plot(trend_line, title = '趋势线', display = display.none) +plot(trend_strength, title = '趋势强度', display = display.none) +plot(price_range, title = '价格范围', display = display.none) +plot(atr_current, title = '当前ATR', display = display.none) + +// 趋势变化警报 +alertcondition(enable_trend_alert and t_change and _direction == -1, title = '价格转为上涨趋势', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格转为上涨趋势","方向":"上涨趋势","信号":"trend_up"}') + +alertcondition(enable_trend_alert and t_change and _direction == 1, title = '价格转为下跌趋势', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格转为下跌趋势","方向":"下跌趋势","信号":"trend_down"}') + +// 震荡状态变化警报 +alertcondition(enable_consolidation_alert and is_consolidating and not was_consolidating[1], title = '市场进入震荡状态', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"市场进入震荡状态","状态":"震荡","信号":"consolidation_start"}') + +alertcondition(enable_consolidation_alert and not is_consolidating and was_consolidating[1], title = '市场突破震荡状态', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"市场突破震荡状态","信号":"consolidation_break"}') + +// 活跃度变化警报 +alertcondition(enable_activity_alert and is_active and not was_active[1], title = '市场活跃度增加', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"市场活跃度增加","活跃状态":"活跃","信号":"activity_increase"}') + +alertcondition(enable_activity_alert and not is_active and was_active[1], title = '市场活跃度降低', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"市场活跃度降低","活跃状态":"不活跃","信号":"activity_decrease"}') + +// 综合市场分析警报 +alertcondition(t_change and (enable_trend_alert or enable_consolidation_alert or enable_activity_alert), title = '综合市场分析', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"综合市场分析","信号":"market_analysis"}') + +// 状态监控警报 +alertcondition(enable_trend_alert and _direction == -1, title = '价格处于上涨趋势', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于上涨趋势","方向":"上涨趋势","信号":"trend_up_status"}') + +alertcondition(enable_trend_alert and _direction == 1, title = '价格处于下跌趋势', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于下跌趋势","方向":"下跌趋势","信号":"trend_down_status"}') + +alertcondition(enable_consolidation_alert and is_consolidating, title = '价格处于震荡状态', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于震荡状态","状态":"震荡","信号":"consolidation_status"}') + +alertcondition(enable_activity_alert and is_active, title = '价格处于活跃状态', message = '{"指标名称":"MaxTrendPoints","交易对":"{{ticker}}","周期":"{{interval}}","趋势线":"{{plot("趋势线")}}","趋势强度":"{{plot("趋势强度")}}","价格范围":"{{plot("价格范围")}}","当前ATR":"{{plot("当前ATR")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于活跃状态","活跃状态":"活跃","信号":"activity_status"}') + +// PLOT ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――{ +plotshape(t_change ? trend_line : na, "趋势变化", style = shape.circle, location = location.absolute, color = t_color, size = size.tiny) + +t_p = plot(trend_line, "趋势线", color = t_change ? color.new(t_color, 100) : t_color, linewidth = 2) +m_p = plot(hl2, color = color.new(color.gray, 100), editable = false) + +fill(t_p, m_p, hl2, trend_line, color.new(t_color, 95), color.new(t_color, 90)) + +// Status Panel +var table panel = table.new(position.top_right, 3, 4, bgcolor=color.new(color.black, 80)) +if barstate.islast + table.cell(panel, 0, 0, "市场状态", text_color=color.white, text_size=size.small) + table.cell(panel, 1, 0, "当前值", text_color=color.white, text_size=size.small) + + table.cell(panel, 0, 1, "趋势方向", text_color=color.gray, text_size=size.small) + table.cell(panel, 1, 1, _direction == -1 ? "↑ 上涨" : "↓ 下跌", + text_color=_direction == -1 ? col_up : col_dn, text_size=size.small) + + table.cell(panel, 0, 2, "市场状态", text_color=color.gray, text_size=size.small) + table.cell(panel, 1, 2, is_consolidating ? "震荡中" : "趋势中", + text_color=is_consolidating ? color.rgb(43, 43, 39) : color.green, text_size=size.small) + + table.cell(panel, 0, 3, "活跃度", text_color=color.gray, text_size=size.small) + table.cell(panel, 1, 3, is_active ? "活跃" : "平静", + text_color=is_active ? color.rgb(60, 60, 60) : color.blue, text_size=size.small) +// } \ No newline at end of file diff --git a/Swing Failure Pattern.pine b/Swing Failure Pattern.pine new file mode 100644 index 0000000..179b0f8 --- /dev/null +++ b/Swing Failure Pattern.pine @@ -0,0 +1,399 @@ +// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/ +// © LuxAlgo + +//@version=5 +indicator( 'SwingFailurePattern [LuxAlgo]', 'LuxAlgo - Swing Failure Pattern', max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500, overlay = true) +//---------------------------------------------------------------------------------------------------------------------} +//Settings +//---------------------------------------------------------------------------------------------------------------------{ +sp = '            ' +len = input.int ( 5 , 'Swings' , minval= 1 ) +bull = input.bool ( true , 'Bullish SFP' ) +bear = input.bool ( true , 'Bearish SFP' ) + +iVal = input.string ('None', 'Validation'+sp+ '   ', group='Volume Validation' + , options = ['Volume outside swing < Threshold' ,'Volume outside swing > Threshold','None']) +percent = input.float (25, 'Volume Threshold %' , inline='valy', group='Volume Validation' + , tooltip = '% of Total Volume' , minval= 0 , maxval=100 ) +auto = input.bool (true, '' , inline='auto', group='Volume Validation' ) +mlt = input.int (50 , '      Auto' + sp , inline='auto', group='Volume Validation' ) +res = input.timeframe('1' , sp +'   LTF' + sp +'  ', inline='ltf' , group='Volume Validation' ) +prem = input.bool (false ,'    Premium' , group='Volume Validation' + , tooltip = 'Premium Plan or higher' ) +showDash = input.bool (true , 'Show Dashboard' , group= 'Dashboard' ) +dashLoc = input.string ( 'Top Right' , 'Location' + , options = ['Top Right', 'Bottom Right', 'Bottom Left'] , group= 'Dashboard' ) +textSize = input.string ( 'Normal' , 'Size' + , options = ['Tiny', 'Small', 'Normal'] , group= 'Dashboard' ) +dSwingLine = input.bool (true , 'Swing Lines' , group= 'Style' ) +dOpposLine = input.bool (true , 'Confirmation Lines' , group= 'Style' ) +dSFP_Line = input.bool (true , 'Swing Failure Wick' , group= 'Style' ) +dSFP_Label = input.bool (true , 'Swing Failure Label' , group= 'Style' ) +showAlerts = input.bool (true , 'Show Alert Markers' , group= 'Style' ) +alertSize = input.string ('Normal', 'Alert Marker Size' , group= 'Style' + , options = ['Small', 'Normal', 'Large'] ) +colBl = input.color (#089981 , 'Lines / Labels' , inline='lin' , group= 'Style' ) +colBr = input.color (#f23645 , '' , inline='lin' , group= 'Style' + , tooltip = 'Bullish/Bearish' ) +colBl2 = input.color (#08998180, 'SFP Wicks      ', inline='wic' , group= 'Style' ) +colBr2 = input.color (#f2364580, '' , inline='wic' , group= 'Style' + , tooltip = 'Wick outside Swing, Bullish/Bearish' ) + +//-----------------------------------------------------------------------------} +//UDT +//-----------------------------------------------------------------------------{ +type piv + float swing_prc // price + int swing_bix // bar_index + float oppos_prc // price + int oppos_bix // bar_index + bool active + bool confirmed + line swing_line + line oppos_line + line wicky_line + label wicky_label + +type swing + int bix + float prc + +//-----------------------------------------------------------------------------} +//Variables +//-----------------------------------------------------------------------------{ +n = bar_index +INV = color(na) +FGc = chart.fg_color + +table_position = dashLoc == 'Bottom Left' ? position.bottom_left + : dashLoc == 'Top Right' ? position.top_right + : position.bottom_right + +table_size = textSize == 'Tiny' ? size.tiny + : textSize == 'Small' ? size.small + : size.normal + +alert_marker_size = alertSize == 'Small' ? size.small + : alertSize == 'Large' ? size.large + : size.normal + +var tb = table.new(table_position, 2, 3 + , bgcolor = #1e222d + , border_color = #373a46 + , border_width = 1 + , frame_color = #373a46 + , frame_width = 1) + +var swing swingH = swing.new() +var swing swingL = swing.new() +var piv pivH = piv.new() +var piv pivL = piv.new() + +validate = iVal != 'None' +valHigher= iVal == 'Volume outside swing > Threshold' +valLower = iVal == 'Volume outside swing < Threshold' + +//---------------------------------------------------------------------------------------------------------------------} +//Method +//---------------------------------------------------------------------------------------------------------------------{ +method n(float piv) => bool out = not na(piv) + +//---------------------------------------------------------------------------------------------------------------------} +//Execution +//---------------------------------------------------------------------------------------------------------------------{ +tfS = timeframe.in_seconds( res ) +tfC = timeframe.in_seconds(timeframe.period) +rs = auto ? tfC / mlt : tfS +if not validate + res := timeframe.period +else + rs := prem ? rs : math.max(60, rs) + res := timeframe.from_seconds(math.min(tfC, rs)) + +ph = ta.pivothigh(len, 1) +pl = ta.pivotlow (len, 1) + +[ltf_close, ltf_volume] = request.security_lower_tf(syminfo.tickerid, res, [close, volume]) + +ltf_size = ltf_close.size() + +if validate + if ltf_size > 0 and ltf_size[1] == 0 + line.new(n, close, n, close + syminfo.mintick, color=color.silver, style=line.style_dotted, extend=extend.both) + +//---------------------------------------------------------------------------------------------------------------------} +//Bearish Pattern +//---------------------------------------------------------------------------------------------------------------------{ +if bear + + if ph.n() + swingH.bix := n-1 + swingH.prc := ph + + sw = swingH.prc + bx = swingH.bix + if high > sw + and open < sw + and close < sw + valid = true + if validate + if ltf_close.size() > 0 + outsideVolume = 0. + totalVolume = ltf_volume.sum() + for j = 0 to ltf_close.size() -1 + if ltf_close.get(j) > sw + outsideVolume += ltf_volume.get(j) + if (valHigher ? 100 / totalVolume * outsideVolume < percent + : 100 / totalVolume * outsideVolume > percent + ) + valid := false + + //if valid + // label.new(n, high, text=str.format("Total Volume: {0}\nWick Volume: {1}", totalVolume, outsideVolume)) + + if valid + + opposL = sw + opposB = n + + for i = 1 to n - bx -1 + if low [i] < opposL + opposL := low [i] + opposB := n - i + + if not pivH.confirmed + pivH.swing_line .delete() + pivH.oppos_line .delete() + pivH.wicky_line .delete() + pivH.wicky_label.delete() + + pivH := piv.new(sw, bx, opposL, opposB, true, false) + + if dSwingLine + pivH.swing_line := line.new (bx , sw , n, sw , color=colBr) + if dOpposLine + pivH.oppos_line := line.new (opposB, opposL, n, opposL, color=colBr, style=line.style_dotted) + if dSFP_Line + pivH.wicky_line := line.new (n , high , n, sw , color=colBr2, width=3) + if dSFP_Label + pivH.wicky_label := label.new(n , high + , style=label.style_label_down + , text='SFP', textcolor=colBr + , color=INV, size=size.normal + ) + + if pivH.active and not pivH.confirmed + + pivH.swing_line.set_x2(n) + pivH.oppos_line.set_x2(n) + + if close < pivH.oppos_prc + pivH.confirmed := true + + if pivH.wicky_label.get_x() == n + pivH.wicky_label.set_text('SFP\n▼') + else + label.new(n, high, style=label.style_label_down, text='▼', textcolor=colBr, color=INV, size=size.normal) + + if n - pivH.swing_bix > 500 + or close > pivH.swing_prc + pivH.active := false + if not pivH.confirmed + pivH.swing_line .delete() + pivH.oppos_line .delete() + pivH.wicky_line .delete() + pivH.wicky_label.delete() + +//---------------------------------------------------------------------------------------------------------------------} +//Bullish Pattern +//---------------------------------------------------------------------------------------------------------------------{ +if bull + + if pl.n() + swingL.bix := n-1 + swingL.prc := pl + + sw = swingL.prc + bx = swingL.bix + if low < sw + and open > sw + and close > sw + valid = true + if validate + if ltf_close.size() > 0 + outsideVolume = 0. + totalVolume = ltf_volume.sum() + for j = 0 to ltf_close.size() -1 + if ltf_close.get(j) < sw + outsideVolume += ltf_volume.get(j) + if (valHigher ? 100 / totalVolume * outsideVolume < percent + : 100 / totalVolume * outsideVolume > percent + ) + valid := false + if valid + + opposH = sw + opposB = n + + for i = 1 to n - bx -1 + if high[i] > opposH + opposH := high[i] + opposB := n - i + + if not pivL.confirmed + pivL.swing_line .delete() + pivL.oppos_line .delete() + pivL.wicky_line .delete() + pivL.wicky_label.delete() + + pivL := piv.new(sw, bx, opposH, opposB, true, false) + + if dSwingLine + pivL.swing_line := line.new (bx , sw , n, sw , color=colBl ) + if dOpposLine + pivL.oppos_line := line.new (opposB, opposH, n, opposH, color=colBl , style=line.style_dotted) + if dSFP_Line + pivL.wicky_line := line.new (n , low , n, sw , color=colBl2, width=3) + if dSFP_Label + pivL.wicky_label := label.new(n , low + , style=label.style_label_up + , text='SFP', textcolor=colBl + , color=INV, size=size.normal + ) + + if pivL.active and not pivL.confirmed + + pivL.swing_line.set_x2(n) + pivL.oppos_line.set_x2(n) + + if close > pivL.oppos_prc + pivL.confirmed := true + + if pivL.wicky_label.get_x() == n + pivL.wicky_label.set_text('▲\nSFP') + else + label.new(n, low, style=label.style_label_up, text='▲', textcolor=colBl, color=INV, size=size.normal) + + if n - pivL.swing_bix > 500 + or close < pivL.swing_prc + pivL.active := false + if not pivL.confirmed + pivL.swing_line .delete() + pivL.oppos_line .delete() + pivL.wicky_line .delete() + pivL.wicky_label.delete() + +//---------------------------------------------------------------------------------------------------------------------} +//Dashboard +//---------------------------------------------------------------------------------------------------------------------{ +if barstate.islast and validate and showDash + tb.cell(0, 0, str.format("LTF: {0}", res), text_color=color.white, text_size=table_size) + +//---------------------------------------------------------------------------------------------------------------------} +//Alert System +//---------------------------------------------------------------------------------------------------------------------{ + +// ===== 统一警报系统 ===== +// 统一警报系统 - 只需添加一次警报即可捕获所有信号 + +// 警报频率限制 - 每分钟只触发一次 +var int last_alert_bearish_sfp = 0 +var int last_alert_bullish_sfp = 0 +var int last_alert_bearish_sfp_confirmed = 0 +var int last_alert_bullish_sfp_confirmed = 0 + +// 获取当前时间(分钟级别) +current_minute = math.floor(timenow / 60000) + +// 检测所有警报条件并生成对应的JSON消息 +alert_message = "" + +// 看跌SFP信号检测 - 每分钟限制 +bearish_sfp_signal = bear and high > swingH.prc and open < swingH.prc and close < swingH.prc and not pivH.confirmed +if bearish_sfp_signal and current_minute > last_alert_bearish_sfp + alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动高点":"' + str.tostring(swingH.prc, '#.####') + '","事件":"看跌摆动失败模式触发","信号":"bearish_sfp","备注":"突破高点后看跌回落"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_bearish_sfp := current_minute + + // 在K线上绘制看跌SFP信号标记 + if showAlerts + label.new(bar_index, high + (high - low) * 0.1, + text='🔻SFP', + style=label.style_label_down, + color=color.new(color.red, 20), + textcolor=color.white, + size=alert_marker_size, + tooltip='突破高点后看跌回落\n摆动高点: ' + str.tostring(swingH.prc, '#.####')) + +// 看涨SFP信号检测 - 每分钟限制 +bullish_sfp_signal = bull and low < swingL.prc and open > swingL.prc and close > swingL.prc and not pivL.confirmed +if bullish_sfp_signal and current_minute > last_alert_bullish_sfp + alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动低点":"' + str.tostring(swingL.prc, '#.####') + '","事件":"看涨摆动失败模式触发","信号":"bullish_sfp","备注":"跌破低点后看涨反弹"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_bullish_sfp := current_minute + + // 在K线上绘制看涨SFP信号标记 + if showAlerts + label.new(bar_index, low - (high - low) * 0.1, + text='🔺SFP', + style=label.style_label_up, + color=color.new(color.green, 20), + textcolor=color.white, + size=alert_marker_size, + tooltip='跌破低点后看涨反弹\n摆动低点: ' + str.tostring(swingL.prc, '#.####')) + +// 看跌SFP确认信号 - 每分钟限制 +bearish_sfp_confirmed = pivH.active and not pivH.confirmed[1] and pivH.confirmed and close < pivH.oppos_prc +if bearish_sfp_confirmed and current_minute > last_alert_bearish_sfp_confirmed + alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动高点":"' + str.tostring(pivH.swing_prc, '#.####') + '","对立价格":"' + str.tostring(pivH.oppos_prc, '#.####') + '","事件":"看跌摆动失败模式确认","信号":"bearish_sfp_confirmed","备注":"跌破对立低点确认看跌"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_bearish_sfp_confirmed := current_minute + + // 在K线上绘制看跌SFP确认信号标记 + if showAlerts + label.new(bar_index, high + (high - low) * 0.15, + text='✅🔻', + style=label.style_label_down, + color=color.new(color.maroon, 10), + textcolor=color.white, + size=alert_marker_size, + tooltip='跌破对立低点确认看跌\n摆动高点: ' + str.tostring(pivH.swing_prc, '#.####') + '\n对立价格: ' + str.tostring(pivH.oppos_prc, '#.####')) + +// 看涨SFP确认信号 - 每分钟限制 +bullish_sfp_confirmed = pivL.active and not pivL.confirmed[1] and pivL.confirmed and close > pivL.oppos_prc +if bullish_sfp_confirmed and current_minute > last_alert_bullish_sfp_confirmed + alert_message := '{"指标名称":"SwingFailurePattern","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","摆动低点":"' + str.tostring(pivL.swing_prc, '#.####') + '","对立价格":"' + str.tostring(pivL.oppos_prc, '#.####') + '","事件":"看涨摆动失败模式确认","信号":"bullish_sfp_confirmed","备注":"突破对立高点确认看涨"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_bullish_sfp_confirmed := current_minute + + // 在K线上绘制看涨SFP确认信号标记 + if showAlerts + label.new(bar_index, low - (high - low) * 0.15, + text='✅🔺', + style=label.style_label_up, + color=color.new(color.teal, 10), + textcolor=color.white, + size=alert_marker_size, + tooltip='突破对立高点确认看涨\n摆动低点: ' + str.tostring(pivL.swing_prc, '#.####') + '\n对立价格: ' + str.tostring(pivL.oppos_prc, '#.####')) + +// ===== 传统警报条件(保留兼容性)===== +// 注意:使用统一警报系统时,建议只使用上面的alert()函数 +// 以下alertcondition保留用于需要单独设置警报的情况 + +// 创建用于警报的plot变量 +plot(swingH.prc, title = '摆动高点', display = display.none) +plot(swingL.prc, title = '摆动低点', display = display.none) +plot(pivH.oppos_prc, title = '看跌对立价格', display = display.none) +plot(pivL.oppos_prc, title = '看涨对立价格', display = display.none) + +// SFP信号警报 +alertcondition(bearish_sfp_signal, title = '看跌摆动失败模式', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动高点":"{{plot("摆动高点")}}","事件":"看跌摆动失败模式触发","信号":"bearish_sfp","备注":"突破高点后看跌回落"}') + +alertcondition(bullish_sfp_signal, title = '看涨摆动失败模式', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动低点":"{{plot("摆动低点")}}","事件":"看涨摆动失败模式触发","信号":"bullish_sfp","备注":"跌破低点后看涨反弹"}') + +// SFP确认信号警报 +alertcondition(bearish_sfp_confirmed, title = '看跌摆动失败模式确认', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动高点":"{{plot("摆动高点")}}","对立价格":"{{plot("看跌对立价格")}}","事件":"看跌摆动失败模式确认","信号":"bearish_sfp_confirmed","备注":"跌破对立低点确认看跌"}') + +alertcondition(bullish_sfp_confirmed, title = '看涨摆动失败模式确认', message = '{"指标名称":"SwingFailurePattern","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","摆动低点":"{{plot("摆动低点")}}","对立价格":"{{plot("看涨对立价格")}}","事件":"看涨摆动失败模式确认","信号":"bullish_sfp_confirmed","备注":"突破对立高点确认看涨"}') + +//---------------------------------------------------------------------------------------------------------------------} \ No newline at end of file diff --git a/mrc copy.pine b/mrc copy.pine new file mode 100644 index 0000000..1213f80 --- /dev/null +++ b/mrc copy.pine @@ -0,0 +1,844 @@ +//@version=6 +indicator('Mean Reversion Channel - 带价格标签 -ma200', shorttitle = 'MRC2ma200', overlay = true, format = format.inherit) + +//************************************************************************************************************ +// Parameter +//************************************************************************************************************ + +indiSet = input(false, '═════════ MRC Parameter ════════') +source = input(hlc3, title = 'Price Source') +type = input.string('SuperSmoother', title = 'Filter Type', options = ['SuperSmoother', 'Ehlers EMA', 'Gaussian', 'Butterworth', 'BandStop', 'SMA', 'EMA', 'RMA']) +length = input.int(200, title = 'Lookback Period', minval = 1) +innermult = input.float(1.0, title = 'Inner Channel Size Multiplier', minval = 0.1) +outermult = input.float(2.415, title = 'Outer Channel Size Multiplier', minval = 0.1) + +// ————————————— MA200 参数 ————————————— +maSet = input(false, '═════════ MA200 Parameter ════════') +exponential = input.bool(true, title='使用指数移动平均线 (EMA)') +show_ma_lines = input.bool(true, title='显示MA线条') +show_ma_labels = input.bool(true, title='显示MA价格标签') + +ChartSet = input(false, '═════════ Chart Setting ════════') +drawchannel = input(true, title = 'Draw Channel') +displayzone = input(true, title = 'Draw Zone (With Channel)') +zonetransp = input.int(60, title = 'Zone Transparency', minval = 0, maxval = 100) +displayline = input(true, title = 'Display Line Extension') + +MTFSet = input(false, '═════════ MTF Setting ════════') +enable_mtf = input(true, title = 'Enable Multiple TimeFrame Analysis') +mtf_disp_typ = input.string('On Hover', title = 'MTF Display Type', options = ['Always Display', 'On Hover']) +mtf_typ = input.string('Auto', title = 'Multiple TimeFrame Type', options = ['Auto', 'Custom']) +mtf_lvl1 = input.timeframe('D', title = 'Custom MTF Level 1') +mtf_lvl2 = input.timeframe('W', title = 'Custom MTF Level 2') + +//************************************************************************************************************ +// Functions Start { +//************************************************************************************************************ +var pi = 2 * math.asin(1) +var mult = pi * innermult +var mult2 = pi * outermult +var gradsize = 0.5 +var gradtransp = zonetransp + +//----------------------- +// Ehler SwissArmyKnife Function +//----------------------- +SAK_smoothing(_type, _src, _length) => + c0 = 1.0 + c1 = 0.0 + b0 = 1.0 + b1 = 0.0 + b2 = 0.0 + a1 = 0.0 + a2 = 0.0 + alpha = 0.0 + beta = 0.0 + gamma = 0.0 + cycle = 2 * pi / _length + + if _type == 'Ehlers EMA' + alpha := (math.cos(cycle) + math.sin(cycle) - 1) / math.cos(cycle) + b0 := alpha + a1 := 1 - alpha + a1 + if _type == 'Gaussian' + beta := 2.415 * (1 - math.cos(cycle)) + alpha := -beta + math.sqrt(beta * beta + 2 * beta) + c0 := alpha * alpha + a1 := 2 * (1 - alpha) + a2 := -(1 - alpha) * (1 - alpha) + a2 + if _type == 'Butterworth' + beta := 2.415 * (1 - math.cos(cycle)) + alpha := -beta + math.sqrt(beta * beta + 2 * beta) + c0 := alpha * alpha / 4 + b1 := 2 + b2 := 1 + a1 := 2 * (1 - alpha) + a2 := -(1 - alpha) * (1 - alpha) + a2 + if _type == 'BandStop' + beta := math.cos(cycle) + gamma := 1 / math.cos(cycle * 2 * 0.1) // delta default to 0.1. Acceptable delta -- 0.05 + s_a1 = math.exp(-math.sqrt(2) * pi / _length) + s_b1 = 2 * s_a1 * math.cos(math.sqrt(2) * pi / _length) + s_c3 = -math.pow(s_a1, 2) + s_c2 = s_b1 + s_c1 = 1 - s_c2 - s_c3 + ss = 0.0 + ss := s_c1 * _src + s_c2 * nz(ss[1], _src[1]) + s_c3 * nz(ss[2], _src[2]) + ss + +//----------------------- +// Auto TimeFrame Function +//----------------------- +// ————— Converts current chart resolution into a float minutes value. +f_resInMinutes() => + _resInMinutes = timeframe.multiplier * (timeframe.isseconds ? 1. / 60 : timeframe.isminutes ? 1. : timeframe.isdaily ? 60. * 24 : timeframe.isweekly ? 60. * 24 * 7 : timeframe.ismonthly ? 60. * 24 * 30.4375 : na) + _resInMinutes + +get_tf(_lvl) => + y = f_resInMinutes() + z = timeframe.period + if mtf_typ == 'Auto' + if y < 1 + z := _lvl == 1 ? '1' : _lvl == 2 ? '5' : z + z + else if y <= 3 + z := _lvl == 1 ? '5' : _lvl == 2 ? '15' : z + z + else if y <= 10 + z := _lvl == 1 ? '15' : _lvl == 2 ? '60' : z + z + else if y <= 30 + z := _lvl == 1 ? '60' : _lvl == 2 ? '240' : z + z + else if y <= 120 + z := _lvl == 1 ? '240' : _lvl == 2 ? 'D' : z + z + else if y <= 240 + z := _lvl == 1 ? 'D' : _lvl == 2 ? 'W' : z + z + else if y <= 1440 + z := _lvl == 1 ? 'W' : _lvl == 2 ? 'M' : z + z + else if y <= 10080 + z := _lvl == 1 ? 'M' : z + z + else + z := z + z + else + z := _lvl == 1 ? mtf_lvl1 : _lvl == 2 ? mtf_lvl2 : z + z + + z + +//----------------------- +// Mean Reversion Channel Function +//----------------------- +get_mrc() => + v_condition = 0 + v_meanline = source + v_meanrange = supersmoother(ta.tr, length) + + //-- Get Line value + if type == 'SuperSmoother' + v_meanline := supersmoother(source, length) + v_meanline + + if type != 'SuperSmoother' + v_meanline := SAK_smoothing(type, source, length) + v_meanline + + v_upband1 = v_meanline + v_meanrange * mult + v_loband1 = v_meanline - v_meanrange * mult + v_upband2 = v_meanline + v_meanrange * mult2 + v_loband2 = v_meanline - v_meanrange * mult2 + + //-- Check Condition + if close > v_meanline + v_upband2_1 = v_upband2 + v_meanrange * gradsize * 4 + v_upband2_9 = v_upband2 + v_meanrange * gradsize * -4 + if high >= v_upband2_9 and high < v_upband2 + v_condition := 1 + v_condition + else if high >= v_upband2 and high < v_upband2_1 + v_condition := 2 + v_condition + else if high >= v_upband2_1 + v_condition := 3 + v_condition + else if close <= v_meanline + v_meanrange + v_condition := 4 + v_condition + else + v_condition := 5 + v_condition + + if close < v_meanline + v_loband2_1 = v_loband2 - v_meanrange * gradsize * 4 + v_loband2_9 = v_loband2 - v_meanrange * gradsize * -4 + if low <= v_loband2_9 and low > v_loband2 + v_condition := -1 + v_condition + else if low <= v_loband2 and low > v_loband2_1 + v_condition := -2 + v_condition + else if low <= v_loband2_1 + v_condition := -3 + v_condition + else if close >= v_meanline + v_meanrange + v_condition := -4 + v_condition + else + v_condition := -5 + v_condition + + [v_meanline, v_meanrange, v_upband1, v_loband1, v_upband2, v_loband2, v_condition] + +//----------------------- +// MTF Analysis +//----------------------- + +get_stat(_cond) => + ret = 'Price at Mean Line\n' + if _cond == 1 + ret := 'Overbought (Weak)\n' + ret + else if _cond == 2 + ret := 'Overbought\n' + ret + else if _cond == 3 + ret := 'Overbought (Strong)\n' + ret + else if _cond == 4 + ret := 'Price Near Mean\n' + ret + else if _cond == 5 + ret := 'Price Above Mean\n' + ret + else if _cond == -1 + ret := 'Oversold (Weak)\n' + ret + else if _cond == -2 + ret := 'Oversold\n' + ret + else if _cond == -3 + ret := 'Oversold (Strong)\n' + ret + else if _cond == -4 + ret := 'Price Near Mean\n' + ret + else if _cond == -5 + ret := 'Price Below Mean\n' + ret + ret + +//----------------------- +// Chart Drawing Function +//----------------------- +format_price(x) => + y = str.tostring(x, '0.00000') + if x > 10 + y := str.tostring(x, '0.000') + y + if x > 1000 + y := str.tostring(x, '0.00') + y + y + +f_PriceLine(_ref, linecol) => + line.new(x1 = bar_index, x2 = bar_index - 1, y1 = _ref, y2 = _ref, extend = extend.left, color = linecol) + +f_MTFLabel(_txt, _yloc) => + label.new(x = time + math.round(ta.change(time) * 20), y = _yloc, xloc = xloc.bar_time, text = mtf_disp_typ == 'Always Display' ? _txt : 'Check MTF', tooltip = mtf_disp_typ == 'Always Display' ? '' : _txt, color = color.black, textcolor = color.white, size = size.normal, style = mtf_disp_typ == 'On Hover' and displayline ? label.style_label_lower_left : label.style_label_left, textalign = text.align_left) + +//} Function End + +//************************************************************************************************************ +// Calculate MA200, MA100, MA50 +//************************************************************************************************************ +// ————————————— 计算移动平均线 ————————————— +src = close +ma50 = exponential ? ta.ema(src, 50) : ta.sma(src, 50) +ma100 = exponential ? ta.ema(src, 100) : ta.sma(src, 100) +ma200 = exponential ? ta.ema(src, 200) : ta.sma(src, 200) + +// ————————————— 移动平均线颜色函数 ————————————— +maColor(ma, ref) => ma > ref ? color.lime : color.red + +//************************************************************************************************************ +// Calculate Channel +//************************************************************************************************************ +var tf_0 = timeframe.period +var tf_1 = get_tf(1) +var tf_2 = get_tf(2) + +[meanline, meanrange, upband1, loband1, upband2, loband2, condition] = get_mrc() +[mtf1_meanline, mtf1_meanrange, mtf1_upband1, mtf1_loband1, mtf1_upband2, mtf1_loband2, mtf1_condition] = request.security(syminfo.tickerid, tf_1, get_mrc()) +[mtf2_meanline, mtf2_meanrange, mtf2_upband1, mtf2_loband1, mtf2_upband2, mtf2_loband2, mtf2_condition] = request.security(syminfo.tickerid, tf_2, get_mrc()) + +//************************************************************************************************************ +// Drawing Start { +//************************************************************************************************************ +float p_meanline = drawchannel ? meanline : na +float p_upband1 = drawchannel ? upband1 : na +float p_loband1 = drawchannel ? loband1 : na +float p_upband2 = drawchannel ? upband2 : na +float p_loband2 = drawchannel ? loband2 : na + +// 保留 MEAN、R1、S1 的绘制用于警报,注释掉 R2、S2 +z = plot(p_meanline, color = color.new(color.black, 0), style = plot.style_line, title = ' Mean', linewidth = 2) +x1 = plot(p_upband1, color = color.new(color.black, 0), style = plot.style_line, title = ' R1', linewidth = 2) +x2 = plot(p_loband1, color = color.new(color.black, 0), style = plot.style_line, title = ' S1', linewidth = 2) +// 注释掉 R2、S2 的绘制以减少线条数量 +// y1 = plot(p_upband2, color = color.new(color.red, 50), style = plot.style_line, title = ' R2', linewidth = 1) +// y2 = plot(p_loband2, color = color.new(color.red, 50), style = plot.style_line, title = ' S2', linewidth = 1) + +// ————————————— 绘制 MA50/MA100/MA200 ————————————— +plot(show_ma_lines ? ma50 : na, color=maColor(ma50, ma100), linewidth=2, title='MA50', display=display.all) +plot(show_ma_lines ? ma100 : na, color=maColor(ma100, ma200), linewidth=2, title='MA100', display=display.all) +plot(show_ma_lines ? ma200 : na, color=color.rgb(25, 58, 243), linewidth=4, title='MA200', display=display.all) + +//----------------------- +// Draw zone +//----------------------- +//--- +var color1 = #FF0000 +var color2 = #FF4200 +var color3 = #FF5D00 +var color4 = #FF7400 +var color5 = #FF9700 +var color6 = #FFAE00 +var color7 = #FFC500 +var color8 = #FFCD00 +//--- +float upband2_1 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 4 : na +float loband2_1 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 4 : na +float upband2_2 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 3 : na +float loband2_2 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 3 : na +float upband2_3 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 2 : na +float loband2_3 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 2 : na +float upband2_4 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 1 : na +float loband2_4 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 1 : na +float upband2_5 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 0 : na +float loband2_5 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 0 : na +float upband2_6 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -1 : na +float loband2_6 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -1 : na +float upband2_7 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -2 : na +float loband2_7 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -2 : na +float upband2_8 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -3 : na +float loband2_8 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -3 : na +float upband2_9 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -4 : na +float loband2_9 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -4 : na + +//--- +// 注释掉扩展区域的 plot 以减少线条数量,避免超过 TradingView 64条线限制 +// 保留计算但不绘制,警报仍可使用这些值 +// plot_upband2_1 = plot(upband2_1, color = na, title = 'R2_1') +// plot_loband2_1 = plot(loband2_1, color = na, title = 'S2_1') + +// 注释掉其他隐藏的 plot +// plot_upband2_2 = plot(upband2_2, color = na, display = display.none) +// plot_loband2_2 = plot(loband2_2, color = na, display = display.none) +// plot_upband2_3 = plot(upband2_3, color = na, display = display.none) +// plot_loband2_3 = plot(loband2_3, color = na, display = display.none) +// plot_upband2_4 = plot(upband2_4, color = na, display = display.none) +// plot_loband2_4 = plot(loband2_4, color = na, display = display.none) +// plot_upband2_5 = plot(upband2_5, color = na, display = display.none) +// plot_loband2_5 = plot(loband2_5, color = na, display = display.none) +// plot_upband2_6 = plot(upband2_6, color = na, display = display.none) +// plot_loband2_6 = plot(loband2_6, color = na, display = display.none) +// plot_upband2_7 = plot(upband2_7, color = na, display = display.none) +// plot_loband2_7 = plot(loband2_7, color = na, display = display.none) +// plot_upband2_8 = plot(upband2_8, color = na, display = display.none) +// plot_loband2_8 = plot(loband2_8, color = na, display = display.none) + +// 注释掉 R2_9 和 S2_9 的 plot +// plot_upband2_9 = plot(upband2_9, color = na, title = 'R2_9') +// plot_loband2_9 = plot(loband2_9, color = na, title = 'S2_9') + +//--- +// 注释掉扩展区域的填充,减少绘制元素 +// fill(plot_upband2_1, plot_upband2_2, color = color1) +// fill(plot_loband2_1, plot_loband2_2, color = color1) +// fill(plot_upband2_2, plot_upband2_3, color = color2) +// fill(plot_loband2_2, plot_loband2_3, color = color2) +// fill(plot_upband2_3, plot_upband2_4, color = color3) +// fill(plot_loband2_3, plot_loband2_4, color = color3) +// fill(plot_upband2_4, plot_upband2_5, color = color4) +// fill(plot_loband2_4, plot_loband2_5, color = color4) +// fill(plot_upband2_5, plot_upband2_6, color = color5) +// fill(plot_loband2_5, plot_loband2_6, color = color5) +// fill(plot_upband2_6, plot_upband2_7, color = color6) +// fill(plot_loband2_6, plot_loband2_7, color = color6) +// fill(plot_upband2_7, plot_upband2_8, color = color7) +// fill(plot_loband2_7, plot_loband2_8, color = color7) +// fill(plot_upband2_8, plot_upband2_9, color = color8) +// fill(plot_loband2_8, plot_loband2_9, color = color8) + +//----------------------- +// Plot Extension +//----------------------- +if displayline and enable_mtf and mtf_disp_typ == 'Always Display' + displayline := false + displayline + +var line mean = na +line.delete(mean) +mean := displayline ? f_PriceLine(meanline, #FFCD00) : na +var line res1 = na +line.delete(res1) +res1 := displayline ? f_PriceLine(upband1, color.green) : na +var line sup1 = na +line.delete(sup1) +sup1 := displayline ? f_PriceLine(loband1, color.green) : na +var line res2 = na +line.delete(res2) +res2 := displayline ? f_PriceLine(upband2, color.red) : na +var line sup2 = na +line.delete(sup2) +sup2 := displayline ? f_PriceLine(loband2, color.red) : na + +//-------------- +// Prep MTF Label +//-------------- +var brl = '\n--------------------------------------' +dist_0 = 'Distance from Mean: ' + str.tostring((close - meanline) / close * 100, '#.##') + ' %' +dist_1 = 'Distance from Mean: ' + str.tostring((close - mtf1_meanline) / close * 100, '#.##') + ' %' +dist_2 = 'Distance from Mean: ' + str.tostring((close - mtf2_meanline) / close * 100, '#.##') + ' %' + +var title = 'Mean Reversion Channel\nMultiple TimeFrame Analysis' + brl +tf0 = '\n\nTimeframe: ' + tf_0 + ' (Current)\n\nStatus: ' + get_stat(condition) + dist_0 + brl + +tf1 = not timeframe.ismonthly ? '\n\nTimeframe: ' + tf_1 + '\n\nStatus: ' + get_stat(mtf1_condition) + dist_1 + brl : '' + +tf2 = not timeframe.isweekly and not timeframe.ismonthly ? '\n\nTimeframe: ' + tf_2 + '\n\nStatus: ' + get_stat(mtf2_condition) + dist_2 + brl : '' + +mtf_lbl = title + tf0 + tf1 + tf2 +var label label_mtf = na +label.delete(label_mtf) +label_mtf := enable_mtf ? f_MTFLabel(mtf_lbl, meanline) : na + +//************************************************************************************************************ +// Real-time Price Labels for Extended Lines (Placed on the right side of the last candle) +//************************************************************************************************************ + +// Define persistent label variables so that they update rather than create new labels each bar +var label mean_label = na +var label res1_label = na +var label sup1_label = na +var label res2_label = na +var label sup2_label = na +var label res2_9_label = na // Label for upband2_9 +var label sup2_9_label = na // Label for loband2_9 +var label res2_1_label = na // New label for upband2_1 +var label sup2_1_label = na // New label for loband2_1 + +if displayline and barstate.islast + // MEAN label + if na(mean_label) + mean_label := label.new(bar_index, meanline, "MEAN: " + format_price(meanline), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.black, 0), textcolor = color.white) + else + label.set_xy(mean_label, bar_index, meanline) + label.set_text(mean_label, "MEAN: " + format_price(meanline)) + // R1 label + if na(res1_label) + res1_label := label.new(bar_index, upband1, "R1: " + format_price(upband1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.green, 0), textcolor = color.white) + else + label.set_xy(res1_label, bar_index, upband1) + label.set_text(res1_label, "R1: " + format_price(upband1)) + // S1 label + if na(sup1_label) + sup1_label := label.new(bar_index, loband1, "S1: " + format_price(loband1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.green, 0), textcolor = color.white) + else + label.set_xy(sup1_label, bar_index, loband1) + label.set_text(sup1_label, "S1: " + format_price(loband1)) + // R2 label + if na(res2_label) + res2_label := label.new(bar_index, upband2, "R2: " + format_price(upband2), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 0), textcolor = color.white) + else + label.set_xy(res2_label, bar_index, upband2) + label.set_text(res2_label, "R2: " + format_price(upband2)) + // S2 label + if na(sup2_label) + sup2_label := label.new(bar_index, loband2, "S2: " + format_price(loband2), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 0), textcolor = color.white) + else + label.set_xy(sup2_label, bar_index, loband2) + label.set_text(sup2_label, "S2: " + format_price(loband2)) + // R2_9 label (for upband2_9) + if na(res2_9_label) + res2_9_label := label.new(bar_index, upband2_9, "R2_9: " + format_price(upband2_9), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(res2_9_label, bar_index, upband2_9) + label.set_text(res2_9_label, "R2_9: " + format_price(upband2_9)) + // S2_9 label (for loband2_9) + if na(sup2_9_label) + sup2_9_label := label.new(bar_index, loband2_9, "S2_9: " + format_price(loband2_9), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(sup2_9_label, bar_index, loband2_9) + label.set_text(sup2_9_label, "S2_9: " + format_price(loband2_9)) + // R2_1 label (New for upband2_1) + if na(res2_1_label) + res2_1_label := label.new(bar_index, upband2_1, "R2_1: " + format_price(upband2_1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(res2_1_label, bar_index, upband2_1) + label.set_text(res2_1_label, "R2_1: " + format_price(upband2_1)) + // S2_1 label (New for loband2_1) + if na(sup2_1_label) + sup2_1_label := label.new(bar_index, loband2_1, "S2_1: " + format_price(loband2_1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(sup2_1_label, bar_index, loband2_1) + label.set_text(sup2_1_label, "S2_1: " + format_price(loband2_1)) + +//************************************************************************************************************ +// MA Price Labels (Placed on the right side of the last candle) +//************************************************************************************************************ +// ————————————— MA价格标签(单行 label.new) ————————————— +var label lbl50 = na +var label lbl100 = na +var label lbl200 = na + +if show_ma_labels and barstate.islast + if na(lbl50) + lbl50 := label.new(x=time, y=ma50, text='MA50: ' + str.tostring(ma50, '#.####'), xloc=xloc.bar_time, yloc=yloc.price, style=label.style_label_left, color=color.lime, textcolor=color.white) + else + label.set_xy(lbl50, x=time, y=ma50), label.set_text(lbl50, 'MA50: ' + str.tostring(ma50, '#.####')) + + if na(lbl100) + lbl100 := label.new(x=time, y=ma100, text='MA100: ' + str.tostring(ma100, '#.####'), xloc=xloc.bar_time, yloc=yloc.price, style=label.style_label_left, color=color.white, textcolor=color.black) + else + label.set_xy(lbl100, x=time, y=ma100), label.set_text(lbl100, 'MA100: ' + str.tostring(ma100, '#.####')) + + if na(lbl200) + lbl200 := label.new(x=time, y=ma200, text='MA200: ' + str.tostring(ma200, '#.####'), xloc=xloc.bar_time, yloc=yloc.price, style=label.style_label_left, color=color.white, textcolor=color.black) + else + label.set_xy(lbl200, x=time, y=ma200), label.set_text(lbl200, 'MA200: ' + str.tostring(ma200, '#.####')) +//************************************************************************************************************ +// ===== 统一警报系统 ===== +// 统一警报系统 - 只需添加一次警报即可捕获所有信号 +//************************************************************************************************************ + +// 警报频率限制 - 每分钟只触发一次 +var int last_alert_r1_breakout_up = 0 +var int last_alert_s1_breakout_down = 0 +var int last_alert_range_r1_s1 = 0 +var int last_alert_above_r1 = 0 +var int last_alert_below_s1 = 0 +var int last_alert_above_mean = 0 +var int last_alert_below_mean = 0 +var int last_alert_mean_above_ma200 = 0 +var int last_alert_mean_below_ma200 = 0 +var int last_alert_mean_above_ma50 = 0 +var int last_alert_mean_below_ma50 = 0 +var int last_alert_mean_above_ma100 = 0 +var int last_alert_mean_below_ma100 = 0 + +// 获取当前时间(分钟级别) +current_minute = math.floor(timenow / 60000) + +// 检测所有警报条件并生成对应的JSON消息 +alert_message = "" + +// 价格触碰关键位置警报 +// if ta.cross(close, meanline) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到MEAN","位置":"MEAN","信号":"mean_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, upband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到R1","位置":"R1","信号":"r1_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, loband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到S1","位置":"S1","信号":"s1_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, upband2) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到R2","位置":"R2","信号":"r2_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, loband2) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到S2","位置":"S2","信号":"s2_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// 价格区间警报 +// if close > meanline and close < upband1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN和R1之间","位置":"MEAN-R1","信号":"range_mean_r1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < meanline and close > loband1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN和S1之间","位置":"MEAN-S1","信号":"range_mean_s1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close > upband1 and close < upband2 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1和R2之间","位置":"R1-R2","信号":"range_r1_r2"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < loband1 and close > loband2 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S1和S2之间","位置":"S1-S2","信号":"range_s1_s2"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close > upband1 and close < upband2_9 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1和R2_9之间","位置":"R1-R2_9","信号":"range_r1_r2_9"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close > upband2_9 and close < upband2_1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R2_9和R2_1之间","位置":"R2_9-R2_1","信号":"range_r2_9_r2_1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < loband1 and close > loband2_9 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S1和S2_9之间","位置":"S1-S2_9","信号":"range_s1_s2_9"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < loband2_9 and close > loband2_1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S2_9和S2_1之间","位置":"S2_9-S2_1","信号":"range_s2_9_s2_1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// // 价格穿越警报 - 每分钟限制 +// if ta.crossover(close, upband1) and current_minute > last_alert_r1_breakout_up +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿R1","位置":"R1","信号":"r1_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) +// last_alert_r1_breakout_up := current_minute + +// if ta.crossunder(close, upband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿R1","位置":"R1","信号":"r1_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossunder(close, loband1) and current_minute > last_alert_s1_breakout_down +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿S1","位置":"S1","信号":"s1_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) +// last_alert_s1_breakout_down := current_minute + +// if ta.crossover(close, loband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿S1","位置":"S1","信号":"s1_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossover(close, meanline) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿MEAN","位置":"MEAN","信号":"mean_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossunder(close, meanline) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿MEAN","位置":"MEAN","信号":"mean_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossover(close, loband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿S2_9","位置":"S2_9","信号":"s2_9_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossunder(close, loband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿S2_9","位置":"S2_9","信号":"s2_9_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossover(close, upband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿R2_9","位置":"R2_9","信号":"r2_9_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossunder(close, upband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿R2_9","位置":"R2_9","信号":"r2_9_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// ===== 新增的5个警报条件 - 每分钟限制 ===== +// 1. 价格处于R1和S1之间时 +// if close <= upband1 and close >= loband1 and current_minute > last_alert_range_r1_s1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1和S1之间","位置":"R1-S1区间","信号":"range_r1_s1"}' +// alert(alert_message, alert.freq_once_per_bar) +// last_alert_range_r1_s1 := current_minute + +// // 2. 价格处于R1以上时 +// if close > upband1 and current_minute > last_alert_above_r1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1以上","位置":"R1以上","信号":"above_r1"}' +// alert(alert_message, alert.freq_once_per_bar) +// last_alert_above_r1 := current_minute + +// // 3. 价格处于S1以下时 +// if close < loband1 and current_minute > last_alert_below_s1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S1以下","位置":"S1以下","信号":"below_s1"}' +// alert(alert_message, alert.freq_once_per_bar) +// last_alert_below_s1 := current_minute + +// // 4. 价格处于MEAN以上时 +// if close > meanline and current_minute > last_alert_above_mean +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN以上","位置":"MEAN以上","信号":"above_mean"}' +// alert(alert_message, alert.freq_once_per_bar) +// last_alert_above_mean := current_minute + +// // 5. 价格处于MEAN以下时 +// if close < meanline and current_minute > last_alert_below_mean +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN以下","位置":"MEAN以下","信号":"below_mean"}' +// alert(alert_message, alert.freq_once_per_bar) +// last_alert_below_mean := current_minute + +// ===== 新增的MA相关警报条件 - 每分钟限制 ===== +// 计算MEAN线与各MA的距离 +mean_ma200_distance = math.abs(meanline - ma200) +mean_ma50_distance = math.abs(meanline - ma50) +mean_ma100_distance = math.abs(meanline - ma100) + +// 6. MEAN线价格处于MA200以上时 +if meanline > ma200 and current_minute > last_alert_mean_above_ma200 + alert_message := '{"指标名称":"MA200","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","MA50":"' + str.tostring(ma50, '#.####') + '","MA100":"' + str.tostring(ma100, '#.####') + '","MA200":"' + str.tostring(ma200, '#.####') + '","距离(美元)":"' + str.tostring(mean_ma200_distance, '#.##') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"MEAN线处于MA200以上","位置":"MA200以上"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_mean_above_ma200 := current_minute + +// 7. MEAN线价格处于MA200以下时 +if meanline < ma200 and current_minute > last_alert_mean_below_ma200 + alert_message := '{"指标名称":"MA200","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","MA50":"' + str.tostring(ma50, '#.####') + '","MA100":"' + str.tostring(ma100, '#.####') + '","MA200":"' + str.tostring(ma200, '#.####') + '","距离(美元)":"' + str.tostring(mean_ma200_distance, '#.##') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"MEAN线处于MA200以下","位置":"MA200以下"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_mean_below_ma200 := current_minute + +// 8. MEAN线价格处于MA50以上时 +if meanline > ma50 and current_minute > last_alert_mean_above_ma50 + alert_message := '{"指标名称":"MA200","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","MA50":"' + str.tostring(ma50, '#.####') + '","MA100":"' + str.tostring(ma100, '#.####') + '","MA200":"' + str.tostring(ma200, '#.####') + '","距离(美元)":"' + str.tostring(mean_ma50_distance, '#.##') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"MEAN线处于MA50以上","位置":"MA50以上"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_mean_above_ma50 := current_minute + +// 9. MEAN线价格处于MA50以下时 +if meanline < ma50 and current_minute > last_alert_mean_below_ma50 + alert_message := '{"指标名称":"MA200","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","MA50":"' + str.tostring(ma50, '#.####') + '","MA100":"' + str.tostring(ma100, '#.####') + '","MA200":"' + str.tostring(ma200, '#.####') + '","距离(美元)":"' + str.tostring(mean_ma50_distance, '#.##') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"MEAN线处于MA50以下","位置":"MA50以下"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_mean_below_ma50 := current_minute + +// 10. MEAN线价格处于MA100以上时 +if meanline > ma100 and current_minute > last_alert_mean_above_ma100 + alert_message := '{"指标名称":"MA200","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","MA50":"' + str.tostring(ma50, '#.####') + '","MA100":"' + str.tostring(ma100, '#.####') + '","MA200":"' + str.tostring(ma200, '#.####') + '","距离(美元)":"' + str.tostring(mean_ma100_distance, '#.##') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"MEAN线处于MA100以上","位置":"MA100以上"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_mean_above_ma100 := current_minute + +// 11. MEAN线价格处于MA100以下时 +if meanline < ma100 and current_minute > last_alert_mean_below_ma100 + alert_message := '{"指标名称":"MA200","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","MA50":"' + str.tostring(ma50, '#.####') + '","MA100":"' + str.tostring(ma100, '#.####') + '","MA200":"' + str.tostring(ma200, '#.####') + '","距离(美元)":"' + str.tostring(mean_ma100_distance, '#.##') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"MEAN线处于MA100以下","位置":"MA100以下"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_mean_below_ma100 := current_minute + +// ===== 传统警报条件(保留兼容性)===== +// 注意:使用统一警报系统时,建议只使用上面的alert()函数 +// 以下alertcondition保留用于需要单独设置警报的情况 + +// 创建用于警报的plot变量 +plot(ma50, title = 'MA50值', display = display.none) +plot(ma100, title = 'MA100值', display = display.none) +plot(ma200, title = 'MA200值', display = display.none) +plot(mean_ma200_distance, title = 'MEAN距离MA200', display = display.none) +plot(mean_ma50_distance, title = 'MEAN距离MA50', display = display.none) +plot(mean_ma100_distance, title = 'MEAN距离MA100', display = display.none) + +// 为传统警报创建隐藏的plot变量(不显示但支持警报引用) +plot(upband2, title = ' R2', display = display.none) // R2 隐藏绘制用于警报 +plot(loband2, title = ' S2', display = display.none) // S2 隐藏绘制用于警报 +plot(upband2_1, title = 'R2_1', display = display.none) +plot(loband2_1, title = 'S2_1', display = display.none) +plot(upband2_9, title = 'R2_9', display = display.none) +plot(loband2_9, title = 'S2_9', display = display.none) + +// 价格穿越警报 +alertcondition(ta.crossover(close, upband1), title = '价格上穿R1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R1","位置":"R1","信号":"r1_breakout_up"}') + +alertcondition(ta.crossunder(close, upband1), title = '价格下穿R1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R1","位置":"R1","信号":"r1_breakout_down"}') + +alertcondition(ta.crossunder(close, loband1), title = '价格下穿S1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S1","位置":"S1","信号":"s1_breakout_down"}') + +alertcondition(ta.crossover(close, loband1), title = '价格上穿S1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S1","位置":"S1","信号":"s1_breakout_up"}') + +alertcondition(ta.crossover(close, meanline), title = '价格上穿MEAN', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿MEAN","位置":"MEAN","信号":"mean_breakout_up"}') + +alertcondition(ta.crossunder(close, meanline), title = '价格下穿MEAN', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿MEAN","位置":"MEAN","信号":"mean_breakout_down"}') + +// R2和S2穿越警报 +alertcondition(ta.crossover(close, upband2), title = '价格上穿R2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R2","位置":"R2","信号":"r2_breakout_up"}') + +alertcondition(ta.crossunder(close, upband2), title = '价格下穿R2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R2","位置":"R2","信号":"r2_breakout_down"}') + +alertcondition(ta.crossunder(close, loband2), title = '价格下穿S2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S2","位置":"S2","信号":"s2_breakout_down"}') + +alertcondition(ta.crossover(close, loband2), title = '价格上穿S2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S2","位置":"S2","信号":"s2_breakout_up"}') + +// 扩展区域穿越警报 +alertcondition(ta.crossover(close, upband2_9), title = '价格上穿R2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R2_9","位置":"R2_9","信号":"r2_9_breakout_up"}') + +alertcondition(ta.crossunder(close, upband2_9), title = '价格下穿R2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R2_9","位置":"R2_9","信号":"r2_9_breakout_down"}') + +alertcondition(ta.crossunder(close, loband2_9), title = '价格下穿S2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S2_9","位置":"S2_9","信号":"s2_9_breakout_down"}') + +alertcondition(ta.crossover(close, loband2_9), title = '价格上穿S2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S2_9","位置":"S2_9","信号":"s2_9_breakout_up"}') + +alertcondition(ta.crossover(close, upband2_1), title = '价格上穿R2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R2_1","位置":"R2_1","信号":"r2_1_breakout_up"}') + +alertcondition(ta.crossunder(close, upband2_1), title = '价格下穿R2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R2_1","位置":"R2_1","信号":"r2_1_breakout_down"}') + +alertcondition(ta.crossunder(close, loband2_1), title = '价格下穿S2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S2_1","位置":"S2_1","信号":"s2_1_breakout_down"}') + +alertcondition(ta.crossover(close, loband2_1), title = '价格上穿S2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S2_1","位置":"S2_1","信号":"s2_1_breakout_up"}') + +// 价格区间状态警报 +alertcondition(close <= upband1 and close >= loband1, title = '价格处于R1和S1之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R1和S1之间","位置":"R1-S1区间","信号":"range_r1_s1"}') + +alertcondition(close > upband1, title = '价格处于R1以上', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R1以上","位置":"R1以上","信号":"above_r1"}') + +alertcondition(close < loband1, title = '价格处于S1以下', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S1以下","位置":"S1以下","信号":"below_s1"}') + +alertcondition(close > meanline, title = '价格处于MEAN以上', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于MEAN以上","位置":"MEAN以上","信号":"above_mean"}') + +alertcondition(close < meanline, title = '价格处于MEAN以下', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于MEAN以下","位置":"MEAN以下","信号":"below_mean"}') + +// 区间范围警报 +alertcondition(close > upband1 and close < upband2, title = '价格处于R1和R2之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R1和R2之间","位置":"R1-R2","信号":"range_r1_r2"}') + +alertcondition(close < loband1 and close > loband2, title = '价格处于S1和S2之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S1和S2之间","位置":"S1-S2","信号":"range_s1_s2"}') + +alertcondition(close > upband2 and close < upband2_1, title = '价格处于R2和R2_1之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R2和R2_1之间","位置":"R2-R2_1","信号":"range_r2_r2_1"}') + +alertcondition(close < loband2 and close > loband2_1, title = '价格处于S2和S2_1之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S2和S2_1之间","位置":"S2-S2_1","信号":"range_s2_s2_1"}') + +// 极端区域警报 +alertcondition(close > upband2_1, title = '价格处于R2_1以上', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R2_1以上","位置":"R2_1以上","信号":"above_r2_1"}') + +alertcondition(close < loband2_1, title = '价格处于S2_1以下', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S2_1以下","位置":"S2_1以下","信号":"below_s2_1"}') + +// ===== 新增的MA200相关传统警报条件 ===== +// MEAN线与MA200关系警报 +alertcondition(meanline > ma200, title = 'MEAN线处于MA200以上', message = '{"指标名称":"MA200","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","MA50":"{{plot("MA50值")}}","MA100":"{{plot("MA100值")}}","MA200":"{{plot("MA200值")}}","距离(美元)":"{{plot("MEAN距离MA200")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"MEAN线处于MA200以上","位置":"MA200以上","信号":"mean_above_ma200"}') + +alertcondition(meanline < ma200, title = 'MEAN线处于MA200以下', message = '{"指标名称":"MA200","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","MA50":"{{plot("MA50值")}}","MA100":"{{plot("MA100值")}}","MA200":"{{plot("MA200值")}}","距离(美元)":"{{plot("MEAN距离MA200")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"MEAN线处于MA200以下","位置":"MA200以下","信号":"mean_below_ma200"}') + +// MEAN线与MA50关系警报 +alertcondition(meanline > ma50, title = 'MEAN线处于MA50以上', message = '{"指标名称":"MA200","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","MA50":"{{plot("MA50值")}}","MA100":"{{plot("MA100值")}}","MA200":"{{plot("MA200值")}}","距离(美元)":"{{plot("MEAN距离MA50")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"MEAN线处于MA50以上","位置":"MA50以上","信号":"mean_above_ma50"}') + +alertcondition(meanline < ma50, title = 'MEAN线处于MA50以下', message = '{"指标名称":"MA200","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","MA50":"{{plot("MA50值")}}","MA100":"{{plot("MA100值")}}","MA200":"{{plot("MA200值")}}","距离(美元)":"{{plot("MEAN距离MA50")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"MEAN线处于MA50以下","位置":"MA50以下","信号":"mean_below_ma50"}') + +// MEAN线与MA100关系警报 +alertcondition(meanline > ma100, title = 'MEAN线处于MA100以上', message = '{"指标名称":"MA200","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","MA50":"{{plot("MA50值")}}","MA100":"{{plot("MA100值")}}","MA200":"{{plot("MA200值")}}","距离(美元)":"{{plot("MEAN距离MA100")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"MEAN线处于MA100以上","位置":"MA100以上","信号":"mean_above_ma100"}') + +alertcondition(meanline < ma100, title = 'MEAN线处于MA100以下', message = '{"指标名称":"MA200","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","MA50":"{{plot("MA50值")}}","MA100":"{{plot("MA100值")}}","MA200":"{{plot("MA200值")}}","距离(美元)":"{{plot("MEAN距离MA100")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"MEAN线处于MA100以下","位置":"MA100以下","信号":"mean_below_ma100"}') \ No newline at end of file diff --git a/mrc.pine b/mrc.pine new file mode 100644 index 0000000..b33a2a5 --- /dev/null +++ b/mrc.pine @@ -0,0 +1,715 @@ +//@version=6 +indicator('Mean Reversion Channel - 带价格标签', shorttitle = 'MRC2', overlay = true, format = format.inherit) + +//************************************************************************************************************ +// Parameter +//************************************************************************************************************ + +indiSet = input(false, '═════════ MRC Parameter ════════') +source = input(hlc3, title = 'Price Source') +type = input.string('SuperSmoother', title = 'Filter Type', options = ['SuperSmoother', 'Ehlers EMA', 'Gaussian', 'Butterworth', 'BandStop', 'SMA', 'EMA', 'RMA']) +length = input.int(200, title = 'Lookback Period', minval = 1) +innermult = input.float(1.0, title = 'Inner Channel Size Multiplier', minval = 0.1) +outermult = input.float(2.415, title = 'Outer Channel Size Multiplier', minval = 0.1) + +ChartSet = input(false, '═════════ Chart Setting ════════') +drawchannel = input(true, title = 'Draw Channel') +displayzone = input(true, title = 'Draw Zone (With Channel)') +zonetransp = input.int(60, title = 'Zone Transparency', minval = 0, maxval = 100) +displayline = input(true, title = 'Display Line Extension') + +MTFSet = input(false, '═════════ MTF Setting ════════') +enable_mtf = input(true, title = 'Enable Multiple TimeFrame Analysis') +mtf_disp_typ = input.string('On Hover', title = 'MTF Display Type', options = ['Always Display', 'On Hover']) +mtf_typ = input.string('Auto', title = 'Multiple TimeFrame Type', options = ['Auto', 'Custom']) +mtf_lvl1 = input.timeframe('D', title = 'Custom MTF Level 1') +mtf_lvl2 = input.timeframe('W', title = 'Custom MTF Level 2') + +//************************************************************************************************************ +// Functions Start { +//************************************************************************************************************ +var pi = 2 * math.asin(1) +var mult = pi * innermult +var mult2 = pi * outermult +var gradsize = 0.5 +var gradtransp = zonetransp + +//----------------------- +// Ehler SwissArmyKnife Function +//----------------------- +SAK_smoothing(_type, _src, _length) => + c0 = 1.0 + c1 = 0.0 + b0 = 1.0 + b1 = 0.0 + b2 = 0.0 + a1 = 0.0 + a2 = 0.0 + alpha = 0.0 + beta = 0.0 + gamma = 0.0 + cycle = 2 * pi / _length + + if _type == 'Ehlers EMA' + alpha := (math.cos(cycle) + math.sin(cycle) - 1) / math.cos(cycle) + b0 := alpha + a1 := 1 - alpha + a1 + if _type == 'Gaussian' + beta := 2.415 * (1 - math.cos(cycle)) + alpha := -beta + math.sqrt(beta * beta + 2 * beta) + c0 := alpha * alpha + a1 := 2 * (1 - alpha) + a2 := -(1 - alpha) * (1 - alpha) + a2 + if _type == 'Butterworth' + beta := 2.415 * (1 - math.cos(cycle)) + alpha := -beta + math.sqrt(beta * beta + 2 * beta) + c0 := alpha * alpha / 4 + b1 := 2 + b2 := 1 + a1 := 2 * (1 - alpha) + a2 := -(1 - alpha) * (1 - alpha) + a2 + if _type == 'BandStop' + beta := math.cos(cycle) + gamma := 1 / math.cos(cycle * 2 * 0.1) // delta default to 0.1. Acceptable delta -- 0.05 + s_a1 = math.exp(-math.sqrt(2) * pi / _length) + s_b1 = 2 * s_a1 * math.cos(math.sqrt(2) * pi / _length) + s_c3 = -math.pow(s_a1, 2) + s_c2 = s_b1 + s_c1 = 1 - s_c2 - s_c3 + ss = 0.0 + ss := s_c1 * _src + s_c2 * nz(ss[1], _src[1]) + s_c3 * nz(ss[2], _src[2]) + ss + +//----------------------- +// Auto TimeFrame Function +//----------------------- +// ————— Converts current chart resolution into a float minutes value. +f_resInMinutes() => + _resInMinutes = timeframe.multiplier * (timeframe.isseconds ? 1. / 60 : timeframe.isminutes ? 1. : timeframe.isdaily ? 60. * 24 : timeframe.isweekly ? 60. * 24 * 7 : timeframe.ismonthly ? 60. * 24 * 30.4375 : na) + _resInMinutes + +get_tf(_lvl) => + y = f_resInMinutes() + z = timeframe.period + if mtf_typ == 'Auto' + if y < 1 + z := _lvl == 1 ? '1' : _lvl == 2 ? '5' : z + z + else if y <= 3 + z := _lvl == 1 ? '5' : _lvl == 2 ? '15' : z + z + else if y <= 10 + z := _lvl == 1 ? '15' : _lvl == 2 ? '60' : z + z + else if y <= 30 + z := _lvl == 1 ? '60' : _lvl == 2 ? '240' : z + z + else if y <= 120 + z := _lvl == 1 ? '240' : _lvl == 2 ? 'D' : z + z + else if y <= 240 + z := _lvl == 1 ? 'D' : _lvl == 2 ? 'W' : z + z + else if y <= 1440 + z := _lvl == 1 ? 'W' : _lvl == 2 ? 'M' : z + z + else if y <= 10080 + z := _lvl == 1 ? 'M' : z + z + else + z := z + z + else + z := _lvl == 1 ? mtf_lvl1 : _lvl == 2 ? mtf_lvl2 : z + z + + z + +//----------------------- +// Mean Reversion Channel Function +//----------------------- +get_mrc() => + v_condition = 0 + v_meanline = source + v_meanrange = supersmoother(ta.tr, length) + + //-- Get Line value + if type == 'SuperSmoother' + v_meanline := supersmoother(source, length) + v_meanline + + if type != 'SuperSmoother' + v_meanline := SAK_smoothing(type, source, length) + v_meanline + + v_upband1 = v_meanline + v_meanrange * mult + v_loband1 = v_meanline - v_meanrange * mult + v_upband2 = v_meanline + v_meanrange * mult2 + v_loband2 = v_meanline - v_meanrange * mult2 + + //-- Check Condition + if close > v_meanline + v_upband2_1 = v_upband2 + v_meanrange * gradsize * 4 + v_upband2_9 = v_upband2 + v_meanrange * gradsize * -4 + if high >= v_upband2_9 and high < v_upband2 + v_condition := 1 + v_condition + else if high >= v_upband2 and high < v_upband2_1 + v_condition := 2 + v_condition + else if high >= v_upband2_1 + v_condition := 3 + v_condition + else if close <= v_meanline + v_meanrange + v_condition := 4 + v_condition + else + v_condition := 5 + v_condition + + if close < v_meanline + v_loband2_1 = v_loband2 - v_meanrange * gradsize * 4 + v_loband2_9 = v_loband2 - v_meanrange * gradsize * -4 + if low <= v_loband2_9 and low > v_loband2 + v_condition := -1 + v_condition + else if low <= v_loband2 and low > v_loband2_1 + v_condition := -2 + v_condition + else if low <= v_loband2_1 + v_condition := -3 + v_condition + else if close >= v_meanline + v_meanrange + v_condition := -4 + v_condition + else + v_condition := -5 + v_condition + + [v_meanline, v_meanrange, v_upband1, v_loband1, v_upband2, v_loband2, v_condition] + +//----------------------- +// MTF Analysis +//----------------------- + +get_stat(_cond) => + ret = 'Price at Mean Line\n' + if _cond == 1 + ret := 'Overbought (Weak)\n' + ret + else if _cond == 2 + ret := 'Overbought\n' + ret + else if _cond == 3 + ret := 'Overbought (Strong)\n' + ret + else if _cond == 4 + ret := 'Price Near Mean\n' + ret + else if _cond == 5 + ret := 'Price Above Mean\n' + ret + else if _cond == -1 + ret := 'Oversold (Weak)\n' + ret + else if _cond == -2 + ret := 'Oversold\n' + ret + else if _cond == -3 + ret := 'Oversold (Strong)\n' + ret + else if _cond == -4 + ret := 'Price Near Mean\n' + ret + else if _cond == -5 + ret := 'Price Below Mean\n' + ret + ret + +//----------------------- +// Chart Drawing Function +//----------------------- +format_price(x) => + y = str.tostring(x, '0.00000') + if x > 10 + y := str.tostring(x, '0.000') + y + if x > 1000 + y := str.tostring(x, '0.00') + y + y + +f_PriceLine(_ref, linecol) => + line.new(x1 = bar_index, x2 = bar_index - 1, y1 = _ref, y2 = _ref, extend = extend.left, color = linecol) + +f_MTFLabel(_txt, _yloc) => + label.new(x = time + math.round(ta.change(time) * 20), y = _yloc, xloc = xloc.bar_time, text = mtf_disp_typ == 'Always Display' ? _txt : 'Check MTF', tooltip = mtf_disp_typ == 'Always Display' ? '' : _txt, color = color.black, textcolor = color.white, size = size.normal, style = mtf_disp_typ == 'On Hover' and displayline ? label.style_label_lower_left : label.style_label_left, textalign = text.align_left) + +//} Function End + +//************************************************************************************************************ +// Calculate Channel +//************************************************************************************************************ +var tf_0 = timeframe.period +var tf_1 = get_tf(1) +var tf_2 = get_tf(2) + +[meanline, meanrange, upband1, loband1, upband2, loband2, condition] = get_mrc() +[mtf1_meanline, mtf1_meanrange, mtf1_upband1, mtf1_loband1, mtf1_upband2, mtf1_loband2, mtf1_condition] = request.security(syminfo.tickerid, tf_1, get_mrc()) +[mtf2_meanline, mtf2_meanrange, mtf2_upband1, mtf2_loband1, mtf2_upband2, mtf2_loband2, mtf2_condition] = request.security(syminfo.tickerid, tf_2, get_mrc()) + +//************************************************************************************************************ +// Drawing Start { +//************************************************************************************************************ +float p_meanline = drawchannel ? meanline : na +float p_upband1 = drawchannel ? upband1 : na +float p_loband1 = drawchannel ? loband1 : na +float p_upband2 = drawchannel ? upband2 : na +float p_loband2 = drawchannel ? loband2 : na + +z = plot(p_meanline, color = color.new(color.black, 0), style = plot.style_line, title = ' Mean', linewidth = 2) +x1 = plot(p_upband1, color = color.new(color.black, 0), style = plot.style_line, title = ' R1', linewidth = 2) +x2 = plot(p_loband1, color = color.new(color.black, 0), style = plot.style_line, title = ' S1', linewidth = 2) +y1 = plot(p_upband2, color = color.new(color.red, 50), style = plot.style_line, title = ' R2', linewidth = 1) +y2 = plot(p_loband2, color = color.new(color.red, 50), style = plot.style_line, title = ' S2', linewidth = 1) + +//----------------------- +// Draw zone +//----------------------- +//--- +var color1 = #FF0000 +var color2 = #FF4200 +var color3 = #FF5D00 +var color4 = #FF7400 +var color5 = #FF9700 +var color6 = #FFAE00 +var color7 = #FFC500 +var color8 = #FFCD00 +//--- +float upband2_1 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 4 : na +float loband2_1 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 4 : na +float upband2_2 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 3 : na +float loband2_2 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 3 : na +float upband2_3 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 2 : na +float loband2_3 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 2 : na +float upband2_4 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 1 : na +float loband2_4 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 1 : na +float upband2_5 = drawchannel and displayzone ? upband2 + meanrange * gradsize * 0 : na +float loband2_5 = drawchannel and displayzone ? loband2 - meanrange * gradsize * 0 : na +float upband2_6 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -1 : na +float loband2_6 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -1 : na +float upband2_7 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -2 : na +float loband2_7 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -2 : na +float upband2_8 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -3 : na +float loband2_8 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -3 : na +float upband2_9 = drawchannel and displayzone ? upband2 + meanrange * gradsize * -4 : na +float loband2_9 = drawchannel and displayzone ? loband2 - meanrange * gradsize * -4 : na + +//--- +// 为了让警报消息能正确引用这些值,我们需要创建可见的 plot +// R2_1 和 S2_1 (plot_5 和 plot_6) +plot_upband2_1 = plot(upband2_1, color = na, title = 'R2_1') +plot_loband2_1 = plot(loband2_1, color = na, title = 'S2_1') + +// 其他隐藏的 plot 用于填充 +plot_upband2_2 = plot(upband2_2, color = na, display = display.none) +plot_loband2_2 = plot(loband2_2, color = na, display = display.none) +plot_upband2_3 = plot(upband2_3, color = na, display = display.none) +plot_loband2_3 = plot(loband2_3, color = na, display = display.none) +plot_upband2_4 = plot(upband2_4, color = na, display = display.none) +plot_loband2_4 = plot(loband2_4, color = na, display = display.none) +plot_upband2_5 = plot(upband2_5, color = na, display = display.none) +plot_loband2_5 = plot(loband2_5, color = na, display = display.none) +plot_upband2_6 = plot(upband2_6, color = na, display = display.none) +plot_loband2_6 = plot(loband2_6, color = na, display = display.none) +plot_upband2_7 = plot(upband2_7, color = na, display = display.none) +plot_loband2_7 = plot(loband2_7, color = na, display = display.none) +plot_upband2_8 = plot(upband2_8, color = na, display = display.none) +plot_loband2_8 = plot(loband2_8, color = na, display = display.none) + +// 在这里添加 R2_9 和 S2_9 的 plot,确保它们在正确的位置 +// 这些将成为 plot_19 和 plot_20 +plot_upband2_9 = plot(upband2_9, color = na, title = 'R2_9') +plot_loband2_9 = plot(loband2_9, color = na, title = 'S2_9') + +//--- +fill(plot_upband2_1, plot_upband2_2, color = color1) +fill(plot_loband2_1, plot_loband2_2, color = color1) +fill(plot_upband2_2, plot_upband2_3, color = color2) +fill(plot_loband2_2, plot_loband2_3, color = color2) +fill(plot_upband2_3, plot_upband2_4, color = color3) +fill(plot_loband2_3, plot_loband2_4, color = color3) +fill(plot_upband2_4, plot_upband2_5, color = color4) +fill(plot_loband2_4, plot_loband2_5, color = color4) +fill(plot_upband2_5, plot_upband2_6, color = color5) +fill(plot_loband2_5, plot_loband2_6, color = color5) +fill(plot_upband2_6, plot_upband2_7, color = color6) +fill(plot_loband2_6, plot_loband2_7, color = color6) +fill(plot_upband2_7, plot_upband2_8, color = color7) +fill(plot_loband2_7, plot_loband2_8, color = color7) +fill(plot_upband2_8, plot_upband2_9, color = color8) +fill(plot_loband2_8, plot_loband2_9, color = color8) + +//----------------------- +// Plot Extension +//----------------------- +if displayline and enable_mtf and mtf_disp_typ == 'Always Display' + displayline := false + displayline + +var line mean = na +line.delete(mean) +mean := displayline ? f_PriceLine(meanline, #FFCD00) : na +var line res1 = na +line.delete(res1) +res1 := displayline ? f_PriceLine(upband1, color.green) : na +var line sup1 = na +line.delete(sup1) +sup1 := displayline ? f_PriceLine(loband1, color.green) : na +var line res2 = na +line.delete(res2) +res2 := displayline ? f_PriceLine(upband2, color.red) : na +var line sup2 = na +line.delete(sup2) +sup2 := displayline ? f_PriceLine(loband2, color.red) : na + +//-------------- +// Prep MTF Label +//-------------- +var brl = '\n--------------------------------------' +dist_0 = 'Distance from Mean: ' + str.tostring((close - meanline) / close * 100, '#.##') + ' %' +dist_1 = 'Distance from Mean: ' + str.tostring((close - mtf1_meanline) / close * 100, '#.##') + ' %' +dist_2 = 'Distance from Mean: ' + str.tostring((close - mtf2_meanline) / close * 100, '#.##') + ' %' + +var title = 'Mean Reversion Channel\nMultiple TimeFrame Analysis' + brl +tf0 = '\n\nTimeframe: ' + tf_0 + ' (Current)\n\nStatus: ' + get_stat(condition) + dist_0 + brl + +tf1 = not timeframe.ismonthly ? '\n\nTimeframe: ' + tf_1 + '\n\nStatus: ' + get_stat(mtf1_condition) + dist_1 + brl : '' + +tf2 = not timeframe.isweekly and not timeframe.ismonthly ? '\n\nTimeframe: ' + tf_2 + '\n\nStatus: ' + get_stat(mtf2_condition) + dist_2 + brl : '' + +mtf_lbl = title + tf0 + tf1 + tf2 +var label label_mtf = na +label.delete(label_mtf) +label_mtf := enable_mtf ? f_MTFLabel(mtf_lbl, meanline) : na + +//************************************************************************************************************ +// Real-time Price Labels for Extended Lines (Placed on the right side of the last candle) +//************************************************************************************************************ + +// Define persistent label variables so that they update rather than create new labels each bar +var label mean_label = na +var label res1_label = na +var label sup1_label = na +var label res2_label = na +var label sup2_label = na +var label res2_9_label = na // Label for upband2_9 +var label sup2_9_label = na // Label for loband2_9 +var label res2_1_label = na // New label for upband2_1 +var label sup2_1_label = na // New label for loband2_1 + +if displayline and barstate.islast + // MEAN label + if na(mean_label) + mean_label := label.new(bar_index, meanline, "MEAN: " + format_price(meanline), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.black, 0), textcolor = color.white) + else + label.set_xy(mean_label, bar_index, meanline) + label.set_text(mean_label, "MEAN: " + format_price(meanline)) + // R1 label + if na(res1_label) + res1_label := label.new(bar_index, upband1, "R1: " + format_price(upband1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.green, 0), textcolor = color.white) + else + label.set_xy(res1_label, bar_index, upband1) + label.set_text(res1_label, "R1: " + format_price(upband1)) + // S1 label + if na(sup1_label) + sup1_label := label.new(bar_index, loband1, "S1: " + format_price(loband1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.green, 0), textcolor = color.white) + else + label.set_xy(sup1_label, bar_index, loband1) + label.set_text(sup1_label, "S1: " + format_price(loband1)) + // R2 label + if na(res2_label) + res2_label := label.new(bar_index, upband2, "R2: " + format_price(upband2), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 0), textcolor = color.white) + else + label.set_xy(res2_label, bar_index, upband2) + label.set_text(res2_label, "R2: " + format_price(upband2)) + // S2 label + if na(sup2_label) + sup2_label := label.new(bar_index, loband2, "S2: " + format_price(loband2), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 0), textcolor = color.white) + else + label.set_xy(sup2_label, bar_index, loband2) + label.set_text(sup2_label, "S2: " + format_price(loband2)) + // R2_9 label (for upband2_9) + if na(res2_9_label) + res2_9_label := label.new(bar_index, upband2_9, "R2_9: " + format_price(upband2_9), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(res2_9_label, bar_index, upband2_9) + label.set_text(res2_9_label, "R2_9: " + format_price(upband2_9)) + // S2_9 label (for loband2_9) + if na(sup2_9_label) + sup2_9_label := label.new(bar_index, loband2_9, "S2_9: " + format_price(loband2_9), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(sup2_9_label, bar_index, loband2_9) + label.set_text(sup2_9_label, "S2_9: " + format_price(loband2_9)) + // R2_1 label (New for upband2_1) + if na(res2_1_label) + res2_1_label := label.new(bar_index, upband2_1, "R2_1: " + format_price(upband2_1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(res2_1_label, bar_index, upband2_1) + label.set_text(res2_1_label, "R2_1: " + format_price(upband2_1)) + // S2_1 label (New for loband2_1) + if na(sup2_1_label) + sup2_1_label := label.new(bar_index, loband2_1, "S2_1: " + format_price(loband2_1), xloc = xloc.bar_index, style = label.style_label_left, color = color.new(color.red, 50), textcolor = color.white) + else + label.set_xy(sup2_1_label, bar_index, loband2_1) + label.set_text(sup2_1_label, "S2_1: " + format_price(loband2_1)) +//************************************************************************************************************ +// ===== 统一警报系统 ===== +// 统一警报系统 - 只需添加一次警报即可捕获所有信号 +//************************************************************************************************************ + +// 警报频率限制 - 每分钟只触发一次 +var int last_alert_r1_breakout_up = 0 +var int last_alert_s1_breakout_down = 0 +var int last_alert_range_r1_s1 = 0 +var int last_alert_above_r1 = 0 +var int last_alert_below_s1 = 0 +var int last_alert_above_mean = 0 +var int last_alert_below_mean = 0 + +// 获取当前时间(分钟级别) +current_minute = math.floor(timenow / 60000) + +// 检测所有警报条件并生成对应的JSON消息 +alert_message = "" + +// 价格触碰关键位置警报 +// if ta.cross(close, meanline) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到MEAN","位置":"MEAN","信号":"mean_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, upband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到R1","位置":"R1","信号":"r1_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, loband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到S1","位置":"S1","信号":"s1_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, upband2) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到R2","位置":"R2","信号":"r2_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.cross(close, loband2) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow, "yyyy-MM-dd HH:mm:ss") + '","时间":"' + str.tostring(time, "yyyy-MM-dd HH:mm:ss") + '","事件":"碰到S2","位置":"S2","信号":"s2_touch"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// 价格区间警报 +// if close > meanline and close < upband1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN和R1之间","位置":"MEAN-R1","信号":"range_mean_r1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < meanline and close > loband1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN和S1之间","位置":"MEAN-S1","信号":"range_mean_s1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close > upband1 and close < upband2 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1和R2之间","位置":"R1-R2","信号":"range_r1_r2"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < loband1 and close > loband2 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S1和S2之间","位置":"S1-S2","信号":"range_s1_s2"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close > upband1 and close < upband2_9 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1和R2_9之间","位置":"R1-R2_9","信号":"range_r1_r2_9"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close > upband2_9 and close < upband2_1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R2_9和R2_1之间","位置":"R2_9-R2_1","信号":"range_r2_9_r2_1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < loband1 and close > loband2_9 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S1和S2_9之间","位置":"S1-S2_9","信号":"range_s1_s2_9"}' +// alert(alert_message, alert.freq_once_per_bar) + +// if close < loband2_9 and close > loband2_1 +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S2_9和S2_1之间","位置":"S2_9-S2_1","信号":"range_s2_9_s2_1"}' +// alert(alert_message, alert.freq_once_per_bar) + +// 价格穿越警报 - 每分钟限制 +if ta.crossover(close, upband1) and current_minute > last_alert_r1_breakout_up + alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿R1","位置":"R1","信号":"r1_breakout_up"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_r1_breakout_up := current_minute + +// if ta.crossunder(close, upband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿R1","位置":"R1","信号":"r1_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +if ta.crossunder(close, loband1) and current_minute > last_alert_s1_breakout_down + alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿S1","位置":"S1","信号":"s1_breakout_down"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_s1_breakout_down := current_minute + +// if ta.crossover(close, loband1) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿S1","位置":"S1","信号":"s1_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossover(close, meanline) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿MEAN","位置":"MEAN","信号":"mean_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossunder(close, meanline) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿MEAN","位置":"MEAN","信号":"mean_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossover(close, loband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿S2_9","位置":"S2_9","信号":"s2_9_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossunder(close, loband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿S2_9","位置":"S2_9","信号":"s2_9_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossover(close, upband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格上穿R2_9","位置":"R2_9","信号":"r2_9_breakout_up"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// if ta.crossunder(close, upband2_9) +// alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格下穿R2_9","位置":"R2_9","信号":"r2_9_breakout_down"}' +// alert(alert_message, alert.freq_once_per_bar_close) + +// ===== 新增的5个警报条件 - 每分钟限制 ===== +// 1. 价格处于R1和S1之间时 +if close <= upband1 and close >= loband1 and current_minute > last_alert_range_r1_s1 + alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1和S1之间","位置":"R1-S1区间","信号":"range_r1_s1"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_range_r1_s1 := current_minute + +// 2. 价格处于R1以上时 +if close > upband1 and current_minute > last_alert_above_r1 + alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于R1以上","位置":"R1以上","信号":"above_r1"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_above_r1 := current_minute + +// 3. 价格处于S1以下时 +if close < loband1 and current_minute > last_alert_below_s1 + alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于S1以下","位置":"S1以下","信号":"below_s1"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_below_s1 := current_minute + +// 4. 价格处于MEAN以上时 +if close > meanline and current_minute > last_alert_above_mean + alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN以上","位置":"MEAN以上","信号":"above_mean"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_above_mean := current_minute + +// 5. 价格处于MEAN以下时 +if close < meanline and current_minute > last_alert_below_mean + alert_message := '{"指标名称":"MRC","交易对":"' + syminfo.ticker + '","周期":"' + timeframe.period + '","MEAN":"' + str.tostring(meanline, '#.####') + '","R1":"' + str.tostring(upband1, '#.####') + '","S1":"' + str.tostring(loband1, '#.####') + '","R2":"' + str.tostring(upband2, '#.####') + '","S2":"' + str.tostring(loband2, '#.####') + '","R2_9":"' + str.tostring(upband2_9, '#.####') + '","S2_9":"' + str.tostring(loband2_9, '#.####') + '","R2_1":"' + str.tostring(upband2_1, '#.####') + '","S2_1":"' + str.tostring(loband2_1, '#.####') + '","开盘价":"' + str.tostring(open, '#.####') + '","收盘价":"' + str.tostring(close, '#.####') + '","最高价":"' + str.tostring(high, '#.####') + '","最低价":"' + str.tostring(low, '#.####') + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","事件":"价格处于MEAN以下","位置":"MEAN以下","信号":"below_mean"}' + alert(alert_message, alert.freq_once_per_bar) + last_alert_below_mean := current_minute + +// ===== 传统警报条件(保留兼容性)===== +// 注意:使用统一警报系统时,建议只使用上面的alert()函数 +// 以下alertcondition保留用于需要单独设置警报的情况 + +// 价格穿越警报 +alertcondition(ta.crossover(close, upband1), title = '价格上穿R1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R1","位置":"R1","信号":"r1_breakout_up"}') + +alertcondition(ta.crossunder(close, upband1), title = '价格下穿R1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R1","位置":"R1","信号":"r1_breakout_down"}') + +alertcondition(ta.crossunder(close, loband1), title = '价格下穿S1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S1","位置":"S1","信号":"s1_breakout_down"}') + +alertcondition(ta.crossover(close, loband1), title = '价格上穿S1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S1","位置":"S1","信号":"s1_breakout_up"}') + +alertcondition(ta.crossover(close, meanline), title = '价格上穿MEAN', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿MEAN","位置":"MEAN","信号":"mean_breakout_up"}') + +alertcondition(ta.crossunder(close, meanline), title = '价格下穿MEAN', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿MEAN","位置":"MEAN","信号":"mean_breakout_down"}') + +// R2和S2穿越警报 +alertcondition(ta.crossover(close, upband2), title = '价格上穿R2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R2","位置":"R2","信号":"r2_breakout_up"}') + +alertcondition(ta.crossunder(close, upband2), title = '价格下穿R2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R2","位置":"R2","信号":"r2_breakout_down"}') + +alertcondition(ta.crossunder(close, loband2), title = '价格下穿S2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S2","位置":"S2","信号":"s2_breakout_down"}') + +alertcondition(ta.crossover(close, loband2), title = '价格上穿S2', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S2","位置":"S2","信号":"s2_breakout_up"}') + +// 扩展区域穿越警报 +alertcondition(ta.crossover(close, upband2_9), title = '价格上穿R2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R2_9","位置":"R2_9","信号":"r2_9_breakout_up"}') + +alertcondition(ta.crossunder(close, upband2_9), title = '价格下穿R2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R2_9","位置":"R2_9","信号":"r2_9_breakout_down"}') + +alertcondition(ta.crossunder(close, loband2_9), title = '价格下穿S2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S2_9","位置":"S2_9","信号":"s2_9_breakout_down"}') + +alertcondition(ta.crossover(close, loband2_9), title = '价格上穿S2_9', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S2_9","位置":"S2_9","信号":"s2_9_breakout_up"}') + +alertcondition(ta.crossover(close, upband2_1), title = '价格上穿R2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿R2_1","位置":"R2_1","信号":"r2_1_breakout_up"}') + +alertcondition(ta.crossunder(close, upband2_1), title = '价格下穿R2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿R2_1","位置":"R2_1","信号":"r2_1_breakout_down"}') + +alertcondition(ta.crossunder(close, loband2_1), title = '价格下穿S2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格下穿S2_1","位置":"S2_1","信号":"s2_1_breakout_down"}') + +alertcondition(ta.crossover(close, loband2_1), title = '价格上穿S2_1', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格上穿S2_1","位置":"S2_1","信号":"s2_1_breakout_up"}') + +// 价格区间状态警报 +alertcondition(close <= upband1 and close >= loband1, title = '价格处于R1和S1之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R1和S1之间","位置":"R1-S1区间","信号":"range_r1_s1"}') + +alertcondition(close > upband1, title = '价格处于R1以上', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R1以上","位置":"R1以上","信号":"above_r1"}') + +alertcondition(close < loband1, title = '价格处于S1以下', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S1以下","位置":"S1以下","信号":"below_s1"}') + +alertcondition(close > meanline, title = '价格处于MEAN以上', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于MEAN以上","位置":"MEAN以上","信号":"above_mean"}') + +alertcondition(close < meanline, title = '价格处于MEAN以下', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于MEAN以下","位置":"MEAN以下","信号":"below_mean"}') + +// 区间范围警报 +alertcondition(close > upband1 and close < upband2, title = '价格处于R1和R2之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R1和R2之间","位置":"R1-R2","信号":"range_r1_r2"}') + +alertcondition(close < loband1 and close > loband2, title = '价格处于S1和S2之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S1和S2之间","位置":"S1-S2","信号":"range_s1_s2"}') + +alertcondition(close > upband2 and close < upband2_1, title = '价格处于R2和R2_1之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R2和R2_1之间","位置":"R2-R2_1","信号":"range_r2_r2_1"}') + +alertcondition(close < loband2 and close > loband2_1, title = '价格处于S2和S2_1之间', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S2和S2_1之间","位置":"S2-S2_1","信号":"range_s2_s2_1"}') + +// 极端区域警报 +alertcondition(close > upband2_1, title = '价格处于R2_1以上', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于R2_1以上","位置":"R2_1以上","信号":"above_r2_1"}') + +alertcondition(close < loband2_1, title = '价格处于S2_1以下', message = '{"指标名称":"MRC","交易对":"{{ticker}}","周期":"{{interval}}","MEAN":"{{plot(" Mean")}}","R1":"{{plot(" R1")}}","S1":"{{plot(" S1")}}","R2":"{{plot(" R2")}}","S2":"{{plot(" S2")}}","R2_9":"{{plot("R2_9")}}","S2_9":"{{plot("S2_9")}}","R2_1":"{{plot("R2_1")}}","S2_1":"{{plot("S2_1")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"价格处于S2_1以下","位置":"S2_1以下","信号":"below_s2_1"}') \ No newline at end of file diff --git a/rsi.pine b/rsi.pine new file mode 100644 index 0000000..d6330f4 --- /dev/null +++ b/rsi.pine @@ -0,0 +1,441 @@ +// 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","备注":"空头信号"}') \ No newline at end of file diff --git a/rsi2.pine b/rsi2.pine new file mode 100644 index 0000000..db7fbec --- /dev/null +++ b/rsi2.pine @@ -0,0 +1,637 @@ +// 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) diff --git a/rsi_conbinined.pine b/rsi_conbinined.pine new file mode 100644 index 0000000..d244e00 --- /dev/null +++ b/rsi_conbinined.pine @@ -0,0 +1,293 @@ +//@version=6 +indicator('RSI 窗口二显示', shorttitle='RSI-W2', overlay=false, max_labels_count=500) + +//************************************************************************************************************ +// RSI 参数设置 +//************************************************************************************************************ + +// RSI 参数组 +rsi_src = input.source(close, 'RSI Calculation Source', group='RSI') +rsiLength = input.int(14, 'RSI Length', minval=2, group='RSI') +smooth = input.bool(true, 'Smooth RSI?', group='RSI') +maType = input.string('Ema', 'Moving Average Type', options=['SMA', 'Hull', 'Ema', 'Wma', 'DEMA', 'RMA', 'LINREG', 'TEMA', 'ALMA', 'T3'], group='RSI') +smoothP = input.int(4, 'Smoothing Period', group='RSI') +sig = input.int(6, 'Sigma for ALMA', group='RSI') + +// 显示设置 +show_segments = input.bool(true, 'Show RSI Segments?', group='Display') +show_counting = input.bool(true, 'Show Segment Counting?', group='Display') +show_rsi_table = input.bool(true, 'Show RSI Info Table?', group='Display') +show_price_labels = input.bool(true, 'Show Price Labels?', group='Display') + +//************************************************************************************************************ +// RSI 辅助函数 +//************************************************************************************************************ + +// 自定义移动平均函数 +dema(src, length) => + ema1 = ta.ema(src, length) + ema2 = ta.ema(ema1, length) + 2 * ema1 - ema2 + +tema(src, length) => + ema1 = ta.ema(src, length) + ema2 = ta.ema(ema1, length) + ema3 = ta.ema(ema2, length) + 3 * ema1 - 3 * ema2 + ema3 + +t3(src, length, vfactor) => + ema1 = ta.ema(src, length) + ema2 = ta.ema(ema1, length) + ema3 = ta.ema(ema2, length) + ema4 = ta.ema(ema3, length) + ema5 = ta.ema(ema4, length) + ema6 = ta.ema(ema5, length) + + c1 = -vfactor * vfactor * vfactor + c2 = 3 * vfactor * vfactor + 3 * vfactor * vfactor * vfactor + c3 = -6 * vfactor * vfactor - 3 * vfactor - 3 * vfactor * vfactor * vfactor + c4 = 1 + 3 * vfactor + vfactor * vfactor * vfactor + 3 * vfactor * vfactor + + c1 * ema6 + c2 * ema5 + c3 * ema4 + c4 * ema3 + +ma(src, len, type, almaSig) => + switch type + 'SMA' => ta.sma(src, len) + 'Hull' => ta.hma(src, len) + 'Ema' => ta.ema(src, len) + 'Wma' => ta.wma(src, len) + 'DEMA' => dema(src, len) + 'RMA' => ta.rma(src, len) + 'LINREG' => ta.linreg(src, len, 0) + 'TEMA' => tema(src, len) + 'ALMA' => ta.alma(src, len, 0, almaSig) + 'T3' => t3(src, len, 0.7) + +//************************************************************************************************************ +// RSI 计算 +//************************************************************************************************************ + +// 计算RSI +rsi = ta.rsi(rsi_src, rsiLength) +if smooth + rsi := ma(rsi, smoothP, maType, sig) + +// RSI 机器学习阈值计算 +var rsi_values = array.new_float(0) +if last_bar_index - bar_index <= 1000 + array.push(rsi_values, rsi) + +var centroids = array.new_float(3) +if array.size(rsi_values) > 3 + array.set(centroids, 0, array.percentile_linear_interpolation(rsi_values, 25)) + array.set(centroids, 1, array.percentile_linear_interpolation(rsi_values, 50)) + array.set(centroids, 2, array.percentile_linear_interpolation(rsi_values, 75)) + +long_S = array.get(centroids, 2) +short_S = array.get(centroids, 0) + +// RSI 状态和计数逻辑 +var int rsi_state = 0 +var int prev_rsi_state = 0 +var int extreme_type = 0 +var int segment_count = 0 + +// 计数标签变量 - 不再使用全局变量,每次状态变化时创建新标签以保留历史 + +current_state = rsi > long_S ? 1 : rsi < short_S ? -1 : 0 + +if current_state != rsi_state and not na(rsi) + // 确定分段线颜色 + segment_color = color.black + + if prev_rsi_state == 0 and current_state == 1 + segment_color := color.new(color.red, 0) // 从中性区进入超买区 + else if prev_rsi_state == 0 and current_state == -1 + segment_color := color.new(color.blue, 0) // 从中性区进入超卖区 + else if prev_rsi_state == 1 and current_state == 0 + segment_color := color.new(color.orange, 0) // 从超买区进入中性区 + else if prev_rsi_state == -1 and current_state == 0 + segment_color := color.new(color.green, 0) // 从超卖区进入中性区 + else if prev_rsi_state == 1 and current_state == -1 + segment_color := color.new(color.purple, 0) // 从超买区直接到超卖区 + else if prev_rsi_state == -1 and current_state == 1 + segment_color := color.new(color.yellow, 0) // 从超卖区直接到超买区 + else if prev_rsi_state == 1 and current_state == 1 + segment_color := color.new(color.maroon, 0) // 超买区内的重复进入 + else if prev_rsi_state == -1 and current_state == -1 + segment_color := color.new(color.navy, 0) // 超卖区内的重复进入 + + // 绘制分段线 + if show_segments + line.new(bar_index, 0, bar_index, 100, + color = segment_color, + width = 2, + style = line.style_solid, + extend = extend.none) + + // 更新计数逻辑 + if current_state == 1 + if extreme_type != 1 + extreme_type := 1 + segment_count := 1 + else + segment_count := segment_count + 1 + + // 显示超买计数标签(保留历史标签) + if show_counting + label.new(bar_index, rsi, + text = str.tostring(segment_count), + style = label.style_label_down, + color = color.red, + textcolor = color.white, + size = size.small) + + else if current_state == -1 + if extreme_type != -1 + extreme_type := -1 + segment_count := -1 + else + segment_count := segment_count - 1 + + // 显示超卖计数标签(保留历史标签) + if show_counting + label.new(bar_index, rsi, + text = str.tostring(segment_count), + style = label.style_label_up, + color = color.blue, + textcolor = color.white, + size = size.small) + + // 更新前一个状态 + prev_rsi_state := rsi_state + rsi_state := current_state + +//************************************************************************************************************ +// RSI 绘图 +//************************************************************************************************************ + +// RSI 主线 +rsi_color = rsi > long_S ? color.red : rsi < short_S ? color.blue : color.gray +plot(rsi, 'RSI', color=rsi_color, linewidth=2) + +// 动态阈值线 +plot(long_S, 'Long ML Threshold', color=color.red, linewidth=1) +plot(short_S, 'Short ML Threshold', color=color.blue, linewidth=1) + +// 50中线 +hline(50, 'RSI 50', color=color.gray, linestyle=hline.style_dashed) + +//************************************************************************************************************ +// RSI 变化方向计算 +//************************************************************************************************************ + +// 计算RSI变化 +rsi_change = not na(rsi[1]) ? rsi - rsi[1] : 0 +rsi_direction = rsi_change > 0.1 ? "↗" : rsi_change < -0.1 ? "↘" : "→" +rsi_direction_text = rsi_change > 0.1 ? "上升" : rsi_change < -0.1 ? "下降" : "平稳" +rsi_change_value = str.tostring(math.abs(rsi_change), '#.##') + +//************************************************************************************************************ +// RSI 价格标签显示(参考MRC原始实现) +//************************************************************************************************************ + +// 价格格式化函数 +format_rsi_value(value) => + str.tostring(value, '#.##') + +// 声明RSI标签变量 +var label rsi_label = na +var label long_threshold_label = na +var label short_threshold_label = na +var label midline_label = na + +if show_price_labels and barstate.islast + // RSI 主线标签(包含变化方向) + rsi_label_text = "RSI: " + format_rsi_value(rsi) + " " + rsi_direction + " (" + rsi_direction_text + " " + rsi_change_value + ")" + if na(rsi_label) + rsi_label := label.new(bar_index, rsi, rsi_label_text, xloc=xloc.bar_index, style=label.style_label_left, color=rsi_color, textcolor=color.white) + else + label.set_xy(rsi_label, bar_index, rsi) + label.set_text(rsi_label, rsi_label_text) + label.set_color(rsi_label, rsi_color) + + // 超买阈值标签 + if na(long_threshold_label) + long_threshold_label := label.new(bar_index, long_S, "超买: " + format_rsi_value(long_S), xloc=xloc.bar_index, style=label.style_label_left, color=color.red, textcolor=color.white) + else + label.set_xy(long_threshold_label, bar_index, long_S) + label.set_text(long_threshold_label, "超买: " + format_rsi_value(long_S)) + + // 超卖阈值标签 + if na(short_threshold_label) + short_threshold_label := label.new(bar_index, short_S, "超卖: " + format_rsi_value(short_S), xloc=xloc.bar_index, style=label.style_label_left, color=color.blue, textcolor=color.white) + else + label.set_xy(short_threshold_label, bar_index, short_S) + label.set_text(short_threshold_label, "超卖: " + format_rsi_value(short_S)) + + // 50中线标签 + if na(midline_label) + midline_label := label.new(bar_index, 50, "中线: 50.00", xloc=xloc.bar_index, style=label.style_label_left, color=color.gray, textcolor=color.white) + else + label.set_xy(midline_label, bar_index, 50) + label.set_text(midline_label, "中线: 50.00") + +//************************************************************************************************************ +// RSI 信息表格 +//************************************************************************************************************ + +if show_rsi_table and barstate.islast + // RSI 状态 + rsi_position = rsi > long_S ? "超买区" : rsi < short_S ? "超卖区" : "中性区" + rsi_cross_count = math.abs(segment_count) + cross_type = segment_count > 0 ? "超买穿越" : segment_count < 0 ? "超卖穿越" : "无穿越" + + // 创建RSI表格(扩展为10行) + var table rsi_table = table.new( + position = position.bottom_right, + columns = 2, + rows = 10, + bgcolor = color.new(color.yellow, 80), + border_width = 1) + + // 清空表格 + table.clear(rsi_table, 0, 0, 1, 9) + + // 添加RSI标题 + table.cell(rsi_table, 0, 0, "RSI指标", text_color=color.black, bgcolor=color.new(color.orange, 70), text_size=size.small) + table.cell(rsi_table, 1, 0, "实时数值", text_color=color.black, bgcolor=color.new(color.orange, 70), text_size=size.small) + + // RSI 值 + table.cell(rsi_table, 0, 1, "RSI值", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 1, str.tostring(rsi, '#.##'), text_color=color.black, text_size=size.tiny) + + // RSI 位置 + table.cell(rsi_table, 0, 2, "RSI位置", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 2, rsi_position, text_color=color.black, text_size=size.tiny) + + // RSI 阈值 + table.cell(rsi_table, 0, 3, "超买阈值", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 3, str.tostring(long_S, '#.##'), text_color=color.black, text_size=size.tiny) + + table.cell(rsi_table, 0, 4, "超卖阈值", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 4, str.tostring(short_S, '#.##'), text_color=color.black, text_size=size.tiny) + + // RSI 穿越次数 + table.cell(rsi_table, 0, 5, "穿越次数", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 5, str.tostring(rsi_cross_count), text_color=color.black, text_size=size.tiny) + + // RSI 穿越类型 + table.cell(rsi_table, 0, 6, "穿越类型", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 6, cross_type, text_color=color.black, text_size=size.tiny) + + // RSI 状态标记 + table.cell(rsi_table, 0, 7, "状态标记", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 7, str.tostring(segment_count), text_color=color.black, text_size=size.tiny) + + // RSI 变化方向 + table.cell(rsi_table, 0, 8, "变化方向", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 8, rsi_direction_text + " " + rsi_direction, text_color=color.black, text_size=size.tiny) + + // RSI 变化幅度 + table.cell(rsi_table, 0, 9, "变化幅度", text_color=color.black, text_size=size.tiny) + table.cell(rsi_table, 1, 9, rsi_change_value, text_color=color.black, text_size=size.tiny) diff --git a/srbr.pine b/srbr.pine new file mode 100644 index 0000000..6f72b48 --- /dev/null +++ b/srbr.pine @@ -0,0 +1,714 @@ +// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ +// © ChartPrime + +//@version=6 +indicator('SRBR 支撑阻力突破回测 [多时间周期表格]', shorttitle = 'SRBR MTF', overlay = true, max_boxes_count = 50) + + +// ---------------------------------------------------------------------------------------------------------------------} +// 𝙐𝙎𝙀𝙍 𝙄𝙉𝙋𝙐𝙏𝙎 +// ---------------------------------------------------------------------------------------------------------------------{ +int lookbackPeriod = input.int(20, 'Lookback Period', minval = 1, group = 'Settings') +int vol_len = input.int(2, 'Delta Volume Filter Length', tooltip = 'Higher input, will filter low volume boxes', group = 'Settings') +float box_withd = input.float(1, 'Adjust Box Width', maxval = 1000, minval = 0, step = 0.1, group = 'Settings') + +// ═════════ 多时间周期表格设置 ════════ +mtfSet = input(false, '═════════ 多时间周期表格设置 ════════') +show_mtf_table = input.bool(true, title='显示多时间周期表格', group='MTF Table') +mtf_1m = input.bool(true, title='显示1分钟数据', group='MTF Table') +mtf_5m = input.bool(true, title='显示5分钟数据', group='MTF Table') +mtf_15m = input.bool(true, title='显示15分钟数据', group='MTF Table') +mtf_30m = input.bool(true, title='显示30分钟数据', group='MTF Table') +mtf_45m = input.bool(true, title='显示45分钟数据', group='MTF Table') +mtf_1h = input.bool(true, title='显示1小时数据', group='MTF Table') +mtf_4h = input.bool(true, title='显示4小时数据', group='MTF Table') + +// ═════════ 表格显示设置 ════════ +tableSet = input(false, '═════════ 表格显示设置 ════════') +table_position = input.string('bottom_right', title='表格位置', options=['top_left', 'top_center', 'top_right', 'middle_left', 'middle_center', 'middle_right', 'bottom_left', 'bottom_center', 'bottom_right'], group='Table Display') +table_text_size = input.string('small', title='表格文字大小', options=['auto', 'tiny', 'small', 'normal', 'large', 'huge'], group='Table Display') +mtf_table_rows = input.int(9, title='多时间框架表格行数', minval=6, maxval=12, group='Table Display') +mtf_table_columns = input.int(8, title='多时间框架表格列数', minval=6, maxval=10, group='Table Display') + + +//************************************************************************************************************ +// 辅助函数定义 +//************************************************************************************************************ + +// ═════════ 表格文字大小转换 ════════ +get_text_size() => + switch table_text_size + 'auto' => size.auto + 'tiny' => size.tiny + 'small' => size.small + 'normal' => size.normal + 'large' => size.large + 'huge' => size.huge + => size.small + +// ═════════ 表格位置转换 ════════ +get_table_position() => + switch table_position + 'top_left' => position.top_left + 'top_center' => position.top_center + 'top_right' => position.top_right + 'middle_left' => position.middle_left + 'middle_center' => position.middle_center + 'middle_right' => position.middle_right + 'bottom_left' => position.bottom_left + 'bottom_center' => position.bottom_center + 'bottom_right' => position.bottom_right + => position.bottom_left + +// ═════════ 价格状态判断函数 ════════ +get_price_status(price_val, support_val, resistance_val) => + if na(price_val) or na(support_val) or na(resistance_val) + "N/A" + else if price_val > resistance_val + "看多" + else if price_val < support_val + "看空" + else + "震荡" + +// ═════════ 价格状态颜色函数 ════════ +get_price_status_color(price_val, support_val, resistance_val) => + if na(price_val) or na(support_val) or na(resistance_val) + color.new(color.gray, 60) + else if price_val > resistance_val + color.new(color.green, 40) // 看多 - 绿色 + else if price_val < support_val + color.new(color.red, 40) // 看空 - 红色 + else + color.new(color.orange, 40) // 震荡 - 橙色 + +// ═════════ 距离计算函数 ════════ +get_distance_to_levels(price_val, support_val, resistance_val) => + if na(price_val) or na(support_val) or na(resistance_val) + [na, na] + else + distance_to_support = math.abs(price_val - support_val) + distance_to_resistance = math.abs(price_val - resistance_val) + [distance_to_support, distance_to_resistance] + +// ---------------------------------------------------------------------------------------------------------------------} +// 𝙄𝙉𝘿𝙄𝘾𝘼𝙏𝙊𝙍 𝘾𝘼𝙇𝘾𝙐𝙇𝘼𝙏𝙄𝙊𝙉𝙎 +// ---------------------------------------------------------------------------------------------------------------------{ +// Delta Volume Function +upAndDownVolume() => + posVol = 0.0 + negVol = 0.0 + + var isBuyVolume = true + + switch + close > open => + isBuyVolume := true + isBuyVolume + close < open => + isBuyVolume := false + isBuyVolume + + if isBuyVolume + posVol := posVol + volume + posVol + else + negVol := negVol - volume + negVol + + posVol + negVol + + +// Function to identify support and resistance boxes +calcSupportResistance(src, lookbackPeriod) => + // Volume + Vol = upAndDownVolume() + vol_hi = ta.highest(Vol / 2.5, vol_len) + vol_lo = ta.lowest(Vol / 2.5, vol_len) + + var float supportLevel = na + var float supportLevel_1 = na + var float resistanceLevel = na + var float resistanceLevel_1 = na + var box sup = na + var box res = na + var color res_color = na + var color sup_color = na + var float multi = na + + var bool brekout_res = false + var bool brekout_sup = false + var bool res_holds = false + var bool sup_holds = false + + // Find pivot points + pivotHigh = ta.pivothigh(src, lookbackPeriod, lookbackPeriod) + pivotLow = ta.pivotlow(src, lookbackPeriod, lookbackPeriod) + // Box width + atr = ta.atr(200) + withd = atr * box_withd + + // Volume range for color gradient + vol_highest = ta.highest(Vol, 25) + vol_lowest = ta.lowest(Vol, 25) + + // Find support levels with Positive Volume + if not na(pivotLow) and Vol > vol_hi + + supportLevel := pivotLow + supportLevel_1 := supportLevel - withd + + topLeft = chart.point.from_index(bar_index - lookbackPeriod, supportLevel) + bottomRight = chart.point.from_index(bar_index, supportLevel_1) + + sup_color := color.from_gradient(Vol, 0, vol_highest, color(na), color.new(color.green, 30)) + + // 注释掉支撑位box绘图,保留计算逻辑 + // sup := box.new(top_left = topLeft, bottom_right = bottomRight, border_color = color.green, border_width = 1, bgcolor = sup_color, text = 'Vol: ' + str.tostring(math.round(Vol, 2)), text_color = chart.fg_color, text_size = size.small) + // sup + na + + + // Find resistance levels with Negative Volume + if not na(pivotHigh) and Vol < vol_lo + + resistanceLevel := pivotHigh + resistanceLevel_1 := resistanceLevel + withd + + topLeft = chart.point.from_index(bar_index - lookbackPeriod, resistanceLevel) + bottomRight = chart.point.from_index(bar_index, resistanceLevel_1) + + res_color := color.from_gradient(Vol, vol_lowest, 0, color.new(color.red, 30), color(na)) + + // 注释掉阻力位box绘图,保留计算逻辑 + // res := box.new(top_left = topLeft, bottom_right = bottomRight, border_color = color.red, border_width = 1, bgcolor = res_color, text = 'Vol: ' + str.tostring(math.round(Vol, 2)), text_color = chart.fg_color, text_size = size.small) + // res + na + + // Adaptive Box Len - 已注释掉box操作 + // sup.set_right(bar_index + 1) + // res.set_right(bar_index + 1) + + // Break of support or resistance conditions + brekout_res := ta.crossover(low, resistanceLevel_1) + res_holds := ta.crossunder(high, resistanceLevel) + + sup_holds := ta.crossover(low, supportLevel) + brekout_sup := ta.crossunder(high, supportLevel_1) + + // Change Color of Support to red if it was break, change color of resistance to green if it was break - 已注释掉box操作 + // if brekout_sup + // sup.set_bgcolor(color.new(color.red, 80)) + // sup.set_border_color(color.red) + // sup.set_border_style(line.style_dashed) + + // if sup_holds + // sup.set_bgcolor(sup_color) + // sup.set_border_color(color.green) + // sup.set_border_style(line.style_solid) + + // if brekout_res + // res.set_bgcolor(color.new(color.green, 80)) + // res.set_border_color(color.new(color.green, 0)) + // res.set_border_style(line.style_dashed) + + // if res_holds + // res.set_bgcolor(res_color) + // res.set_border_color(color.new(color.red, 0)) + // res.set_border_style(line.style_solid) + + [supportLevel, resistanceLevel, brekout_res, res_holds, sup_holds, brekout_sup] + + +// Calculate support and resistance levels and their breakouts +[supportLevel, resistanceLevel, brekout_res, res_holds, sup_holds, brekout_sup] = calcSupportResistance(close, lookbackPeriod) + + +// Check if Resistance become Support or Support Become Resistance +var bool res_is_sup = false +var bool sup_is_res = false + +switch + brekout_res => + res_is_sup := true + res_is_sup + res_holds => + res_is_sup := false + res_is_sup + +switch + brekout_sup => + sup_is_res := true + sup_is_res + sup_holds => + sup_is_res := false + sup_is_res + + +// ---------------------------------------------------------------------------------------------------------------------} +// 𝙑𝙄𝙎𝙐𝘼𝙇𝙄𝙕𝘼𝙏𝙄𝙊𝙉 +// ---------------------------------------------------------------------------------------------------------------------{ +// Plot Res and Sup breakouts and holds - 已注释掉图形显示 +// plotchar(res_holds, 'Resistance Holds', '◆', color = #e92929, size = size.tiny, location = location.abovebar, offset = -1) +// plotchar(sup_holds, 'Support Holds', '◆', color = #20ca26, size = size.tiny, location = location.belowbar, offset = -1) + +// plotchar(brekout_res and res_is_sup[1], 'Resistance as Support Holds', '◆', color = #20ca26, size = size.tiny, location = location.belowbar, offset = -1) +// plotchar(brekout_sup and sup_is_res[1], 'Support as Resistance Holds', '◆', color = #e92929, size = size.tiny, location = location.abovebar, offset = -1) + +// Break Out Labels - 已注释掉图形显示 +// if brekout_sup and not sup_is_res[1] +// label.new(bar_index[1], supportLevel[1], text = 'Break Sup', style = label.style_label_down, color = #7e1e1e, textcolor = chart.fg_color, size = size.small) + +// if brekout_res and not res_is_sup[1] +// label.new(bar_index[1], resistanceLevel[1], text = 'Break Res', style = label.style_label_up, color = #2b6d2d, textcolor = chart.fg_color, size = size.small) + +// 警报标记 - 醒目的视觉提示 - 已注释掉图形显示 +// 支撑位保持警报标记 +// if sup_holds +// label.new(bar_index, low, text = '🔔SUP', style = label.style_label_down, color = color.new(color.lime, 0), textcolor = color.black, size = size.large, tooltip = '支撑位保持警报') + +// 阻力位保持警报标记 +// if res_holds +// label.new(bar_index, high, text = '🔔RES', style = label.style_label_up, color = color.new(color.red, 0), textcolor = color.white, size = size.large, tooltip = '阻力位保持警报') + +// 支撑位突破警报标记 +// if brekout_sup +// label.new(bar_index[1], supportLevel[1], text = '🚨SUP⬇', style = label.style_label_down, color = color.new(color.orange, 0), textcolor = color.black, size = size.large, tooltip = '支撑位突破警报') + +// 阻力位突破警报标记 +// if brekout_res +// label.new(bar_index[1], resistanceLevel[1], text = '🚨RES⬆', style = label.style_label_up, color = color.new(color.aqua, 0), textcolor = color.black, size = size.large, tooltip = '阻力位突破警报') + +// 阻力转支撑警报标记 +// if brekout_res and res_is_sup[1] +// label.new(bar_index[1], resistanceLevel[1], text = '⭐R→S', style = label.style_label_down, color = color.new(color.purple, 0), textcolor = color.white, size = size.large, tooltip = '阻力转支撑保持警报') + +// 支撑转阻力警报标记 +// if brekout_sup and sup_is_res[1] +// label.new(bar_index[1], supportLevel[1], text = '⭐S→R', style = label.style_label_up, color = color.new(color.maroon, 0), textcolor = color.white, size = size.large, tooltip = '支撑转阻力保持警报') + +// 全局作用域的plotshape - 警报形状标记 - 已注释掉图形显示 +// plotshape(sup_holds, title = '支撑保持警报', style = shape.triangleup, location = location.belowbar, color = color.new(color.lime, 0), size = size.large) +// plotshape(res_holds, title = '阻力保持警报', style = shape.triangledown, location = location.abovebar, color = color.new(color.red, 0), size = size.large) +// plotshape(brekout_sup, title = '支撑突破警报', style = shape.xcross, location = location.belowbar, color = color.new(color.orange, 0), size = size.large) +// plotshape(brekout_res, title = '阻力突破警报', style = shape.xcross, location = location.abovebar, color = color.new(color.aqua, 0), size = size.large) +// plotshape(brekout_res and res_is_sup[1], title = '阻力转支撑警报', style = shape.diamond, location = location.belowbar, color = color.new(color.purple, 0), size = size.large) +// plotshape(brekout_sup and sup_is_res[1], title = '支撑转阻力警报', style = shape.diamond, location = location.abovebar, color = color.new(color.maroon, 0), size = size.large) + + +//************************************************************************************************************ +// 多时间周期计算 +//************************************************************************************************************ + +// ═════════ 多时间周期专用计算函数(无绘图操作) ════════ +// 为了避免request.security()中的绘图函数错误,创建简化版本 +calcSRForMTF(src, lookback) => + // 使用简化的支撑阻力计算,基于pivot点但无绘图 + pivotHigh = ta.pivothigh(src, lookback, lookback) + pivotLow = ta.pivotlow(src, lookback, lookback) + + // 简化的支撑阻力位识别 + var float mtf_support = na + var float mtf_resistance = na + + if not na(pivotLow) + mtf_support := pivotLow + if not na(pivotHigh) + mtf_resistance := pivotHigh + + // 简化的突破判断 + mtf_brekout_res = src > mtf_resistance + mtf_res_holds = src <= mtf_resistance and src >= mtf_resistance * 0.995 + mtf_sup_holds = src >= mtf_support and src <= mtf_support * 1.005 + mtf_brekout_sup = src < mtf_support + + [mtf_support, mtf_resistance, mtf_brekout_res, mtf_res_holds, mtf_sup_holds, mtf_brekout_sup] + +// ═════════ 多时间周期数据获取函数 ════════ +get_mtf_data(tf) => + request.security(syminfo.tickerid, tf, calcSRForMTF(close, lookbackPeriod), lookahead=barmerge.lookahead_off) + +get_mtf_close(tf) => + request.security(syminfo.tickerid, tf, close, lookahead=barmerge.lookahead_off) + +// ═════════ 多时间周期数据获取 ═════════ +// 1分钟数据 +var float supportLevel_1m = na +var float resistanceLevel_1m = na +var bool brekout_res_1m = false +var bool res_holds_1m = false +var bool sup_holds_1m = false +var bool brekout_sup_1m = false +var float close_1m = na + +if mtf_1m + [sup_1m, res_1m, br_res_1m, r_holds_1m, s_holds_1m, br_sup_1m] = get_mtf_data("1") + supportLevel_1m := sup_1m + resistanceLevel_1m := res_1m + brekout_res_1m := br_res_1m + res_holds_1m := r_holds_1m + sup_holds_1m := s_holds_1m + brekout_sup_1m := br_sup_1m + close_1m := get_mtf_close("1") + +// 5分钟数据 +var float supportLevel_5m = na +var float resistanceLevel_5m = na +var bool brekout_res_5m = false +var bool res_holds_5m = false +var bool sup_holds_5m = false +var bool brekout_sup_5m = false +var float close_5m = na + +if mtf_5m + [sup_5m, res_5m, br_res_5m, r_holds_5m, s_holds_5m, br_sup_5m] = get_mtf_data("5") + supportLevel_5m := sup_5m + resistanceLevel_5m := res_5m + brekout_res_5m := br_res_5m + res_holds_5m := r_holds_5m + sup_holds_5m := s_holds_5m + brekout_sup_5m := br_sup_5m + close_5m := get_mtf_close("5") + +// 15分钟数据 +var float supportLevel_15m = na +var float resistanceLevel_15m = na +var bool brekout_res_15m = false +var bool res_holds_15m = false +var bool sup_holds_15m = false +var bool brekout_sup_15m = false +var float close_15m = na + +if mtf_15m + [sup_15m, res_15m, br_res_15m, r_holds_15m, s_holds_15m, br_sup_15m] = get_mtf_data("15") + supportLevel_15m := sup_15m + resistanceLevel_15m := res_15m + brekout_res_15m := br_res_15m + res_holds_15m := r_holds_15m + sup_holds_15m := s_holds_15m + brekout_sup_15m := br_sup_15m + close_15m := get_mtf_close("15") + +// 30分钟数据 +var float supportLevel_30m = na +var float resistanceLevel_30m = na +var bool brekout_res_30m = false +var bool res_holds_30m = false +var bool sup_holds_30m = false +var bool brekout_sup_30m = false +var float close_30m = na + +if mtf_30m + [sup_30m, res_30m, br_res_30m, r_holds_30m, s_holds_30m, br_sup_30m] = get_mtf_data("30") + supportLevel_30m := sup_30m + resistanceLevel_30m := res_30m + brekout_res_30m := br_res_30m + res_holds_30m := r_holds_30m + sup_holds_30m := s_holds_30m + brekout_sup_30m := br_sup_30m + close_30m := get_mtf_close("30") + +// 45分钟数据 +var float supportLevel_45m = na +var float resistanceLevel_45m = na +var bool brekout_res_45m = false +var bool res_holds_45m = false +var bool sup_holds_45m = false +var bool brekout_sup_45m = false +var float close_45m = na + +if mtf_45m + [sup_45m, res_45m, br_res_45m, r_holds_45m, s_holds_45m, br_sup_45m] = get_mtf_data("45") + supportLevel_45m := sup_45m + resistanceLevel_45m := res_45m + brekout_res_45m := br_res_45m + res_holds_45m := r_holds_45m + sup_holds_45m := s_holds_45m + brekout_sup_45m := br_sup_45m + close_45m := get_mtf_close("45") + +// 1小时数据 +var float supportLevel_1h = na +var float resistanceLevel_1h = na +var bool brekout_res_1h = false +var bool res_holds_1h = false +var bool sup_holds_1h = false +var bool brekout_sup_1h = false +var float close_1h = na + +if mtf_1h + [sup_1h, res_1h, br_res_1h, r_holds_1h, s_holds_1h, br_sup_1h] = get_mtf_data("60") + supportLevel_1h := sup_1h + resistanceLevel_1h := res_1h + brekout_res_1h := br_res_1h + res_holds_1h := r_holds_1h + sup_holds_1h := s_holds_1h + brekout_sup_1h := br_sup_1h + close_1h := get_mtf_close("60") + +// 4小时数据 +var float supportLevel_4h = na +var float resistanceLevel_4h = na +var bool brekout_res_4h = false +var bool res_holds_4h = false +var bool sup_holds_4h = false +var bool brekout_sup_4h = false +var float close_4h = na + +if mtf_4h + [sup_4h, res_4h, br_res_4h, r_holds_4h, s_holds_4h, br_sup_4h] = get_mtf_data("240") + supportLevel_4h := sup_4h + resistanceLevel_4h := res_4h + brekout_res_4h := br_res_4h + res_holds_4h := r_holds_4h + sup_holds_4h := s_holds_4h + brekout_sup_4h := br_sup_4h + close_4h := get_mtf_close("240") + +//************************************************************************************************************ +// 多时间周期表格显示 +//************************************************************************************************************ + +if show_mtf_table and barstate.islast + // 创建多时间周期表格 + var table mtf_table = table.new( + position = get_table_position(), + columns = mtf_table_columns, + rows = mtf_table_rows, + bgcolor = color.new(color.white, 85), + border_width = 1) + + // 清空表格 + table.clear(mtf_table, 0, 0, mtf_table_columns-1, mtf_table_rows-1) + + // 获取配置的文字大小 + text_size = get_text_size() + + // ═════════ 表格标题行 ═════════ + table.cell(mtf_table, 0, 0, "时间周期", text_color=color.white, bgcolor=color.new(color.purple, 30), text_size=text_size) + table.cell(mtf_table, 1, 0, "当前价格", text_color=color.white, bgcolor=color.new(color.purple, 30), text_size=text_size) + table.cell(mtf_table, 2, 0, "支撑位", text_color=color.white, bgcolor=color.new(color.green, 30), text_size=text_size) + table.cell(mtf_table, 3, 0, "阻力位", text_color=color.white, bgcolor=color.new(color.red, 30), text_size=text_size) + table.cell(mtf_table, 4, 0, "距支撑", text_color=color.white, bgcolor=color.new(color.green, 30), text_size=text_size) + table.cell(mtf_table, 5, 0, "距阻力", text_color=color.white, bgcolor=color.new(color.red, 30), text_size=text_size) + table.cell(mtf_table, 6, 0, "价格状态", text_color=color.white, bgcolor=color.new(color.blue, 30), text_size=text_size) + table.cell(mtf_table, 7, 0, "市场观点", text_color=color.white, bgcolor=color.new(color.orange, 30), text_size=text_size) + + // ═════════ 数据行 ═════════ + row = 1 + + // 1分钟数据行 + if mtf_1m and row < mtf_table_rows + [dist_to_sup_1m, dist_to_res_1m] = get_distance_to_levels(close_1m, supportLevel_1m, resistanceLevel_1m) + status_1m = get_price_status(close_1m, supportLevel_1m, resistanceLevel_1m) + status_color_1m = get_price_status_color(close_1m, supportLevel_1m, resistanceLevel_1m) + + table.cell(mtf_table, 0, row, "1分钟", text_color=color.black, bgcolor=color.new(color.gray, 80), text_size=text_size) + table.cell(mtf_table, 1, row, str.tostring(close_1m, '#.##'), text_color=color.black, bgcolor=color.white, text_size=text_size) + table.cell(mtf_table, 2, row, str.tostring(supportLevel_1m, '#.##'), text_color=color.white, bgcolor=color.new(color.green, 60), text_size=text_size) + table.cell(mtf_table, 3, row, str.tostring(resistanceLevel_1m, '#.##'), text_color=color.white, bgcolor=color.new(color.red, 60), text_size=text_size) + table.cell(mtf_table, 4, row, str.tostring(dist_to_sup_1m, '#.##'), text_color=color.black, bgcolor=color.new(color.green, 80), text_size=text_size) + table.cell(mtf_table, 5, row, str.tostring(dist_to_res_1m, '#.##'), text_color=color.black, bgcolor=color.new(color.red, 80), text_size=text_size) + table.cell(mtf_table, 6, row, status_1m, text_color=color.white, bgcolor=status_color_1m, text_size=text_size) + table.cell(mtf_table, 7, row, status_1m, text_color=color.white, bgcolor=status_color_1m, text_size=text_size) + row := row + 1 + + // 5分钟数据行 + if mtf_5m and row < mtf_table_rows + [dist_to_sup_5m, dist_to_res_5m] = get_distance_to_levels(close_5m, supportLevel_5m, resistanceLevel_5m) + status_5m = get_price_status(close_5m, supportLevel_5m, resistanceLevel_5m) + status_color_5m = get_price_status_color(close_5m, supportLevel_5m, resistanceLevel_5m) + + table.cell(mtf_table, 0, row, "5分钟", text_color=color.black, bgcolor=color.new(color.gray, 80), text_size=text_size) + table.cell(mtf_table, 1, row, str.tostring(close_5m, '#.##'), text_color=color.black, bgcolor=color.white, text_size=text_size) + table.cell(mtf_table, 2, row, str.tostring(supportLevel_5m, '#.##'), text_color=color.white, bgcolor=color.new(color.green, 60), text_size=text_size) + table.cell(mtf_table, 3, row, str.tostring(resistanceLevel_5m, '#.##'), text_color=color.white, bgcolor=color.new(color.red, 60), text_size=text_size) + table.cell(mtf_table, 4, row, str.tostring(dist_to_sup_5m, '#.##'), text_color=color.black, bgcolor=color.new(color.green, 80), text_size=text_size) + table.cell(mtf_table, 5, row, str.tostring(dist_to_res_5m, '#.##'), text_color=color.black, bgcolor=color.new(color.red, 80), text_size=text_size) + table.cell(mtf_table, 6, row, status_5m, text_color=color.white, bgcolor=status_color_5m, text_size=text_size) + table.cell(mtf_table, 7, row, status_5m, text_color=color.white, bgcolor=status_color_5m, text_size=text_size) + row := row + 1 + + // 15分钟数据行 + if mtf_15m and row < mtf_table_rows + [dist_to_sup_15m, dist_to_res_15m] = get_distance_to_levels(close_15m, supportLevel_15m, resistanceLevel_15m) + status_15m = get_price_status(close_15m, supportLevel_15m, resistanceLevel_15m) + status_color_15m = get_price_status_color(close_15m, supportLevel_15m, resistanceLevel_15m) + + table.cell(mtf_table, 0, row, "15分钟", text_color=color.black, bgcolor=color.new(color.gray, 80), text_size=text_size) + table.cell(mtf_table, 1, row, str.tostring(close_15m, '#.##'), text_color=color.black, bgcolor=color.white, text_size=text_size) + table.cell(mtf_table, 2, row, str.tostring(supportLevel_15m, '#.##'), text_color=color.white, bgcolor=color.new(color.green, 60), text_size=text_size) + table.cell(mtf_table, 3, row, str.tostring(resistanceLevel_15m, '#.##'), text_color=color.white, bgcolor=color.new(color.red, 60), text_size=text_size) + table.cell(mtf_table, 4, row, str.tostring(dist_to_sup_15m, '#.##'), text_color=color.black, bgcolor=color.new(color.green, 80), text_size=text_size) + table.cell(mtf_table, 5, row, str.tostring(dist_to_res_15m, '#.##'), text_color=color.black, bgcolor=color.new(color.red, 80), text_size=text_size) + table.cell(mtf_table, 6, row, status_15m, text_color=color.white, bgcolor=status_color_15m, text_size=text_size) + table.cell(mtf_table, 7, row, status_15m, text_color=color.white, bgcolor=status_color_15m, text_size=text_size) + row := row + 1 + + // 30分钟数据行 + if mtf_30m and row < mtf_table_rows + [dist_to_sup_30m, dist_to_res_30m] = get_distance_to_levels(close_30m, supportLevel_30m, resistanceLevel_30m) + status_30m = get_price_status(close_30m, supportLevel_30m, resistanceLevel_30m) + status_color_30m = get_price_status_color(close_30m, supportLevel_30m, resistanceLevel_30m) + + table.cell(mtf_table, 0, row, "30分钟", text_color=color.black, bgcolor=color.new(color.gray, 80), text_size=text_size) + table.cell(mtf_table, 1, row, str.tostring(close_30m, '#.##'), text_color=color.black, bgcolor=color.white, text_size=text_size) + table.cell(mtf_table, 2, row, str.tostring(supportLevel_30m, '#.##'), text_color=color.white, bgcolor=color.new(color.green, 60), text_size=text_size) + table.cell(mtf_table, 3, row, str.tostring(resistanceLevel_30m, '#.##'), text_color=color.white, bgcolor=color.new(color.red, 60), text_size=text_size) + table.cell(mtf_table, 4, row, str.tostring(dist_to_sup_30m, '#.##'), text_color=color.black, bgcolor=color.new(color.green, 80), text_size=text_size) + table.cell(mtf_table, 5, row, str.tostring(dist_to_res_30m, '#.##'), text_color=color.black, bgcolor=color.new(color.red, 80), text_size=text_size) + table.cell(mtf_table, 6, row, status_30m, text_color=color.white, bgcolor=status_color_30m, text_size=text_size) + table.cell(mtf_table, 7, row, status_30m, text_color=color.white, bgcolor=status_color_30m, text_size=text_size) + row := row + 1 + + // 45分钟数据行 + if mtf_45m and row < mtf_table_rows + [dist_to_sup_45m, dist_to_res_45m] = get_distance_to_levels(close_45m, supportLevel_45m, resistanceLevel_45m) + status_45m = get_price_status(close_45m, supportLevel_45m, resistanceLevel_45m) + status_color_45m = get_price_status_color(close_45m, supportLevel_45m, resistanceLevel_45m) + + table.cell(mtf_table, 0, row, "45分钟", text_color=color.black, bgcolor=color.new(color.gray, 80), text_size=text_size) + table.cell(mtf_table, 1, row, str.tostring(close_45m, '#.##'), text_color=color.black, bgcolor=color.white, text_size=text_size) + table.cell(mtf_table, 2, row, str.tostring(supportLevel_45m, '#.##'), text_color=color.white, bgcolor=color.new(color.green, 60), text_size=text_size) + table.cell(mtf_table, 3, row, str.tostring(resistanceLevel_45m, '#.##'), text_color=color.white, bgcolor=color.new(color.red, 60), text_size=text_size) + table.cell(mtf_table, 4, row, str.tostring(dist_to_sup_45m, '#.##'), text_color=color.black, bgcolor=color.new(color.green, 80), text_size=text_size) + table.cell(mtf_table, 5, row, str.tostring(dist_to_res_45m, '#.##'), text_color=color.black, bgcolor=color.new(color.red, 80), text_size=text_size) + table.cell(mtf_table, 6, row, status_45m, text_color=color.white, bgcolor=status_color_45m, text_size=text_size) + table.cell(mtf_table, 7, row, status_45m, text_color=color.white, bgcolor=status_color_45m, text_size=text_size) + row := row + 1 + + // 1小时数据行 + if mtf_1h and row < mtf_table_rows + [dist_to_sup_1h, dist_to_res_1h] = get_distance_to_levels(close_1h, supportLevel_1h, resistanceLevel_1h) + status_1h = get_price_status(close_1h, supportLevel_1h, resistanceLevel_1h) + status_color_1h = get_price_status_color(close_1h, supportLevel_1h, resistanceLevel_1h) + + table.cell(mtf_table, 0, row, "1小时", text_color=color.black, bgcolor=color.new(color.gray, 80), text_size=text_size) + table.cell(mtf_table, 1, row, str.tostring(close_1h, '#.##'), text_color=color.black, bgcolor=color.white, text_size=text_size) + table.cell(mtf_table, 2, row, str.tostring(supportLevel_1h, '#.##'), text_color=color.white, bgcolor=color.new(color.green, 60), text_size=text_size) + table.cell(mtf_table, 3, row, str.tostring(resistanceLevel_1h, '#.##'), text_color=color.white, bgcolor=color.new(color.red, 60), text_size=text_size) + table.cell(mtf_table, 4, row, str.tostring(dist_to_sup_1h, '#.##'), text_color=color.black, bgcolor=color.new(color.green, 80), text_size=text_size) + table.cell(mtf_table, 5, row, str.tostring(dist_to_res_1h, '#.##'), text_color=color.black, bgcolor=color.new(color.red, 80), text_size=text_size) + table.cell(mtf_table, 6, row, status_1h, text_color=color.white, bgcolor=status_color_1h, text_size=text_size) + table.cell(mtf_table, 7, row, status_1h, text_color=color.white, bgcolor=status_color_1h, text_size=text_size) + row := row + 1 + + // 4小时数据行 + if mtf_4h and row < mtf_table_rows + [dist_to_sup_4h, dist_to_res_4h] = get_distance_to_levels(close_4h, supportLevel_4h, resistanceLevel_4h) + status_4h = get_price_status(close_4h, supportLevel_4h, resistanceLevel_4h) + status_color_4h = get_price_status_color(close_4h, supportLevel_4h, resistanceLevel_4h) + + table.cell(mtf_table, 0, row, "4小时", text_color=color.black, bgcolor=color.new(color.gray, 80), text_size=text_size) + table.cell(mtf_table, 1, row, str.tostring(close_4h, '#.##'), text_color=color.black, bgcolor=color.white, text_size=text_size) + table.cell(mtf_table, 2, row, str.tostring(supportLevel_4h, '#.##'), text_color=color.white, bgcolor=color.new(color.green, 60), text_size=text_size) + table.cell(mtf_table, 3, row, str.tostring(resistanceLevel_4h, '#.##'), text_color=color.white, bgcolor=color.new(color.red, 60), text_size=text_size) + table.cell(mtf_table, 4, row, str.tostring(dist_to_sup_4h, '#.##'), text_color=color.black, bgcolor=color.new(color.green, 80), text_size=text_size) + table.cell(mtf_table, 5, row, str.tostring(dist_to_res_4h, '#.##'), text_color=color.black, bgcolor=color.new(color.red, 80), text_size=text_size) + table.cell(mtf_table, 6, row, status_4h, text_color=color.white, bgcolor=status_color_4h, text_size=text_size) + table.cell(mtf_table, 7, row, status_4h, text_color=color.white, bgcolor=status_color_4h, text_size=text_size) + +// ═════════ 输出支撑阻力位数据供webhook使用 ════════ +plot(supportLevel, title="supportLevel", display=display.none) +plot(resistanceLevel, title="resistanceLevel", display=display.none) + +// ◆ +// ---------------------------------------------------------------------------------------------------------------------} +// �𝙍𝙄𝘾𝙀 𝙎𝙏𝘼𝙏𝙐𝙎 & 𝙈𝘼𝙍𝙆𝙀𝙏 𝙊𝙐𝙏𝙇𝙊𝙊𝙆 +// ---------------------------------------------------------------------------------------------------------------------{ + +// ═════════ 当前图表时间周期价格状态分析 ════════ +get_current_price_status() => + current_price = close + support_level = supportLevel + resistance_level = resistanceLevel + + // 计算价格相对位置 + if na(support_level) and na(resistance_level) + "无明确支撑阻力" + else if na(support_level) + if current_price > resistance_level + "突破阻力位上方" + else if current_price >= resistance_level * 0.995 + "接近阻力位" + else + "阻力位下方" + else if na(resistance_level) + if current_price < support_level + "跌破支撑位下方" + else if current_price <= support_level * 1.005 + "接近支撑位" + else + "支撑位上方" + else + range_size = resistance_level - support_level + price_position = (current_price - support_level) / range_size + + if current_price > resistance_level + "突破阻力位上方" + else if current_price < support_level + "跌破支撑位下方" + else if price_position >= 0.8 + "接近阻力位" + else if price_position <= 0.2 + "接近支撑位" + else + "区间中部运行" + +// ═════════ 市场观点生成 ════════ +get_market_outlook() => + current_status = get_current_price_status() + + // 基于价格状态和突破情况生成观点 + if brekout_res and res_is_sup[1] + "看涨:阻力转支撑确认,建议关注回调买入机会" + else if brekout_sup and sup_is_res[1] + "看跌:支撑转阻力确认,建议关注反弹卖出机会" + else if brekout_res + "看涨:突破阻力位,建议关注回踩确认后的追涨机会" + else if brekout_sup + "看跌:跌破支撑位,建议关注反弹确认后的追跌机会" + else if res_holds + "中性偏空:阻力位有效,短期上涨受阻" + else if sup_holds + "中性偏多:支撑位有效,短期下跌受限" + else if current_status == "接近阻力位" + "谨慎:接近关键阻力位,注意突破或回落" + else if current_status == "接近支撑位" + "谨慎:接近关键支撑位,注意反弹或破位" + else if current_status == "区间中部运行" + "中性:价格在支撑阻力区间内震荡" + else if current_status == "突破阻力位上方" + "看涨:已突破阻力位,关注持续性" + else if current_status == "跌破支撑位下方" + "看跌:已跌破支撑位,关注反弹力度" + else + "观望:等待明确的支撑阻力信号" + +// 获取当前价格状态和市场观点 +current_price_status = get_current_price_status() +market_outlook = get_market_outlook() + +// ---------------------------------------------------------------------------------------------------------------------} +// �𝘼𝙇𝙀𝙍𝙏 𝘾𝙊𝙉𝘿𝙄𝙏𝙄𝙊𝙉𝙎 +// ---------------------------------------------------------------------------------------------------------------------{ + +// 支撑位保持警报 +alertcondition(sup_holds, title = '支撑位保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"支撑位保持","位置":"支撑位","信号":"support_holds","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 阻力位保持警报 +alertcondition(res_holds, title = '阻力位保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"阻力位保持","位置":"阻力位","信号":"resistance_holds","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 支撑位突破警报 +alertcondition(brekout_sup, title = '支撑位突破', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"支撑位突破","位置":"支撑位下方","信号":"support_breakout","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 阻力位突破警报 +alertcondition(brekout_res, title = '阻力位突破', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"阻力位突破","位置":"阻力位上方","信号":"resistance_breakout","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 阻力转支撑警报 +alertcondition(brekout_res and res_is_sup[1], title = '阻力转支撑保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"阻力转支撑保持","位置":"前阻力位","信号":"resistance_as_support","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 支撑转阻力警报 +alertcondition(brekout_sup and sup_is_res[1], title = '支撑转阻力保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"支撑转阻力保持","位置":"前支撑位","信号":"support_as_resistance","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// ---------------------------------------------------------------------------------------------------------------------} diff --git a/srbr1.pine b/srbr1.pine new file mode 100644 index 0000000..b8ce7c3 --- /dev/null +++ b/srbr1.pine @@ -0,0 +1,316 @@ +// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ +// © ChartPrime + +//@version=6 +indicator('Support and Resistance (High Volume Boxes) [ChartPrime]2', shorttitle = 'SR Breaks and Retests [ChartPrime]2', overlay = true, max_boxes_count = 50) + + +// ---------------------------------------------------------------------------------------------------------------------} +// 𝙐𝙎𝙀𝙍 𝙄𝙉𝙋𝙐𝙏𝙎 +// ---------------------------------------------------------------------------------------------------------------------{ +int lookbackPeriod = input.int(20, 'Lookback Period', minval = 1, group = 'Settings') +int vol_len = input.int(2, 'Delta Volume Filter Length', tooltip = 'Higher input, will filter low volume boxes', group = 'Settings') +float box_withd = input.float(1, 'Adjust Box Width', maxval = 1000, minval = 0, step = 0.1) + + +// ---------------------------------------------------------------------------------------------------------------------} +// 𝙄𝙉𝘿𝙄𝘾𝘼𝙏𝙊𝙍 𝘾𝘼𝙇𝘾𝙐𝙇𝘼𝙏𝙄𝙊𝙉𝙎 +// ---------------------------------------------------------------------------------------------------------------------{ +// Delta Volume Function +upAndDownVolume() => + posVol = 0.0 + negVol = 0.0 + + var isBuyVolume = true + + switch + close > open => + isBuyVolume := true + isBuyVolume + close < open => + isBuyVolume := false + isBuyVolume + + if isBuyVolume + posVol := posVol + volume + posVol + else + negVol := negVol - volume + negVol + + posVol + negVol + + +// Function to identify support and resistance boxes +calcSupportResistance(src, lookbackPeriod) => + // Volume + Vol = upAndDownVolume() + vol_hi = ta.highest(Vol / 2.5, vol_len) + vol_lo = ta.lowest(Vol / 2.5, vol_len) + + var float supportLevel = na + var float supportLevel_1 = na + var float resistanceLevel = na + var float resistanceLevel_1 = na + var box sup = na + var box res = na + var color res_color = na + var color sup_color = na + var float multi = na + + var bool brekout_res = false + var bool brekout_sup = false + var bool res_holds = false + var bool sup_holds = false + + // Find pivot points + pivotHigh = ta.pivothigh(src, lookbackPeriod, lookbackPeriod) + pivotLow = ta.pivotlow(src, lookbackPeriod, lookbackPeriod) + // Box width + atr = ta.atr(200) + withd = atr * box_withd + + // Volume range for color gradient + vol_highest = ta.highest(Vol, 25) + vol_lowest = ta.lowest(Vol, 25) + + // Find support levels with Positive Volume + if not na(pivotLow) and Vol > vol_hi + + supportLevel := pivotLow + supportLevel_1 := supportLevel - withd + + topLeft = chart.point.from_index(bar_index - lookbackPeriod, supportLevel) + bottomRight = chart.point.from_index(bar_index, supportLevel_1) + + sup_color := color.from_gradient(Vol, 0, vol_highest, color(na), color.new(color.green, 30)) + + sup := box.new(top_left = topLeft, bottom_right = bottomRight, border_color = color.green, border_width = 1, bgcolor = sup_color, text = 'Vol: ' + str.tostring(math.round(Vol, 2)), text_color = chart.fg_color, text_size = size.small) + sup + + + // Find resistance levels with Negative Volume + if not na(pivotHigh) and Vol < vol_lo + + resistanceLevel := pivotHigh + resistanceLevel_1 := resistanceLevel + withd + + topLeft = chart.point.from_index(bar_index - lookbackPeriod, resistanceLevel) + bottomRight = chart.point.from_index(bar_index, resistanceLevel_1) + + res_color := color.from_gradient(Vol, vol_lowest, 0, color.new(color.red, 30), color(na)) + + res := box.new(top_left = topLeft, bottom_right = bottomRight, border_color = color.red, border_width = 1, bgcolor = res_color, text = 'Vol: ' + str.tostring(math.round(Vol, 2)), text_color = chart.fg_color, text_size = size.small) + res + + // Adaptive Box Len + sup.set_right(bar_index + 1) + res.set_right(bar_index + 1) + + // Break of support or resistance conditions + brekout_res := ta.crossover(low, resistanceLevel_1) + res_holds := ta.crossunder(high, resistanceLevel) + + sup_holds := ta.crossover(low, supportLevel) + brekout_sup := ta.crossunder(high, supportLevel_1) + + // Change Color of Support to red if it was break, change color of resistance to green if it was break + if brekout_sup + sup.set_bgcolor(color.new(color.red, 80)) + sup.set_border_color(color.red) + sup.set_border_style(line.style_dashed) + + if sup_holds + sup.set_bgcolor(sup_color) + sup.set_border_color(color.green) + sup.set_border_style(line.style_solid) + + if brekout_res + res.set_bgcolor(color.new(color.green, 80)) + res.set_border_color(color.new(color.green, 0)) + res.set_border_style(line.style_dashed) + + if res_holds + res.set_bgcolor(res_color) + res.set_border_color(color.new(color.red, 0)) + res.set_border_style(line.style_solid) + + [supportLevel, resistanceLevel, brekout_res, res_holds, sup_holds, brekout_sup] + + +// Calculate support and resistance levels and their breakouts +[supportLevel, resistanceLevel, brekout_res, res_holds, sup_holds, brekout_sup] = calcSupportResistance(close, lookbackPeriod) + + +// Check if Resistance become Support or Support Become Resistance +var bool res_is_sup = false +var bool sup_is_res = false + +switch + brekout_res => + res_is_sup := true + res_is_sup + res_holds => + res_is_sup := false + res_is_sup + +switch + brekout_sup => + sup_is_res := true + sup_is_res + sup_holds => + sup_is_res := false + sup_is_res + + +// ---------------------------------------------------------------------------------------------------------------------} +// 𝙑𝙄𝙎𝙐𝘼𝙇𝙄𝙕𝘼𝙏𝙄𝙊𝙉 +// ---------------------------------------------------------------------------------------------------------------------{ +// Plot Res and Sup breakouts and holds +plotchar(res_holds, 'Resistance Holds', '◆', color = #e92929, size = size.tiny, location = location.abovebar, offset = -1) +plotchar(sup_holds, 'Support Holds', '◆', color = #20ca26, size = size.tiny, location = location.belowbar, offset = -1) + +plotchar(brekout_res and res_is_sup[1], 'Resistance as Support Holds', '◆', color = #20ca26, size = size.tiny, location = location.belowbar, offset = -1) +plotchar(brekout_sup and sup_is_res[1], 'Support as Resistance Holds', '◆', color = #e92929, size = size.tiny, location = location.abovebar, offset = -1) + +// Break Out Labels +if brekout_sup and not sup_is_res[1] + label.new(bar_index[1], supportLevel[1], text = 'Break Sup', style = label.style_label_down, color = #7e1e1e, textcolor = chart.fg_color, size = size.small) + +if brekout_res and not res_is_sup[1] + label.new(bar_index[1], resistanceLevel[1], text = 'Break Res', style = label.style_label_up, color = #2b6d2d, textcolor = chart.fg_color, size = size.small) + +// 警报标记 - 醒目的视觉提示 +// 支撑位保持警报标记 +if sup_holds + label.new(bar_index, low, text = '🔔SUP', style = label.style_label_down, color = color.new(color.lime, 0), textcolor = color.black, size = size.large, tooltip = '支撑位保持警报') + +// 阻力位保持警报标记 +if res_holds + label.new(bar_index, high, text = '🔔RES', style = label.style_label_up, color = color.new(color.red, 0), textcolor = color.white, size = size.large, tooltip = '阻力位保持警报') + +// 支撑位突破警报标记 +if brekout_sup + label.new(bar_index[1], supportLevel[1], text = '🚨SUP⬇', style = label.style_label_down, color = color.new(color.orange, 0), textcolor = color.black, size = size.large, tooltip = '支撑位突破警报') + +// 阻力位突破警报标记 +if brekout_res + label.new(bar_index[1], resistanceLevel[1], text = '🚨RES⬆', style = label.style_label_up, color = color.new(color.aqua, 0), textcolor = color.black, size = size.large, tooltip = '阻力位突破警报') + +// 阻力转支撑警报标记 +if brekout_res and res_is_sup[1] + label.new(bar_index[1], resistanceLevel[1], text = '⭐R→S', style = label.style_label_down, color = color.new(color.purple, 0), textcolor = color.white, size = size.large, tooltip = '阻力转支撑保持警报') + +// 支撑转阻力警报标记 +if brekout_sup and sup_is_res[1] + label.new(bar_index[1], supportLevel[1], text = '⭐S→R', style = label.style_label_up, color = color.new(color.maroon, 0), textcolor = color.white, size = size.large, tooltip = '支撑转阻力保持警报') + +// 全局作用域的plotshape - 警报形状标记 +plotshape(sup_holds, title = '支撑保持警报', style = shape.triangleup, location = location.belowbar, color = color.new(color.lime, 0), size = size.large) +plotshape(res_holds, title = '阻力保持警报', style = shape.triangledown, location = location.abovebar, color = color.new(color.red, 0), size = size.large) +plotshape(brekout_sup, title = '支撑突破警报', style = shape.xcross, location = location.belowbar, color = color.new(color.orange, 0), size = size.large) +plotshape(brekout_res, title = '阻力突破警报', style = shape.xcross, location = location.abovebar, color = color.new(color.aqua, 0), size = size.large) +plotshape(brekout_res and res_is_sup[1], title = '阻力转支撑警报', style = shape.diamond, location = location.belowbar, color = color.new(color.purple, 0), size = size.large) +plotshape(brekout_sup and sup_is_res[1], title = '支撑转阻力警报', style = shape.diamond, location = location.abovebar, color = color.new(color.maroon, 0), size = size.large) + + +// ◆ +// ---------------------------------------------------------------------------------------------------------------------} +// 𝙋𝙍𝙄𝘾𝙀 𝙎𝙏𝘼𝙏𝙐𝙎 & 𝙈𝘼𝙍𝙆𝙀𝙏 𝙊𝙐𝙏𝙇𝙊𝙊𝙆 +// ---------------------------------------------------------------------------------------------------------------------{ + +// ═════════ 当前图表时间周期价格状态分析 ════════ +get_current_price_status() => + current_price = close + support_level = supportLevel + resistance_level = resistanceLevel + + // 计算价格相对位置 + if na(support_level) and na(resistance_level) + "无明确支撑阻力" + else if na(support_level) + if current_price > resistance_level + "突破阻力位上方" + else if current_price >= resistance_level * 0.995 + "接近阻力位" + else + "阻力位下方" + else if na(resistance_level) + if current_price < support_level + "跌破支撑位下方" + else if current_price <= support_level * 1.005 + "接近支撑位" + else + "支撑位上方" + else + range_size = resistance_level - support_level + price_position = (current_price - support_level) / range_size + + if current_price > resistance_level + "突破阻力位上方" + else if current_price < support_level + "跌破支撑位下方" + else if price_position >= 0.8 + "接近阻力位" + else if price_position <= 0.2 + "接近支撑位" + else + "区间中部运行" + +// ═════════ 市场观点生成 ════════ +get_market_outlook() => + current_status = get_current_price_status() + + // 基于价格状态和突破情况生成观点 + if brekout_res and res_is_sup[1] + "看涨:阻力转支撑确认,建议关注回调买入机会" + else if brekout_sup and sup_is_res[1] + "看跌:支撑转阻力确认,建议关注反弹卖出机会" + else if brekout_res + "看涨:突破阻力位,建议关注回踩确认后的追涨机会" + else if brekout_sup + "看跌:跌破支撑位,建议关注反弹确认后的追跌机会" + else if res_holds + "中性偏空:阻力位有效,短期上涨受阻" + else if sup_holds + "中性偏多:支撑位有效,短期下跌受限" + else if current_status == "接近阻力位" + "谨慎:接近关键阻力位,注意突破或回落" + else if current_status == "接近支撑位" + "谨慎:接近关键支撑位,注意反弹或破位" + else if current_status == "区间中部运行" + "中性:价格在支撑阻力区间内震荡" + else if current_status == "突破阻力位上方" + "看涨:已突破阻力位,关注持续性" + else if current_status == "跌破支撑位下方" + "看跌:已跌破支撑位,关注反弹力度" + else + "观望:等待明确的支撑阻力信号" + +// 获取当前价格状态和市场观点 +current_price_status = get_current_price_status() +market_outlook = get_market_outlook() + +// ---------------------------------------------------------------------------------------------------------------------} +// 𝘼𝙇𝙀𝙍𝙏 𝘾𝙊𝙉𝘿𝙄𝙏𝙄𝙊𝙉𝙎 +// ---------------------------------------------------------------------------------------------------------------------{ + +// 支撑位保持警报 +alertcondition(sup_holds, title = '支撑位保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"支撑位保持","位置":"支撑位","信号":"support_holds","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 阻力位保持警报 +alertcondition(res_holds, title = '阻力位保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"阻力位保持","位置":"阻力位","信号":"resistance_holds","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 支撑位突破警报 +alertcondition(brekout_sup, title = '支撑位突破', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"支撑位突破","位置":"支撑位下方","信号":"support_breakout","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 阻力位突破警报 +alertcondition(brekout_res, title = '阻力位突破', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"阻力位突破","位置":"阻力位上方","信号":"resistance_breakout","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 阻力转支撑警报 +alertcondition(brekout_res and res_is_sup[1], title = '阻力转支撑保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"阻力转支撑保持","位置":"前阻力位","信号":"resistance_as_support","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// 支撑转阻力警报 +alertcondition(brekout_sup and sup_is_res[1], title = '支撑转阻力保持', message = '{"指标名称":"SRBR","交易对":"{{ticker}}","周期":"{{interval}}","支撑位":"{{plot("supportLevel")}}","阻力位":"{{plot("resistanceLevel")}}","开盘价":"{{open}}","收盘价":"{{close}}","最高价":"{{high}}","最低价":"{{low}}","触发时间":"{{timenow}}","时间":"{{time}}","事件":"支撑转阻力保持","位置":"前支撑位","信号":"support_as_resistance","价格状态":"' + current_price_status + '","市场观点":"' + market_outlook + '"}') + +// ---------------------------------------------------------------------------------------------------------------------} diff --git a/tmfs.pine b/tmfs.pine new file mode 100644 index 0000000..9508dab --- /dev/null +++ b/tmfs.pine @@ -0,0 +1,503 @@ +// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) +// https://creativecommons.org/licenses/by-nc-sa/4.0/ +// Original concepts by FibonacciFlux, rewritten for clarity and functionality + +//@version=6 +indicator('Trendline Breaks 带警报', shorttitle = "TMFS Indicator alerts", overlay = true) + +//---------------------------------------------------------- +// SECTION 1: FIBONACCI SUPERTREND CONFIGURATION +//---------------------------------------------------------- + +// Common ATR period for all supertrend calculations +atrPeriod = input.int(13, 'ATR Length', minval = 1, tooltip="ATR period used for volatility calculation") + +// Fibonacci-based supertrend factor inputs +factor1 = input.float(0.618, 'Factor 1 (Weak/Fibonacci)', minval = 0.01, step = 0.01, tooltip="First Fibonacci factor (0.618)") +factor2 = input.float(1.618, 'Factor 2 (Medium/Golden Ratio)', minval = 0.01, step = 0.01, tooltip="Second Fibonacci factor (1.618)") +factor3 = input.float(2.618, 'Factor 3 (Strong/Extended Fib)', minval = 0.01, step = 0.01, tooltip="Third Fibonacci factor (2.618)") + +// Calculate the three Supertrend lines with different sensitivity levels +[supertrend1, direction1] = ta.supertrend(factor1, atrPeriod) +[supertrend2, direction2] = ta.supertrend(factor2, atrPeriod) +[supertrend3, direction3] = ta.supertrend(factor3, atrPeriod) + +// Handle initial values (first bar) +supertrend1 := barstate.isfirst ? na : supertrend1 +supertrend2 := barstate.isfirst ? na : supertrend2 +supertrend3 := barstate.isfirst ? na : supertrend3 + +// Calculate average of the three supertends and a smoothed version +superlength = input.int(21, "Smoothing Length", tooltip="EMA length for smoothing the average supertrend") +average_trend = (supertrend1 + supertrend2 + supertrend3) / 3 +smoothed_trend = ta.ema(average_trend, superlength) + +// Plot the supertrend lines and fill the area between them +trend_plot = plot(average_trend, color = average_trend > average_trend[1] ? color.green : color.red, title="Average Supertrend") +smoothed_plot = plot(smoothed_trend, linewidth = 2, color = smoothed_trend > smoothed_trend[1] ? color.green : color.red, title="Smoothed Supertrend") +fill(trend_plot, smoothed_plot, color = average_trend > smoothed_trend ? color.new(color.green, 50) : color.new(color.red, 50), title="Trend Direction Fill") + +//---------------------------------------------------------- +// SECTION 2: TRENDLINE DETECTION AND DRAWING +//---------------------------------------------------------- + +// User settings for Swing Trend Lines +swing_length = input.int(8, 'Swing Lookback Period', tooltip="Number of bars to look back for pivot points") +atr_multiplier = input.float(1.0, 'ATR Slope Multiplier', minval = 0.0, step = 0.1, tooltip="Multiplier for ATR to control trendline slope") +show_extended_lines = input.bool(true, 'Show Extended Lines', tooltip="Display dashed extensions for trendlines") +enable_backpainting = input.bool(true, 'Use Backpainting', tooltip="Enable backpainting to move indicators to pivot point") + +// Color settings for trendlines +uptrend_color = color.new(color.teal, 0) +downtrend_color = color.new(color.red, 0) + +// Variable initialization for trendlines +var float upper_trendline = na +var float lower_trendline = na +var float upper_slope = na +var float lower_slope = na +var line upline_extension = na +var line downline_extension = na + +// Offset for backpainting +offset = enable_backpainting ? swing_length : 0 +bar_idx = bar_index + +// Get pivot high and low points +pivot_high = ta.pivothigh(swing_length, swing_length) +pivot_low = ta.pivotlow(swing_length, swing_length) + +// Calculate ATR for slope +atr_value = ta.atr(swing_length) +slope = atr_value / swing_length * atr_multiplier + +// Update upper trendline when pivot high is detected or extend existing trendline +if bool(pivot_high) + upper_slope := slope + upper_trendline := pivot_high +else + upper_trendline := nz(upper_trendline) - nz(upper_slope) + +// Update lower trendline when pivot low is detected or extend existing trendline +if bool(pivot_low) + lower_slope := slope + lower_trendline := pivot_low +else + lower_trendline := nz(lower_trendline) + nz(lower_slope) + +// Create and update extended visualization lines if enabled +if show_extended_lines + // Initialize line objects if not already created + if na(upline_extension) + upline_extension := line.new(na, na, na, na, extend = extend.right, style = line.style_dashed, color = uptrend_color) + + if na(downline_extension) + downline_extension := line.new(na, na, na, na, extend = extend.right, style = line.style_dashed, color = downtrend_color) + + // Update upper trendline extension when a new pivot high is confirmed + if bool(pivot_high) + line.set_xy1(upline_extension, bar_idx - offset, enable_backpainting ? pivot_high : upper_trendline) + line.set_xy2(upline_extension, bar_idx - offset + 1, enable_backpainting ? pivot_high - slope : upper_trendline - slope) + + // Update lower trendline extension when a new pivot low is confirmed + if bool(pivot_low) + line.set_xy1(downline_extension, bar_idx - offset, enable_backpainting ? pivot_low : lower_trendline) + line.set_xy2(downline_extension, bar_idx - offset + 1, enable_backpainting ? pivot_low + slope : lower_trendline + slope) + +//---------------------------------------------------------- +// SECTION 3: BREAKOUT DETECTION +//---------------------------------------------------------- + +// Track breakouts above upper trendline and below lower trendline +// Initialize tracking variables for breakout conditions +var int upper_breakout_state = 0 // State variable to track upper breakout condition +var int lower_breakout_state = 0 // State variable to track lower breakout condition + +// Detect breakouts +bool upper_breakout = close > upper_trendline +bool lower_breakout = close < lower_trendline + +// Update state variables to track when breakouts occur +upper_breakout_state := bool(pivot_high) ? 0 : upper_breakout ? 1 : upper_breakout_state +lower_breakout_state := bool(pivot_low) ? 0 : lower_breakout ? 1 : lower_breakout_state + +// Plot trendlines with appropriate offsets +plot(enable_backpainting ? upper_trendline : upper_trendline, color = bool(pivot_high) ? na : uptrend_color, linewidth = 2, offset = -offset, title = 'Upper Trendline') +plot(enable_backpainting ? lower_trendline : lower_trendline, color = bool(pivot_low) ? na : downtrend_color, linewidth = 2, offset = -offset, title = 'Lower Trendline') + +//---------------------------------------------------------- +// SECTION 4: DIRECTIONAL MOVEMENT INDEX (DMI) CALCULATION +//---------------------------------------------------------- + +// DMI Length parameter +dmi_length = input.int(13, "DMI Length", tooltip="Length for DMI calculation") + +// True Range calculation +true_range = math.max(math.max(high - low, math.abs(high - nz(close[1]))), math.abs(low - nz(close[1]))) + +// Directional Movement calculations +directional_movement_plus = high - nz(high[1]) > nz(low[1]) - low ? math.max(high - nz(high[1]), 0) : 0 +directional_movement_minus = nz(low[1]) - low > high - nz(high[1]) ? math.max(nz(low[1]) - low, 0) : 0 + +// Smoothed calculations for DMI +var float smoothed_true_range = 0.0 +smoothed_true_range := nz(smoothed_true_range[1]) - nz(smoothed_true_range[1]) / dmi_length + true_range + +var float smoothed_directional_plus = 0.0 +smoothed_directional_plus := nz(smoothed_directional_plus[1]) - nz(smoothed_directional_plus[1]) / dmi_length + directional_movement_plus + +var float smoothed_directional_minus = 0.0 +smoothed_directional_minus := nz(smoothed_directional_minus[1]) - nz(smoothed_directional_minus[1]) / dmi_length + directional_movement_minus + +// Calculate DI+ and DI- values +di_plus = smoothed_directional_plus / smoothed_true_range * 100 +di_minus = smoothed_directional_minus / smoothed_true_range * 100 + +//---------------------------------------------------------- +// SECTION 5: SIGNAL DEFINITION +//---------------------------------------------------------- + +// SMA settings for trend confirmation +fast_sma_length = input.int(defval = 2, title = 'Fast SMA Length', minval = 1, group = 'Signal Settings') +slow_sma_length = input.int(defval = 3, title = 'Slow SMA Length', minval = 1, group = 'Signal Settings') + +// Calculate SMA crossovers +fast_sma = ta.sma(close, fast_sma_length) +slow_sma = ta.sma(close, slow_sma_length) + +// Detect SMA crossovers +sma_cross_up = ta.crossover(fast_sma, slow_sma) +sma_cross_down = ta.crossunder(fast_sma, slow_sma) + +//---------------------------------------------------------- +// SECTION 6: ENTRY AND EXIT CONDITIONS +//---------------------------------------------------------- + +// Define entry conditions +long_entry_condition = upper_breakout_state > upper_breakout_state[1] and di_plus > di_minus and close > smoothed_trend +short_entry_condition = lower_breakout_state > lower_breakout_state[1] and di_minus > di_plus and close < smoothed_trend + +// Define exit conditions +long_exit_condition = ta.crossunder(close[1], smoothed_trend) +short_exit_condition = ta.crossover(close[1], smoothed_trend) + +// @function Display BUY/SELL labels on chart +plotshape(long_entry_condition, title='BUY', text='BUY', location=location.belowbar, + style=shape.labelup, size=size.tiny, color=color.new(color.green, 0), textcolor=color.new(color.white, 0)) +plotshape(short_entry_condition, title='SELL', text='SELL', location=location.abovebar, + style=shape.labeldown, size=size.tiny, color=color.new(color.red, 0), textcolor=color.new(color.white, 0)) + +//---------------------------------------------------------- +// SECTION 7: STOP LOSS AND TAKE PROFIT SETTINGS +//---------------------------------------------------------- + +// Common settings +atr_length = input.int(13, 'ATR Length for SL/TP', minval = 1, group = 'Risk Management') + +// Variable to track if stop loss is triggered +var bool long_trailing_tp_executed = false +var bool short_trailing_tp_executed = false + +// Store ATR at position entry +float entry_atr = ta.valuewhen(long_entry_condition or short_entry_condition, ta.atr(atr_length), 0) + +// Stop Loss settings +stop_loss_method = input.string(defval = 'ATR', title = 'Stop Loss Method', options = ['PERC', 'ATR'], group = 'Stop Loss') +long_stop_loss_percent = input.float(defval = 0.75, title = 'Long Position Stop Loss %', minval = 0.05, maxval = 100, step = 0.05, group = 'Stop Loss') / 100 +short_stop_loss_percent = input.float(defval = 0.75, title = 'Short Position Stop Loss %', minval = 0.05, maxval = 100, step = 0.05, group = 'Stop Loss') / 100 +long_stop_loss_atr_multiplier = input.float(defval = 8.0, title = 'Long Position ATR Multiplier', minval = 0.1, step = 0.1, group = 'Stop Loss') +short_stop_loss_atr_multiplier = input.float(defval = 8.0, title = 'Short Position ATR Multiplier', minval = 0.1, step = 0.1, group = 'Stop Loss') +stop_loss_trailing_mode = input.string(defval = 'ON', title = 'Trailing Stop Loss Mode', options = ['TP', 'ON', 'OFF'], tooltip = 'ON: Always trail, TP: Trail after Take Profit hit, OFF: Fixed stop', group = 'Stop Loss') +break_even_enabled = input.bool(defval = true, title = 'Move to Break Even After TP', group = 'Stop Loss') + +// Take Profit settings +take_profit_quantity_percent = input.float(defval = 100, title = 'Take Profit Quantity %', minval = 0.0, maxval = 100, step = 1.0, group = 'Take Profit') +take_profit_method = input.string(defval = 'PERC', title = 'Take Profit Method', options = ['PERC', 'ATR', 'RR'], group = 'Take Profit') +long_take_profit_percent = input.float(defval = 1, title = 'Long Position Take Profit %', minval = 0.05, step = 0.05, group = 'Take Profit') / 100 +short_take_profit_percent = input.float(defval = 1, title = 'Short Position Take Profit %', minval = 0.05, step = 0.05, group = 'Take Profit') / 100 +long_take_profit_atr_multiplier = input.float(defval = 1.5, title = 'Long Position TP ATR Multiplier', minval = 0.1, step = 0.1, group = 'Take Profit') +short_take_profit_atr_multiplier = input.float(defval = 2, title = 'Short Position TP ATR Multiplier', minval = 0.1, step = 0.1, group = 'Take Profit') +long_risk_reward_ratio = input.float(defval = 0.6, title = 'Long Position Risk/Reward Ratio', minval = 0.1, step = 0.1, group = 'Take Profit') +short_risk_reward_ratio = input.float(defval = 1, title = 'Short Position Risk/Reward Ratio', minval = 0.1, step = 0.1, group = 'Take Profit') +trailing_take_profit_enabled = input.bool(defval = true, title = 'Enable Trailing Take Profit', group = 'Take Profit') +deviation_method = input.string(defval = 'PERC', title = 'Trailing Deviation Method', options = ['PERC', 'ATR'], group = 'Take Profit') +deviation_percent = input.float(defval = 0.01, title = 'Trailing Deviation %', minval = 0.01, maxval = 100, step = 0.05, group = 'Take Profit') / 100 +deviation_atr_multiplier = input.float(defval = 1.0, title = 'Trailing Deviation ATR Multiplier', minval = 0.01, step = 0.05, group = 'Take Profit') + +//---------------------------------------------------------- +// SECTION 9: CALCULATE STOP LOSS LEVELS +//---------------------------------------------------------- + +// Calculate long position stop loss price based on selected method +get_long_stop_loss_price(base_price) => + switch stop_loss_method + 'PERC' => base_price * (1 - long_stop_loss_percent) + 'ATR' => base_price - long_stop_loss_atr_multiplier * entry_atr + => na + +// Determine if trailing should be active for long position +long_tp_trailing_enabled = stop_loss_trailing_mode == 'ON' or (stop_loss_trailing_mode == 'TP' and long_trailing_tp_executed) + +// Calculate and maintain stop loss price for long positions +var float long_stop_loss_price = na +long_stop_loss_price := if strategy.position_size > 0 + if long_entry_condition + get_long_stop_loss_price(close) + else + stop_price = get_long_stop_loss_price(long_tp_trailing_enabled ? high : strategy.position_avg_price) + stop_price := break_even_enabled and long_trailing_tp_executed ? math.max(stop_price, strategy.position_avg_price) : stop_price + math.max(stop_price, nz(long_stop_loss_price[1])) +else + na + +// Calculate short position stop loss price based on selected method +get_short_stop_loss_price(base_price) => + switch stop_loss_method + 'PERC' => base_price * (1 + short_stop_loss_percent) + 'ATR' => base_price + short_stop_loss_atr_multiplier * entry_atr + => na + +// Determine if trailing should be active for short position +short_tp_trailing_enabled = stop_loss_trailing_mode == 'ON' or (stop_loss_trailing_mode == 'TP' and short_trailing_tp_executed) + +// Calculate and maintain stop loss price for short positions +var float short_stop_loss_price = na +short_stop_loss_price := if strategy.position_size < 0 + if short_entry_condition + get_short_stop_loss_price(close) + else + var float stop_price = get_short_stop_loss_price(short_tp_trailing_enabled ? low : strategy.position_avg_price) // stop_price を var float で宣言 + if break_even_enabled and short_trailing_tp_executed + stop_price := math.min(stop_price, strategy.position_avg_price) + short_stop_loss_price := math.min(stop_price, nz(short_stop_loss_price[1], 999999.9)) +else + na + +//---------------------------------------------------------- +// SECTION 9: CALCULATE TAKE PROFIT LEVELS +//---------------------------------------------------------- + +// Calculate long position take profit price based on selected method +get_long_take_profit_price() => + switch take_profit_method + 'PERC' => close * (1 + long_take_profit_percent) + 'ATR' => close + long_take_profit_atr_multiplier * entry_atr + 'RR' => close + long_risk_reward_ratio * (close - get_long_stop_loss_price(close)) + => na + +// Calculate and maintain take profit price for long positions +var float long_take_profit_price = na +long_take_profit_price := if strategy.position_size > 0 and not long_trailing_tp_executed + if long_entry_condition + get_long_take_profit_price() + else + nz(long_take_profit_price[1], get_long_take_profit_price()) +else + na + +// Update trailing take profit execution flag for long positions +long_trailing_tp_executed := strategy.position_size > 0 and (long_trailing_tp_executed[1] or strategy.position_size < strategy.position_size[1] or (strategy.position_size[1] == 0 and high >= long_take_profit_price)) + +// Calculate trailing step size in ticks for long positions +long_trailing_tp_step_ticks = switch deviation_method + 'PERC' => long_take_profit_price * deviation_percent / syminfo.mintick + 'ATR' => deviation_atr_multiplier * entry_atr / syminfo.mintick + => na + +// Calculate short position take profit price based on selected method +get_short_take_profit_price() => + switch take_profit_method + 'PERC' => close * (1 - short_take_profit_percent) + 'ATR' => close - short_take_profit_atr_multiplier * entry_atr + 'RR' => close - short_risk_reward_ratio * (get_short_stop_loss_price(close) - close) + => na + +// Calculate and maintain take profit price for short positions +var float short_take_profit_price = na +short_take_profit_price := if strategy.position_size < 0 and not short_trailing_tp_executed + if short_entry_condition + get_short_take_profit_price() + else + nz(short_take_profit_price[1], get_short_take_profit_price()) +else + na + +// Update trailing take profit execution flag for short positions +short_trailing_tp_executed := strategy.position_size < 0 and (short_trailing_tp_executed[1] or strategy.position_size > strategy.position_size[1] or (strategy.position_size[1] == 0 and low <= short_take_profit_price)) + +// Calculate trailing step size in ticks for short positions +short_trailing_tp_step_ticks = switch deviation_method + 'PERC' => short_take_profit_price * deviation_percent / syminfo.mintick + 'ATR' => deviation_atr_multiplier * entry_atr / syminfo.mintick + => na + +//---------------------------------------------------------- +// SECTION 11: STRATEGY EXECUTION +//---------------------------------------------------------- + +// Enter long position when conditions are met +if long_entry_condition + strategy.entry('L', strategy.long, comment = "LONG") + +// Enter short position when conditions are met +if short_entry_condition + strategy.entry('S', strategy.short, comment = "SHORT") + +// Exit long position when exit conditions are met +if long_exit_condition + strategy.close('L', comment = "SL Triggered") + +// Exit short position when exit conditions are met +if short_exit_condition + strategy.close('S', comment = "SL Triggered") + +// Set up take profit and stop loss for long positions +strategy.exit(id = 'Long Take Profit / Stop Loss', from_entry = 'L', qty_percent = take_profit_quantity_percent, limit = trailing_take_profit_enabled ? na : long_take_profit_price, stop = long_stop_loss_price, trail_price = trailing_take_profit_enabled ? long_take_profit_price : na, trail_offset = trailing_take_profit_enabled ? long_trailing_tp_step_ticks : na, comment = "TP/SL Triggered") +strategy.exit(id = 'Long Stop Loss', from_entry = 'L', stop = long_stop_loss_price, comment = "SL Triggered") + +// Set up take profit and stop loss for short positions +strategy.exit(id = 'Short Take Profit / Stop Loss', from_entry = 'S', qty_percent = take_profit_quantity_percent, limit = trailing_take_profit_enabled ? na : short_take_profit_price, stop = short_stop_loss_price, trail_price = trailing_take_profit_enabled ? short_take_profit_price : na, trail_offset = trailing_take_profit_enabled ? short_trailing_tp_step_ticks : na, comment = "TP/SL Triggered") +strategy.exit(id = 'Short Stop Loss', from_entry = 'S', stop = short_stop_loss_price, comment = "SL Triggered") + +// ===== 统一警报系统 ===== +// 统一警报系统 - 只需添加一次警报即可捕获所有信号 + +// 警报频率限制 - 每分钟只触发一次 +var int last_alert_long_entry = 0 +var int last_alert_short_entry = 0 +var int last_alert_long_exit = 0 +var int last_alert_short_exit = 0 +var int last_alert_upper_breakout = 0 +var int last_alert_lower_breakout = 0 +var int last_alert_sma_cross_up = 0 +var int last_alert_sma_cross_down = 0 +var int last_alert_dmi_bullish = 0 +var int last_alert_dmi_bearish = 0 +var int last_alert_trend_bullish = 0 +var int last_alert_trend_bearish = 0 + +// 获取当前时间(分钟级别) +current_minute = math.floor(timenow / 60000) + +// 检测所有警报条件并生成对应的JSON消息 +alert_message = "" + +// 入场信号警报 - 每分钟限制 +if long_entry_condition and current_minute > last_alert_long_entry + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","事件":"多头入场信号触发","信号":"buy","备注":"趋势线突破+DMI确认"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_long_entry := current_minute + +if short_entry_condition and current_minute > last_alert_short_entry + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","事件":"空头入场信号触发","信号":"sell","备注":"趋势线突破+DMI确认"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_short_entry := current_minute + +// 出场信号警报 - 每分钟限制 +if long_exit_condition and current_minute > last_alert_long_exit + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","事件":"多头出场信号触发","信号":"exit_long","备注":"价格跌破平滑超趋势"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_long_exit := current_minute + +if short_exit_condition and current_minute > last_alert_short_exit + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","事件":"空头出场信号触发","信号":"exit_short","备注":"价格突破平滑超趋势"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_short_exit := current_minute + +// 趋势线突破警报 - 每分钟限制 +if upper_breakout and not upper_breakout[1] and current_minute > last_alert_upper_breakout + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","上趋势线":"' + str.tostring(upper_trendline, '#.####') + '","事件":"价格突破上趋势线","信号":"upper_breakout","备注":"潜在看涨信号"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_upper_breakout := current_minute + +if lower_breakout and not lower_breakout[1] and current_minute > last_alert_lower_breakout + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","下趋势线":"' + str.tostring(lower_trendline, '#.####') + '","事件":"价格跌破下趋势线","信号":"lower_breakout","备注":"潜在看跌信号"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_lower_breakout := current_minute + +// SMA交叉警报 - 每分钟限制 +if sma_cross_up and current_minute > last_alert_sma_cross_up + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","快速SMA":"' + str.tostring(fast_sma, '#.####') + '","慢速SMA":"' + str.tostring(slow_sma, '#.####') + '","事件":"快速SMA上穿慢速SMA","信号":"sma_cross_up","备注":"短期趋势转多"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_sma_cross_up := current_minute + +if sma_cross_down and current_minute > last_alert_sma_cross_down + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","快速SMA":"' + str.tostring(fast_sma, '#.####') + '","慢速SMA":"' + str.tostring(slow_sma, '#.####') + '","事件":"快速SMA下穿慢速SMA","信号":"sma_cross_down","备注":"短期趋势转空"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_sma_cross_down := current_minute + +// DMI信号警报 - 每分钟限制 +if ta.crossover(di_plus, di_minus) and current_minute > last_alert_dmi_bullish + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","事件":"DI+上穿DI-","信号":"dmi_bullish","备注":"动量转向看涨"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_dmi_bullish := current_minute + +if ta.crossunder(di_plus, di_minus) and current_minute > last_alert_dmi_bearish + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","事件":"DI+下穿DI-","信号":"dmi_bearish","备注":"动量转向看跌"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_dmi_bearish := current_minute + +// 超趋势方向变化警报 - 每分钟限制 +if ta.crossover(close, smoothed_trend) and current_minute > last_alert_trend_bullish + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","事件":"价格上穿平滑超趋势","信号":"trend_bullish","备注":"趋势转向看涨"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_trend_bullish := current_minute + +if ta.crossunder(close, smoothed_trend) and current_minute > last_alert_trend_bearish + alert_message := '{"指标名称":"TMFS","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","事件":"价格下穿平滑超趋势","信号":"trend_bearish","备注":"趋势转向看跌"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_trend_bearish := current_minute + +// ===== 传统警报条件(保留兼容性)===== +// 注意:使用统一警报系统时,建议只使用上面的alert()函数 +// 以下alertcondition保留用于需要单独设置警报的情况 + +// 创建用于警报的plot变量 +plot(smoothed_trend, title = '平滑超趋势', display = display.none) +plot(di_plus, title = 'DI+', display = display.none) +plot(di_minus, title = 'DI-', display = display.none) +plot(fast_sma, title = '快速SMA', display = display.none) +plot(slow_sma, title = '慢速SMA', display = display.none) +plot(upper_trendline, title = '上趋势线', display = display.none) +plot(lower_trendline, title = '下趋势线', display = display.none) + +// 入场信号警报 +alertcondition(long_entry_condition, title = 'TMFS多头入场', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"多头入场信号触发","信号":"buy","备注":"趋势线突破+DMI确认"}') + +alertcondition(short_entry_condition, title = 'TMFS空头入场', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"空头入场信号触发","信号":"sell","备注":"趋势线突破+DMI确认"}') + +// 出场信号警报 +alertcondition(long_exit_condition, title = 'TMFS多头出场', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"多头出场信号触发","信号":"exit_long","备注":"价格跌破平滑超趋势"}') + +alertcondition(short_exit_condition, title = 'TMFS空头出场', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"空头出场信号触发","信号":"exit_short","备注":"价格突破平滑超趋势"}') + +// 趋势线突破警报 +alertcondition(upper_breakout and not upper_breakout[1], title = '价格突破上趋势线', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","上趋势线":"{{plot("上趋势线")}}","事件":"价格突破上趋势线","信号":"upper_breakout","备注":"潜在看涨信号"}') + +alertcondition(lower_breakout and not lower_breakout[1], title = '价格跌破下趋势线', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","下趋势线":"{{plot("下趋势线")}}","事件":"价格跌破下趋势线","信号":"lower_breakout","备注":"潜在看跌信号"}') + +// SMA交叉警报 +alertcondition(sma_cross_up, title = '快速SMA上穿慢速SMA', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","快速SMA":"{{plot("快速SMA")}}","慢速SMA":"{{plot("慢速SMA")}}","事件":"快速SMA上穿慢速SMA","信号":"sma_cross_up","备注":"短期趋势转多"}') + +alertcondition(sma_cross_down, title = '快速SMA下穿慢速SMA', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","快速SMA":"{{plot("快速SMA")}}","慢速SMA":"{{plot("慢速SMA")}}","事件":"快速SMA下穿慢速SMA","信号":"sma_cross_down","备注":"短期趋势转空"}') + +// DMI信号警报 +alertcondition(ta.crossover(di_plus, di_minus), title = 'DI+上穿DI-', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"DI+上穿DI-","信号":"dmi_bullish","备注":"动量转向看涨"}') + +alertcondition(ta.crossunder(di_plus, di_minus), title = 'DI+下穿DI-', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"DI+下穿DI-","信号":"dmi_bearish","备注":"动量转向看跌"}') + +// 超趋势方向变化警报 +alertcondition(ta.crossover(close, smoothed_trend), title = '价格上穿平滑超趋势', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"价格上穿平滑超趋势","信号":"trend_bullish","备注":"趋势转向看涨"}') + +alertcondition(ta.crossunder(close, smoothed_trend), title = '价格下穿平滑超趋势', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"价格下穿平滑超趋势","信号":"trend_bearish","备注":"趋势转向看跌"}') + +// 综合信号警报 +alertcondition(upper_breakout and di_plus > di_minus, title = '强势看涨信号', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","事件":"强势看涨信号","信号":"strong_bullish","备注":"趋势线突破+DMI看涨"}') + +alertcondition(lower_breakout and di_minus > di_plus, title = '强势看跌信号', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","事件":"强势看跌信号","信号":"strong_bearish","备注":"趋势线跌破+DMI看跌"}') + +// 趋势确认警报 +alertcondition(close > smoothed_trend and di_plus > di_minus, title = '多头趋势确认', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"多头趋势确认","信号":"bullish_trend_confirmed","备注":"价格在超趋势上方且DMI看涨"}') + +alertcondition(close < smoothed_trend and di_minus > di_plus, title = '空头趋势确认', message = '{"指标名称":"TMFS","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"空头趋势确认","信号":"bearish_trend_confirmed","备注":"价格在超趋势下方且DMI看跌"}') + diff --git a/tmfs2.pine b/tmfs2.pine new file mode 100644 index 0000000..9a45781 --- /dev/null +++ b/tmfs2.pine @@ -0,0 +1,561 @@ +// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) +// https://creativecommons.org/licenses/by-nc-sa/4.0/ +// Original concepts by FibonacciFlux, rewritten for clarity and functionality + +//@version=6 +strategy('Trendline Breaks 带警报2', shorttitle = "TMFS Strategy alerts2", overlay = true, initial_capital = 10000, default_qty_type = strategy.percent_of_equity, default_qty_value = 4,commission_type = strategy.commission.percent, commission_value = 0.03, pyramiding = 2 ) + +//---------------------------------------------------------- +// SECTION 1: FIBONACCI SUPERTREND CONFIGURATION +//---------------------------------------------------------- + +// Common ATR period for all supertrend calculations +atrPeriod = input.int(13, 'ATR Length', minval = 1, tooltip="ATR period used for volatility calculation") + +// Fibonacci-based supertrend factor inputs +factor1 = input.float(0.618, 'Factor 1 (Weak/Fibonacci)', minval = 0.01, step = 0.01, tooltip="First Fibonacci factor (0.618)") +factor2 = input.float(1.618, 'Factor 2 (Medium/Golden Ratio)', minval = 0.01, step = 0.01, tooltip="Second Fibonacci factor (1.618)") +factor3 = input.float(2.618, 'Factor 3 (Strong/Extended Fib)', minval = 0.01, step = 0.01, tooltip="Third Fibonacci factor (2.618)") + +// Calculate the three Supertrend lines with different sensitivity levels +[supertrend1, direction1] = ta.supertrend(factor1, atrPeriod) +[supertrend2, direction2] = ta.supertrend(factor2, atrPeriod) +[supertrend3, direction3] = ta.supertrend(factor3, atrPeriod) + +// Handle initial values (first bar) +supertrend1 := barstate.isfirst ? na : supertrend1 +supertrend2 := barstate.isfirst ? na : supertrend2 +supertrend3 := barstate.isfirst ? na : supertrend3 + +// Calculate average of the three supertends and a smoothed version +superlength = input.int(21, "Smoothing Length", tooltip="EMA length for smoothing the average supertrend") +average_trend = (supertrend1 + supertrend2 + supertrend3) / 3 +smoothed_trend = ta.ema(average_trend, superlength) + +// Plot the supertrend lines and fill the area between them +trend_plot = plot(average_trend, color = average_trend > average_trend[1] ? color.green : color.red, title="Average Supertrend") +smoothed_plot = plot(smoothed_trend, linewidth = 2, color = smoothed_trend > smoothed_trend[1] ? color.green : color.red, title="Smoothed Supertrend") +fill(trend_plot, smoothed_plot, color = average_trend > smoothed_trend ? color.new(color.green, 50) : color.new(color.red, 50), title="Trend Direction Fill") + +//---------------------------------------------------------- +// SECTION 2: TRENDLINE DETECTION AND DRAWING +//---------------------------------------------------------- + +// User settings for Swing Trend Lines +swing_length = input.int(8, 'Swing Lookback Period', tooltip="Number of bars to look back for pivot points") +atr_multiplier = input.float(1.0, 'ATR Slope Multiplier', minval = 0.0, step = 0.1, tooltip="Multiplier for ATR to control trendline slope") +show_extended_lines = input.bool(true, 'Show Extended Lines', tooltip="Display dashed extensions for trendlines") +enable_backpainting = input.bool(true, 'Use Backpainting', tooltip="Enable backpainting to move indicators to pivot point") + +// Color settings for trendlines +uptrend_color = color.new(color.teal, 0) +downtrend_color = color.new(color.red, 0) + +// Variable initialization for trendlines +var float upper_trendline = na +var float lower_trendline = na +var float upper_slope = na +var float lower_slope = na +var line upline_extension = na +var line downline_extension = na + +// Offset for backpainting +offset = enable_backpainting ? swing_length : 0 +bar_idx = bar_index + +// Get pivot high and low points +pivot_high = ta.pivothigh(swing_length, swing_length) +pivot_low = ta.pivotlow(swing_length, swing_length) + +// Calculate ATR for slope +atr_value = ta.atr(swing_length) +slope = atr_value / swing_length * atr_multiplier + +// Update upper trendline when pivot high is detected or extend existing trendline +if bool(pivot_high) + upper_slope := slope + upper_trendline := pivot_high +else + upper_trendline := nz(upper_trendline) - nz(upper_slope) + +// Update lower trendline when pivot low is detected or extend existing trendline +if bool(pivot_low) + lower_slope := slope + lower_trendline := pivot_low +else + lower_trendline := nz(lower_trendline) + nz(lower_slope) + +// Create and update extended visualization lines if enabled +if show_extended_lines + // Initialize line objects if not already created + if na(upline_extension) + upline_extension := line.new(na, na, na, na, extend = extend.right, style = line.style_dashed, color = uptrend_color) + + if na(downline_extension) + downline_extension := line.new(na, na, na, na, extend = extend.right, style = line.style_dashed, color = downtrend_color) + + // Update upper trendline extension when a new pivot high is confirmed + if bool(pivot_high) + line.set_xy1(upline_extension, bar_idx - offset, enable_backpainting ? pivot_high : upper_trendline) + line.set_xy2(upline_extension, bar_idx - offset + 1, enable_backpainting ? pivot_high - slope : upper_trendline - slope) + + // Update lower trendline extension when a new pivot low is confirmed + if bool(pivot_low) + line.set_xy1(downline_extension, bar_idx - offset, enable_backpainting ? pivot_low : lower_trendline) + line.set_xy2(downline_extension, bar_idx - offset + 1, enable_backpainting ? pivot_low + slope : lower_trendline + slope) + +//---------------------------------------------------------- +// SECTION 3: BREAKOUT DETECTION +//---------------------------------------------------------- + +// Track breakouts above upper trendline and below lower trendline +// Initialize tracking variables for breakout conditions +var int upper_breakout_state = 0 // State variable to track upper breakout condition +var int lower_breakout_state = 0 // State variable to track lower breakout condition + +// 优化的突破检测 - 添加多K线确认机制,避免假突破 +bool upper_breakout = close > upper_trendline and close[1] > upper_trendline[1] and close[2] > upper_trendline[2] +bool lower_breakout = close < lower_trendline and close[1] < lower_trendline[1] and close[2] < lower_trendline[2] + +// Update state variables to track when breakouts occur +upper_breakout_state := bool(pivot_high) ? 0 : upper_breakout ? 1 : upper_breakout_state +lower_breakout_state := bool(pivot_low) ? 0 : lower_breakout ? 1 : lower_breakout_state + +// Plot trendlines with appropriate offsets +plot(enable_backpainting ? upper_trendline : upper_trendline, color = bool(pivot_high) ? na : uptrend_color, linewidth = 2, offset = -offset, title = 'Upper Trendline') +plot(enable_backpainting ? lower_trendline : lower_trendline, color = bool(pivot_low) ? na : downtrend_color, linewidth = 2, offset = -offset, title = 'Lower Trendline') + +//---------------------------------------------------------- +// SECTION 4: DIRECTIONAL MOVEMENT INDEX (DMI) CALCULATION +//---------------------------------------------------------- + +// DMI Length parameter +dmi_length = input.int(13, "DMI Length", tooltip="Length for DMI calculation") + +// True Range calculation +true_range = math.max(math.max(high - low, math.abs(high - nz(close[1]))), math.abs(low - nz(close[1]))) + +// Directional Movement calculations +directional_movement_plus = high - nz(high[1]) > nz(low[1]) - low ? math.max(high - nz(high[1]), 0) : 0 +directional_movement_minus = nz(low[1]) - low > high - nz(high[1]) ? math.max(nz(low[1]) - low, 0) : 0 + +// Smoothed calculations for DMI +var float smoothed_true_range = 0.0 +smoothed_true_range := nz(smoothed_true_range[1]) - nz(smoothed_true_range[1]) / dmi_length + true_range + +var float smoothed_directional_plus = 0.0 +smoothed_directional_plus := nz(smoothed_directional_plus[1]) - nz(smoothed_directional_plus[1]) / dmi_length + directional_movement_plus + +var float smoothed_directional_minus = 0.0 +smoothed_directional_minus := nz(smoothed_directional_minus[1]) - nz(smoothed_directional_minus[1]) / dmi_length + directional_movement_minus + +// Calculate DI+ and DI- values +di_plus = smoothed_directional_plus / smoothed_true_range * 100 +di_minus = smoothed_directional_minus / smoothed_true_range * 100 + +//---------------------------------------------------------- +// SECTION 4.5: 准确性优化设置 +//---------------------------------------------------------- + +// 根据用户偏好使用第10根K线确认,减少误报 +confirmation_lookback = input.int(10, "确认回看期间", minval=1, maxval=20, tooltip="使用第N根K线确认交叉信号,减少误报", group="准确性优化") +dmi_threshold = input.float(20.0, "DMI最小阈值", minval=10.0, maxval=50.0, tooltip="DMI值必须超过此阈值才触发信号", group="准确性优化") +volume_confirmation = input.bool(true, "启用成交量确认", tooltip="要求成交量高于平均值才触发信号", group="准确性优化") +volume_multiplier = input.float(1.2, "成交量倍数", minval=1.0, maxval=3.0, step=0.1, tooltip="成交量必须是平均成交量的倍数", group="准确性优化") +min_alert_interval = input.int(5, "最小警报间隔", minval=1, maxval=20, tooltip="两次警报之间的最小K线间隔", group="准确性优化") + +// 警报去重机制 +var int last_alert_bar = 0 + +//---------------------------------------------------------- +// SECTION 5: STRATEGY DEFINITION +//---------------------------------------------------------- + +// Enable/disable long and short positions +enable_long_positions = input.bool(defval = true, title = 'Enable Long Positions', group = 'Strategy Settings') +enable_short_positions = input.bool(defval = true, title = 'Enable Short Positions', group = 'Strategy Settings') + +// 优化的SMA设置 - 增加周期以减少噪音 +fast_sma_length = input.int(defval = 5, title = 'Fast SMA Length', minval = 1, group = 'Strategy Settings') +slow_sma_length = input.int(defval = 10, title = 'Slow SMA Length', minval = 1, group = 'Strategy Settings') +sma_distance_threshold = input.float(0.001, "SMA距离阈值", minval=0.0001, step=0.0001, tooltip="SMA之间的最小距离阈值", group = 'Strategy Settings') + +// Calculate SMA crossovers +fast_sma = ta.sma(close, fast_sma_length) +slow_sma = ta.sma(close, slow_sma_length) + +// 优化的SMA交叉检测 - 添加距离过滤 +sma_cross_up = ta.crossover(fast_sma, slow_sma) and math.abs(fast_sma - slow_sma) > close * sma_distance_threshold +sma_cross_down = ta.crossunder(fast_sma, slow_sma) and math.abs(fast_sma - slow_sma) > close * sma_distance_threshold + +//---------------------------------------------------------- +// SECTION 6: ENTRY AND EXIT CONDITIONS +//---------------------------------------------------------- + +// 成交量确认 +volume_avg = ta.sma(volume, 20) +volume_confirmed = not volume_confirmation or volume > volume_avg * volume_multiplier + +// 多重确认机制 +trend_confirmed_bullish = true +trend_confirmed_bearish = true +for i = 0 to 2 + trend_confirmed_bullish := trend_confirmed_bullish and close[i] > smoothed_trend[i] + trend_confirmed_bearish := trend_confirmed_bearish and close[i] < smoothed_trend[i] + +// 优化的DMI信号 - 添加强度过滤 +strong_dmi_bullish = di_plus > di_minus and di_plus > dmi_threshold +strong_dmi_bearish = di_minus > di_plus and di_minus > dmi_threshold + +// 优化的入场条件 - 添加多重确认 +long_entry_condition = enable_long_positions and + upper_breakout_state > upper_breakout_state[1] and + strong_dmi_bullish and + trend_confirmed_bullish and + volume_confirmed + +short_entry_condition = enable_short_positions and + lower_breakout_state > lower_breakout_state[1] and + strong_dmi_bearish and + trend_confirmed_bearish and + volume_confirmed + + + +// 优化后的出场条件 - 使用第10根K线确认 +long_exit_condition = ta.crossunder(close[confirmation_lookback], smoothed_trend[confirmation_lookback]) +short_exit_condition = ta.crossover(close[confirmation_lookback], smoothed_trend[confirmation_lookback]) + +// @function Display BUY/SELL labels on chart +plotshape(long_entry_condition, title='BUY', text='BUY', location=location.belowbar, + style=shape.labelup, size=size.tiny, color=color.new(color.green, 0), textcolor=color.new(color.white, 0)) +plotshape(short_entry_condition, title='SELL', text='SELL', location=location.abovebar, + style=shape.labeldown, size=size.tiny, color=color.new(color.red, 0), textcolor=color.new(color.white, 0)) + +//---------------------------------------------------------- +// SECTION 7: STOP LOSS AND TAKE PROFIT SETTINGS +//---------------------------------------------------------- + +// Common settings +atr_length = input.int(13, 'ATR Length for SL/TP', minval = 1, group = 'Risk Management') + +// Variable to track if stop loss is triggered +var bool long_trailing_tp_executed = false +var bool short_trailing_tp_executed = false + +// Store ATR at position entry +float entry_atr = ta.valuewhen(long_entry_condition or short_entry_condition, ta.atr(atr_length), 0) + +// Stop Loss settings +stop_loss_method = input.string(defval = 'ATR', title = 'Stop Loss Method', options = ['PERC', 'ATR'], group = 'Stop Loss') +long_stop_loss_percent = input.float(defval = 0.75, title = 'Long Position Stop Loss %', minval = 0.05, maxval = 100, step = 0.05, group = 'Stop Loss') / 100 +short_stop_loss_percent = input.float(defval = 0.75, title = 'Short Position Stop Loss %', minval = 0.05, maxval = 100, step = 0.05, group = 'Stop Loss') / 100 +long_stop_loss_atr_multiplier = input.float(defval = 8.0, title = 'Long Position ATR Multiplier', minval = 0.1, step = 0.1, group = 'Stop Loss') +short_stop_loss_atr_multiplier = input.float(defval = 8.0, title = 'Short Position ATR Multiplier', minval = 0.1, step = 0.1, group = 'Stop Loss') +stop_loss_trailing_mode = input.string(defval = 'ON', title = 'Trailing Stop Loss Mode', options = ['TP', 'ON', 'OFF'], tooltip = 'ON: Always trail, TP: Trail after Take Profit hit, OFF: Fixed stop', group = 'Stop Loss') +break_even_enabled = input.bool(defval = true, title = 'Move to Break Even After TP', group = 'Stop Loss') + +// Take Profit settings +take_profit_quantity_percent = input.float(defval = 100, title = 'Take Profit Quantity %', minval = 0.0, maxval = 100, step = 1.0, group = 'Take Profit') +take_profit_method = input.string(defval = 'PERC', title = 'Take Profit Method', options = ['PERC', 'ATR', 'RR'], group = 'Take Profit') +long_take_profit_percent = input.float(defval = 1, title = 'Long Position Take Profit %', minval = 0.05, step = 0.05, group = 'Take Profit') / 100 +short_take_profit_percent = input.float(defval = 1, title = 'Short Position Take Profit %', minval = 0.05, step = 0.05, group = 'Take Profit') / 100 +long_take_profit_atr_multiplier = input.float(defval = 1.5, title = 'Long Position TP ATR Multiplier', minval = 0.1, step = 0.1, group = 'Take Profit') +short_take_profit_atr_multiplier = input.float(defval = 2, title = 'Short Position TP ATR Multiplier', minval = 0.1, step = 0.1, group = 'Take Profit') +long_risk_reward_ratio = input.float(defval = 0.6, title = 'Long Position Risk/Reward Ratio', minval = 0.1, step = 0.1, group = 'Take Profit') +short_risk_reward_ratio = input.float(defval = 1, title = 'Short Position Risk/Reward Ratio', minval = 0.1, step = 0.1, group = 'Take Profit') +trailing_take_profit_enabled = input.bool(defval = true, title = 'Enable Trailing Take Profit', group = 'Take Profit') +deviation_method = input.string(defval = 'PERC', title = 'Trailing Deviation Method', options = ['PERC', 'ATR'], group = 'Take Profit') +deviation_percent = input.float(defval = 0.01, title = 'Trailing Deviation %', minval = 0.01, maxval = 100, step = 0.05, group = 'Take Profit') / 100 +deviation_atr_multiplier = input.float(defval = 1.0, title = 'Trailing Deviation ATR Multiplier', minval = 0.01, step = 0.05, group = 'Take Profit') + +//---------------------------------------------------------- +// SECTION 9: CALCULATE STOP LOSS LEVELS +//---------------------------------------------------------- + +// Calculate long position stop loss price based on selected method +get_long_stop_loss_price(base_price) => + switch stop_loss_method + 'PERC' => base_price * (1 - long_stop_loss_percent) + 'ATR' => base_price - long_stop_loss_atr_multiplier * entry_atr + => na + +// Determine if trailing should be active for long position +long_tp_trailing_enabled = stop_loss_trailing_mode == 'ON' or (stop_loss_trailing_mode == 'TP' and long_trailing_tp_executed) + +// Calculate and maintain stop loss price for long positions +var float long_stop_loss_price = na +long_stop_loss_price := if strategy.position_size > 0 + if long_entry_condition + get_long_stop_loss_price(close) + else + stop_price = get_long_stop_loss_price(long_tp_trailing_enabled ? high : strategy.position_avg_price) + stop_price := break_even_enabled and long_trailing_tp_executed ? math.max(stop_price, strategy.position_avg_price) : stop_price + math.max(stop_price, nz(long_stop_loss_price[1])) +else + na + +// Calculate short position stop loss price based on selected method +get_short_stop_loss_price(base_price) => + switch stop_loss_method + 'PERC' => base_price * (1 + short_stop_loss_percent) + 'ATR' => base_price + short_stop_loss_atr_multiplier * entry_atr + => na + +// Determine if trailing should be active for short position +short_tp_trailing_enabled = stop_loss_trailing_mode == 'ON' or (stop_loss_trailing_mode == 'TP' and short_trailing_tp_executed) + +// Calculate and maintain stop loss price for short positions +var float short_stop_loss_price = na +short_stop_loss_price := if strategy.position_size < 0 + if short_entry_condition + get_short_stop_loss_price(close) + else + var float stop_price = get_short_stop_loss_price(short_tp_trailing_enabled ? low : strategy.position_avg_price) // stop_price を var float で宣言 + if break_even_enabled and short_trailing_tp_executed + stop_price := math.min(stop_price, strategy.position_avg_price) + short_stop_loss_price := math.min(stop_price, nz(short_stop_loss_price[1], 999999.9)) +else + na + +//---------------------------------------------------------- +// SECTION 9: CALCULATE TAKE PROFIT LEVELS +//---------------------------------------------------------- + +// Calculate long position take profit price based on selected method +get_long_take_profit_price() => + switch take_profit_method + 'PERC' => close * (1 + long_take_profit_percent) + 'ATR' => close + long_take_profit_atr_multiplier * entry_atr + 'RR' => close + long_risk_reward_ratio * (close - get_long_stop_loss_price(close)) + => na + +// Calculate and maintain take profit price for long positions +var float long_take_profit_price = na +long_take_profit_price := if strategy.position_size > 0 and not long_trailing_tp_executed + if long_entry_condition + get_long_take_profit_price() + else + nz(long_take_profit_price[1], get_long_take_profit_price()) +else + na + +// Update trailing take profit execution flag for long positions +long_trailing_tp_executed := strategy.position_size > 0 and (long_trailing_tp_executed[1] or strategy.position_size < strategy.position_size[1] or (strategy.position_size[1] == 0 and high >= long_take_profit_price)) + +// Calculate trailing step size in ticks for long positions +long_trailing_tp_step_ticks = switch deviation_method + 'PERC' => long_take_profit_price * deviation_percent / syminfo.mintick + 'ATR' => deviation_atr_multiplier * entry_atr / syminfo.mintick + => na + +// Calculate short position take profit price based on selected method +get_short_take_profit_price() => + switch take_profit_method + 'PERC' => close * (1 - short_take_profit_percent) + 'ATR' => close - short_take_profit_atr_multiplier * entry_atr + 'RR' => close - short_risk_reward_ratio * (get_short_stop_loss_price(close) - close) + => na + +// Calculate and maintain take profit price for short positions +var float short_take_profit_price = na +short_take_profit_price := if strategy.position_size < 0 and not short_trailing_tp_executed + if short_entry_condition + get_short_take_profit_price() + else + nz(short_take_profit_price[1], get_short_take_profit_price()) +else + na + +// Update trailing take profit execution flag for short positions +short_trailing_tp_executed := strategy.position_size < 0 and (short_trailing_tp_executed[1] or strategy.position_size > strategy.position_size[1] or (strategy.position_size[1] == 0 and low <= short_take_profit_price)) + +// Calculate trailing step size in ticks for short positions +short_trailing_tp_step_ticks = switch deviation_method + 'PERC' => short_take_profit_price * deviation_percent / syminfo.mintick + 'ATR' => deviation_atr_multiplier * entry_atr / syminfo.mintick + => na + +//---------------------------------------------------------- +// SECTION 11: STRATEGY EXECUTION +//---------------------------------------------------------- + +// Enter long position when conditions are met +if long_entry_condition + strategy.entry('L', strategy.long, comment = "LONG") + +// Enter short position when conditions are met +if short_entry_condition + strategy.entry('S', strategy.short, comment = "SHORT") + +// Exit long position when exit conditions are met +if long_exit_condition + strategy.close('L', comment = "SL Triggered") + +// Exit short position when exit conditions are met +if short_exit_condition + strategy.close('S', comment = "SL Triggered") + +// Set up take profit and stop loss for long positions +strategy.exit(id = 'Long Take Profit / Stop Loss', from_entry = 'L', qty_percent = take_profit_quantity_percent, limit = trailing_take_profit_enabled ? na : long_take_profit_price, stop = long_stop_loss_price, trail_price = trailing_take_profit_enabled ? long_take_profit_price : na, trail_offset = trailing_take_profit_enabled ? long_trailing_tp_step_ticks : na, comment = "TP/SL Triggered") +strategy.exit(id = 'Long Stop Loss', from_entry = 'L', stop = long_stop_loss_price, comment = "SL Triggered") + +// Set up take profit and stop loss for short positions +strategy.exit(id = 'Short Take Profit / Stop Loss', from_entry = 'S', qty_percent = take_profit_quantity_percent, limit = trailing_take_profit_enabled ? na : short_take_profit_price, stop = short_stop_loss_price, trail_price = trailing_take_profit_enabled ? short_take_profit_price : na, trail_offset = trailing_take_profit_enabled ? short_trailing_tp_step_ticks : na, comment = "TP/SL Triggered") +strategy.exit(id = 'Short Stop Loss', from_entry = 'S', stop = short_stop_loss_price, comment = "SL Triggered") + +// ===== 统一警报系统 ===== +// 统一警报系统 - 只需添加一次警报即可捕获所有信号 + +// 警报频率限制 - 每分钟只触发一次 +var int last_alert_long_entry = 0 +var int last_alert_short_entry = 0 +var int last_alert_long_exit = 0 +var int last_alert_short_exit = 0 +var int last_alert_upper_breakout = 0 +var int last_alert_lower_breakout = 0 +var int last_alert_sma_cross_up = 0 +var int last_alert_sma_cross_down = 0 +var int last_alert_dmi_bullish = 0 +var int last_alert_dmi_bearish = 0 +var int last_alert_trend_bullish = 0 +var int last_alert_trend_bearish = 0 + +// 获取当前时间(分钟级别) +current_minute = math.floor(timenow / 60000) + +// 检测所有警报条件并生成对应的JSON消息 +alert_message = "" + +// 入场信号警报 - 每分钟限制 +if long_entry_condition and current_minute > last_alert_long_entry + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","成交量倍数":"' + str.tostring(volume/volume_avg, '#.##') + '","事件":"多头入场信号触发","信号":"buy","备注":"多重确认+成交量验证"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_long_entry := current_minute + +if short_entry_condition and current_minute > last_alert_short_entry + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","成交量倍数":"' + str.tostring(volume/volume_avg, '#.##') + '","事件":"空头入场信号触发","信号":"sell","备注":"多重确认+成交量验证"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_short_entry := current_minute + +// 出场信号警报 - 每分钟限制 +if long_exit_condition and current_minute > last_alert_long_exit + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","确认期间":"' + str.tostring(confirmation_lookback) + '","事件":"多头出场信号触发","信号":"exit_long","备注":"第' + str.tostring(confirmation_lookback) + '根K线确认跌破"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_long_exit := current_minute + +if short_exit_condition and current_minute > last_alert_short_exit + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","确认期间":"' + str.tostring(confirmation_lookback) + '","事件":"空头出场信号触发","信号":"exit_short","备注":"第' + str.tostring(confirmation_lookback) + '根K线确认突破"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_short_exit := current_minute + +// 优化的趋势线突破警报 - 每分钟限制 +if upper_breakout and not upper_breakout[1] and current_minute > last_alert_upper_breakout + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","上趋势线":"' + str.tostring(upper_trendline, '#.####') + '","突破强度":"连续3K线","事件":"价格确认突破上趋势线","信号":"upper_breakout","备注":"3K线确认突破,过滤假突破"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_upper_breakout := current_minute + +if lower_breakout and not lower_breakout[1] and current_minute > last_alert_lower_breakout + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","下趋势线":"' + str.tostring(lower_trendline, '#.####') + '","突破强度":"连续3K线","事件":"价格确认跌破下趋势线","信号":"lower_breakout","备注":"3K线确认跌破,过滤假突破"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_lower_breakout := current_minute + +// 优化的SMA交叉警报 - 每分钟限制 +if sma_cross_up and current_minute > last_alert_sma_cross_up + sma_distance = math.abs(fast_sma - slow_sma) / close * 100 + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","快速SMA":"' + str.tostring(fast_sma, '#.####') + '","慢速SMA":"' + str.tostring(slow_sma, '#.####') + '","SMA距离":"' + str.tostring(sma_distance, '#.###') + '%","事件":"SMA金叉确认","信号":"sma_cross_up","备注":"5/10周期+距离过滤"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_sma_cross_up := current_minute + +if sma_cross_down and current_minute > last_alert_sma_cross_down + sma_distance = math.abs(fast_sma - slow_sma) / close * 100 + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","快速SMA":"' + str.tostring(fast_sma, '#.####') + '","慢速SMA":"' + str.tostring(slow_sma, '#.####') + '","SMA距离":"' + str.tostring(sma_distance, '#.###') + '%","事件":"SMA死叉确认","信号":"sma_cross_down","备注":"5/10周期+距离过滤"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_sma_cross_down := current_minute + +// 优化的DMI信号警报 - 每分钟限制 +if ta.crossover(di_plus, di_minus) and di_plus > dmi_threshold and current_minute > last_alert_dmi_bullish + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","DMI阈值":"' + str.tostring(dmi_threshold, '#.##') + '","事件":"强势DI+上穿DI-","信号":"dmi_bullish","备注":"动量转向看涨(已过滤弱信号)"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_dmi_bullish := current_minute + +if ta.crossunder(di_plus, di_minus) and di_minus > dmi_threshold and current_minute > last_alert_dmi_bearish + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","DI+":"' + str.tostring(di_plus, '#.##') + '","DI-":"' + str.tostring(di_minus, '#.##') + '","DMI阈值":"' + str.tostring(dmi_threshold, '#.##') + '","事件":"强势DI+下穿DI-","信号":"dmi_bearish","备注":"动量转向看跌(已过滤弱信号)"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_dmi_bearish := current_minute + +// 超趋势方向变化警报 - 每分钟限制 +if ta.crossover(close, smoothed_trend) and current_minute > last_alert_trend_bullish + trend_strength = math.abs(close - smoothed_trend) / close * 100 + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.###') + '%","事件":"价格上穿平滑超趋势","信号":"trend_bullish","备注":"趋势转向看涨"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_trend_bullish := current_minute + +if ta.crossunder(close, smoothed_trend) and current_minute > last_alert_trend_bearish + trend_strength = math.abs(close - smoothed_trend) / close * 100 + alert_message := '{"指标名称":"TMFS_V2","交易对":"' + syminfo.ticker + '","触发时间":"' + str.tostring(timenow) + '","时间":"' + str.tostring(time) + '","价格":"' + str.tostring(close, '#.####') + '","平滑超趋势":"' + str.tostring(smoothed_trend, '#.####') + '","趋势强度":"' + str.tostring(trend_strength, '#.###') + '%","事件":"价格下穿平滑超趋势","信号":"trend_bearish","备注":"趋势转向看跌"}' + alert(alert_message, alert.freq_once_per_bar_close) + last_alert_trend_bearish := current_minute + +// ===== 传统警报条件(保留兼容性)===== +// 注意:使用统一警报系统时,建议只使用上面的alert()函数 +// 以下alertcondition保留用于需要单独设置警报的情况 + +// 创建用于警报的plot变量 +plot(smoothed_trend, title = '平滑超趋势', display = display.none) +plot(di_plus, title = 'DI+', display = display.none) +plot(di_minus, title = 'DI-', display = display.none) +plot(fast_sma, title = '快速SMA', display = display.none) +plot(slow_sma, title = '慢速SMA', display = display.none) +plot(upper_trendline, title = '上趋势线', display = display.none) +plot(lower_trendline, title = '下趋势线', display = display.none) +plot(volume/volume_avg, title = '成交量倍数', display = display.none) + +// 入场信号警报 +alertcondition(long_entry_condition, title = 'TMFS_V2多头入场', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","成交量倍数":"{{plot("成交量倍数")}}","事件":"多头入场信号触发","信号":"buy","备注":"多重确认+成交量验证"}') + +alertcondition(short_entry_condition, title = 'TMFS_V2空头入场', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","成交量倍数":"{{plot("成交量倍数")}}","事件":"空头入场信号触发","信号":"sell","备注":"多重确认+成交量验证"}') + +// 出场信号警报 +alertcondition(long_exit_condition, title = 'TMFS_V2多头出场', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"多头出场信号触发","信号":"exit_long","备注":"第10根K线确认跌破"}') + +alertcondition(short_exit_condition, title = 'TMFS_V2空头出场', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"空头出场信号触发","信号":"exit_short","备注":"第10根K线确认突破"}') + +// 优化的趋势线突破警报 +alertcondition(upper_breakout and not upper_breakout[1], title = '价格确认突破上趋势线', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","上趋势线":"{{plot("上趋势线")}}","事件":"价格确认突破上趋势线","信号":"upper_breakout","备注":"3K线确认突破,过滤假突破"}') + +alertcondition(lower_breakout and not lower_breakout[1], title = '价格确认跌破下趋势线', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","下趋势线":"{{plot("下趋势线")}}","事件":"价格确认跌破下趋势线","信号":"lower_breakout","备注":"3K线确认跌破,过滤假突破"}') + +// 优化的SMA交叉警报 +alertcondition(sma_cross_up, title = 'SMA金叉确认', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","快速SMA":"{{plot("快速SMA")}}","慢速SMA":"{{plot("慢速SMA")}}","事件":"SMA金叉确认","信号":"sma_cross_up","备注":"5/10周期+距离过滤"}') + +alertcondition(sma_cross_down, title = 'SMA死叉确认', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","快速SMA":"{{plot("快速SMA")}}","慢速SMA":"{{plot("慢速SMA")}}","事件":"SMA死叉确认","信号":"sma_cross_down","备注":"5/10周期+距离过滤"}') + +// 优化的DMI信号警报 +alertcondition(ta.crossover(di_plus, di_minus) and di_plus > dmi_threshold, title = '强势DI+上穿DI-', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"强势DI+上穿DI-","信号":"dmi_bullish","备注":"动量转向看涨(已过滤弱信号)"}') + +alertcondition(ta.crossunder(di_plus, di_minus) and di_minus > dmi_threshold, title = '强势DI+下穿DI-', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"强势DI+下穿DI-","信号":"dmi_bearish","备注":"动量转向看跌(已过滤弱信号)"}') + +// 超趋势方向变化警报 +alertcondition(ta.crossover(close, smoothed_trend), title = '价格上穿平滑超趋势', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"价格上穿平滑超趋势","信号":"trend_bullish","备注":"趋势转向看涨"}') + +alertcondition(ta.crossunder(close, smoothed_trend), title = '价格下穿平滑超趋势', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","事件":"价格下穿平滑超趋势","信号":"trend_bearish","备注":"趋势转向看跌"}') + +// 综合强势信号警报 +alertcondition(upper_breakout and strong_dmi_bullish and volume_confirmed, title = '强势看涨信号', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","成交量倍数":"{{plot("成交量倍数")}}","事件":"强势看涨信号","信号":"strong_bullish","备注":"趋势线突破+强势DMI+成交量确认"}') + +alertcondition(lower_breakout and strong_dmi_bearish and volume_confirmed, title = '强势看跌信号', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","成交量倍数":"{{plot("成交量倍数")}}","事件":"强势看跌信号","信号":"strong_bearish","备注":"趋势线跌破+强势DMI+成交量确认"}') + +// 趋势确认警报 +alertcondition(trend_confirmed_bullish and strong_dmi_bullish, title = '多头趋势确认', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"多头趋势确认","信号":"bullish_trend_confirmed","备注":"3K线确认在超趋势上方且强势DMI看涨"}') + +alertcondition(trend_confirmed_bearish and strong_dmi_bearish, title = '空头趋势确认', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","平滑超趋势":"{{plot("平滑超趋势")}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"空头趋势确认","信号":"bearish_trend_confirmed","备注":"3K线确认在超趋势下方且强势DMI看跌"}') + +// 成交量异常警报 +alertcondition(volume > volume_avg * volume_multiplier * 2, title = '成交量异常放大', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","成交量倍数":"{{plot("成交量倍数")}}","事件":"成交量异常放大","信号":"volume_spike","备注":"成交量超过平均值2倍以上"}') + +// 基础DMI状态警报 +alertcondition(di_plus > dmi_threshold and di_plus > di_minus, title = 'DI+强势状态', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"DI+强势状态","信号":"dmi_plus_strong","备注":"DI+高于阈值且大于DI-"}') + +alertcondition(di_minus > dmi_threshold and di_minus > di_plus, title = 'DI-强势状态', message = '{"指标名称":"TMFS_V2","交易对":"{{ticker}}","触发时间":"{{timenow}}","时间":"{{time}}","价格":"{{close}}","DI+":"{{plot("DI+")}}","DI-":"{{plot("DI-")}}","事件":"DI-强势状态","信号":"dmi_minus_strong","备注":"DI-高于阈值且大于DI+"}') +