# SenpAI Market-Data Consumer

NATS subscriber + feature engine + signal bus for the SenpAI/Gordon trading agent.

Consumes normalised events from `market-data-service`, computes real-time features, and publishes signals back to NATS.

## Architecture

```
market-data-service                  SenpAI MD Consumer
┌──────────────┐                    ┌────────────────────────────────┐
│ Binance WS   │                    │                                │
│ Bybit WS     │──► NATS ──────────►  NATSConsumer                  │
│ Alpaca WS    │   md.events.>      │    ↓ (bounded queue)           │
└──────────────┘                    │  State Store                   │
                                    │    ├─ LatestState (trade/quote)│
                                    │    └─ RollingWindow (60s deque)│
                                    │    ↓                           │
                                    │  Feature Engine                │
                                    │    ├─ mid, spread, vwap        │
                                    │    ├─ return_10s, vol_60s      │
                                    │    └─ latency p50/p95          │
                                    │    ↓                           │
                                    │  Publisher ──► NATS            │
                                    │    ├─ senpai.features.{symbol} │
                                    │    ├─ senpai.signals.{symbol}  │
                                    │    └─ senpai.alerts            │
                                    │                                │
                                    │  HTTP API (:8892)              │
                                    │    /health /metrics /stats     │
                                    │    /state/latest /features     │
                                    └────────────────────────────────┘
```

## Quick Start

### 1. Install

```bash
cd services/senpai-md-consumer
pip install -r requirements.txt
cp .env.example .env
```

### 2. Start NATS (if not running)

```bash
docker run -d --name nats -p 4222:4222 -p 8222:8222 nats:2.10-alpine --js -m 8222
```

### 3. Start market-data-service (producer)

```bash
cd ../market-data-service
python -m app run --provider binance --symbols BTCUSDT,ETHUSDT
```

### 4. Start SenpAI MD Consumer

```bash
cd ../senpai-md-consumer
python -m senpai.md_consumer
```

### 5. Verify

```bash
# Health
curl http://localhost:8892/health

# Stats
curl http://localhost:8892/stats

# Latest state
curl "http://localhost:8892/state/latest?symbol=BTCUSDT"

# Computed features
curl "http://localhost:8892/features/latest?symbol=BTCUSDT"

# Prometheus metrics
curl http://localhost:8892/metrics
```

## Docker

### Standalone (with NATS)

```bash
docker-compose -f docker-compose.senpai.yml up -d
```

### Part of NODE1 stack

```bash
docker-compose -f docker-compose.node1.yml up -d market-data-service senpai-md-consumer
```

## NATS Subjects

### Consumed (from market-data-service)

| Subject | Description |
|---|---|
| `md.events.trade.{symbol}` | Trade events |
| `md.events.quote.{symbol}` | Quote events |
| `md.events.book_l2.{symbol}` | L2 book snapshots |
| `md.events.heartbeat.__system__` | Provider heartbeats |

### Published (for SenpAI/other consumers)

| Subject | Description |
|---|---|
| `senpai.features.{symbol}` | Feature snapshots (rate-limited to 10Hz/symbol) |
| `senpai.signals.{symbol}` | Trade signals (long/short) |
| `senpai.alerts` | System alerts (latency, gaps, backpressure) |

## Features Computed

| Feature | Description |
|---|---|
| `mid` | (bid + ask) / 2 |
| `spread_abs` | ask - bid |
| `spread_bps` | spread in basis points |
| `trade_vwap_10s` | VWAP over 10 seconds |
| `trade_vwap_60s` | VWAP over 60 seconds |
| `trade_count_10s` | Number of trades in 10s |
| `trade_volume_10s` | Total volume in 10s |
| `return_10s` | Price return over 10 seconds |
| `realized_vol_60s` | Realised volatility (60s log-return std) |
| `latency_ms_p50` | p50 exchange-to-receive latency |
| `latency_ms_p95` | p95 exchange-to-receive latency |

