This document details the architectural design and implementation of real-time streaming optimizations, memory efficiency, and battery conservation for RealizeAlpha (robinhood-options-mobile), resolving #124.
Financial market streaming feeds (such as Charles Schwab WebSocket Level 1 quotes, option Greeks, futures, and forex) emit rapid, sub-millisecond price ticks during active market hours. Unchecked propagation of high-frequency quote ticks directly to the Flutter UI layer causes:
+------------------------------------+
| SchwabStreamerService (WebSocket) |
+-----------------+------------------+
|
v
+-------------------------------+
| StreamThrottleTransformer |
| (Per-symbol leading/trailing) |
+---------------+---------------+
|
+----------------+----------------+
| |
v v
+---------------------------+ +---------------------------+
| ViewportStreamCoordinator| | NavigationStatefulWidget |
| - Max 15 active symbols | | - AppLifecycleState |
| - Delta-diffing bounds | | - pauseAll() / resumeAll()|
+---------------------------+ +---------------------------+
lib/utils/stream_throttle_transformer.dart)AAPL, MSFT, /ES, EUR/USD) using a keySelector callback, ensuring one active stock tick never starves or delays updates for another ticker.lib/services/schwab_streamer_service.dart)Provides rate-limited convenience streams alongside raw streams:
throttledEquityQuotes: Level 1 equities quote updates throttled to 250ms per ticker.throttledOptionQuotes: Options quotes with Greeks throttled to 250ms per contract.throttledFuturesQuotes: Futures quotes (/ES, /NQ, etc.) throttled to 250ms per contract.throttledForexQuotes: Forex quotes throttled to 250ms per currency pair.SchwabStreamerService automatically tracks active instances in _activeInstances and removes them upon dispose().SchwabStreamerService.pauseAll(): Invoked on AppLifecycleState.paused or inactive. Caches active subscriptions, sends UNSUBS commands to the remote gateway, sets isPaused = true, and silences incoming packets.SchwabStreamerService.resumeAll(): Invoked on AppLifecycleState.resumed. Restores cached subscription sets, sets isPaused = false, and resubscribes all active feeds.NavigationStatefulWidget: Hooks directly into didChangeAppLifecycleState(...).lib/services/viewport_stream_coordinator.dart)updateVisibleEquities(...) calculates set differences:
\(\Delta_{\text{unsub}} = \text{Visible}_{\text{prev}} \setminus \text{Visible}_{\text{curr}}\)
\(\Delta_{\text{sub}} = \text{Visible}_{\text{curr}} \setminus \text{Visible}_{\text{prev}}\)
Only off-screen symbols are unsubscribed, and newly visible symbols are subscribed, completely eliminating redundant WebSocket traffic.clearAll() unsubscribes all feeds when navigating away from streaming screens.Comprehensive automated test coverage verifies the throttling and lifecycle subsystems:
test/stream_throttle_transformer_test.dart:
test/schwab_streamer_service_lifecycle_test.dart:
SchwabStreamerService.activeInstances.pauseStreaming() sending UNSUBS, caching subscriptions, and dropping incoming data.resumeStreaming() restoring cached sets and issuing SUBS commands.pauseAll() and resumeAll().test/viewport_stream_coordinator_test.dart:
maxActiveSubscriptions.clearAll() unsubscription.test/schwab_streamer_test.dart: