Tutorial 27 August 2026 · About 12 minutes

How to Auto-Execute TradingView Alerts on Alpaca (DIY vs No-Code)

TradingView can tell you a setup fired. Alpaca can place the order. The awkward part is that nothing officially connects the two — an alert notifies you, it does not execute. This guide covers what the webhook actually sends, how to build the bridge yourself with working code, and the failure cases that turn a weekend project into a liability.

The gap, stated plainly

If you connect Alpaca to TradingView through the built-in broker panel, you get a genuinely nice trading experience: your positions on the chart, an order ticket, one-click entries. What you do not get is unattended execution. That integration is built for a human clicking buttons. When your alert fires at 2am, it emails you. It does not place the order.

TradingView alerts can send a webhook — an HTTP POST to any URL you choose the moment the alert triggers. But Alpaca is not that URL. Alpaca's trading API expects an authenticated request in its own format, and TradingView has no idea how to produce one. Something has to sit in the middle: receive the alert, authenticate it, translate it into an Alpaca order, and handle everything that can go wrong.

You have two options. Build that middle piece yourself, or use a service that maintains it. This article covers both honestly, starting with the DIY route — because you should know exactly what you would be signing up to maintain before you decide.

Everything below uses a paper account. Alpaca paper trading is free and behaves like the real thing. Do not point any of this at a live account until you have watched it place, reject and close orders on paper for a while.

What a TradingView webhook actually sends

A webhook alert is simply an HTTP POST. The body is whatever you typed into the alert's message box, sent verbatim. TradingView does not impose a schema, which means the format is entirely your problem — and the reason so many DIY bridges break is that the message box is a plain text field with no validation.

Three practical constraints are worth knowing before you start:

Requests originate from a small, published set of TradingView IP addresses. At the time of writing those are:

52.89.214.238
34.212.75.30
54.218.53.128
52.32.178.7

Allow-listing them is worth doing, but treat it as a speed bump rather than a lock. IP allow-lists can go stale when a vendor changes infrastructure, and they do nothing about anyone else on the internet who guesses your URL. Real authentication comes from a shared secret in the payload, which we will add below.

Design your alert message as JSON

Because the message is free text, the sane move is to make it JSON and parse it on your side. A workable shape:

{
  "secret": "a-long-random-string-you-generate",
  "symbol": "{{ticker}}",
  "action": "buy",
  "quantity": 10
}

TradingView substitutes placeholders like {{ticker}}, {{close}}, {{time}} and {{strategy.order.action}} before sending. The last one is useful if you are firing from a strategy rather than a plain indicator alert, since it resolves to buy or sell automatically.

Watch the quoting. A placeholder that resolves to text needs quotes around it in your template, and one that resolves to a number must not have them. Getting this wrong produces malformed JSON that fails silently at 2am. Test every alert template by firing it manually once before you rely on it.

Building the bridge yourself

The minimum viable bridge is a small web service with one route. Here it is in Python with Flask. This is complete and it works — but read the section after it before you trust it with anything.

Step 1: get paper API keys

Create a free Alpaca account, switch to paper trading, and generate an API key ID and secret. Put them in environment variables. Never hard-code them, and never commit them — a leaked key with trading permission is a very bad day.

Step 2: the receiver

import os
import hmac
import logging

import requests
from flask import Flask, request, jsonify

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

ALPACA_BASE = "https://paper-api.alpaca.markets/v2"
ALPACA_HEADERS = {
    "APCA-API-KEY-ID": os.environ["ALPACA_KEY_ID"],
    "APCA-API-SECRET-KEY": os.environ["ALPACA_SECRET_KEY"],
}
WEBHOOK_SECRET = os.environ["WEBHOOK_SECRET"]


@app.post("/webhook")
def webhook():
    payload = request.get_json(silent=True)
    if payload is None:
        logging.warning("Rejected: body was not valid JSON")
        return jsonify(error="invalid json"), 400

    # compare_digest avoids leaking the secret via response timing
    if not hmac.compare_digest(str(payload.get("secret", "")), WEBHOOK_SECRET):
        logging.warning("Rejected: bad secret")
        return jsonify(error="unauthorized"), 401

    symbol = payload.get("symbol")
    action = payload.get("action")
    quantity = payload.get("quantity")

    if not symbol or action not in ("buy", "sell") or not quantity:
        logging.warning("Rejected: incomplete payload %s", payload)
        return jsonify(error="bad payload"), 400

    order = {
        "symbol": symbol,
        "qty": quantity,
        "side": action,
        "type": "market",
        "time_in_force": "day",
    }

    response = requests.post(
        f"{ALPACA_BASE}/orders",
        json=order,
        headers=ALPACA_HEADERS,
        timeout=10,
    )

    if response.status_code >= 400:
        # This is the line that matters most. See "what breaks" below.
        logging.error("Alpaca rejected the order: %s %s",
                      response.status_code, response.text)
        return jsonify(error="broker rejected"), 502

    logging.info("Order accepted: %s", response.json().get("id"))
    return jsonify(status="ok"), 200

