# Physics-Informed Neural Network (PINN) Predictor

> **Status**: Phase 4 Complete (Correlation-Aware Portfolio PINN)
> **Migration**: 244
> **Module**: `packages/be/src/trading/algorithms/pinn/`

---

## Overview

The PINN predictor is a trainable neural network that embeds trading physics constraints directly into the learning process. Unlike traditional ML approaches that apply guardrails post-prediction, PINN learns *constrained dynamics* from the start.

**Key Advantages:**
- Physically plausible predictions (velocity consistent with price change)
- Liquidation risk awareness built into the model
- Kelly sizing constraints as soft penalties
- Better extrapolation on out-of-distribution regimes

---

## Architecture

```
┌─────────────────────────────────────────────────────────────────────┐
│                         PINN Predictor                               │
│  ┌─────────────┐    ┌──────────────┐    ┌─────────────────────────┐ │
│  │ Input Layer │ →  │ Hidden Layers│ →  │ Physics-Constrained     │ │
│  │ [16 features│    │ (4x64 ReLU)  │    │ Output Layer            │ │
│  │  x 20 bars] │    │              │    │ [price_t+1, dS/dt,      │ │
│  │             │    │              │    │  liquidation_risk]      │ │
│  └─────────────┘    └──────────────┘    └─────────────────────────┘ │
│         ↓                                         ↓                  │
│  ┌─────────────────────────────────────────────────────────────────┐│
│  │                    Physics Loss Function                        ││
│  │  L = MSE(forecast) + λ₁·|physics_residual| + λ₂·|constraint|   ││
│  │      + λ₃·|kelly_violation|                                    ││
│  └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘
```

### Input Features (16 dimensions)

| # | Feature | Range | Description |
|---|---------|-------|-------------|
| 1 | `priceNormalized` | 0-1 | Current price normalized to training range |
| 2 | `returns5d` | -1 to 1 | 5-day return (scaled) |
| 3 | `returns20d` | -1 to 1 | 20-day return (scaled) |
| 4 | `volatility10d` | 0-1 | 10-day realized volatility |
| 5 | `volatility30d` | 0-1 | 30-day realized volatility |
| 6 | `rsi14` | 0-1 | RSI normalized |
| 7 | `hurstExponent` | 0-1 | Mean-reversion indicator |
| 8-12 | `regime*` | 0/1 | One-hot: BULL/BEAR/SIDEWAYS/CRISIS/RECOVERY |
| 13 | `leverage` | 0-1 | Current leverage / 125 |
| 14 | `marginUsage` | 0-1 | Margin utilization |
| 15 | `liquidationDistance` | 0-1 | Distance to liquidation price |
| 16 | `volumeNormalized` | 0-1 | Volume relative to 20d avg |

### Output (3 dimensions)

| Output | Activation | Description |
|--------|------------|-------------|
| `price_t1` | Sigmoid (0-1) | Next-bar price (normalized) |
| `velocity` (dS/dt) | Tanh (-1 to 1) | Price velocity / rate of change |
| `liquidation_risk` | Sigmoid (0-1) | Probability of liquidation |

### Sequence Length

- **20 bars** lookback window
- Minimum 100 bars required for training
- Minimum 50 bars for Hurst exponent calculation

---

## Physics Loss Function

The loss combines four components:

```typescript
L_total = λ_mse * MSE(price)
        + λ_physics * |predicted_velocity - actual_velocity|
        + λ_constraint * MSE(liquidation_risk)
        + λ_kelly * ReLU(|velocity| - max_position)
```

**Default Weights:**
| Component | Weight | Purpose |
|-----------|--------|---------|
| MSE | 1.0 | Standard prediction accuracy |
| Physics | 0.1 | Velocity should match price change |
| Constraint | 0.05 | Liquidation risk calibration |
| Kelly | 0.02 | Penalize predictions exceeding Kelly bounds |

---

## Files

### Core Module (`packages/be/src/trading/algorithms/pinn/`)

| File | Lines | Purpose |
|------|-------|---------|
| `types.ts` | 305 | Type definitions for features, outputs, configs, metrics |
| `features.ts` | 400 | Feature engineering: RSI, Hurst, regime detection, normalization |
| `model.ts` | 270 | TensorFlow.js model architecture + physics loss function |
| `trainer.ts` | 330 | Training pipeline with early stopping + constraint validation |
| `predictor.ts` | 470 | Main prediction function returning `AlgorithmPrediction` |
| `persistence.ts` | 450 | S3 model save/load with in-memory caching |
| `validation.ts` | 400 | Physics constraint + graduation gate validation |
| `tensorflow.d.ts` | 170 | TypeScript stubs for TensorFlow.js |
| `index.ts` | 90 | Module exports |

### Integration Points

| File | Change |
|------|--------|
| `packages/shared/src/trading.ts` | Added `'pinn'` to `AlgorithmName` |
| `packages/be/package.json` | Added `@tensorflow/tfjs-node` |
| `packages/be/src/trading/algorithms/index.ts` | Registered PINN (weight 0.22) |
| `packages/be/src/trading/algorithms/ensemble-predictor.ts` | Added `runPINNPrediction` case |
| `packages/be/src/trading/services/signal-generator.ts` | Updated default weights |
| `packages/be/src/trading/services/learning-feedback.ts` | Added PINN to weight lookups |

### Database (Migration 244)

```sql
predict.pinn_models          -- Model metadata storage
predict.pinn_training_jobs   -- Training job tracking
predict.pinn_predictions     -- Prediction audit log
predict.pinn_model_performance -- Performance metrics over time
```

---

## Usage

### Basic Prediction

