Crypto Bots

How to Use Webhooks for TradingView to Automate Trades

TradingView's free alert system can trigger real trades. Connect Pine Script alerts to a webhook endpoint and auto-execute strategies on any exchange in minutes.

A
AI Agents Hubยท2026-01-18ยท3 min readยท584 words

Builder of AI agents, crypto trading bots, and open-source automation tools. Sharing practical guides on how to build, deploy, and profit from AI and DeFi technology.

TradingView has over 50 million users โ€” many use it for charting but don't know its alerts can execute real trades. This guide shows you how to connect TradingView alerts to live trading in 15 minutes.

How It Works

TradingView can send an HTTP POST request (webhook) to any URL when an alert fires. Your server receives this webhook and executes the trade.

TradingView Alert โ†’ HTTP POST โ†’ Your Server โ†’ Exchange API

Step 1: Create the Alert in TradingView

Option A: Simple condition alert

  1. Open BTC/USDT chart
  2. Click the Alert button (clock icon) โ†’ Create Alert
  3. Condition: "RSI(14)" crosses below "30"
  4. Webhook URL: https://your-server.com/trade?secret=abc123
  5. Message body (JSON):
{
  "action": "buy",
  "symbol": "BTCUSDT",
  "price": "{{close}}",
  "time": "{{time}}"
}

Option B: Pine Script strategy alert

Add this to your Pine Script to trigger on specific signals:

//@version=5
indicator("RSI Alert Bot", overlay=false)

rsi = ta.rsi(close, 14)

// Alert when RSI crosses key levels
if ta.crossunder(rsi, 30)
    alert('{"action":"buy","symbol":"' + syminfo.ticker + '","price":"' + str.tostring(close) + '"}', alert.freq_once_per_bar_close)

if ta.crossover(rsi, 70)
    alert('{"action":"sell","symbol":"' + syminfo.ticker + '","price":"' + str.tostring(close) + '"}', alert.freq_once_per_bar_close)

plot(rsi)
hline(70, "Overbought", color.red)
hline(30, "Oversold", color.green)

Step 2: Minimal Webhook Server

from flask import Flask, request, jsonify
import ccxt, os, logging
from dotenv import load_dotenv

load_dotenv()
logging.basicConfig(level=logging.INFO)
app = Flask(__name__)

exchange = ccxt.binance({
    'apiKey': os.getenv('BINANCE_API_KEY'),
    'secret': os.getenv('BINANCE_SECRET'),
    'enableRateLimit': True,
})
if os.getenv('USE_TESTNET') == 'true':
    exchange.set_sandbox_mode(True)

WEBHOOK_SECRET = os.getenv('WEBHOOK_SECRET', 'changeme')

@app.route('/trade', methods=['POST'])
def handle_trade():
    if request.args.get('secret') != WEBHOOK_SECRET:
        return jsonify({'error': 'Unauthorized'}), 401
    
    data = request.json or {}
    action = data.get('action', '').lower()
    symbol = data.get('symbol', 'BTCUSDT')
    
    logging.info(f"Signal: {action} {symbol} @ {data.get('price')}")
    
    # Normalize symbol format
    if '/' not in symbol:
        symbol = symbol.replace('USDT', '/USDT')
    
    try:
        if action == 'buy':
            balance = exchange.fetch_balance()['USDT']['free']
            size = min(float(os.getenv('TRADE_SIZE_USDT', '50')), balance * 0.95)
            if size < 10:
                return jsonify({'status': 'skipped', 'reason': 'insufficient balance'})
            order = exchange.create_market_buy_order(symbol, None, {'quoteOrderQty': size})
            return jsonify({'status': 'bought', 'price': order['average'], 'qty': order['filled']})
        
        elif action == 'sell':
            base = symbol.split('/')[0]
            qty = exchange.fetch_balance()[base]['free']
            if qty < 0.0001:
                return jsonify({'status': 'skipped', 'reason': 'no position'})
            qty = float(exchange.amount_to_precision(symbol, qty))
            order = exchange.create_market_sell_order(symbol, qty)
            return jsonify({'status': 'sold', 'price': order['average'], 'qty': order['filled']})
        
        return jsonify({'status': 'no action'})
    
    except Exception as e:
        logging.error(f"Trade failed: {e}")
        return jsonify({'error': str(e)}), 500

@app.route('/health')
def health():
    return jsonify({'status': 'ok'})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(os.getenv('PORT', 8080)))

Step 3: Deploy Free in 5 Minutes

Railway.app (easiest):

# Create Procfile
echo "web: python app.py" > Procfile

# requirements.txt
echo "flask\nccxt\npython-dotenv\ngunicorn" > requirements.txt

# Deploy
railway login
railway init
railway up

Railway gives you a public HTTPS URL instantly. Paste it in TradingView's webhook field.

Configure via Environment Variables

Set these in your server's environment (Railway โ†’ Variables):

BINANCE_API_KEY=your_key
BINANCE_SECRET=your_secret
WEBHOOK_SECRET=random_secret_string_here
TRADE_SIZE_USDT=100
USE_TESTNET=false

Testing Before Going Live

  1. Set USE_TESTNET=true and get Binance testnet credentials
  2. Create a TradingView alert with your webhook URL
  3. Trigger it manually: TradingView โ†’ Alert โ†’ "Fire" button
  4. Check your server logs and testnet account

When you're confident it works, switch to real API keys and set USE_TESTNET=false.

Common Issues

"Order not found" on Binance: Symbol format mismatch. Make sure BTCUSDT becomes BTC/USDT in ccxt.

Webhook doesn't fire: TradingView needs the webhook URL to respond with 200 status within 5 seconds. Make sure your server is running and accessible.

Trade executes twice: Set alert.freq_once_per_bar_close in Pine Script to prevent firing on bar updates.

For a more complete implementation with stop-losses and monitoring, see our full trading bot guide.

Related Articles