# TradingAgents result contract v1alpha1 draft Status: draft Audience: backend, desktop, frontend, verification Format: JSON-oriented contract notes with examples ## 1. Goals `result-contract-v1alpha1` defines the stable shapes exchanged across: - analysis start/status APIs - websocket progress events - live orchestrator streaming - persisted task state - historical report projection The contract should be application-facing, not raw domain dataclasses. ## 2. Design principles - version every externally consumed payload - keep transport-neutral field meanings - allow partial/degraded results when quant or LLM lane fails - distinguish task lifecycle from signal outcome - keep raw domain metadata nested, not smeared across top-level fields ## 3. Core enums ## 3.1 Task status ```json ["pending", "running", "completed", "failed", "cancelled"] ``` ## 3.2 Stage name ```json ["analysts", "research", "trading", "risk", "portfolio"] ``` ## 3.3 Decision rating ```json ["BUY", "OVERWEIGHT", "HOLD", "UNDERWEIGHT", "SELL"] ``` ## 4. Canonical envelope All application-facing payloads should include: ```json { "contract_version": "v1alpha1" } ``` Optional transport-specific wrapper fields such as WebSocket `type` may sit outside the contract body. ## 5. Analysis task contract ## 5.1 Accepted response ```json { "contract_version": "v1alpha1", "task_id": "600519.SS_20260413_120000_ab12cd", "ticker": "600519.SS", "date": "2026-04-13", "status": "running" } ``` ## 5.2 Status / progress document ```json { "contract_version": "v1alpha1", "task_id": "600519.SS_20260413_120000_ab12cd", "ticker": "600519.SS", "date": "2026-04-13", "status": "running", "progress": 40, "current_stage": "research", "created_at": "2026-04-13T12:00:00Z", "elapsed_seconds": 18, "stages": [ {"name": "analysts", "status": "completed", "completed_at": "12:00:05"}, {"name": "research", "status": "running", "completed_at": null}, {"name": "trading", "status": "pending", "completed_at": null}, {"name": "risk", "status": "pending", "completed_at": null}, {"name": "portfolio", "status": "pending", "completed_at": null} ], "result": null, "error": null } ``` Notes: - `elapsed_seconds` is preferred over the current loosely typed `elapsed`. - stage entries should carry explicit `name`; current positional arrays are fragile. - `result` remains nullable until completion. ## 5.3 Completed result payload ```json { "contract_version": "v1alpha1", "task_id": "600519.SS_20260413_120000_ab12cd", "ticker": "600519.SS", "date": "2026-04-13", "status": "completed", "progress": 100, "current_stage": "portfolio", "result": { "decision": "OVERWEIGHT", "confidence": 0.64, "signals": { "merged": {"direction": 1, "rating": "OVERWEIGHT"}, "quant": {"direction": 1, "rating": "OVERWEIGHT", "available": true}, "llm": {"direction": 1, "rating": "BUY", "available": true} }, "degraded": false, "report": { "path": "results/600519.SS/2026-04-13/complete_report.md", "available": true } }, "error": null } ``` ## 5.4 Failed result payload ```json { "contract_version": "v1alpha1", "task_id": "600519.SS_20260413_120000_ab12cd", "ticker": "600519.SS", "date": "2026-04-13", "status": "failed", "progress": 60, "current_stage": "trading", "result": null, "error": { "code": "analysis_failed", "message": "both quant and llm signals are None", "retryable": false } } ``` ## 6. Live signal batch contract This covers `/ws/orchestrator` style responses currently produced by `LiveMode`. ```json { "contract_version": "v1alpha1", "signals": [ { "ticker": "600519.SS", "date": "2026-04-13", "status": "completed", "result": { "direction": 1, "confidence": 0.64, "quant_direction": 1, "llm_direction": 1, "timestamp": "2026-04-13T12:00:11Z" }, "error": null }, { "ticker": "300750.SZ", "date": "2026-04-13", "status": "failed", "result": null, "error": { "code": "live_signal_failed", "message": "both quant and llm signals are None", "retryable": false } } ] } ``` ## 7. Historical report contract ```json { "contract_version": "v1alpha1", "ticker": "600519.SS", "date": "2026-04-13", "decision": "OVERWEIGHT", "report": "# TradingAgents ...", "artifacts": { "complete_report": true, "stage_reports": { "analysts": true, "research": true, "trading": true, "risk": true, "portfolio": false } } } ``` ## 8. Mapping from current implementation Current backend fields in `web_dashboard/backend/main.py` map roughly as follows: - `decision` -> `result.decision` - `quant_signal` -> `result.signals.quant.rating` - `llm_signal` -> `result.signals.llm.rating` - `confidence` -> `result.confidence` - top-level `error` string -> structured `error` - positional `stages[]` -> named `stages[]` ## 9. Compatibility notes ### v1alpha1 tolerances Consumers should tolerate: - absent `result.signals.quant` when quant path is unavailable - absent `result.signals.llm` when LLM path is unavailable - `result.degraded = true` when only one lane produced a usable signal ### fields to avoid freezing yet Do not freeze these until config-schema work lands: - provider-specific configuration echo fields - raw metadata blobs from quant/LLM internals - report summary extraction fields ## 10. Open review questions - Should `rating` remain duplicated with `direction`, or should one be derived client-side? - Should task progress timestamps standardize on RFC 3339 instead of mixed clock-only strings? - Should historical report APIs return extracted summary separately from full markdown?