```typescript
import { runPINNPrediction } from '@agencio-predict/be/trading/algorithms';

const prediction = await runPINNPrediction(
  'AAPL',           // symbol
  priceHistory,     // PriceHistory[]
  '1d',             // timeframe
  1,                // leverage (optional, default 1)
  0,                // marginUsage (optional, default 0)
  0                 // liquidationPrice (optional, default 0)
);

// Returns AlgorithmPrediction:
// {
//   algorithm: 'pinn',
//   direction: 'long' | 'short' | 'neutral',
//   confidence: 0.65,
//   magnitude: 2.5,
//   timeframe: '1d',
//   signals: [...],
//   metadata: {
//     predictedPrice: 185.50,
//     currentPrice: 181.25,
//     velocity: 0.023,
//     liquidationRisk: 0.05,
//     physicsResidual: 0.008,
//     hurstExponent: 0.42,
//     marketRegime: 'BULL',
//     ...
//   }
// }
```

### Force Retrain

```typescript
import { forceRetrainPINNModel } from '@agencio-predict/be/trading/algorithms';

const success = await forceRetrainPINNModel('AAPL', priceHistory);
```

### Get Model Info

```typescript
import { getPINNModelInfo } from '@agencio-predict/be/trading/algorithms';

const info = await getPINNModelInfo('AAPL');
// { exists: true, cached: true, inMemory: false, metadata: { ... } }
```

### Clear Cache

```typescript
import { clearInMemoryModels } from '@agencio-predict/be/trading/algorithms';

clearInMemoryModels(); // Disposes all cached TensorFlow models
```

---

## Training Strategy

### On-Demand Per Symbol

Models are trained when first requested, not on a schedule:

1. User requests prediction for symbol (e.g., SPY)
2. Check if trained model exists in cache (memory) or S3
3. If exists and <24h old → use cached model
4. If missing or stale → train new model (~30-60s)
5. Cache in memory + persist to S3
6. Return prediction

### Training Configuration

```typescript
const DEFAULT_TRAIN_CONFIG = {
  epochs: 100,              // Max epochs (early stopping usually stops earlier)
  batchSize: 32,
  learningRate: 0.001,
  validationSplit: 0.2,     // 80/20 train/val split
  earlyStoppingPatience: 10,
  lossWeights: {
    mse: 1.0,
    physics: 0.1,
    constraint: 0.05,
    kelly: 0.02,
  },
  physicsConstraints: {
    frictionCoefficient: 0.001,
    drift: 0.0001,
    sigma: 0.02,
    liquidationSensitivity: 10,
    kellyMultiplier: 0.5,
    maxPosition: 0.25,
  },
};
```

### Quick Training (On-Demand)

For on-demand requests, a faster config is used:
- epochs: 30 (vs 100)
- earlyStoppingPatience: 5 (vs 10)

---

## Validation & Graduation Gates

### Validation Thresholds

| Metric | Threshold | Purpose |
|--------|-----------|---------|
| Physics Residual | ≤ 0.05 | Velocity prediction accuracy |
| Constraint Violations | ≤ 5% | Kelly/liquidation constraint compliance |
| Directional Accuracy | ≥ 52% | Better than coin flip |
| Brier Score | ≤ 0.25 | Liquidation risk calibration |

### Graduation Gates (for production deployment)

Before PINN replaces LSTM as the primary predictor in production:

1. **Physics Gate**: Avg residual < 0.05
2. **Accuracy Gate**: Directional accuracy > 52%
3. **Risk Gate**: Brier score < 0.25
4. **Constraint Gate**: Violation rate < 5%
5. **Sharpe Gate**: Validation Sharpe > 0.5 (if trading metrics available)
6. **Drawdown Gate**: Max DD < 20%
7. **Win Rate Gate**: Win rate > 45%

---

## Algorithm Weights

PINN is now the primary ML predictor with the highest weight:

| Algorithm | Previous Weight | New Weight |
|-----------|-----------------|------------|
| **pinn** | - | **0.22** |
| lstm | 0.20 | 0.10 |
| xgboost | 0.18 | 0.18 |
| arima | 0.12 | 0.10 |
| garch | 0.10 | 0.08 |
| random_forest | 0.15 | 0.10 |
| technical | 0.12 | 0.12 |
| sentiment | 0.08 | 0.08 |
| ensemble | 0.05 | 0.02 |

---

## Model Persistence

### S3 Storage

- **Bucket**: `predict-pinn-models` (configurable via `PINN_MODELS_BUCKET`)
- **Path**: `models/{SYMBOL}/{model_id}/`
  - `model.json` — Serialized TensorFlow.js model weights
  - `metadata.json` — Model metadata (training config, validation metrics)
  - `norm_params.json` — Normalization parameters for inference

### In-Memory Cache

- **TTL**: 24 hours
- **Max Size**: 10 models (LRU eviction)
- **Fallback**: If S3 unavailable, uses in-memory storage only

---

## Metadata in AlgorithmPrediction

The PINN predictor adds rich metadata to predictions:

```typescript
metadata: {
  predictedPrice: number,      // Denormalized predicted price
  currentPrice: number,        // Latest bar close
  velocity: number,            // dS/dt from model
  liquidationRisk: number,     // 0-1 probability
  physicsResidual: number,     // |predicted_velocity - actual|
  constraintSatisfaction: number, // 1 - liquidation_risk
  hurstExponent: number,       // Mean-reversion indicator
  hurstInterpretation: 'mean_reverting' | 'random_walk' | 'trending',
  marketRegime: 'BULL' | 'BEAR' | 'SIDEWAYS' | 'CRISIS' | 'RECOVERY',
  regimeConfidence: number,    // 0-1 regime detection confidence
  leverage: number,            // Input leverage used
}
```

---

## Signal Contributions

Each PINN prediction generates 3 signal contributions:

1. **pinn_price** (weight 0.5)
   - Source: Price prediction
   - Value: Percentage change
   - Direction: Based on pct change (>0.5% = long, <-0.5% = short)

2. **pinn_velocity** (weight 0.3)
   - Source: dS/dt output
   - Value: Velocity
   - Direction: Based on velocity sign

3. **liquidation_risk** (weight 0.2)
   - Source: Liquidation risk output
   - Value: Risk probability
   - Direction: If >0.5, signals caution (short bias)

---

## Dependencies

```json
{
  "@tensorflow/tfjs-node": "^4.22.0"
}
```

