Spectr Architecture Refactor Plan
Goals
- Clear separation between Live and Backtest modes (no cross‑talk).
- Decouple business logic from UI (rendering becomes a thin layer).
- Improve testability, reliability, and maintainability.
Key Changes (Overview)
- Mode management: Centralize enter/exit logic; pause/resume services atomically.
- Services: Extract polling, scanner, and equity updaters from the App class.
- Store: Unidirectional data flow with a typed state store and events.
- Rendering: Introduce view models and a dedicated renderer around plotext.
- Backtest pipeline: Dedicated service returning immutable reports/models.
Details
- Mode Separation
- Replace
is_backtest: boolwithModeenum (LIVE, BACKTEST, …). - Add
ModeManagerthat:- Starts/stops services (polling/scanner/equity) on mode change.
- Suspends UI update streams for live views during backtest.
- Emits structured events for transitions (entered/exited).
- Isolate backtest into a
BacktestSession(input + immutable outputs) so UI never touches live caches during results.
- Services and Lifecycles
LivePollingService: Periodically fetch quotes + historical deltas; detect signals; publish updates per symbol.ScannerService: Background symbol discovery; publishes changes to the store.EquityService: Computes/evolves equity curve with cadence; publishes to store.- Services expose
start(),pause(),resume(),stop()and are supervised byModeManager.
- State and Data Flow
- Introduce an
AppStoretyped with dataclasses:AppState(mode, active symbol, config, strategy, etc.)SymbolState(df, indicators, last quote, signals)PortfolioState(cash, positions, orders, equity)
- UI subscribes to store selectors; UI never mutates the store directly.
- Intents/commands (e.g., “set strategy”, “add symbol”) go through a controller that updates the store/services.
- Rendering and View Models
ChartViewModel: normalized input for charts (x labels, series, overlays, markers, y-range, titles).ChartTransformer: pure functions from DataFrame(+indicators) →ChartViewModel.PlotRenderer:render_price(model) -> str,render_macd(model) -> str, etc.; encapsulates plotext usage + global lock.- Views (Graph/MACD/Volume/Equity) depend only on view models or a renderer call, not raw DataFrames.
- Backtest Pipeline
BacktestService.run(input) -> BacktestReportwhereinputincludes symbol, date range, starting cash, strategy + params.BacktestReport: immutable dataclass withfinal_value,equity_curve,trades, andprice_slice.- Add
BacktestTransformerto produceChartViewModelfor the results screen. UI consumes only report + model. - Keep indicator analysis (
metrics.analyze_indicators) centralized so live/backtest stay consistent.
- Concurrency and Scheduling
- Replace per-widget
set_intervalwhere possible with a central scheduler or store-driven updates. - Use
asyncio.TaskGroup/managed tasks for services; ensure clean cancellation on mode change and shutdown. - Use an async channel/event bus with coalescing per symbol to avoid unbounded queues.
- Error Handling and Logging
- Replace blanket
except Exceptionwith specific exceptions; propagate failures to a central UI/error service. - Add structured logs on service start/stop and mode transitions.
- Ensure user-facing errors are rendered by a dedicated overlay service (UI stays passive).
- Types and Config
- Migrate to dataclasses (or Pydantic) for
AppConfig, strategy params, args snapshot, backtest inputs/outputs. - Replace
SimpleNamespacewith typed models. - Use enums/constants for strategy names, intervals, and style tokens.
- Testing Strategy
- Unit-test services in isolation: polling, repositories, backtest service, renderer.
- Mode transition tests: entering/exiting backtest pauses/resumes services, UI ignores live updates in backtest.
- Snapshot tests for renderer output given deterministic models.
- Contract tests for strategy parameter mapping to guard refactors.
Incremental Migration Steps
- Introduce
PlotRenderer+ChartViewModel; refactorGraphViewto use them behind current API. - Add
ModeManager; route all mode transitions through it; remove scatteredis_backtestbranches. - Extract
LivePollingService,EquityService,ScannerService; wire toModeManager. - Introduce
AppStore/controllers; migrate key flows (symbol changes, set strategy, add/remove symbol). - Build
BacktestServicereturningBacktestReport; adapt results screen to consume report + chart model. - Backfill tests for services, mode transitions, and renderer snapshots.
Notes
- The existing global plot lock remains as a safety harness even after introducing the renderer; the renderer should own the lock.
- Keep compatibility shims in
SpectrAppuntil migration completes; aim for smaller PRs to keep tests green.