# VS-06: Stress Testing — Vertical Slice Specification

**Domain:** Risk & Portfolio Management  
**Capability:** Run scenario analysis (bull/bear/rate-shock/vol-spike); measure portfolio impact  
**User Goal:** "I need to understand how my portfolio performs under stressed market conditions"

---

## Non-Goals

- Reverse stress testing (maximum loss scenario)
- Monte Carlo simulations (future)
- Correlation structure changes (simplified model)
- Tail risk modeling (future)

---

## Requirements

### Functional

| Req ID | Description | RBAC | SLA | Evidence |
|--------|-------------|------|-----|----------|
| **STRESS-001** | POST /api/portfolio/{id}/stress | RiskAnalyst | 202 Accepted | Job queued + scenarioId |
| **STRESS-002** | Define 4 scenarios: Bull/Bear/RateShock/VolSpike | System | N/A | Hardcoded scenario library |
| **STRESS-003** | Calculate portfolio loss under each scenario | System | <30s | Batch processing |
| **STRESS-004** | Return scenario results with worst-case loss | System | <200ms (GET) | Sorted by impact |
| **STRESS-005** | Support custom scenario definition | RiskAnalyst | N/A | User-provided shocks |

### Non-Functional

- **Accuracy:** Scenario shocks calibrated to historical crises (2008, 2020)
- **Latency:** Batch calculations <30s, GET response <200ms
- **Audit:** Full scenario audit trail (inputs → outputs)
- **Reproducibility:** Same scenario + portfolio = deterministic results

---

## State Transitions

```
Portfolio (Current) + Risk Metrics (from VS-05)
  ↓ POST /stress (trigger scenario)
Stress Test Job (Queued via Hangfire)
  ↓ execution
Apply scenario shocks to prices → calculate new VAR/Sharpe
  ↓ results
Portfolio Stress Test Results (stored)
  ↓ event
PortfolioStressTestCompleted event published
  ↓ inbox consumer
Risk dashboard updated, alerts evaluated
```

**Frequency:** On-demand + daily overnight (pre-market analysis)  
**Idempotency:** Same `{portfolio_id, scenario_id, run_date, correlation_id}` → no re-run

---

## Scenario Library

| Scenario | Shock Applied | Use Case |
|----------|---------------|----------|
| **Bull** | +15% equity, -50 bps bond yields | Upside capture |
| **Bear** | -20% equity, +150 bps bond yields | Downside protection |
| **Rate Shock** | +200 bps rates (duration impact) | Rising rate risk |
| **Vol Spike** | +5x implied volatility | Derivatives exposure |

**Custom Scenarios:** User provides `{shock_type, magnitude, asset_class}`

---

## Data & API Contracts

### POST /api/portfolio/{portfolioId}/stress

**Request:**
```json
{
  "scenarioId": "bear",
  "parameters": {
    "equityShock": -0.20,
    "bondYieldShock": 0.015,
    "volatilityMultiplier": 1.5
  }
}
```

**Response (202 Accepted):**
```json
{
  "stressTestId": "550e8400-e29b-41d4-a716-446655440006",
  "portfolioId": "550e8400-e29b-41d4-a716-446655440001",
  "scenarioId": "bear",
  "status": "Queued",
  "correlationId": "stress-2026-08-05-001",
  "queuedAt": "2026-08-05T10:00:00Z"
}
```

### GET /api/portfolio/{portfolioId}/stress/{scenarioId}

**Response (200 OK):**
```json
{
  "stressTestId": "550e8400-e29b-41d4-a716-446655440006",
  "portfolioId": "550e8400-e29b-41d4-a716-446655440001",
  "scenarioId": "bear",
  "runDate": "2026-08-05",
  "results": {
    "baselineVAR95": 15250.00,
    "stressedVAR95": 42800.00,
    "varChange": {
      "amount": 27550.00,
      "percent": 180.7
    },
    "baslinePortfolioValue": 292500.00,
    "stressedPortfolioValue": 234000.00,
    "portfolioLoss": {
      "amount": 58500.00,
      "percent": -20.0
    },
    "exposureByAssetClass": [
      {
        "assetClass": "Equities",
        "baselineValue": 150000.00,
        "stressedValue": 120000.00,
        "loss": -30000.00
      },
      {
        "assetClass": "Bonds",
        "baselineValue": 142500.00,
        "stressedValue": 114000.00,
        "loss": -28500.00
      }
    ],
    "worstPosition": {
      "symbol": "AAPL",
      "loss": -15000.00
    }
  },
  "completedAt": "2026-08-05T10:05:00Z"
}
```

### Events

**PortfolioStressTestCompleted:**
```json
{
  "eventId": "550e8400-e29b-41d4-a716-446655440007",
  "eventType": "PortfolioStressTestCompleted",
  "portfolioId": "550e8400-e29b-41d4-a716-446655440001",
  "scenarioId": "bear",
  "stressedVAR95": 42800.00,
  "portfolioLossPercent": -20.0,
  "completedAt": "2026-08-05T10:05:00Z",
  "correlationId": "stress-2026-08-05-001"
}
```

---

## RBAC & Authorization

| Operation | Role | Condition |
|-----------|------|-----------|
| VIEW results | DataReader | Own portfolio only |
| TRIGGER test | RiskAnalyst | Own portfolio + standard scenarios |
| DEFINE scenario | RiskHead | Organization-wide scenarios |

---

## Testing Strategy

1. **Unit:** Scenario application (10 tests)
   - Apply equity shock to prices
   - Calculate new VAR under stressed prices
   - Measure portfolio loss

2. **Integration:** DB persistence (3 tests)
   - Insert stress test result
   - Query by scenario_id
   - Event published to outbox

3. **E2E:** API flow (2 tests)
   - POST /stress queues job
   - GET /stress returns results

4. **Golden:** Scenario accuracy (3 tests)
   - Known portfolio + known scenario = expected loss
   - Worst-case position identified
   - VAR increase reasonable

---

## Assumptions

- Scenarios are applied uniformly (no correlation changes)
- Bond prices use simple duration approximation (not full curve)
- Derivatives marked to market under new assumptions
- Scenario shocks are immediate (no gradual transition)

---

## Vertical Slice Boundary

✅ **In Scope:** Scenario definition + price shock application + loss calculation + event publishing  
❌ **Out of Scope:** Reverse stress testing (inverse scenario), correlation structure modeling

**Rationale:** Supports risk monitoring; results feed dashboard (VS-08) and alerts (VS-07)
