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
| Threat | Mitigation |
|---|---|
| Spoofed webhooks | Shared secret in payload + matching IP from TradingView's published egress list |
| Replay attacks | idempotency_key uniqueness window (5 min) |
| Payload bloat | 4 KB body cap |
| DoS | 10 req/sec per fund_id rate limit |
| Wrong fund / wrong symbol | Strict 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 format | MTS1B canonical |
|---|---|
BINANCE:BTCUSDT | BTC-USD (with venue=binance hint) |
COINBASE:BTCUSD | BTC-USD (with venue=coinbase hint) |
NASDAQ:AAPL | AAPL |
BATS:SPY | SPY |
OANDA:EURUSD | EUR-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, duplicatetv_webhook_latency_secondstv_orders_submitted_totaltv_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
| Version | Items |
|---|---|
| 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
mts1b-oms— downstream consumer- Foundation Order type — what TV payloads translate to