---
phase: 04-combined-websocket-streams
plan: 01
subsystem: streaming
tags: [websocket, binance, fault-isolation, feature-flag, pyo3]

requires: []
provides:
  - WsMode enum (Combined/PerSymbol) for WS connection strategy
  - split_symbols_by_volume() function for balanced 2-connection split
  - OPENDEVIATIONBAR_STREAMING_WS_MODE env var for runtime mode selection
  - Per-symbol fallback path (D-01 rollback design)
affects: [04-02-PLAN, sidecar deployment, deploy env files]

tech-stack:
  added: []
  patterns:
    - "Feature flag pattern: env var -> Python config -> PyO3 -> Rust enum"
    - "Volume-ranked symbol splitting for balanced WS connection groups"

key-files:
  created: []
  modified:
    - crates/opendeviationbar-streaming/src/live_engine/types.rs
    - crates/opendeviationbar-streaming/src/live_engine/mod.rs
    - crates/opendeviationbar-streaming/src/lib.rs
    - src/stream_manager_bindings.rs
    - python/opendeviationbar/config/sidecar.py
    - python/opendeviationbar/sidecar/__init__.py
    - python/opendeviationbar/sidecar/__init__.pyi

key-decisions:
  - "Hardcoded volume ranking for symbol split -- stable Binance spot top 13, avoids runtime API call"
  - "Combined mode uses child CancellationTokens so each connection can be cancelled independently"
  - "WsMode default is Combined (current production behavior) -- zero breaking change on deploy"

patterns-established:
  - "WsMode feature flag: end-to-end env var -> Literal type -> PyO3 str -> Rust enum"

requirements-completed: [WS-01, WS-02]

duration: 10min
completed: 2026-03-24
---

# Phase 04 Plan 01: WS Mode Feature Flag Summary

**WsMode enum with 2-connection combined split (volume-ranked) and per-symbol fallback, plumbed end-to-end from OPENDEVIATIONBAR_STREAMING_WS_MODE env var through Python config and PyO3 to Rust LiveEngineConfig**

## Performance

- **Duration:** 10 min
- **Started:** 2026-03-24T20:33:10Z
- **Completed:** 2026-03-24T20:43:30Z
- **Tasks:** 2
- **Files modified:** 7

## Accomplishments

- WsMode enum (Combined/PerSymbol) with Combined as default -- zero breaking change
- split_symbols_by_volume() partitions symbols into 2 balanced groups by Binance spot volume ranking
- start_ws() branches on ws_mode: Combined spawns 2 CombinedWebSocketStream tasks with independent Antaeus backoff (WS-02 inherited), PerSymbol spawns N BinanceWebSocketStream tasks
- Full end-to-end wiring: env var -> SidecarConfig -> StreamManager -> LiveEngineConfig -> start_ws()
- 5 unit tests for WsMode defaults and split_symbols_by_volume edge cases

## Task Commits

Each task was committed atomically:

1. **Task 1: Add WsMode enum and 2-connection split (TDD RED)** - `bf9f3ac` (test)
2. **Task 1: Add WsMode enum and 2-connection split (TDD GREEN)** - `278ac1a` (feat)
3. **Task 2: Wire WS mode from Python config through PyO3** - `63694e8` (feat)

## Files Created/Modified

- `crates/opendeviationbar-streaming/src/live_engine/types.rs` - WsMode enum, split_symbols_by_volume(), ws_mode field on LiveEngineConfig, 5 unit tests
- `crates/opendeviationbar-streaming/src/live_engine/mod.rs` - start_ws() branches on WsMode: Combined (2 connections) or PerSymbol (N connections)
- `crates/opendeviationbar-streaming/src/lib.rs` - Re-export WsMode from crate root
- `src/stream_manager_bindings.rs` - ws_mode parameter on PyStreamManager::new(), parsed to WsMode enum
- `python/opendeviationbar/config/sidecar.py` - ws_mode field (Literal["combined", "per_symbol"]) with env var
- `python/opendeviationbar/sidecar/__init__.py` - Pass config.ws_mode to StreamManager constructor
- `python/opendeviationbar/sidecar/__init__.pyi` - Updated type stub with ws_mode field

## Decisions Made

- Hardcoded volume ranking (top 13 Binance spot symbols) rather than runtime API call -- stable ranking, zero latency
- Combined mode uses CancellationToken child tokens for each WS connection -- independent shutdown capability
- Default is "combined" matching current production behavior -- setting OPENDEVIATIONBAR_STREAMING_WS_MODE=per_symbol activates rollback path

## Deviations from Plan

None - plan executed exactly as written.

## Known Stubs

None.

## Issues Encountered

- The `live_engine` module is behind `#[cfg(feature = "binance-integration")]` feature gate -- unit tests only run with `--features binance-integration`. Discovered during TDD RED phase when tests appeared to not compile. Resolved by adding the feature flag to test commands.

## User Setup Required

None - no external service configuration required. The default ws_mode is "combined" which matches current production behavior.

## Next Phase Readiness

- WS mode feature flag is ready for 04-02 (validation and integration testing)
- Deploy: add `OPENDEVIATIONBAR_STREAMING_WS_MODE=combined` to sidecar.env for explicit documentation (optional -- defaults correctly)

---

## Self-Check: PASSED

All 7 modified files verified present. All 3 commit hashes verified in git log.

---

_Phase: 04-combined-websocket-streams_
_Completed: 2026-03-24_