**Note**: TensorFlow.js Node requires native bindings. On deployment:
- Linux: Auto-installs via npm
- Mac: May need XCode CLI tools
- Windows: May need Visual Studio Build Tools

---

## Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `PINN_MODELS_BUCKET` | `predict-pinn-models` | S3 bucket for model storage |
| `AWS_REGION` | `ap-southeast-1` | AWS region for S3 |

---

## Phase 2A: Volatility PINN with Heston Model

Phase 2A adds stochastic volatility modeling based on the Heston model, with three key enhancements:

### Heston Model Overview

The Heston stochastic volatility model describes variance dynamics as:

```
dv = κ(θ - v)dt + σ_v √v dW_v
dS = μS dt + √v S dW_S
dW_S · dW_v = ρ dt
```

Where:
- **κ (kappa)**: Mean reversion speed — how fast variance reverts to θ
- **θ (theta)**: Long-run variance — equilibrium variance level
- **σ_v (sigmaV)**: Volatility of volatility — how volatile the variance is
- **ρ (rho)**: Price-volatility correlation — leverage effect, typically negative

### 1. Feller Condition

The Feller condition ensures the variance process stays strictly positive:

```
2κθ > σ_v²
```

**Implementation:**
- Loss function penalizes violations: `L_feller = max(0, σ_v² - 2κθ)`
- `checkFellerCondition()` returns satisfaction status and ratio
- `VolatilityPrediction` includes `fellerResidual` metric
- Validation tracks `fellerViolationRate` and `avgFellerRatio`

**Location:** `volatility-model.ts:412-424`, `volatility-loss.ts:146-151`

### 2. Correlated Brownian Motions (Leverage Effect)

Models the correlation ρ between price returns and volatility changes:

- **Equities**: ρ ≈ -0.5 to -0.7 (volatility increases when prices drop)
- **Crypto**: ρ can be positive or near zero

**Implementation:**
- Feature: `priceVolCorrelation` computed via `computePriceVolCorrelation()`
- Output head: tanh activation constrains ρ to [-1, 1]
- Loss: `L_corr = MSE(ρ_predicted, ρ_empirical)`
- `VolatilityPrediction` includes `correlationResidual`

**Location:** `volatility-features.ts:306-349`, `volatility-loss.ts:153-167`

### 3. MC Dropout Uncertainty Quantification

Monte Carlo Dropout provides prediction intervals without ensemble training:

**How it works:**
1. Run N forward passes (default: 30) with dropout enabled
2. Collect samples for volatility, κ, θ, σ_v, ρ
3. Compute mean, std, 5th/95th percentiles
4. Track Feller satisfaction rate across samples

**Implementation:**
- `predictVolatilityWithMCDropout()` uses `model.apply()` with `training: true`
- `aggregateMCDropoutSamples()` computes uncertainty statistics
- `VolatilityPrediction` optionally includes `uncertainty` field

**Location:** `volatility-model.ts:550-664`

### Volatility PINN Output (10 dimensions)

| Index | Output | Activation | Description |
|-------|--------|------------|-------------|
| 0 | volatilityForecast | softplus | σ² (daily variance) |
| 1-3 | regimeLogits | linear | [low_vol, normal_vol, high_vol] |
| 4 | persistence | sigmoid | GARCH persistence (α + β) |
| 5 | alphaRatio | sigmoid | α / persistence ratio |
| 6 | kappa | softplus | Mean reversion speed |
| 7 | theta | softplus | Long-run variance |
| 8 | sigmaV | softplus | Vol-of-vol |
| 9 | rho | tanh | Price-vol correlation |

### Loss Function (Phase 2A)

```typescript
L_total = λ_vol * MSE(σ²)
        + λ_regime * CrossEntropy(regime)
        + λ_garch * |α,β residual|
        + λ_constraint * (neg_var + stationarity)
        + λ_markov * smoothness
        + λ_feller * max(0, σ_v² - 2κθ)      // Phase 2A
        + λ_corr * MSE(ρ_pred, ρ_empirical)  // Phase 2A
        + λ_heston * param_regularization     // Phase 2A
```

**Default Weights (Phase 2A):**
| Component | Weight | Purpose |
|-----------|--------|---------|
| volatility | 1.0 | Variance forecast accuracy |
| regime | 0.5 | Regime classification |
| garch | 0.3 | GARCH parameter matching |
| constraint | 10.0 | Hard constraints (σ² ≥ 0, α + β < 1) |
| markov | 0.1 | Regime transition smoothness |
| **feller** | **5.0** | Feller condition (Phase 2A) |
| **correlation** | **0.3** | Price-vol correlation (Phase 2A) |
| **hestonRegularization** | **0.1** | Heston param bounds (Phase 2A) |

### Input Features (14 dimensions)

Phase 2A adds 2 new features to the original 12:

| # | Feature | Range | Description |
|---|---------|-------|-------------|
| 1-12 | (original) | ... | See original feature list |
| 13 | `priceVolCorrelation` | 0-1 | Rolling ρ estimate (mapped from [-1,1]) |
| 14 | `volOfVolEstimate` | 0-1 | Rolling σ_v estimate |

### Validation Metrics (Phase 2A)

New metrics added to `VolatilityValidationMetrics`:

| Metric | Description |
|--------|-------------|
| `fellerViolationRate` | Rate of 2κθ < σ_v² violations (should be 0) |
| `avgFellerRatio` | Average 2κθ/σ_v² (should be > 1) |
| `correlationMAE` | Error on predicted ρ vs empirical |
| `avgKappa` | Average learned mean reversion speed |
| `avgTheta` | Average learned long-run variance |
| `avgSigmaV` | Average learned vol-of-vol |
| `avgRho` | Average learned price-vol correlation |

---

## Phase 2B: Joint Spot-Vol Dynamics

Phase 2B extends the volatility PINN to jointly model price (S) and volatility (v) under the full Heston stochastic volatility framework. This is based on SSRN-4598180 (Noguer i Alonso & Camarena, 2023): "Physics-Informed Neural Networks (PINNs) in Finance".

### Heston Joint SDEs

