TradingAgents/docs/architecture/application-boundary.md

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:

  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;
  • task state persistence in app.state.task_results and data/task_status/*.json;
  • 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.

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

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) -> 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:

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.