11.Alerts, strategy alerts, & webhooks
Two alert layers: simple price/threshold rules, and AND/OR/NOT strategy alerts composed over them. Three delivery channels: in-app toast, Discord webhook, Slack webhook, generic JSON POST. Fires are logged, retried with backoff, and replayable.
Layer 1 — simple alerts
GET/POST /api/alerts. Each alert row is a (symbol, condition, threshold, status) tuple:
- Price crosses above / below X.
- % change from open / prior close / arbitrary anchor > X.
- Volume > X / RVOL > X.
- Indicator condition — any custom-indicator expression (step 10).
- Time-of-day window — only fire between HH:MM and HH:MM ET.
Alerts evaluate against the live tick stream (step 06). Firing an alert pushes a row to alert_fires and dispatches the configured delivery (in-app toast at minimum; webhooks if attached). The alert row's status moves armed → fired; a manual toggle re-arms.
POST /api/alerts/{id}/toggle arms / disarms without deleting. POST /api/alerts/{id}/test sends a synthetic fire through the delivery chain so you can verify your Discord / Slack endpoint works without waiting for a real condition.
Layer 2 — strategy alerts (AND/OR/NOT)
GET/POST /api/strategy-alerts. A strategy alert is a boolean expression over base alerts:
// "AAPL gap-and-go signal" (AAPL_price_above_premarket_high AND AAPL_rvol_above_3) AND NOT halt_AAPL AND time_window_0930_1030
The composer is a GUI tree builder — drag base alerts in, choose AND / OR / NOT at each branch. The expression compiles to stryke bytecode (same VM as backtest / custom indicators) and evaluates on every base-alert state change rather than every tick — much cheaper.
Fires are logged to strategy_alert_fires with the expression's evaluation trace (which base alerts were true, which were false, the time the eval flipped). GET /api/strategy-alerts/fires is the query endpoint; the view is a paginated list with re-fire / replay buttons.
Webhook delivery — Discord, Slack, generic
Webhooks live in their own table (webhooks) and attach to alerts / strategy alerts as delivery targets. Three preset types:
- Discord — embeds with title / description / fields / color matching alert severity. Pings
@here/@everyoneconfigurable. - Slack — Block Kit payload with the same structured content. Channel routing via webhook URL.
- Generic JSON — POST a templated JSON body to an arbitrary URL. Template variables include alert ID, symbol, price, condition, timestamp, fire ID. Custom headers supported (e.g.
Authorization: Bearer …for your own backend).
Each webhook has a test button — POST /api/webhooks/{id}/test sends a fixed sample payload through. Use this when configuring the webhook URL the first time; you don't want to wait for a real alert to discover that you fat-fingered the URL.
Retry & dead-letter
Webhook POSTs that 5xx (or time out) are retried with exponential backoff: 5 s → 30 s → 5 min → 30 min → 2 h, capped at 5 attempts. After the cap the fire row is marked dead; the Webhooks view surfaces dead deliveries with a one-click manual-retry button.
4xx responses are not retried — a 401 from your Slack webhook means the URL is wrong; retrying makes it worse. The row is marked failed with the body of the 4xx response captured for debugging.
The Fires view
The Alerts → Fires view is a chronological log of every alert that fired in the date range. Each row shows the alert / strategy-alert that fired, the symbol, the price / value at fire time, the delivery status per channel, and a replay button that re-runs delivery (useful when you fix a webhook URL after a real fire was missed).
Right-click → Mute alert for 1 hr / today / forever when an alert is spamming. Re-arm later from the Alerts view.
What this composes with
- TTS voice — the same in-app voice synth that reads halts / scanner spikes also reads alert fires. Per-alert toggle.
- Browser notifications — under Tauri's custom scheme
Notification.requestPermissioncan throw; wrapped in try/catch. In web mode notifications work normally if the user grants permission. - System tray — the desktop binary has an optional tray icon that flashes on alert fire (configurable).