## Signal Rules (MVP)

**Long signal** emitted when ALL conditions met:
- `return_10s > 0.3%` (configurable)
- `trade_volume_10s > 1.0` (configurable)
- `spread_bps < 20` (configurable)

**Short signal**: same but `return_10s < -0.3%`

## Backpressure Policy

- Queue < 90% → accept all events
- Queue >= 90% → drop heartbeats, quotes, book snapshots
- **Trades are NEVER dropped**

## HTTP Endpoints

| Endpoint | Description |
|---|---|
| `GET /health` | Service health + tracked symbols |
| `GET /metrics` | Prometheus metrics |
| `GET /state/latest?symbol=` | Latest trade + quote |
| `GET /features/latest?symbol=` | Current computed features |
| `GET /stats` | Queue fill, drops, events/sec |

## Prometheus Metrics

| Metric | Type | Description |
|---|---|---|
| `senpai_events_in_total` | Counter | Events received {type, provider} |
| `senpai_events_dropped_total` | Counter | Dropped events {reason, type} |
| `senpai_queue_fill_ratio` | Gauge | Queue fill 0..1 |
| `senpai_processing_latency_ms` | Histogram | Processing latency |
| `senpai_feature_publish_total` | Counter | Feature publishes {symbol} |
| `senpai_signals_emitted_total` | Counter | Signals {symbol, direction} |
| `senpai_nats_connected` | Gauge | NATS connection status |

## Tests

```bash
pytest tests/ -v
```

41 tests:
- 11 model parsing tests (tolerant parsing, edge cases)
- 10 state/rolling window tests (eviction, lookup)
- 16 feature math tests (VWAP, vol, signals, percentile)
- 5 rate-limit tests (publish throttling, error handling)

## Troubleshooting

### NATS connection refused
```
nats.error: error=could not connect to server
```
Ensure NATS is running:
```bash
docker run -d --name nats -p 4222:4222 nats:2.10-alpine --js
```
Or check `NATS_URL` in `.env`.

### No events arriving (queue stays at 0)
1. Verify `market-data-service` is running and `NATS_ENABLED=true`
2. Check subject match: producer publishes to `md.events.trade.BTCUSDT`, consumer subscribes to `md.events.>`
3. Check NATS monitoring: `curl http://localhost:8222/connz` — both services should appear

### JetStream errors
If `USE_JETSTREAM=true` but NATS started without `--js`:
```bash
# Restart NATS with JetStream
docker rm -f nats
docker run -d -p 4222:4222 -p 8222:8222 nats:2.10-alpine --js -m 8222
```
Or set `USE_JETSTREAM=false` for core NATS (simpler, works for MVP).

### Port 8892 already in use
```bash
lsof -ti:8892 | xargs kill -9
```

### Features show `null` for all values
Normal on startup — features populate after first trade+quote arrive. Wait a few seconds for Binance data to flow through.

### No signals emitted
Signal rules require ALL conditions simultaneously:
- `return_10s > 0.3%` — needs price movement
- `volume_10s > 1.0` — needs trading activity
- `spread_bps < 20` — needs tight spread

In low-volatility markets, signals may be rare. Lower thresholds in `.env` for testing:
```env
SIGNAL_RETURN_THRESHOLD=0.001
SIGNAL_VOLUME_THRESHOLD=0.1
```

### High memory usage
Rolling windows grow per symbol. With many symbols, reduce window:
```env
ROLLING_WINDOW_SECONDS=30
```

## Configuration (ENV)

See `.env.example` for all available settings.

Key settings:
- `NATS_URL` — NATS server URL
- `FEATURES_PUB_RATE_HZ` — max feature publishes per symbol per second
- `SIGNAL_RETURN_THRESHOLD` — min return for signal trigger
- `ROLLING_WINDOW_SECONDS` — rolling window duration
