229 lines
8.1 KiB
Markdown
229 lines
8.1 KiB
Markdown
# 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
|
|
|
|
## Current status snapshot (2026-04)
|
|
|
|
This document is still the **target boundary** document, but several convergence pieces are already landed on the mainline:
|
|
|
|
- `web_dashboard/backend/services/job_service.py` now owns public task/job projection logic;
|
|
- `web_dashboard/backend/services/result_store.py` persists result contracts under `results/<task_id>/result.v1alpha1.json`;
|
|
- `web_dashboard/backend/services/analysis_service.py` and `api/portfolio.py` already expose contract-first result payloads by default;
|
|
- task lifecycle query/command routing for `status/list/cancel` now sits behind backend task services instead of route-local orchestration in `main.py`;
|
|
- `/ws/analysis/{task_id}` and `/ws/orchestrator` already carry `contract_version = "v1alpha1"` and include result/degradation/data-quality metadata.
|
|
|
|
What is **not** fully finished yet:
|
|
|
|
- `web_dashboard/backend/main.py` still contains too much orchestration glue and transport-local logic outside the task lifecycle slice;
|
|
- route handlers are thinner than before, but the application layer has not fully absorbed reports/export and every remaining lifecycle branch;
|
|
- migration flags/modes still coexist with legacy compatibility paths.
|
|
|
|
## 1. Why this document exists
|
|
|
|
The current backend mixes three concerns inside `web_dashboard/backend/main.py`:
|
|
|
|
1. transport concerns: FastAPI routes, headers, WebSocket sessions, task persistence;
|
|
2. application orchestration: task lifecycle, stage progress, subprocess wiring, result projection;
|
|
3. 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.py` owns quant runner + LLM runner composition.
|
|
- `orchestrator/signals.py` owns `Signal`, `FinalSignal`, and merge math.
|
|
- `orchestrator/live_mode.py` owns 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;
|
|
- conversion from `FinalSignal` to UI-oriented fields such as `decision`, `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.
|
|
|
|
At the same time, current mainline no longer matches the oldest “all logic sits in routes” description exactly. The codebase now sits in a **mid-migration** state:
|
|
|
|
- merge semantics remain in `orchestrator/`;
|
|
- public payload shaping has started moving into backend services;
|
|
- task lifecycle query/command paths now route through backend task services;
|
|
- legacy compatibility fields still exist for UI safety.
|
|
|
|
## 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:
|
|
|
|
- `TradingOrchestrator`
|
|
- `SignalMerger`
|
|
- `QuantRunner`
|
|
- `LLMRunner`
|
|
- `TradingAgentsGraph`
|
|
|
|
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
|
|
|
|
```text
|
|
transport adapter -> application service -> domain kernel
|
|
transport adapter -> application service -> persistence adapter
|
|
application service -> result contract mapper
|
|
```
|
|
|
|
Forbidden direction:
|
|
|
|
```text
|
|
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) -> AnalysisTaskAccepted`
|
|
- `get_analysis_status(task_id) -> AnalysisTaskStatus`
|
|
- `cancel_analysis(task_id) -> AnalysisTaskStatus`
|
|
- `run_live_signals(request) -> LiveSignalBatch`
|
|
- `list_analysis_tasks() -> AnalysisTaskList`
|
|
- `get_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:
|
|
|
|
```text
|
|
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.
|
|
|
|
## 9. Current maintainer guidance
|
|
|
|
When touching backend convergence code, treat these files as the current application-facing boundary:
|
|
|
|
- `web_dashboard/backend/services/job_service.py`
|
|
- `web_dashboard/backend/services/result_store.py`
|
|
- `web_dashboard/backend/services/analysis_service.py`
|
|
- `web_dashboard/backend/api/portfolio.py`
|
|
|
|
If a change adds or removes externally visible fields, update `docs/contracts/result-contract-v1alpha1.md` in the same change set.
|