Исходный код
Ограничение в 500 активных алертов на аккаунт TradingView может стать серьезным препятствием для трейдеров и разработчиков, использующих сложные торговые стратегии, требующие мониторинга множества инструментов или условий. Однако, используя возможности Pine Script v5 по работе с JSON и вебхуками, можно элегантно обойти это ограничение.
Идея заключается в том, чтобы вместо создания отдельного алерта для каждого торгового условия (например, пересечение скользящих средних, RSI перекупленность/перепроданность, пробой уровня или обнаружение Order Blocks), мы собираем статусы всех интересующих нас условий в один JSON-объект. Этот JSON-объект затем отправляется через единственный алерт TradingView на ваш собственный вебхук-сервер. Ваш сервер, получив JSON, парсит его и уже на своей стороне принимает решение о том, какие уведомления отправлять и куда (Telegram, email, Discord и т.д.).
Представленный ниже скрипт демонстрирует, как мультиплексировать несколько типов алертов (пересечение скользящих средних, RSI перекупленность/перепроданность, всплеск объема) в одно JSON-сообщение. Вы можете легко расширить его, добавив свои собственные условия, например, для отслеживания дивергенций с осциллятором Стохастик или активации трейлинг-стопа по теням свечей.
//@version=5
indicator("Multiplexed Alerts JSON", overlay=true, max_alerts_per_calc=500)
// --- Inputs ---
var string GROUP_WEBHOOK = "Webhook Settings"
webhookUrl = input.string("https://your-webhook-receiver.com/alert", "Webhook URL", group=GROUP_WEBHOOK, tooltip="URL вашего сервера для приема JSON-сообщений. Пример: https://finfluct.com/webhook-processor")
alertMessagePrefix = input.string("TV_ALERT", "Alert Message Prefix", group=GROUP_WEBHOOK, tooltip="Префикс для идентификации сообщений от этого скрипта.")
var string GROUP_MA = "Moving Average Crossover Alert"
enableMAAlert = input.bool(true, "Enable MA Crossover Alert", group=GROUP_MA)
fastMALength = input.int(10, "Fast MA Length", minval=1, group=GROUP_MA)
slowMALength = input.int(30, "Slow MA Length", minval=1, group=GROUP_MA)
var string GROUP_RSI = "RSI Overbought/Oversold Alert"
enableRSIAlert = input.bool(true, "Enable RSI Alert", group=GROUP_RSI)
rsiLength = input.int(14, "RSI Length", minval=1, group=GROUP_RSI)
rsiOverbought = input.int(70, "RSI Overbought Level", minval=0, maxval=100, group=GROUP_RSI)
rsiOversold = input.int(30, "RSI Oversold Level", minval=0, maxval=100, group=GROUP_RSI)
var string GROUP_VOLUME = "Volume Spike Alert"
enableVolumeAlert = input.bool(true, "Enable Volume Spike Alert", group=GROUP_VOLUME)
volumeMultiplier = input.float(2.0, "Volume Avg Multiplier", minval=1.0, group=GROUP_VOLUME, tooltip="Объем должен быть в X раз выше среднего за последние N баров.")
volumeAvgLength = input.int(20, "Volume Avg Length", minval=1, group=GROUP_VOLUME)
// --- Calculations ---
fastMA = ta.sma(close, fastMALength)
slowMA = ta.sma(close, slowMALength)
rsiValue = ta.rsi(close, rsiLength)
avgVolume = ta.sma(volume, volumeAvgLength)
// --- Conditions ---
maCrossoverBullish = enableMAAlert and ta.crossover(fastMA, slowMA)
maCrossoverBearish = enableMAAlert and ta.crossunder(fastMA, slowMA)
rsiOverboughtTrigger = enableRSIAlert and ta.crossunder(rsiValue, rsiOverbought)
rsiOversoldTrigger = enableRSIAlert and ta.crossover(rsiValue, rsiOversold)
volumeSpikeTrigger = enableVolumeAlert and volume > avgVolume * volumeMultiplier
// --- JSON Payload Construction ---
// Создаем map для хранения статусов всех алертов
var map> alertsData = na
// Инициализируем или обновляем map на каждом баре
if barstate.islast
alertsData := map.new()
// MA Crossover
if enableMAAlert
alertsData.put("ma_crossover_bullish", map.new(
"name", "MA Crossover Bullish",
"triggered", maCrossoverBullish,
"fast_ma", fastMA,
"slow_ma", slowMA
))
alertsData.put("ma_crossover_bearish", map.new(
"name", "MA Crossover Bearish",
"triggered", maCrossoverBearish,
"fast_ma", fastMA,
"slow_ma", slowMA
))
// RSI
if enableRSIAlert
alertsData.put("rsi_overbought", map.new(
"name", "RSI Overbought",
"triggered", rsiOverboughtTrigger,
"rsi_value", rsiValue,
"level", float(rsiOverbought)
))
alertsData.put("rsi_oversold", map.new(
"name", "RSI Oversold",
"triggered", rsiOversoldTrigger,
"rsi_value", rsiValue,
"level", float(rsiOversold)
))
// Volume Spike
if enableVolumeAlert
alertsData.put("volume_spike", map.new(
"name", "Volume Spike",
"triggered", volumeSpikeTrigger,
"current_volume", volume,
"avg_volume", avgVolume
))
// --- Main Alert Logic ---
// Проверяем, сработало ли какое-либо из включенных условий
anyAlertTriggered = maCrossoverBullish or maCrossoverBearish or rsiOverboughtTrigger or rsiOversoldTrigger or volumeSpikeTrigger
if anyAlertTriggered
// Собираем полный JSON-payload
payload = map.new()
payload.put("symbol", syminfo.tickerid)
payload.put("interval", timeframe.period)
payload.put("time", str.format("{0,date,yyyy-MM-dd HH:mm:ss}", time))
payload.put("alerts", alertsData) // Встраиваем map с алертами
// Конвертируем map в JSON-строку
jsonString = json.tostring(payload)
// Отправляем алерт с JSON-payload
alert(alertMessagePrefix + " " + jsonString, alert.webhook_url)
// --- Plotting (Optional, for visualization) ---
plot(fastMA, "Fast MA", color.blue)
plot(slowMA, "Slow MA", color.orange)
plot(rsiValue, "RSI", color.purple, display=display.none) // Скрываем график RSI
hline(rsiOverbought, "RSI Overbought Level", color.red, display=display.none)
hline(rsiOversold, "RSI Oversold Level", color.green, display=display.none)
// Отображение срабатываний алертов для визуального подтверждения
plotshape(maCrossoverBullish, title="MA Bullish", location=location.belowbar, color=color.new(color.green, 0), style=shape.triangleup, size=size.small)
plotshape(maCrossoverBearish, title="MA Bearish", location=location.abovebar, color=color.new(color.red, 0), style=shape.triangledown, size=size.small)
plotshape(rsiOversoldTrigger, title="RSI Oversold", location=location.belowbar, color=color.new(color.lime, 0), style=shape.circle, size=size.small)
plotshape(rsiOverboughtTrigger, title="RSI Overbought", location=location.abovebar, color=color.new(color.fuchsia, 0), style=shape.circle, size=size.small)
plotshape(volumeSpikeTrigger, title="Volume Spike", location=location.abovebar, color=color.new(color.yellow, 0), style=shape.arrowdown, size=size.small)
Разбор параметров
webhookUrl: URL вашего сервера, который будет принимать JSON-сообщения от TradingView. Это критически важный параметр, так как именно на этот адрес будут отправляться все мультиплексированные алерты.alertMessagePrefix: Произвольный префикс, который будет добавлен к началу JSON-сообщения. Помогает идентифицировать алерты от данного скрипта на вашем вебхук-сервере.enableMAAlert: Включение/отключение алерта по пересечению скользящих средних.fastMALength: Длина быстрой скользящей средней.slowMALength: Длина медленной скользящей средней.enableRSIAlert: Включение/отключение алерта по RSI (перекупленность/перепроданность).rsiLength: Период расчета RSI.rsiOverbought: Уровень RSI, выше которого считается перекупленность.rsiOversold: Уровень RSI, ниже которого считается перепроданность.enableVolumeAlert: Включение/отключение алерта по всплеску объема.volumeMultiplier: Множитель, определяющий, во сколько раз текущий объем должен превышать средний объем за последниеvolumeAvgLengthбаров, чтобы сработал алерт.volumeAvgLength: Период для расчета среднего объема.
Как запустить
Для успешной реализации этой стратегии вам потребуется не только Pine Script, но и внешний сервер для обработки вебхуков.
-
Добавьте скрипт на график TradingView: Скопируйте предоставленный Pine Script код и добавьте его на любой график в TradingView.
-
Настройте параметры скрипта: В настройках скрипта укажите
webhookUrlвашего сервера. Убедитесь, что этот URL доступен извне и готов принимать POST-запросы. -
Создайте единственный алерт TradingView:
- Нажмите на кнопку ‘Алерт’ на панели инструментов TradingView.
- В качестве условия выберите ваш индикатор (
Multiplexed Alerts JSON). - Для условия срабатывания выберите
Any alert() call. - В разделе ‘Уведомления’ обязательно активируйте опцию ‘Webhook URL’ и вставьте туда тот же
webhookUrl, который вы указали в параметрах скрипта. - В поле ‘Сообщение’ введите
{{strategy.order.alert_message}}. Это гарантирует, что TradingView отправит именно то JSON-сообщение, которое было сформировано в вашем Pine Script. - Нажмите ‘Создать’.
-
Настройте ваш вебхук-сервер: Вам понадобится небольшой серверный скрипт (например, на Python с Flask, Node.js с Express или PHP), который будет:
- Принимать POST-запросы на указанный
webhookUrl. - Парсить входящее JSON-сообщение.
- Анализировать поле
alertsв JSON, чтобы определить, какие из виртуальных алертов сработали (где"triggered": true). - На основе этой информации отправлять индивидуальные уведомления в нужные каналы (Telegram, email, Discord, Slack и т.д.).
Пример простейшего обработчика на Python (Flask):
from flask import Flask, request, jsonify import json app = Flask(__name__) @app.route('/alert', methods=['POST']) def handle_alert(): if request.is_json: data = request.get_json() print(f"Received JSON: {json.dumps(data, indent=2)}") # Extract relevant info symbol = data.get('symbol') interval = data.get('interval') timestamp = data.get('time') alerts = data.get('alerts', {}) triggered_alerts = [] for alert_id, alert_info in alerts.items(): if alert_info.get('triggered'): triggered_alerts.append(alert_info.get('name', alert_id)) if triggered_alerts: message = f"TradingView Alert on {symbol} ({interval}) at {timestamp}:\n" for ta_name in triggered_alerts: message += f"- {ta_name} triggered!\n" # Здесь вы можете отправить уведомление в Telegram, Email и т.д. print(f"Sending notification: {message}") # Пример: send_telegram_message(message) else: print("No specific alerts triggered in this payload.") return jsonify({"status": "success", "message": "Alert processed"}), 200 else: # Handle non-JSON payloads, e.g., if alertMessagePrefix is used without JSON raw_message = request.data.decode('utf-8') if raw_message.startswith("TV_ALERT "): try: json_part = raw_message[len("TV_ALERT "):] data = json.loads(json_part) # Process as above print(f"Received prefixed JSON: {json.dumps(data, indent=2)}") return jsonify({"status": "success", "message": "Prefixed JSON alert processed"}), 200 except json.JSONDecodeError: print(f"Received raw message: {raw_message}") return jsonify({"status": "error", "message": "Could not parse JSON from prefixed message"}), 400 else: print(f"Received raw message: {raw_message}") return jsonify({"status": "error", "message": "Invalid content type or format"}), 400 if __name__ == '__main__': app.run(host='0.0.0.0', port=5000) - Принимать POST-запросы на указанный
Таким образом, вы эффективно обходите лимит в 500 алертов, используя всего один активный алерт на TradingView, который служит шлюзом для передачи неограниченного количества сигналов на ваш собственный, полностью контролируемый сервер.