The model learns parameters for the coupled Heston SDEs:

```
dS = μS dt + √v S dW_S       (price dynamics)
dv = κ(θ - v)dt + σ_v √v dW_v  (volatility dynamics)
Corr(dW_S, dW_v) = ρ          (leverage effect)
```

The physics loss enforces both SDEs:

```
L_physics = |dS/S - μdt - √v·ε_S|² + |dv - κ(θ-v)dt - σ_v√v·ε_v|²
```

### Architecture

The joint Spot-Vol model uses a shared encoder with multi-head outputs:

```
┌─────────────────────────────────────────────────────────────────────┐
│                    Joint Spot-Vol PINN                              │
│  ┌─────────────┐    ┌──────────────┐    ┌─────────────────────────┐ │
│  │ Input Layer │ →  │ Shared LSTM  │ →  │ Multi-Head Output:      │ │
│  │ [18 features│    │ (64 units)   │    │ - Price: Δlog(S), v, μ  │ │
│  │  x 20 bars] │    │ + 3x Dense   │    │ - Vol: σ²               │ │
│  │             │    │   (residual) │    │ - Regime: low/med/high  │ │
│  │             │    │              │    │ - GARCH: α, β           │ │
│  │             │    │              │    │ - Heston: κ, θ, σ_v, ρ  │ │
│  └─────────────┘    └──────────────┘    └─────────────────────────┘ │
│                                                  ↓                   │
│  ┌─────────────────────────────────────────────────────────────────┐│
│  │                  Heston Physics Loss                            ││
│  │  L = L_data + L_price_SDE + L_vol_SDE + L_constraints           ││
│  └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘
```

### Input Features (18 dimensions)

Phase 2B adds 4 price features to the 14 volatility features:

| # | Feature | Range | Description |
|---|---------|-------|-------------|
| 1 | `logPrice` | [-1, 1] | Normalized log-price: log(S/S₀) |
| 2 | `recentReturn` | [-0.2, 0.2] | Single-bar return (clipped) |
| 3 | `momentum5d` | [-0.5, 0.5] | 5-day momentum: log(S_t/S_{t-5}) |
| 4 | `momentum20d` | [-1, 1] | 20-day momentum: log(S_t/S_{t-20}) |
| 5-18 | (volatility features) | ... | See Phase 2A feature list |

### Output (13 dimensions)

| Index | Output | Activation | Description |
|-------|--------|------------|-------------|
| 0 | logPriceChange | linear | Predicted Δlog(S) |
| 1 | priceVelocity | tanh | dS/dt / S (scaled ±10%) |
| 2 | drift | sigmoid | μ (scaled to [0, 0.2]) |
| 3 | volatilityForecast | softplus | σ² (daily variance) |
| 4-6 | regimeLogits | linear | [low_vol, normal_vol, high_vol] |
| 7 | persistence | sigmoid | GARCH α + β |
| 8 | alphaRatio | sigmoid | α ratio for joint GARCH |
| 9 | kappa | softplus | Mean reversion speed (κ) |
| 10 | theta | softplus | Long-run variance (θ) |
| 11 | sigmaV | softplus | Vol-of-vol (σ_v) |
| 12 | rho | tanh | Price-vol correlation (ρ) |

### Loss Function (Phase 2B)

```typescript
L_total = λ_price * MSE(return)              // Price prediction
        + λ_vol * MSE(variance)               // Volatility prediction
        + λ_regime * CrossEntropy(regime)     // Regime classification
        + λ_priceSde * |price_SDE_residual|²  // Heston price SDE
        + λ_volSde * |vol_SDE_residual|²      // Heston vol SDE
        + λ_garch * |α,β residual|            // GARCH constraints
        + λ_constraint * (neg_var + stationarity)
        + λ_feller * max(0, σ_v² - 2κθ)       // Feller condition
        + λ_corr * MSE(ρ_pred, ρ_empirical)   // Correlation matching
        + λ_heston * param_regularization     // Heston bounds
```

**Default Weights (Phase 2B):**
| Component | Weight | Purpose |
|-----------|--------|---------|
| price | 1.0 | Return prediction accuracy |
| volatility | 1.0 | Variance forecast accuracy |
| regime | 0.3 | Regime classification |
| **priceSde** | **0.5** | Heston price SDE residual |
| **volSde** | **0.5** | Heston vol SDE residual |
| garch | 0.2 | GARCH parameter matching |
| constraint | 10.0 | Hard constraints |
| feller | 5.0 | Feller condition |
| correlation | 0.3 | Price-vol correlation |
| hestonRegularization | 0.1 | Heston param bounds |

### Validation Metrics (Phase 2B)

New metrics for joint price-volatility validation:

| Metric | Description |
|--------|-------------|
| `returnMAE` | Mean absolute error on return predictions |
| `returnRMSE` | RMSE on return predictions |
| `directionalAccuracy` | Price direction accuracy (up/down) |
| `priceSdeResidual` | Average Heston price SDE residual |
| `volSdeResidual` | Average Heston vol SDE residual |
| `combinedSdeResidual` | Sum of price + vol SDE residuals |

### Graduation Gates (Phase 2B)

| Gate | Threshold | Description |
|------|-----------|-------------|
| Return MAE | ≤ 2% | Mean absolute error on daily returns |
| Directional Accuracy | ≥ 52% | Better than random on direction |
| Vol Forecast MAE | ≤ 3% | Annualized volatility error |
| Regime Accuracy | ≥ 60% | 3-class regime classification |
| Feller Violation | ≤ 10% | Rate of Feller condition failures |
| Price SDE Residual | ≤ 0.1 | Heston price dynamics compliance |
| Vol SDE Residual | ≤ 0.1 | Heston vol dynamics compliance |
| Correlation MAE | ≤ 0.2 | Accuracy on ρ estimation |

### Files (Phase 2B)

