6.1 KiB
TradingAgents architecture convergence draft: application boundary
Status: draft Audience: backend/dashboard/orchestrator maintainers Scope: define the boundary between HTTP/WebSocket delivery, application service orchestration, and the quant+LLM merge kernel
1. Why this document exists
The current backend mixes three concerns inside web_dashboard/backend/main.py:
- transport concerns: FastAPI routes, headers, WebSocket sessions, task persistence;
- application orchestration: task lifecycle, stage progress, subprocess wiring, result projection;
- domain execution:
TradingOrchestrator,LiveMode, quant+LLM signal merge.
For architecture convergence, these concerns should be separated so that:
- the application service remains a no-strategy orchestration and contract layer;
orchestrator/remains the quant+LLM merge kernel;- transport adapters can migrate without re-embedding business rules.
2. Current evidence in repo
2.1 Merge kernel already exists
orchestrator/orchestrator.pyowns quant runner + LLM runner composition.orchestrator/signals.pyownsSignal,FinalSignal, and merge math.orchestrator/live_mode.pyowns batch live execution against the orchestrator.
This is the correct place for quant/LLM merge semantics.
2.2 Backend currently crosses the boundary
web_dashboard/backend/main.py currently also owns:
- analysis subprocess template creation;
- stage-to-progress mapping;
- task state persistence in
app.state.task_resultsanddata/task_status/*.json; - conversion from
FinalSignalto UI-oriented fields such asdecision,quant_signal,llm_signal,confidence; - report materialization into
results/<ticker>/<date>/complete_report.md.
This makes the transport layer hard to replace and makes result contracts implicit.
3. Target boundary
3.1 Layer model
Transport adapters
Examples:
- FastAPI REST routes
- FastAPI WebSocket endpoints
- future CLI/Tauri/worker adapters
Responsibilities:
- request parsing and auth
- response serialization
- websocket connection management
- mapping application errors to HTTP/WebSocket status
Non-responsibilities:
- no strategy logic
- no quant/LLM weighting logic
- no task-stage business rules beyond rendering application events
Application service
Suggested responsibility set:
- accept typed command/query inputs from transport
- orchestrate analysis execution lifecycle
- map domain results into stable result contracts
- own task ids, progress events, persistence coordination, and rollback-safe migration switches
- decide which backend implementation to call during migration
Non-responsibilities:
- no rating-to-signal research logic
- no quant/LLM merge math
- no provider-specific data acquisition details
Domain kernel
Examples:
TradingOrchestratorSignalMergerQuantRunnerLLMRunnerTradingAgentsGraph
Responsibilities:
- produce quant signal, LLM signal, merged signal
- expose domain-native dataclasses and metadata
- degrade gracefully when one lane fails
3.2 Canonical dependency direction
transport adapter -> application service -> domain kernel
transport adapter -> application service -> persistence adapter
application service -> result contract mapper
Forbidden direction:
transport adapter -> domain kernel + ad hoc mapping + ad hoc persistence
4. Proposed application-service interface
The application service should expose typed use cases instead of letting routes assemble logic inline.
4.1 Commands / queries
Suggested surface:
start_analysis(request) -> AnalysisTaskAcceptedget_analysis_status(task_id) -> AnalysisTaskStatuscancel_analysis(task_id) -> AnalysisTaskStatusrun_live_signals(request) -> LiveSignalBatchlist_analysis_tasks() -> AnalysisTaskListget_report(ticker, date) -> HistoricalReport
4.2 Domain input boundary
Inputs from transport should already be normalized into application DTOs:
- ticker
- trade date
- auth context
- provider/config selection
- execution mode
The application service may choose subprocess/backend/orchestrator execution strategy, but it must not redefine domain semantics.
5. Boundary rules for convergence work
Rule A: result mapping happens once
Current code maps FinalSignal to dashboard fields inside the analysis subprocess template. That mapping should move behind a single application mapper so REST, WebSocket, export, and persisted task status share one contract.
Rule B: stage model belongs to application layer
Stage names such as analysts, research, trading, risk, portfolio are delivery/progress concepts, not merge-kernel concepts. Keep them outside orchestrator/.
Rule C: orchestrator stays contract-light
orchestrator/ should continue returning Signal / FinalSignal and domain metadata. It should not learn about HTTP status, WebSocket payloads, pagination, or UI labels beyond domain rating semantics already present.
Rule D: transport only renders contracts
Routes should call the application service and return the already-shaped DTO/contract. They should not reconstruct decision, quant_signal, llm_signal, or progress math themselves.
6. Suggested module split
One viable split:
web_dashboard/backend/
application/
analysis_service.py
live_signal_service.py
report_service.py
contracts.py
mappers.py
infra/
task_store.py
subprocess_runner.py
report_store.py
api/
fastapi_routes remain thin
This keeps convergence local to backend/application without moving merge logic out of orchestrator/.
7. Non-goals
- Do not move signal merge math into the application service.
- Do not turn the application service into a strategy engine.
- Do not require frontend-specific field naming inside
orchestrator/. - Do not block migration on a full rewrite of existing routes.
8. Review checklist
A change respects this boundary if all are true:
- route handlers mainly validate/auth/call service/return contract;
- application service owns task lifecycle and contract mapping;
orchestrator/remains the only owner of merge semantics;- domain dataclasses can still be tested without FastAPI or WebSocket context.