From Alert to Execution: A Decoupled Automation Architecture
A pragmatic pattern for turning TradingView alerts into validated, idempotent, observable messages — without coupling ingestion to execution.
When people wire TradingView alerts straight into order placement, the result is usually fragile: retries cause duplicates, a slow broker call blocks the webhook, and one bad payload takes everything down. A little architecture fixes all of it.
Engineering content only. This describes message plumbing, not what or whether to trade. Connecting to live execution is your responsibility and risk.
Three decoupled stages
- Ingress — authenticate, validate, deduplicate, enqueue, respond fast.
- Queue — a durable buffer (Redis list or stream) between ingress and work.
- Worker — pops messages and calls downstream logic, with explicit retries.
The point of separation: TradingView retries on any non-200, so ingress must answer immediately and never block on slow work.
app.post("/webhook", async (req, res) => {
if (req.body?.secret !== process.env.WEBHOOK_SECRET) return res.status(401).end();
const { id, symbol, action } = req.body;
if (!id || !symbol) return res.status(422).end();
const fresh = await redis.set(`seen:${id}`, "1", { NX: true, EX: 86400 });
if (!fresh) return res.json({ ok: true, duplicate: true });
await redis.lPush("alerts", JSON.stringify({ id, symbol, action }));
res.json({ ok: true });
});What to monitor
- Queue depth — a rising backlog means the worker can't keep up.
- Dead-letter count — anything that failed processing needs eyes.
- Ingress 4xx/5xx — retries amplify errors quickly.
Full walkthrough
The complete build — worker, Docker Compose, monitoring, and scaling to Redis Streams — is in the project guide Build a TradingView-to-Broker Webhook Bridge.
Decoupling, idempotency, and observability are not optional extras for automation — they are the difference between a toy and something you can trust to run unattended.