# VS-07: Risk Alerts — Vertical Slice Specification **Domain:** Risk & Portfolio Management **Capability:** Monitor thresholds (concentration, VAR, volatility); trigger escalations **User Goal:** "I need automatic alerts when portfolio risk exceeds safe limits" --- ## Non-Goals - Custom alert rules (simple threshold library only) - SMS/Email delivery (platform abstraction, VS-09) - Alert aggregation/deduplication (separate) - AI-based anomaly detection (future) --- ## Requirements ### Functional | Req ID | Description | RBAC | SLA | Evidence | |--------|-------------|------|-----|----------| | **ALERT-001** | Monitor thresholds: concentration >60%, VAR >20%, volatility >30% | System | Real-time | Trigger job after VS-05 metrics | | **ALERT-002** | GET /api/portfolio/{id}/alerts | DataReader | <100ms | JSON array of active alerts | | **ALERT-003** | Support threshold configuration (per portfolio) | PortfolioManager | N/A | UI form (VS-08 FE) | | **ALERT-004** | Alert escalation: initial → warning → critical | System | <5min | Progressive notification | | **ALERT-005** | Soft-delete completed alerts (preserved for audit) | System | N/A | WHERE removed_at IS NULL | ### Non-Functional - **Accuracy:** Threshold breach detected within 5 minutes of metric update - **Latency:** Alert query <100ms, trigger <5min - **Noise:** False-positive rate <1% - **Audit:** Full alert lifecycle tracked (created → escalated → resolved) --- ## State Transitions ``` Portfolio Risk Metrics (from VS-05) ↓ threshold evaluation Threshold Breached? ├─ No → status=OK └─ Yes → create Alert(status=Initial) ↓ after 2 min (no resolution) Alert escalate to status=Warning ↓ after 3 min (still breached) Alert escalate to status=Critical ↓ user resolves Alert(status=Resolved, removed_at=now) ``` **Frequency:** Real-time (evaluated after each metric update) **Escalation:** Progressive (Initial → Warning → Critical over 5min) **Resolution:** Manual or automatic (threshold back to safe level) --- ## Data & API Contracts ### GET /api/portfolio/{portfolioId}/alerts **Response (200 OK):** ```json { "portfolioId": "550e8400-e29b-41d4-a716-446655440001", "activeAlerts": [ { "alertId": "550e8400-e29b-41d4-a716-446655440008", "thresholdType": "concentration", "thresholdName": "Top-5 Holdings > 60%", "currentValue": 65.2, "threshold": 60, "severity": "Warning", "triggeredAt": "2026-08-05T10:30:00Z", "escalatedAt": "2026-08-05T10:35:00Z", "message": "Top 5 holdings now represent 65.2% of portfolio (threshold: 60%)" }, { "alertId": "550e8400-e29b-41d4-a716-446655440009", "thresholdType": "volatility", "thresholdName": "Annualized Volatility > 30%", "currentValue": 31.5, "threshold": 30, "severity": "Initial", "triggeredAt": "2026-08-05T10:45:00Z", "escalatedAt": null, "message": "Portfolio volatility now 31.5% (threshold: 30%)" } ], "resolvedAlerts": [ { "alertId": "550e8400-e29b-41d4-a716-446655440010", "thresholdType": "concentration", "status": "Resolved", "resolvedAt": "2026-08-05T10:50:00Z", "duration": 20 } ] } ``` ### Events **RiskAlertTriggered:** ```json { "eventId": "550e8400-e29b-41d4-a716-446655440011", "eventType": "RiskAlertTriggered", "portfolioId": "550e8400-e29b-41d4-a716-446655440001", "alertId": "550e8400-e29b-41d4-a716-446655440008", "thresholdType": "concentration", "severity": "Warning", "currentValue": 65.2, "threshold": 60, "triggeredAt": "2026-08-05T10:30:00Z", "correlationId": "alert-2026-08-05-001" } ``` **RiskAlertResolved:** ```json { "eventId": "550e8400-e29b-41d4-a716-446655440012", "eventType": "RiskAlertResolved", "alertId": "550e8400-e29b-41d4-a716-446655440008", "resolvedAt": "2026-08-05T10:50:00Z", "durationMinutes": 20, "correlationId": "alert-2026-08-05-001" } ``` --- ## Threshold Library (Defaults) | Type | Default Threshold | Severity Escalation | |------|-------------------|---------------------| | Concentration (top-5) | 60% | Initial (0min) → Warning (2min) → Critical (5min) | | VAR-95 | 20% of portfolio | Initial (0min) → Warning (2min) → Critical (5min) | | Volatility (annual) | 30% | Initial (0min) → Warning (3min) → Critical (7min) | | Single position | 40% | Initial (0min) → Critical (5min) | --- ## RBAC & Authorization | Operation | Role | Condition | |-----------|------|-----------| | VIEW alerts | DataReader | Own portfolio only | | CONFIGURE thresholds | PortfolioManager | Own portfolio only | | RESOLVE alert | PortfolioManager | Own portfolio + manual action | | CREATE portfolio-level rules | RiskHead | Organization-wide override | --- ## Testing Strategy 1. **Unit:** Threshold evaluation (8 tests) - Concentration > threshold → alert triggered - VAR increase → alert escalated - Threshold back to safe → alert resolved 2. **Integration:** DB persistence (3 tests) - Insert alert - Escalate alert - Soft-delete resolved alert 3. **E2E:** API + escalation flow (3 tests) - Threshold breach → alert appears in API - Time-based escalation (Initial → Warning → Critical) - Resolution clears alert 4. **Golden:** Escalation timing (2 tests) - Known breach scenario → correct escalation at 2min, 5min - False positive rate <1% --- ## Assumptions - Thresholds are portfolio-specific (configurable per portfolio) - Escalation uses wall-clock time (not trading time) - Automatic resolution when metric returns to safe level - No deduplication (same threshold breach = one alert) --- ## Vertical Slice Boundary ✅ **In Scope:** Threshold evaluation + alert lifecycle + event publishing ❌ **Out of Scope:** Notification delivery (VS-09), alert aggregation, custom ML rules **Rationale:** Provides alert infrastructure; notifications/delivery separate concern