| File | Lines | Purpose |
|------|-------|---------|
| `spot-vol-model.ts` | ~540 | Joint architecture with shared encoder |
| `spot-vol-loss.ts` | ~430 | Heston SDE physics-informed loss |
| `spot-vol-features.ts` | ~450 | 18-dim feature engineering (price + vol) |
| `spot-vol-predictor.ts` | ~570 | Main prediction + training pipeline |
| `spot-vol-validation.ts` | ~540 | Validation metrics + graduation gates |

### Usage (Phase 2B)

```typescript
import { runSpotVolPrediction } from '@agencio-predict/be/trading/algorithms/pinn/volatility';

const prediction = await runSpotVolPrediction('AAPL', priceHistory, 'stock');

// Returns SpotVolPrediction:
// {
//   // Price predictions
//   expectedReturn: 0.0023,
//   priceVelocity: 0.015,
//   drift: 0.05,
//   priceDirection: 'up',
//
//   // Volatility predictions
//   volatility: 18.5,         // Annualized %
//   dailyVolatility: 1.17,
//   dailyVariance: 0.000136,
//   regime: 'normal_vol',
//   regimeConfidence: 0.72,
//
//   // Heston parameters
//   hestonParams: {
//     kappa: 2.1,    // Mean reversion
//     theta: 0.038,  // Long-run variance
//     sigmaV: 0.28,  // Vol-of-vol
//     rho: -0.65,    // Leverage effect
//     fellerSatisfied: true,
//     fellerRatio: 1.94,
//   },
//
//   // Physics compliance
//   priceSdeResidual: 0.008,
//   volSdeResidual: 0.012,
//   combinedSdeResidual: 0.020,
// }
```

### MC Dropout for Spot-Vol

```typescript
import { runSpotVolPredictionWithUncertainty } from '@agencio-predict/be/trading/algorithms/pinn/volatility';

const result = await runSpotVolPredictionWithUncertainty('AAPL', bars, 'stock', 30);

// Returns prediction + uncertainty:
// {
//   prediction: SpotVolPrediction,
//   uncertainty: {
//     returnStd: 0.0045,
//     volatilityStd: 1.2,
//     regimeEntropy: 0.35,  // Normalized [0,1]
//     confidenceInterval95: {
//       returnLow: -0.0067,
//       returnHigh: 0.0113,
//       volLow: 16.1,
//       volHigh: 20.9,
//     }
//   }
// }
```

---

## Phase 2C: Multi-Horizon Volatility Term Structure

Phase 2C extends the Spot-Vol PINN to predict volatility at multiple horizons (1d, 5d, 20d, 60d) and learn Nelson-Siegel term structure parameters. This enables forecasting the entire volatility term structure rather than just spot volatility.

**Key Features:**
- Multi-horizon variance forecasts (1d, 5d, 20d, 60d)
- Nelson-Siegel term structure fitting
- Term structure shape classification (contango/backwardation/flat/humped)
- Mean reversion consistency enforcement
- Term structure smoothness regularization

### Theory: Nelson-Siegel Model

The volatility term structure is modeled using the Nelson-Siegel parameterization:

```
σ(τ) = β₀ + β₁ * (1 - e^{-λτ}) / (λτ) + β₂ * ((1 - e^{-λτ}) / (λτ) - e^{-λτ})
```

Where:
- **β₀** = Long-run volatility level
- **β₁** = Short-run slope factor (deviation from long-run)
- **β₂** = Curvature factor (medium-term hump)
- **λ** = Decay rate

### Input Features (22 dimensions)

Phase 2C adds 4 term structure features to the 18 Spot-Vol features:

| # | Feature | Range | Description |
|---|---------|-------|-------------|
| 1-18 | (Spot-Vol features) | ... | See Phase 2B feature list |
| 19 | `termStructureSlope` | -1 to 1 | Normalized slope (60d - 1d) |
| 20 | `termStructureCurvature` | -1 to 1 | Normalized curvature |
| 21 | `longShortVolRatio` | 0 to 1 | 60d vol / 1d vol (normalized) |
| 22 | `isContango` | 0 or 1 | Shape indicator |

### Output (19 dimensions)

Phase 2C extends the output to include multi-horizon variances and Nelson-Siegel parameters:

| Output | Range | Description |
|--------|-------|-------------|
| `logPriceChange` | unconstrained | Next-day log return |
| `priceVelocity` | -0.1 to 0.1 | Price velocity |
| `drift` | 0 to 0.2 | Drift coefficient |
| `volatility1d` | > 0 | 1-day variance forecast |
| `regimeLogits[3]` | unconstrained | Regime probabilities |
| `persistence` | 0 to 0.99 | GARCH α + β |
| `alphaRatio` | 0 to 1 | α / (α + β) |
| `kappa` | > 0.1 | Heston mean reversion speed |
| `theta` | > 0.001 | Heston long-run variance |
| `sigmaV` | > 0.05 | Volatility of volatility |
| `rho` | -0.99 to 0.99 | Price-vol correlation |
| `volatility5d` | > 0 | 5-day variance forecast |
| `volatility20d` | > 0 | 20-day variance forecast |
| `volatility60d` | > 0 | 60-day variance forecast |
| `nsLevel` | > 0 | Nelson-Siegel β₀ |
| `nsSlope` | unconstrained | Nelson-Siegel β₁ |
| `nsCurvature` | unconstrained | Nelson-Siegel β₂ |

### Loss Function (Phase 2C)

The physics loss extends Phase 2B with multi-horizon and term structure terms:

```
L_total = λ_price * MSE(return)
        + λ_vol1d * MSE(variance_1d)
        + λ_vol5d * MSE(variance_5d)
        + λ_vol20d * MSE(variance_20d)
        + λ_vol60d * MSE(variance_60d)
        + λ_regime * CrossEntropy(regime)
        + λ_priceSde * |price_sde_residual|
        + λ_volSde * |vol_sde_residual|
        + λ_tsConsistency * |term_structure_consistency|
        + λ_garch * |garch_residual|
        + λ_constraint * |constraint_violation|
        + λ_feller * max(0, σ_v² - 2κθ)
        + λ_correlation * MSE(ρ)
        + λ_hestonReg * param_regularization
        + λ_nsSmooth * |ns_parameter_deviation|
        + λ_meanRev * |long_vol - sqrt(θ)|
```

