245 lines
5.7 KiB
Markdown
245 lines
5.7 KiB
Markdown
# 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?
|