Skip to main content

mts1b-tradingview

TradingView webhook → OMS bridge. Validates payload, normalizes order intent, forwards to mts1b-oms.

Repo: github.com/MTS1B/mts1b-tradingview Layer: 5 Wave: 2 (months 4-7) Depends on: foundation, platform, oms, FastAPI Audience: TradingView users who want to route alerts to MTS1B

What it is

A small FastAPI service that receives TradingView webhook payloads, validates them, dedupes, and submits to mts1b-oms. The OMS still applies all 7 risk gates — this service is just a translator.

Why a separate repo

TradingView alerts are popular but their payload format is messy and weakly authenticated. Isolating this in its own repo:

  • Keeps the OMS surface minimal (only one source-of-truth for orders)
  • Makes the auth + replay-protection logic auditable
  • Lets you disable the integration entirely without touching core trading code

Webhook format

TradingView lets you set arbitrary JSON in an alert. We define a strict schema:

{
"secret": "<shared secret from Vault>",
"strategy_id": "tv_breakout_v1",
"fund_id": "paper-tv-test",
"symbol": "BTC-USD",
"side": "buy",
"quantity": "0.05",
"order_type": "limit",
"limit_price": "95000",
"tif": "day",
"actor": "tradingview",
"idempotency_key": "{{strategy.id}}-{{time}}",
"thesis": "20-bar breakout with vol confirmation"
}

The idempotency_key should be derived from {{strategy.id}}-{{time}} (TradingView template variables) so reruns are de-duped.

Module layout

mts1b_tradingview/
├── api/
│ ├── webhook.py # POST /webhook handler
│ └── auth.py # shared-secret + IP allowlist
├── translator/
│ ├── normalizer.py # TV payload → foundation.Order
│ └── symbology.py # TV symbol formats → mts1b symbology
├── replay_protection/
│ └── dedupe.py
└── workers/
└── delivery.py # submit to OMS, retry on transient errors

Security

ThreatMitigation
Spoofed webhooksShared secret in payload + matching IP from TradingView's published egress list
Replay attacksidempotency_key uniqueness window (5 min)
Payload bloat4 KB body cap
DoS10 req/sec per fund_id rate limit
Wrong fund / wrong symbolStrict pydantic validation + envelope allows

The shared secret is per-fund and stored in Vault at secret/mts1b/tradingview/<fund_id>.

Operator setup

# In Vault: set secret for the fund
vault kv put secret/mts1b/tradingview/paper-tv-test \
webhook_secret=<random_32_char_string>

# Restart service
mts1b-deploy restart mts1b-tradingview

# Endpoint is now at:
# https://mts1b.investmentparadisellc.com/tradingview/webhook

Configure the alert in TradingView:

  • Webhook URL: https://mts1b.investmentparadisellc.com/tradingview/webhook
  • Message: the JSON template above with {{strategy.id}}, {{time}}, {{strategy.order.action}}, {{ticker}}

Symbology translation

TradingView format differs from MTS1B canonical:

TV formatMTS1B canonical
BINANCE:BTCUSDTBTC-USD (with venue=binance hint)
COINBASE:BTCUSDBTC-USD (with venue=coinbase hint)
NASDAQ:AAPLAAPL
BATS:SPYSPY
OANDA:EURUSDEUR-USD

The translator maps + emits a hint to OMS routing about preferred venue.

Replay protection

Within a 5-min window, two identical idempotency_keys → second is dropped with HTTP 409:

POST /tradingview/webhook
< 409 Conflict
{ "error": "duplicate_idempotency_key", "key": "tv_breakout_v1-1716496837" }

This is intentional — TradingView retries on failure, and you don't want a network blip to double-fire.

Observability

Standard metrics emitted:

  • tv_webhooks_total{fund_id, outcome} — accepted, rejected, duplicate
  • tv_webhook_latency_seconds
  • tv_orders_submitted_total
  • tv_orders_rejected_total{reason}

Demo

Quick smoke test:

curl -X POST https://mts1b.investmentparadisellc.com/tradingview/webhook \
-H "Content-Type: application/json" \
-d '{
"secret": "...",
"strategy_id": "demo",
"fund_id": "paper-tv-test",
"symbol": "BTC-USD",
"side": "buy",
"quantity": "0.001",
"order_type": "market",
"actor": "tradingview",
"idempotency_key": "manual-test-1"
}'

Build + test

pip install -e ".[dev]"
pytest -m unit
pytest -m integration # with OMS + NATS up

Roadmap

VersionItems
0.1 (Wave 2)Webhook + auth + replay protection + symbology translation
0.2 (Wave 2)Slack/Discord webhook adapters (same primitives)
0.3 (Wave 3)Inbound order-modification messages
1.0 (LTS)Stable webhook schema

See also