**Default Weights (Phase 2C):**
| Component | Weight | Purpose |
|-----------|--------|---------|
| price | 1.0 | Return prediction |
| volatility1d | 1.0 | 1-day variance accuracy |
| volatility5d | 0.8 | 5-day variance (less weight) |
| volatility20d | 0.6 | 20-day variance |
| volatility60d | 0.4 | 60-day variance (lowest) |
| regime | 0.3 | Regime classification |
| priceSde | 0.5 | Price SDE physics |
| volSde | 0.5 | Volatility SDE physics |
| termStructureConsistency | 0.5 | Variance convergence |
| garch | 0.2 | GARCH fitting |
| constraint | 10.0 | Hard constraints |
| feller | 5.0 | Feller condition |
| correlation | 0.3 | Price-vol correlation |
| hestonRegularization | 0.1 | Heston bounds |
| nelsonSiegelSmooth | 0.2 | NS parameter smoothness |
| meanReversionMatch | 0.3 | Long-run vol consistency |

### Graduation Gates (Phase 2C)

| Gate | Threshold | Description |
|------|-----------|-------------|
| Return MAE | < 0.03 | 3% daily return error |
| Directional accuracy | > 50% | Better than random |
| Vol 1d MAE | < 0.01 | 1% variance error |
| Vol 20d MAE | < 0.015 | Medium-term accuracy |
| Vol 60d MAE | < 0.02 | Long-term accuracy |
| Avg term structure error | < 0.02 | Cross-horizon consistency |
| Shape accuracy | > 60% | Correct shape classification |
| Negative variance rate | < 1% | Variance positivity |
| Stationarity violation rate | < 5% | GARCH stationarity |
| Feller violation rate | < 10% | Heston validity |
| Combined SDE residual | < 0.1 | Physics consistency |

### Files (Phase 2C)

Located in `packages/be/src/trading/algorithms/pinn/volatility/`:

| File | Lines | Purpose |
|------|-------|---------|
| `term-structure-types.ts` | 440 | Type definitions for term structure model |
| `term-structure-features.ts` | 673 | Multi-horizon vol computation, Nelson-Siegel fitting |
| `term-structure-model.ts` | 575 | TensorFlow.js model architecture (19 outputs) |
| `term-structure-loss.ts` | 415 | Physics-informed loss with term structure terms |
| `term-structure-validation.ts` | 460 | Validation metrics and graduation gates |
| `term-structure-predictor.ts` | 600 | Main prediction and training pipeline |

### Usage (Phase 2C)

```typescript
import {
  trainTermStructureModel,
  predictTermStructurePrediction,
  validateTermStructureModelForProduction,
} from '@agencio-predict/be/trading/algorithms/pinn/volatility/term-structure-predictor';

// Train the model
const trainResult = await trainTermStructureModel(bars, 'AAPL', 'stock');

// Validate graduation gates
const { ready, gates, recommendation } = validateTermStructureModelForProduction(trainResult);
console.log(`Ready for production: ${ready}`);
console.log(`Gates passed: ${gates.passedCount}/${gates.totalGates}`);

// Generate prediction
const prediction = await predictTermStructurePrediction(bars, 'AAPL');

// Access term structure
console.log('Term structure:');
for (const horizon of prediction.termStructure.horizons) {
  const idx = prediction.termStructure.horizons.indexOf(horizon);
  console.log(`  ${horizon}d: ${prediction.termStructure.volatilities[idx].toFixed(2)}%`);
}
console.log(`Shape: ${prediction.termStructure.shape}`);
console.log(`Slope: ${prediction.termStructure.slope.toFixed(4)}`);
```

---

## Phase 3: Slippage-Aware Position Sizing PINN

Phase 3 implements a Physics-Informed Neural Network for optimal position sizing that embeds the **Almgren-Chriss market impact model** as physics constraints. This enables learning non-linear market impact functions while maintaining physics consistency.

### Almgren-Chriss Model

The market impact follows the square-root law:

```
Impact ∝ σ × √(Q/V)
```

Where:
- **σ** = Volatility
- **Q** = Order quantity
- **V** = Average daily volume

The model learns permanent and temporary impact components:
- **Permanent impact**: η × σ × √(participation_rate)
- **Temporary impact**: γ × σ × √(participation_rate)

Constants: `η = 0.1` (ETA_PERMANENT), `γ = 0.4` (GAMMA_TEMPORARY)

### Input Features (14 dimensions)

| # | Feature | Range | Description |
|---|---------|-------|-------------|
| 1 | `sizeUsd` | 0-1 | Order size normalized by ADV |
| 2 | `participationRate` | 0-0.2 | Order / daily volume |
| 3 | `bidAskSpreadBps` | 0-100 | Current bid-ask spread |
| 4 | `volatility` | 0-1 | Annualized volatility |
| 5 | `adv` | 0-1 | Average daily volume (log-scaled) |
| 6-8 | `regime*` | 0/1 | One-hot: low/normal/high vol |
| 9 | `edgeBps` | 0-200 | Expected strategy edge |
| 10 | `riskTolerance` | 0-1 | User risk preference |
| 11 | `timeOfDay` | 0-1 | Market session timing |
| 12 | `momentum` | -1 to 1 | Recent price momentum |
| 13 | `orderImbalance` | -1 to 1 | Buy/sell pressure |
| 14 | `recentVolatility` | 0-1 | Short-term vol estimate |

### Output (3 dimensions)

| Output | Activation | Description |
|--------|------------|-------------|
| `expectedSlippage` | softplus | Expected slippage (≥ 0) |
| `optimalSizeRatio` | sigmoid | Recommended size / requested (0-1) |
| `marketImpact` | softplus | Market impact estimate (≥ 0) |

### Loss Function (Phase 3)

