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:
-
The URL must be HTTPS on a standard port and publicly reachable.
TradingView's servers make the request, not your browser, so
localhostis not an option. You will need real hosting or a tunnel. - Webhooks require an eligible TradingView plan and two-factor authentication on your account. If the webhook option is greyed out, that is usually why.
- There is no delivery guarantee and no retry you control. If your endpoint is down, times out, or returns an error, the alert is gone. This single fact drives most of the engineering in the second half of this article.
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
- API keys need rotating, and the rotation must not silently break the bridge.
- Your host will restart, run out of disk, or expire a TLS certificate.
- A dependency will publish a breaking change.
- You will want a kill switch — one action that stops everything, immediately, from your phone.
- You will want a record of what fired, what executed, and what did not.
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 trialNo 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
- Open an Alpaca paper account and confirm you can place an order manually.
- Get one alert firing to any endpoint and confirm the payload arrives intact.
- Only then connect the order placement.
- 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.
- 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.