Step 3: put it on the internet

TradingView has to reach it over HTTPS. For testing, a tunnel such as ngrok or Cloudflare Tunnel is fine. For anything you actually depend on you want real hosting — a small VM, a container service, or a serverless function. Whatever you pick now becomes something you patch, monitor and pay for indefinitely.

Step 4: point the alert at it

In TradingView, create your alert, open Notifications, tick Webhook URL, and paste your endpoint. Put the JSON from earlier into the message box. Save, then trigger it once deliberately and confirm an order appears in your Alpaca paper account.

If it works, congratulations — you have automated the happy path. That is roughly ten percent of the job.

What breaks, and why the happy path is the easy part

Everything below is a real failure mode that the code above handles badly or not at all. None of them are exotic. All of them will happen to you.

Rejections are invisible

The script logs a rejection and returns 502. Who reads that log? TradingView will not tell you. You will discover it days later when you check your positions and find that the trade you thought you were in never existed. Two extremely common rejections:

{"code": 40310000,
 "message": "insufficient balance for USD (requested: 160877.66, available: 99211.36)"}

{"code": 42210000, "message": "asset \"ZZZZZ\" not found"}

Both are perfectly clear — if someone sees them. Turning a broker rejection into a message that reaches a human within seconds is not optional for unattended trading, and it is the first thing DIY builds skip.

Opening and closing orders are not equally urgent

Sooner or later you will add a rate limit, because a misbehaving indicator that fires forty alerts in a minute is a genuine hazard. The trap is applying that limit uniformly.

Never let your own safety logic block an exit. Throttling a new position is prudent. Throttling the order that closes a losing position is how a small loss becomes a large one. Classify opening versus closing orders and treat them differently.

Duplicate and repeated alerts

TradingView can fire the same alert more than once — on bar close and again on retest, or simply because your condition remains true. The script above will dutifully place a second order. You need idempotency: a way to recognise "I have already acted on this signal" and do nothing the second time.

Stocks and crypto do not behave the same

Crypto trades continuously; equities do not. A market order sent to a closed equity market behaves differently from one sent at midday. Crypto uses fractional quantities and a different buying-power calculation — margin does not apply, so your cash balance is the real constraint. One code path for both asset classes will surprise you eventually.

The broker itself can be down

This is the one that separates a toy from a tool. When an order fails, there are two very different explanations: something is wrong with your account or your order, or something is wrong with the broker. They demand opposite responses, and you cannot tell them apart from an HTTP status code alone.

The dangerous instinct is to add a retry loop. During an outage, blind retries are how you end up with three copies of the same position once service returns. The safer design is to halt and report: stop, check the broker's status page, tell the human what happened and what was not done, and let them decide. Fail safe, never fail open.

Secrets, uptime and the long tail

None of this is intellectually hard. It is simply a permanent, unpaid, low-grade operational job attached to something that moves your money.

DIY versus a maintained service

Concern Build it yourself Maintained service
Up-front cost An afternoon for the happy path Minutes
Ongoing cost Hosting, plus your time, forever A subscription
Rejection alerts You build them Included
Duplicate protection You build it Included
Broker-outage handling You build it (and it is easy to get wrong) Included
Control and flexibility Total — change anything Bounded by what the service supports
Who is on call You Them

DIY is genuinely the right answer for some people. If your logic is unusual, you enjoy the infrastructure work, or you want no third party near your keys, build it. The code above is a reasonable starting point and you now know what to add to it.

If what you actually want is for the alert to place the order while you get on with your life, paying someone to own that problem is the cheaper trade.

That middle piece is what we maintain

TradingMixer receives your TradingView or TrendSpider alert and places the corresponding order in your own Alpaca account. It is non-custodial — your money stays with your broker, and we do not tell you what to trade. You keep the strategy; we run the plumbing described in this article, including failure-alert emails, duplicate protection, a kill switch, and the halt-and-report behaviour when Alpaca is having a bad day.

One plan, $9/month. Stocks and crypto, paper or live. New positions are rate-limited to one per webhook per 15 minutes, which suits swing and position trading on hourly-or-slower charts — orders that close a position are never rate-limited.

It is not for you if you are scalping sub-minute timeframes, need a broker other than Alpaca, or trade options — we support stocks and crypto only.

Start a 30-day free trial

No credit card required. Works with an Alpaca paper account, so you can watch it run before risking anything.

A sensible order to do this in

  1. Open an Alpaca paper account and confirm you can place an order manually.
  2. Get one alert firing to any endpoint and confirm the payload arrives intact.
  3. Only then connect the order placement.
  4. Deliberately break it: send a symbol that does not exist, and an order larger than your buying power. Confirm you find out about both without checking a log.
  5. Run it on paper across a few real signals before going live.

Step four is the one people skip, and it is the one that matters. An automation you cannot trust to tell you when it failed is worse than no automation, because you will act as though a position exists when it does not.

Trading carries risk of loss, and automating it does not reduce that risk — it removes the hesitation that sometimes saved you. Automate a strategy you already trust, on paper first.