```typescript
L_total = λ_pred * MSE(slippage, size_ratio, impact)
        + λ_almgren * |predicted_impact - η·σ·√(Q/V)|²   // Physics constraint
        + λ_monotonicity * penalty(impact_decreasing)     // Impact must increase with size
        + λ_bounds * penalty(negative_slippage)           // Slippage ≥ 0
        + λ_sizing * MSE(sizing_quality)                  // Size recommendation accuracy
```

**Default Weights:**
| Component | Weight | Purpose |
|-----------|--------|---------|
| prediction | 1.0 | Slippage/size prediction accuracy |
| almgrenChriss | 0.5 | Square-root impact law |
| monotonicity | 0.3 | Impact increases with size |
| bounds | 10.0 | Hard constraint: slippage ≥ 0 |
| sizing | 0.3 | Position size optimization |

### Graduation Gates (Phase 3)

| Gate | Threshold | Description |
|------|-----------|-------------|
| Slippage MAE | ≤ 5 bps | Mean absolute error on slippage |
| Slippage RMSE | ≤ 8 bps | Root mean squared error |
| Almgren Residual | ≤ 0.01 | Physics constraint satisfaction |
| Monotonicity Score | ≥ 95% | Impact increases with size |
| Regime Correlation | ≥ 0.3 | High vol → smaller size |

### Files (Phase 3)

Located in `packages/be/src/trading/algorithms/pinn/sizing/`:

| File | Lines | Purpose |
|------|-------|---------|
| `types.ts` | ~380 | Type definitions, constants (η, γ) |
| `sizing-features.ts` | ~350 | 14-dim feature extraction |
| `sizing-model.ts` | ~320 | TensorFlow.js architecture |
| `sizing-loss.ts` | ~280 | Almgren-Chriss physics loss |
| `sizing-validation.ts` | ~360 | Validation metrics, graduation |
| `sizing-predictor.ts` | ~460 | Main prediction pipeline |
| `sizing-training.ts` | ~420 | Training from historical trades |
| `index.ts` | ~160 | Module exports |

### Usage (Phase 3)

```typescript
import { runSizingPrediction } from '@agencio-predict/be/trading/algorithms/pinn/sizing';

const decision = await runSizingPrediction({
  symbol: 'AAPL',
  assetClass: 'stock',
  requestedSizeUsd: 50000,
  edgeBps: 30,
});

// Returns SizingDecision:
// {
//   originalSizeUsd: 50000,
//   recommendedSizeUsd: 35000,    // Reduced due to impact
//   expectedSlippageBps: 4.2,
//   spreadCostBps: 1.5,
//   permanentImpactBps: 1.2,
//   temporaryImpactBps: 1.5,
//   volatilityRegime: 'normal_vol',
//   regimeAdjustment: 0.9,
//   physicsResidual: 0.008,       // Almgren-Chriss compliance
//   source: 'pinn',               // or 'statistical' fallback
//   confidence: 0.85,
//   wasCapped: true,
//   reason: 'pinn_capped',
// }
```

### DSL Primitives (Phase 3)

Six new DSL primitives for algorithm strategies:

| Primitive | Returns | Description |
|-----------|---------|-------------|
| `pinn_expected_slippage(symbol, size)` | number | Expected slippage in bps |
| `pinn_optimal_size(symbol, edge, size)` | number | Recommended position size |
| `pinn_market_impact(symbol, size)` | number | Market impact in bps |
| `pinn_sizing_confidence(symbol)` | number | Model confidence (0-1) |
| `pinn_almgren_residual(symbol)` | number | Physics residual |
| `pinn_regime_size_multiplier(symbol)` | number | Regime-based size adjustment |

### Integration with Volatility PINN

The sizing PINN integrates with Phase 2 volatility predictions:

1. **Regime-aware sizing**: High-volatility regime → smaller positions
2. **Volatility input**: Uses Phase 2 volatility forecast as feature
3. **Consistent physics**: Both models share the volatility estimate

```typescript
// Prefetch both for a tick
const prefetched = await prefetchPINNData(['AAPL'], 'stock', {
  requestedSizeUsd: 50000,
  edgeBps: 30,
});

// Access sizing decision
const sizing = prefetched.sizingDecisions.get('AAPL');
console.log(`Recommended: $${sizing.recommendedSizeUsd}`);
console.log(`Impact: ${sizing.permanentImpactBps + sizing.temporaryImpactBps} bps`);
```

### Statistical Fallback

When no trained PINN model exists, the system falls back to the existing `slippage-fitter.ts`:

1. Retrieves historical slippage data from `algorithm_trades`
2. Fits linear regression: `slippage = intercept + slope * size`
3. Caps size where `expected_slippage > edge / 3`
4. Applies regime multiplier from volatility PINN

---

## Phase 4: Correlation-Aware Multi-Asset Portfolio PINN

Phase 4 implements a PINN for multi-asset portfolio optimization that embeds:
- **Markowitz mean-variance optimization** as physics constraint
- **Dynamic Conditional Correlation (DCC)** dynamics
- **Positive Semi-Definite (PSD)** constraint via Cholesky parameterization

### Theory: Cholesky Parameterization for PSD Guarantee

The key insight is that if L is a lower triangular matrix with positive diagonal, then R = LL' is guaranteed to be PSD. By having the model output Cholesky factors instead of the correlation matrix directly, we get PSD by construction.

```
R = LL'  where L is lower triangular with softplus(diagonal) > 0
```

### Theory: DCC Dynamics

The Dynamic Conditional Correlation model captures time-varying correlations:

```
Q_t = (1-a-b)Q̄ + a×ε_{t-1}ε'_{t-1} + b×Q_{t-1}
R_t = diag(Q_t)^{-1/2} × Q_t × diag(Q_t)^{-1/2}
```

Where:
- **a** = reaction coefficient (default 0.05)
- **b** = persistence coefficient (default 0.90)
- **Q̄** = unconditional correlation matrix

### Input Features (per asset: 8, global: 6)

