---
phase: 06-combined-ws-implementation
plan: "01"
subsystem: streaming
tags: [websocket, binance, combined-streams, tokio, serde, demux]

requires:
  - phase: 04-parallel-fill-from-rest
    provides: parallel fill_from_rest, WS channel 50K capacity
provides:
  - CombinedWebSocketStream struct with connect_and_demux + run_with_reconnect
  - Combined stream envelope parsing (CombinedEnvelope)
  - Per-symbol trade demuxing to mpsc channels
  - BinanceAggTrade pub(crate) visibility for cross-module access
affects: [06-02, 07-overlapping-reconnection, 08-integration-testing]

tech-stack:
  added: []
  patterns:
    - "Combined WS envelope: {stream, data} parsed to per-symbol AggTrade channels"
    - "extract_symbol() from stream field with uppercase normalization"

key-files:
  created:
    - crates/opendeviationbar-providers/src/binance/combined_ws.rs
  modified:
    - crates/opendeviationbar-providers/src/binance/websocket.rs
    - crates/opendeviationbar-providers/src/binance/mod.rs

key-decisions:
  - "BinanceAggTrade made pub(crate) instead of pub — minimal visibility for cross-module use"
  - "Unknown symbols in combined stream logged and skipped, not panicked — handles delistings gracefully"
  - "Single closed channel does not terminate demux loop — other symbols may still be active"

patterns-established:
  - "CombinedEnvelope deserialization reuses BinanceAggTrade from websocket.rs"
  - "Antaeus reconnection pattern shared between per-symbol and combined streams"

requirements-completed: [COMB-02, COMB-03]

duration: 2min
completed: 2026-03-19
---

# Phase 6 Plan 01: CombinedWebSocketStream + Envelope Parsing + Demux Summary

**CombinedWebSocketStream with envelope parsing, per-symbol demux routing, and Antaeus reconnection for Binance combined WS streams**

## Performance

- **Duration:** 2 min
- **Started:** 2026-03-19T22:57:22Z
- **Completed:** 2026-03-19T22:59:56Z
- **Tasks:** 2
- **Files modified:** 3

## Accomplishments

- Created `CombinedWebSocketStream` struct with `connect_and_demux()` that parses combined stream envelopes and routes trades to per-symbol `mpsc::Sender<AggTrade>` channels
- Implemented `run_with_reconnect()` reusing the Antaeus backoff pattern from `BinanceWebSocketStream`
- Made `BinanceAggTrade` and `to_agg_trade()` `pub(crate)` for cross-module access
- All 7 unit tests passing (envelope parsing, URL construction, symbol extraction, 18-symbol URL)

## Task Commits

Each task was committed atomically:

1. **Task 1: CombinedWebSocketStream struct + module registration** - `c54d686` (feat)
2. **Task 2: Unit tests** - included in `c54d686` (tests co-located in same file per Rust convention)

## Files Created/Modified

- `crates/opendeviationbar-providers/src/binance/combined_ws.rs` - New combined WS stream with envelope parsing, demux, and Antaeus reconnection
- `crates/opendeviationbar-providers/src/binance/websocket.rs` - Made BinanceAggTrade and to_agg_trade() pub(crate)
- `crates/opendeviationbar-providers/src/binance/mod.rs` - Registered combined_ws module, re-exported CombinedWebSocketStream

## Decisions Made

- Made `BinanceAggTrade` `pub(crate)` (not `pub`) to minimize visibility — only needed within the crate
- Unknown symbols in combined stream are logged and skipped rather than causing errors — graceful handling for delistings or unexpected symbols
- A single closed per-symbol channel does not terminate the entire demux loop — other symbols continue receiving trades

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 2 - Missing Critical] Added extract_symbol() helper function**

- **Found during:** Task 1 (CombinedWebSocketStream implementation)
- **Issue:** Plan described symbol extraction inline but it warranted a dedicated function for testability
- **Fix:** Created `extract_symbol()` function with proper Option handling
- **Files modified:** combined_ws.rs
- **Verification:** Dedicated tests for extract_symbol()
- **Committed in:** c54d686

**2. [Rule 3 - Blocking] Tests committed with Task 1 instead of separate Task 2 commit**

- **Found during:** Task 2 (Unit tests)
- **Issue:** Tests are in the same file (combined_ws.rs) per Rust convention with `#[cfg(test)]` gating. Splitting into a separate commit would require artificial file separation
- **Fix:** Tests included in Task 1 commit. All 7 tests verified passing via `cargo nextest run`
- **Verification:** 7/7 tests pass

---

**Total deviations:** 2 auto-fixed (1 missing critical, 1 blocking)
**Impact on plan:** Minor structural adjustments. All acceptance criteria met. No scope creep.

## Issues Encountered

None

## User Setup Required

None - no external service configuration required.

## Next Phase Readiness

- CombinedWebSocketStream ready for integration into StreamManager (Plan 06-02)
- Per-symbol `mpsc::Sender<AggTrade>` interface matches existing `symbol_task()` consumer pattern
- Antaeus reconnection pattern proven for combined streams

---

_Phase: 06-combined-ws-implementation_
_Completed: 2026-03-19_