**Per-Asset Features (8 per asset):**
| # | Feature | Range | Description |
|---|---------|-------|-------------|
| 1 | `returns20d` | -1 to 1 | 20-day return |
| 2 | `returns5d` | -1 to 1 | 5-day return |
| 3 | `volatility` | 0-1 | From Phase 2 or estimated |
| 4-6 | `regime*` | 0/1 | One-hot low/normal/high vol |
| 7 | `rsi14` | 0-1 | RSI normalized |
| 8 | `sectorId` | 0-1 | Sector index normalized |

**Global Features (6):**
| # | Feature | Range | Description |
|---|---------|-------|-------------|
| 1 | `vix` | 0-1 | VIX / 40 |
| 2 | `marketRegime` | 0-1 | Bear/neutral/bull |
| 3 | `riskTolerance` | 0-1 | User preference |
| 4 | `correlationRegime` | 0-1 | Low/normal/high |
| 5 | `numAssets` | 0-1 | Portfolio size / 20 |
| 6 | `rebalanceUrgency` | 0-1 | Days since rebalance / 30 |

### Output

| Output | Activation | Description |
|--------|------------|-------------|
| `choleskyFactors` | softplus(diag) | N×(N+1)/2 Cholesky parameters |
| `rawWeights` | softmax | N portfolio weights |
| `rawRisk` | softplus | Portfolio volatility |

### Loss Function (Phase 4)

```typescript
L_total = λ_corr * MSE(correlation)
        + λ_psd * max(0, -min_eigenvalue)²
        + λ_dcc * |DCC_residual|²
        + λ_weightSum * |Σw - 1|²
        + λ_markowitz * (-Sharpe)
        + λ_riskTarget * |predicted_risk - realized_risk|²
```

**Default Weights:**
| Component | Weight | Purpose |
|-----------|--------|---------|
| correlation | 1.0 | Correlation prediction accuracy |
| psd | 10.0 | PSD constraint (high penalty) |
| dcc | 0.5 | DCC dynamics |
| weightSum | 10.0 | Weights sum to 1 |
| markowitz | 0.3 | Efficiency constraint |
| riskTarget | 0.2 | Risk prediction accuracy |

### Graduation Gates (Phase 4)

| Gate | Threshold | Description |
|------|-----------|-------------|
| Correlation MAE | ≤ 0.10 | Correlation prediction accuracy |
| PSD Violation Rate | = 0% | Must be PSD |
| DCC Residual | ≤ 0.05 | DCC dynamics compliance |
| Markowitz Efficiency | ≥ 95% | Near efficient frontier |
| Sharpe Improvement | > 0 | Must beat equal-weight |

### Files (Phase 4)

Located in `packages/be/src/trading/algorithms/pinn/correlation/`:

| File | Lines | Purpose |
|------|-------|---------|
| `types.ts` | ~424 | Type definitions, DCC params, sectors |
| `cholesky-utils.ts` | ~477 | PSD utilities, TensorFlow implementations |
| `correlation-features.ts` | ~500 | Per-asset + global feature extraction |
| `correlation-model.ts` | ~291 | Dense network architecture |
| `correlation-loss.ts` | ~416 | Physics loss with DCC + Markowitz |
| `correlation-predictor.ts` | ~523 | Prediction with equal-weight fallback |
| `correlation-training.ts` | ~400 | Training pipeline |
| `correlation-validation.ts` | ~300 | Validation + graduation gates |
| `index.ts` | ~115 | Module exports |

### Usage (Phase 4)

```typescript
import { runCorrelationPrediction } from '@agencio-predict/be/trading/algorithms/pinn/correlation';

const result = await runCorrelationPrediction({
  userId: 'user-123',
  symbols: ['AAPL', 'MSFT', 'GOOGL', 'NVDA'],
  riskTolerance: 0.5,
});

// Returns CorrelationPINNOutput:
// {
//   correlationMatrix: {
//     matrix: [[1, 0.7, ...], ...],
//     minEigenvalue: 0.12,
//     isPSD: true,
//     confidence: 0.8,
//   },
//   correlationRegime: 'normal',
//   portfolioWeights: {
//     weights: [0.25, 0.30, 0.25, 0.20],
//     weightsBySymbol: { AAPL: 0.25, MSFT: 0.30, ... },
//     expectedReturn: 0.12,
//     portfolioVolatility: 0.18,
//     sharpeRatio: 0.67,
//   },
//   psdViolation: 0,
//   dccResidual: 0.02,
//   markowitzResidual: 0.01,
//   source: 'pinn',  // or 'fallback' if no model
// }
```

### DSL Primitives (Phase 4)

| Primitive | Returns | Description |
|-----------|---------|-------------|
| `getCorrelation(sym1, sym2, userId)` | number | Pairwise correlation |
| `getOptimalWeight(sym, userId, portfolio)` | number | Optimal weight for symbol |
| `getCorrelationRegime(userId, symbols)` | string | low/normal/high |
| `getPortfolioRisk(userId, symbols)` | number | Portfolio volatility |

### Fallback Behavior

When no trained model exists:
- Returns equal-weight portfolio (1/N)
- Uses conservative uniform correlation (ρ = 0.3-0.5)
- Sets `source: 'fallback'` in output
- Confidence is low (0.3)

---

## Future Enhancements (Phase 5+)

See `TODO.md` for the full PINN roadmap:

- **Phase 5**: Market Regime PINN (5-state regime detection) - *Implemented*
- **Phase 6**: Cointegration-Aware Multi-Asset PINN

---

## Troubleshooting

### Model Training Fails

1. Check minimum data requirement (100 bars)
2. Verify TensorFlow.js is installed (`npm ls @tensorflow/tfjs-node`)
3. Check S3 permissions for model persistence
4. Review training logs for early stopping triggers

### High Physics Residual

- Indicates velocity prediction diverging from price change
- Possible causes: overfitting, regime shift, insufficient training data
- Solution: Force retrain with more data, adjust physics loss weight

### Low Directional Accuracy

- If <52%, model is worse than random
- Check for look-ahead bias in features
- Verify normalization parameters are computed correctly
- Consider longer training (increase epochs/patience)

### S3 Connection Issues

- Models fall back to in-memory storage
- Check AWS credentials and bucket permissions
- Verify `AWS_REGION` matches bucket region
