docs(README): 更新项目说明文档,完善架构与启动指南

- 调整 CLAUDE.md 内容,增加使用指导和架构概览
- 补充微服务启动说明及单服务启动命令
- 明确 Gateway 服务器四阶段启动流程和职责划分
- 细化后端目录结构说明,补充主要文件职责描述
- 新增系统分层结构图,优化整体架构理解
- 更新 .gitignore,添加 runs 目录忽略规则
- 同步 .omc 相关状态文件,更新项目状态跟踪信息
This commit is contained in:
2026-03-24 17:19:31 +08:00
parent da6d642aaa
commit 16bb3c4211
20 changed files with 556 additions and 563 deletions

5
.gitignore vendored
View File

@@ -54,10 +54,13 @@ outputs/
/smoke_live_mock/
# Local tooling state
/.omc/
.omc/
/.pydeps/
/referance/
# Run outputs
/runs/
# Data files
backend/data/ret_data/

View File

@@ -1,6 +1,6 @@
{
"version": "1.0.0",
"lastScanned": 1773938154948,
"lastScanned": 1774313111650,
"projectRoot": "/Users/cillin/workspeace/evotraders",
"techStack": {
"languages": [
@@ -11,14 +11,6 @@
"markers": [
"pyproject.toml"
]
},
{
"name": "C/C++",
"version": null,
"confidence": "high",
"markers": [
"Makefile"
]
}
],
"frameworks": [
@@ -32,8 +24,8 @@
"runtime": null
},
"build": {
"buildCommand": "make build",
"testCommand": "make test",
"buildCommand": null,
"testCommand": "pytest",
"lintCommand": "ruff check",
"devCommand": null,
"scripts": {}
@@ -58,24 +50,13 @@
},
"customNotes": [],
"directoryMap": {
"agent-service": {
"path": "agent-service",
"purpose": null,
"fileCount": 2,
"lastAccessed": 1773938154941,
"keyFiles": [
"Dockerfile",
"requirements.txt"
]
},
"backend": {
"path": "backend",
"purpose": null,
"fileCount": 5,
"lastAccessed": 1773938154941,
"fileCount": 4,
"lastAccessed": 1774313111639,
"keyFiles": [
"__init__.py",
"app.py",
"cli.py",
"gateway_server.py",
"main.py"
@@ -85,37 +66,41 @@
"path": "backtest",
"purpose": null,
"fileCount": 0,
"lastAccessed": 1773938154941,
"lastAccessed": 1774313111640,
"keyFiles": []
},
"data": {
"path": "data",
"purpose": "Data files",
"fileCount": 1,
"lastAccessed": 1773938154941,
"fileCount": 3,
"lastAccessed": 1774313111640,
"keyFiles": [
"market_research.db"
"market_research.db",
"market_research.db-shm",
"market_research.db-wal"
]
},
"deploy": {
"path": "deploy",
"purpose": null,
"fileCount": 0,
"lastAccessed": 1773938154942,
"lastAccessed": 1774313111640,
"keyFiles": []
},
"docs": {
"path": "docs",
"purpose": "Documentation",
"fileCount": 0,
"lastAccessed": 1773938154942,
"keyFiles": []
"fileCount": 1,
"lastAccessed": 1774313111641,
"keyFiles": [
"compat-removal-plan.md"
]
},
"evotraders.egg-info": {
"path": "evotraders.egg-info",
"purpose": null,
"fileCount": 6,
"lastAccessed": 1773938154942,
"lastAccessed": 1774313111641,
"keyFiles": [
"PKG-INFO",
"SOURCES.txt",
@@ -128,7 +113,7 @@
"path": "frontend",
"purpose": null,
"fileCount": 13,
"lastAccessed": 1773938154942,
"lastAccessed": 1774313111641,
"keyFiles": [
"README.md",
"components.json",
@@ -141,51 +126,41 @@
"path": "live",
"purpose": null,
"fileCount": 0,
"lastAccessed": 1773938154943,
"lastAccessed": 1774313111642,
"keyFiles": []
},
"logs": {
"path": "logs",
"purpose": null,
"fileCount": 7,
"lastAccessed": 1773938154943,
"fileCount": 6,
"lastAccessed": 1774313111642,
"keyFiles": [
"2026-03-16_00-48-03.log",
"2026-03-18_23-17-29.log",
"2026-03-18_23-17-30.2026-03-18_23-17-30_000801.log.zip",
"2026-03-18_23-17-30.log",
"2026-03-19_00-18-04.log"
]
},
"news-service": {
"path": "news-service",
"purpose": null,
"fileCount": 3,
"lastAccessed": 1773938154943,
"keyFiles": [
"Dockerfile",
"requirements.txt"
"2026-03-19_00-18-04.log",
"2026-03-19_00-34-21.log"
]
},
"reference": {
"path": "reference",
"purpose": null,
"fileCount": 0,
"lastAccessed": 1773938154943,
"lastAccessed": 1774313111643,
"keyFiles": []
},
"runs": {
"path": "runs",
"purpose": null,
"fileCount": 0,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111643,
"keyFiles": []
},
"scripts": {
"path": "scripts",
"purpose": "Build/utility scripts",
"fileCount": 1,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111644,
"keyFiles": [
"run_prod.sh"
]
@@ -194,7 +169,7 @@
"path": "services",
"purpose": "Business logic services",
"fileCount": 1,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111644,
"keyFiles": [
"README.md"
]
@@ -203,43 +178,21 @@
"path": "shared",
"purpose": null,
"fileCount": 0,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111644,
"keyFiles": []
},
"trading-service": {
"path": "trading-service",
"purpose": null,
"fileCount": 4,
"lastAccessed": 1773938154944,
"keyFiles": [
"Dockerfile",
"README.md",
"requirements.txt"
]
},
"workspaces": {
"path": "workspaces",
"purpose": null,
"fileCount": 0,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111645,
"keyFiles": []
},
"agent-service/src": {
"path": "agent-service/src",
"purpose": "Source code",
"fileCount": 5,
"lastAccessed": 1773938154944,
"keyFiles": [
"__init__.py",
"config.py",
"main.py"
]
},
"backend/api": {
"path": "backend/api",
"purpose": "API routes",
"fileCount": 5,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111645,
"keyFiles": [
"__init__.py",
"agents.py",
@@ -250,7 +203,7 @@
"path": "backend/config",
"purpose": "Configuration files",
"fileCount": 6,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111646,
"keyFiles": [
"__init__.py",
"agent_profiles.yaml",
@@ -261,7 +214,7 @@
"path": "backend/data",
"purpose": "Data files",
"fileCount": 13,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111647,
"keyFiles": [
"__init__.py",
"cache.py",
@@ -272,7 +225,7 @@
"path": "docs/assets",
"purpose": "Static assets",
"fileCount": 5,
"lastAccessed": 1773938154944,
"lastAccessed": 1774313111647,
"keyFiles": [
"dashboard.jpg",
"evotraders_demo.gif",
@@ -283,7 +236,7 @@
"path": "frontend/dist",
"purpose": "Distribution/build output",
"fileCount": 2,
"lastAccessed": 1773938154945,
"lastAccessed": 1774313111647,
"keyFiles": [
"index.html",
"trading_logo.png"
@@ -293,331 +246,261 @@
"path": "frontend/node_modules",
"purpose": "Dependencies",
"fileCount": 1,
"lastAccessed": 1773938154947,
"lastAccessed": 1774313111650,
"keyFiles": []
},
"news-service/src": {
"path": "news-service/src",
"purpose": "Source code",
"fileCount": 3,
"lastAccessed": 1773938154948,
"keyFiles": [
"__init__.py",
"config.py",
"main.py"
]
},
"trading-service/src": {
"path": "trading-service/src",
"purpose": "Source code",
"fileCount": 8,
"lastAccessed": 1773938154948,
"keyFiles": [
"__init__.py",
"config.py",
"main.py"
]
}
},
"hotPaths": [
{
"path": "backend/agents/factory.py",
"accessCount": 17,
"lastAccessed": 1773939950376,
"path": "CLAUDE.md",
"accessCount": 15,
"lastAccessed": 1774342728155,
"type": "directory"
},
{
"path": "frontend/src/App.jsx",
"accessCount": 10,
"lastAccessed": 1774339397617,
"type": "file"
},
{
"path": "backend",
"accessCount": 16,
"lastAccessed": 1773940042371,
"type": "directory"
"path": "frontend/src/hooks/useWebsocketSessionSync.js",
"accessCount": 4,
"lastAccessed": 1774313470024,
"type": "file"
},
{
"path": "",
"accessCount": 13,
"lastAccessed": 1773939899611,
"accessCount": 4,
"lastAccessed": 1774339108220,
"type": "directory"
},
{
"path": "backend/main.py",
"accessCount": 7,
"lastAccessed": 1773939993951,
"type": "file"
},
{
"path": "backend/gateway_server.py",
"accessCount": 7,
"lastAccessed": 1773940004402,
"type": "file"
},
{
"path": "backend/services/news/main.py",
"accessCount": 5,
"lastAccessed": 1773938385662,
"type": "file"
},
{
"path": "backend/core/pipeline.py",
"accessCount": 5,
"lastAccessed": 1773940024933,
"type": "file"
},
{
"path": "backend/services/news/enrich/news_enricher.py",
"accessCount": 4,
"lastAccessed": 1773938508417,
"type": "file"
},
{
"path": "start-dev.sh",
"accessCount": 4,
"lastAccessed": 1773939259381,
"type": "file"
},
{
"path": "services/README.md",
"accessCount": 4,
"lastAccessed": 1773939281935,
"type": "file"
},
{
"path": "backend/app.py",
"accessCount": 4,
"lastAccessed": 1773939648215,
"type": "file"
},
{
"path": "backend/services/news/routes/news.py",
"accessCount": 3,
"lastAccessed": 1773938438928,
"type": "file"
},
{
"path": "backend/services/news",
"accessCount": 3,
"lastAccessed": 1773938468730,
"type": "directory"
},
{
"path": "frontend/src/config/constants.js",
"accessCount": 3,
"lastAccessed": 1773939204395,
"type": "file"
},
{
"path": "backend/services/gateway.py",
"accessCount": 3,
"lastAccessed": 1773939672930,
"lastAccessed": 1774339389171,
"type": "file"
},
{
"path": "backend/core/__init__.py",
"path": "backend/main.py",
"accessCount": 3,
"lastAccessed": 1773939963627,
"lastAccessed": 1774342613364,
"type": "file"
},
{
"path": "backend/services/trading/main.py",
"path": "frontend/src/store/runtimeStore.js",
"accessCount": 2,
"lastAccessed": 1773938360736,
"lastAccessed": 1774317990919,
"type": "file"
},
{
"path": "backend/services/agents/main.py",
"path": "frontend/src/services/websocket.js",
"accessCount": 2,
"lastAccessed": 1773938361040,
"lastAccessed": 1774318009819,
"type": "file"
},
{
"path": "backend/services/trading/data/__init__.py",
"path": "backend/core/pipeline_runner.py",
"accessCount": 2,
"lastAccessed": 1773938402496,
"lastAccessed": 1774339367538,
"type": "file"
},
{
"path": "backend/services/news/explain/__init__.py",
"path": "backend/runtime/manager.py",
"accessCount": 2,
"lastAccessed": 1773938460019,
"lastAccessed": 1774339367572,
"type": "file"
},
{
"path": "backend/services/news/enrich/__init__.py",
"accessCount": 2,
"lastAccessed": 1773938465216,
"type": "file"
},
{
"path": "backend/services/news/explain/range_explainer.py",
"accessCount": 2,
"lastAccessed": 1773938481152,
"type": "file"
},
{
"path": "backend/services/news/enrich/llm_enricher.py",
"accessCount": 2,
"lastAccessed": 1773938499885,
"type": "file"
},
{
"path": "CLAUDE.md",
"accessCount": 2,
"lastAccessed": 1773939273598,
"type": "file"
},
{
"path": "backend/agents/__init__.py",
"accessCount": 2,
"lastAccessed": 1773939883015,
"type": "file"
},
{
"path": "backend/agents/agent_core.py",
"accessCount": 2,
"lastAccessed": 1773939886997,
"type": "file"
},
{
"path": "Makefile",
"path": "frontend/src/store/marketStore.js",
"accessCount": 1,
"lastAccessed": 1773938226307,
"lastAccessed": 1774313140483,
"type": "file"
},
{
"path": "docker-compose.yml",
"path": "frontend/src/hooks/useFeedProcessor.js",
"accessCount": 1,
"lastAccessed": 1773938226360,
"lastAccessed": 1774313148279,
"type": "file"
},
{
"path": "backend/services/news/shared/trading_client.py",
"path": "frontend/src/components/Header.jsx",
"accessCount": 1,
"lastAccessed": 1773938370618,
"lastAccessed": 1774313156696,
"type": "file"
},
{
"path": "backend/services/agents",
"path": "frontend/src/components/TraderView.jsx",
"accessCount": 1,
"lastAccessed": 1773938397772,
"type": "directory"
},
{
"path": "backend/services/trading",
"accessCount": 1,
"lastAccessed": 1773938397823,
"type": "directory"
},
{
"path": "backend/services",
"accessCount": 1,
"lastAccessed": 1773938405541,
"type": "directory"
},
{
"path": "backend/services/news/config.py",
"accessCount": 1,
"lastAccessed": 1773938638664,
"lastAccessed": 1774313156753,
"type": "file"
},
{
"path": "shared/client/news_client.py",
"path": "frontend/src/store/uiStore.js",
"accessCount": 1,
"lastAccessed": 1773938638715,
"lastAccessed": 1774313187460,
"type": "file"
},
{
"path": "frontend/src/store/portfolioStore.js",
"accessCount": 1,
"lastAccessed": 1774313187511,
"type": "file"
},
{
"path": "frontend/src/store/agentStore.js",
"accessCount": 1,
"lastAccessed": 1774313187573,
"type": "file"
},
{
"path": "frontend/src/hooks/useWebSocketConnection.js",
"accessCount": 1,
"lastAccessed": 1774313279414,
"type": "file"
},
{
"path": "frontend/src/hooks/useStockDataRequests.js",
"accessCount": 1,
"lastAccessed": 1774313319716,
"type": "file"
},
{
"path": "frontend/src/hooks/useAgentDataRequests.js",
"accessCount": 1,
"lastAccessed": 1774313347455,
"type": "file"
},
{
"path": "frontend/src/components/AppShell.jsx",
"accessCount": 1,
"lastAccessed": 1774313396331,
"type": "file"
},
{
"path": "start-dev.sh",
"accessCount": 1,
"lastAccessed": 1774317979859,
"type": "file"
},
{
"path": "backend/apps/agent_service.py",
"accessCount": 1,
"lastAccessed": 1774317984348,
"type": "file"
},
{
"path": "shared/client/trading_client.py",
"accessCount": 1,
"lastAccessed": 1773938638770,
"lastAccessed": 1774317984365,
"type": "file"
},
{
"path": "backend/api",
"path": "backend/apps/trading_service.py",
"accessCount": 1,
"lastAccessed": 1773938669143,
"type": "directory"
},
{
"path": "frontend",
"accessCount": 1,
"lastAccessed": 1773938669195,
"type": "directory"
},
{
"path": ".env.example",
"accessCount": 1,
"lastAccessed": 1773938849397,
"type": "file"
},
{
"path": "frontend/src/services/websocket.js",
"accessCount": 1,
"lastAccessed": 1773938849448,
"type": "file"
},
{
"path": "frontend/src/services/runtimeApi.js",
"accessCount": 1,
"lastAccessed": 1773938849500,
"type": "file"
},
{
"path": "backend/services/agents/routes/websocket.py",
"accessCount": 1,
"lastAccessed": 1773939001692,
"type": "file"
},
{
"path": "backend/services/agents/routes/agents.py",
"accessCount": 1,
"lastAccessed": 1773939016291,
"type": "file"
},
{
"path": "backend/services/agents/routes/run.py",
"accessCount": 1,
"lastAccessed": 1773939016343,
"type": "file"
},
{
"path": "backend/__init__.py",
"accessCount": 1,
"lastAccessed": 1773939648323,
"type": "file"
},
{
"path": "backend/api/__init__.py",
"accessCount": 1,
"lastAccessed": 1773939658650,
"type": "file"
},
{
"path": "backend/runtime/__init__.py",
"accessCount": 1,
"lastAccessed": 1773939658687,
"type": "file"
},
{
"path": "backend/agents/base/evo_agent.py",
"accessCount": 1,
"lastAccessed": 1773939664916,
"type": "file"
},
{
"path": "backend/agents/analyst.py",
"accessCount": 1,
"lastAccessed": 1773939664967,
"type": "file"
},
{
"path": "backend/agents/base/hooks.py",
"accessCount": 1,
"lastAccessed": 1773939672727,
"lastAccessed": 1774317984408,
"type": "file"
},
{
"path": "pyproject.toml",
"accessCount": 1,
"lastAccessed": 1773939672778,
"lastAccessed": 1774317990970,
"type": "file"
},
{
"path": "backend/agents/factory.py",
"accessCount": 1,
"lastAccessed": 1774318009867,
"type": "file"
},
{
"path": "backend/config/constants.py",
"accessCount": 1,
"lastAccessed": 1774318009922,
"type": "file"
},
{
"path": "backend/api/__init__.py",
"accessCount": 1,
"lastAccessed": 1774318009973,
"type": "file"
},
{
"path": "README.md",
"accessCount": 1,
"lastAccessed": 1774339107381,
"type": "file"
},
{
"path": "backend/runtime/registry.py",
"accessCount": 1,
"lastAccessed": 1774339380024,
"type": "file"
},
{
"path": "backend/runtime/session.py",
"accessCount": 1,
"lastAccessed": 1774339380084,
"type": "file"
},
{
"path": "backend/runtime/context.py",
"accessCount": 1,
"lastAccessed": 1774339380120,
"type": "file"
},
{
"path": "backend/runtime/agent_runtime.py",
"accessCount": 1,
"lastAccessed": 1774339380185,
"type": "file"
},
{
"path": "backend/process/supervisor.py",
"accessCount": 1,
"lastAccessed": 1774339389110,
"type": "file"
},
{
"path": "backend/core/pipeline.py",
"accessCount": 1,
"lastAccessed": 1774339389187,
"type": "file"
},
{
"path": "backend/process/models.py",
"accessCount": 1,
"lastAccessed": 1774339397557,
"type": "file"
},
{
"path": "backend/process/registry.py",
"accessCount": 1,
"lastAccessed": 1774339397577,
"type": "file"
},
{
"path": "backend/config/env_config.py",
"accessCount": 1,
"lastAccessed": 1774342678236,
"type": "file"
},
{
"path": "backend/config/data_config.py",
"accessCount": 1,
"lastAccessed": 1774342678253,
"type": "file"
},
{
"path": "frontend/env.template",
"accessCount": 1,
"lastAccessed": 1774342678290,
"type": "file"
},
{
"path": "env.template",
"accessCount": 1,
"lastAccessed": 1774342678310,
"type": "file"
}
],

View File

@@ -1,6 +1,6 @@
{
"timestamp": "2026-03-19T16:36:52.471Z",
"timestamp": "2026-03-24T07:58:12.123Z",
"backgroundTasks": [],
"sessionStartTimestamp": "2026-03-19T16:36:42.224Z",
"sessionId": "ef02339a-1eec-4c7a-95ac-c8cfa0b5067d"
"sessionStartTimestamp": "2026-03-24T07:58:09.417Z",
"sessionId": "fda34772-7bd2-402e-86b2-d656296416f3"
}

View File

@@ -1 +1 @@
{"session_id":"ef02339a-1eec-4c7a-95ac-c8cfa0b5067d","transcript_path":"/Users/cillin/.claude/projects/-Users-cillin-workspeace-evotraders/ef02339a-1eec-4c7a-95ac-c8cfa0b5067d.jsonl","cwd":"/Users/cillin/workspeace/evotraders","model":{"id":"MiniMax-M2.7-highspeed","display_name":"MiniMax-M2.7-highspeed"},"workspace":{"current_dir":"/Users/cillin/workspeace/evotraders","project_dir":"/Users/cillin/workspeace/evotraders","added_dirs":[]},"version":"2.1.78","output_style":{"name":"default"},"cost":{"total_cost_usd":17.458779250000003,"total_duration_ms":1866224,"total_api_duration_ms":1188013,"total_lines_added":257,"total_lines_removed":290},"context_window":{"total_input_tokens":195204,"total_output_tokens":48917,"context_window_size":200000,"current_usage":{"input_tokens":481,"output_tokens":0,"cache_creation_input_tokens":149,"cache_read_input_tokens":163286},"used_percentage":82,"remaining_percentage":18},"exceeds_200k_tokens":false}
{"session_id":"fda34772-7bd2-402e-86b2-d656296416f3","transcript_path":"/Users/cillin/.claude/projects/-Users-cillin-workspeace-evotraders/fda34772-7bd2-402e-86b2-d656296416f3.jsonl","cwd":"/Users/cillin/workspeace/evotraders","model":{"id":"MiniMax-M2.7-highspeed","display_name":"MiniMax-M2.7-highspeed"},"workspace":{"current_dir":"/Users/cillin/workspeace/evotraders","project_dir":"/Users/cillin/workspeace/evotraders","added_dirs":[]},"version":"2.1.78","output_style":{"name":"default"},"cost":{"total_cost_usd":36.63980749999998,"total_duration_ms":69778027,"total_api_duration_ms":2925118,"total_lines_added":3056,"total_lines_removed":4537},"context_window":{"total_input_tokens":910503,"total_output_tokens":145207,"context_window_size":200000,"current_usage":{"input_tokens":507,"output_tokens":247,"cache_creation_input_tokens":4132,"cache_read_input_tokens":96553},"used_percentage":51,"remaining_percentage":49},"exceeds_200k_tokens":false}

View File

@@ -1,3 +1,3 @@
{
"lastSentAt": "2026-03-19T17:02:32.170Z"
"lastSentAt": "2026-03-24T08:58:57.965Z"
}

View File

@@ -1,17 +1,26 @@
{
"agents": [
{
"agent_id": "a8305a91e192b2196",
"agent_id": "abeaf609b74a2b7ee",
"agent_type": "Explore",
"started_at": "2026-03-19T17:00:33.284Z",
"started_at": "2026-03-24T08:01:40.015Z",
"parent_mode": "none",
"status": "completed",
"completed_at": "2026-03-19T17:02:19.439Z",
"duration_ms": 106155
"completed_at": "2026-03-24T08:02:31.822Z",
"duration_ms": 51807
},
{
"agent_id": "afb6750eaae72bc72",
"agent_type": "Explore",
"started_at": "2026-03-24T08:56:21.471Z",
"parent_mode": "none",
"status": "completed",
"completed_at": "2026-03-24T08:57:27.856Z",
"duration_ms": 66385
}
],
"total_spawned": 1,
"total_completed": 1,
"total_spawned": 2,
"total_completed": 2,
"total_failed": 0,
"last_updated": "2026-03-19T17:02:39.175Z"
"last_updated": "2026-03-24T08:59:06.380Z"
}

390
CLAUDE.md
View File

@@ -1,5 +1,7 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
本文件为 Claude Code (claude.ai/code) 在此代码库中工作时提供指导。
## 项目概述
@@ -23,18 +25,20 @@ evotraders live -t 22:30 # 定时每日交易
evotraders frontend # 启动可视化界面
# 开发服务器
./start-dev.sh # 启动全部 4 个微服务
./start-dev.sh # 启动全部 4 个微服务 (agent, runtime, trading, news)
# 单独启动某个服务
python -m uvicorn backend.apps.agent_service:app --host 0.0.0.0 --port 8000 --reload
# Gateway WebSocket 服务
python backend/main.py --mode live --config-name mock --mock
# 单独启动微服务
python -m uvicorn backend.apps.runtime_service:app --host 0.0.0.0 --port 8003 --reload
python -m uvicorn backend.apps.agent_service:app --host 0.0.0.0 --port 8000 --reload
python -m uvicorn backend.apps.trading_service:app --host 0.0.0.0 --port 8001 --reload
python -m uvicorn backend.apps.news_service:app --host 0.0.0.0 --port 8002 --reload
# 测试
pytest backend/tests # 运行全部测试
pytest backend/tests/test_news_service_app.py -v # 运行单个测试文件
pytest backend/tests/test_news_service_app.py::test_news_service_routes_are_exposed -v # 运行单个测试
pytest backend/tests/test_news_service_app.py -v # 运行单个测试
```
### Frontend (React)
@@ -46,142 +50,237 @@ npm run build # 生产构建
npm run lint # ESLint 检查
npm run lint:fix # ESLint 自动修复
npm run test # Vitest 单元测试
npm run test:watch # 监听模式
```
## 架构概览
### 微服务架构 (`backend/apps/`)
### 系统分层
项目采用 split-first 微服务架构4 个独立的 FastAPI 服务:
| 服务 | 入口 | 端口 | 职责 |
|------|------|------|------|
| agent_service | `backend.apps.agent_service:app` | 8000 | Agent 生命周期、工作区管理 |
| runtime_service | `backend.apps.runtime_service:app` | 8003 | 运行时配置、任务启动 |
| trading_service | `backend.apps.trading_service:app` | 8001 | 市场数据、交易操作 |
| news_service | `backend.apps.news_service:app` | 8002 | 新闻、新闻富化、解释功能 |
服务间通过环境变量通信(详见 `start-dev.sh`
```bash
export TRADING_SERVICE_URL=http://localhost:8001
export NEWS_SERVICE_URL=http://localhost:8002
export RUNTIME_SERVICE_URL=http://localhost:8003
```
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React) │
│ WebSocket ws://localhost:8765 连接 Gateway │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Gateway (backend/services/gateway.py) │
│ WebSocket 服务器,编排 Pipeline4 阶段启动 │
└─────────────────────────────────────────────────────────────┘
│ │ │ │
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ Market │ │ Storage │ │ Pipeline │ │ Scheduler │
│ Service │ │ Service │ │ │ │ │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Analysts │ │ PM │ │ Risk │
│ (4 个) │ │ │ │ Manager │
└──────────┘ └──────────┘ └──────────┘
```
### Gateway 网关 (`backend/services/gateway.py`)
### 微服务架构 (`backend/apps/`)
Gateway 是统一的请求路由器,根据路径前缀将请求转发到对应的微服务:
- `/control/*` → agent_service
- `/runtime/*` → runtime_service
- `/trading/*` → trading_service
- `/news/*` → news_service
| 服务 | 端口 | 职责 |
|------|------|------|
| runtime_service | 8003 | 运行时配置、任务启动、Pipeline Runner |
| agent_service | 8000 | Agent 生命周期、工作区管理 |
| trading_service | 8001 | 市场数据、交易操作 |
| news_service | 8002 | 新闻、新闻富化、解释功能 |
新增接口时应注册到对应的 service app而非直接添加到 gateway。
### Gateway 4 阶段启动 (`backend/services/gateway.py`)
### 共享客户端 (`shared/client/`)
1. **WebSocket Server** - 前端立即可连接
2. **Market Service** - 价格数据开始推送
3. **Market Status Monitor** - 市场状态监控
4. **Scheduler** - 交易周期开始
统一的服务客户端库,所有前端和后端服务间通信都使用此处定义的客户端:
### 运行时管理层 (`backend/runtime/`)
| 客户端 | 用途 |
|--------|------|
| `ControlPlaneClient` | Agent 服务通信 |
| `RuntimeServiceClient` | 运行时服务通信 |
| `TradingServiceClient` | 交易服务通信 |
| `NewsServiceClient` | 新闻服务通信 |
| 文件 | 职责 |
|------|------|
| `manager.py` | TradingRuntimeManager - 全局运行时管理器agent 注册、会话、事件快照 |
| `agent_runtime.py` | AgentRuntimeState - 单 agent 状态status、last_session |
| `context.py` | TradingRunContext - 运行上下文 |
| `session.py` | TradingSessionKey - 交易日会话键 |
| `registry.py` | RuntimeRegistry - agent 状态注册表 |
### 领域层 (`backend/domains/`)
快照持久化到 `runs/<run_id>/state/runtime_state.json`
业务逻辑按领域分离:
### Pipeline 执行 (`backend/core/`)
- `news.py` - 新闻领域操作
- `trading.py` - 交易领域操作
| 文件 | 职责 |
|------|------|
| `pipeline.py` | TradingPipeline - 核心编排器(分析→沟通→决策→执行→评估) |
| `pipeline_runner.py` | REST API 触发的独立执行5 阶段启动 |
| `scheduler.py` | BacktestScheduler、Scheduler - 回测/实盘调度 |
| `state_sync.py` | StateSync - 状态同步和广播 |
## 后端结构
```
backend/
├── agents/ # 多智能体实现
│ ├── base/ # 核心类、Hooks、评估
│ ├── evo_agent.py # 基于 AgentScope 的核心实现
│ ├── hooks.py # 生命周期 Hooks
│ │ │ ├── BootstrapHook # 启动初始化
│ │ │ ├── MemoryCompactionHook # 内存压缩(基于 CoPaw
│ │ │ ├── HeartbeatHook # 心跳检测
│ │ │ └── WorkspaceWatchHook # 工作区监控
│ │ ├── evaluation_hook.py # 执行后评估
│ │ ├── skill_adaptation_hook.py # 动态技能适配
│ │ └── tool_guard.py # 工具调用守卫
│ ├── prompts/ # Agent 提示词和角色定义
│ │ ├── analyst/personas.yaml # 分析师角色配置
│ │ └── portfolio_manager/
│ ├── team/ # 团队协作逻辑
│ │ ├── registry.py # Agent 注册表
│ │ ├── coordinator.py # 协作协调器
│ │ ├── messenger.py # 消息传递
│ │ └── task_delegator.py # 任务分发
│ ├── analyst.py # AnalystAgent 基类
│ ├── portfolio_manager.py # PMAgent 投资经理
│ ├── risk_manager.py # RiskAgent 风控经理
│ ├── factory.py # Agent 实例工厂
│ ├── skills_manager.py # 技能加载管理6 种作用域)
── toolkit_factory.py # 工具集工厂
├── apps/ # 微服务入口split-first
│ ├── agent_service.py
│ ├── runtime_service.py
│ ├── trading_service.py
── news_service.py
│ ├── toolkit_factory.py # 工具集工厂
── skills_manager.py # 技能加载管理
│ ├── workspace_manager.py # 工作区管理
│ ├── skill_loader.py # 技能加载器
│ ├── agent_workspace.py # Agent 工作区
│ ├── prompt_loader.py # Prompt 加载器
── prompt_factory.py # Prompt 工厂
│ ├── skill_metadata.py # 技能元数据
│ ├── registry.py # Agent 注册表
│ ├── team_pipeline_config.py # 团队 Pipeline 配置
│ ├── compat.py # 兼容性层
│ ├── templates.py # 模板
│ ├── workspace.py # 工作区
│ ├── base/ # 核心类、Hooks
│ │ ├── evo_agent.py # 基于 AgentScope 的核心实现
│ │ └── hooks.py # 生命周期 Hooks
│ └── prompts/ # Agent 提示词
│ └── analyst/personas.yaml
├── apps/ # 微服务入口
│ ├── runtime_service.py # 运行时服务(端口 8003
│ ├── agent_service.py # Agent 服务(端口 8000
│ ├── trading_service.py # 交易服务(端口 8001
│ ├── news_service.py # 新闻服务(端口 8002
│ └── cors.py
├── runtime/ # 运行时管理层
│ ├── manager.py # TradingRuntimeManager
│ ├── agent_runtime.py # AgentRuntimeState
│ ├── context.py # TradingRunContext
│ ├── session.py # TradingSessionKey
│ └── registry.py # RuntimeRegistry
├── process/ # 进程监管层
│ ├── supervisor.py # ProcessSupervisor
│ ├── registry.py # RunRegistry
│ └── models.py # ProcessRun、ProcessRunState
├── core/ # Pipeline 执行
│ ├── pipeline.py # TradingPipeline核心编排器
│ ├── pipeline_runner.py # 独立 Pipeline 执行
│ ├── scheduler.py # 调度器
│ └── state_sync.py # 状态同步
├── services/ # Gateway 和服务
│ ├── gateway.py # WebSocket 网关
│ ├── gateway_*.py # Gateway 子模块
│ ├── market.py # 市场数据服务
│ ├── storage.py # 存储服务
│ ├── runtime_db.py # 运行时数据库
│ └── research_db.py # 研究数据库
├── data/ # 市场数据处理
│ ├── provider_router.py # 数据源路由
│ ├── provider_utils.py # 数据源工具
│ ├── market_store.py # 市场数据存储
│ ├── market_ingest.py # 数据采集
│ ├── cache.py # 缓存
│ ├── schema.py # 数据 schema
│ ├── historical_price_manager.py # 历史价格管理
│ ├── polling_price_manager.py # 轮询价格管理
│ ├── mock_price_manager.py # Mock 价格管理
│ ├── news_alignment.py # 新闻对齐
│ ├── polygon_client.py # Polygon.io 客户端
│ └── ret_data_updater.py # 离线数据更新
├── config/ # 配置
│ ├── constants.py # Agent 配置、显示名称
│ ├── bootstrap_config.py # 启动配置解析
│ ├── env_config.py # 环境变量配置
│ ├── data_config.py # 数据源配置
│ └── agent_profiles.yaml # Agent Profile 配置
├── domains/ # 领域业务逻辑
│ ├── news.py
│ └── trading.py
├── services/ # Gateway 和辅助服务
│ ├── gateway.py # 统一路由网关
│ ├── gateway_*.py # Gateway 子模块
│ └── market.py # 市场数据服务
├── api/ # FastAPI 端点
├── config/ # 常量和配置
│ └── constants.py # Agent 配置、显示名称等
├── core/ # Pipeline 执行逻辑
├── data/ # 市场数据处理
│ ├── provider_router.py # 数据源路由
│ └── schema.py # 数据 schema
├── enrich/ # LLM 响应富化
├── explain/ # 交易决策解释
├── llm/ # LLM 集成
│ └── models.py # RetryChatModel、TokenRecordingModelWrapper
├── skills/ # 技能定义(内置 + 自定义)
├── skills/ # 技能定义
├── tools/ # 交易和分析工具
── utils/ # 工具函数
── enrich/ # LLM 响应富化
├── explain/ # 交易决策解释
├── utils/ # 工具函数
│ ├── settlement.py # 结算协调器
│ ├── trade_executor.py # 交易执行器
│ ├── terminal_dashboard.py # 终端仪表板
│ ├── analyst_tracker.py # 分析师追踪
│ ├── baselines.py # 基准线
│ ├── msg_adapter.py # 消息适配器
│ └── progress.py # 进度追踪
├── api/ # FastAPI 端点
│ └── runtime.py
└── main.py # 主入口点
```
## 前端结构
```
frontend/src/
├── App.jsx # React 主应用
├── components/ # React 组件
├── App.jsx # 主应用LiveTradingApp
├── AppShell.jsx # App 外壳(布局、侧边栏)
├── components/
│ ├── RuntimeView.jsx # 交易运行时 UI
│ ├── TraderView.jsx # 交易员界面
│ ├── RoomView.jsx # 聊天室视图
│ ├── StockExplainView.jsx # 股票解释视图
│ ├── RuntimeSettingsPanel.jsx # 运行时设置面板
│ ├── RuntimeLogsModal.jsx # 运行时日志弹窗
│ ├── WatchlistPanel.jsx # 关注列表
│ ├── PerformanceView.jsx # 绩效视图
│ ├── StatisticsView.jsx # 统计视图
│ ├── NetValueChart.jsx # 净值曲线图
│ ├── AgentCard.jsx # Agent 卡片
│ ├── AgentFeed.jsx # Agent 动态
── explain/ # 解释相关组件
── Header.jsx # 头部
│ ├── MarkdownModal.jsx # Markdown 弹窗
│ ├── StockLogo.jsx # 股票 Logo
│ └── explain/ # 解释组件
│ ├── ExplainNewsSection.jsx
│ ├── ExplainRangeSection.jsx
│ ├── ExplainSimilarDaysSection.jsx
│ ├── ExplainStorySection.jsx
│ └── useExplainModel.js
├── services/ # API 服务
│ ├── runtimeApi.js # 运行时 API 调用
│ ├── websocket.js # WebSocket 实时通信
│ ├── newsApi.js # 新闻服务客户端
── tradingApi.js # 交易服务客户端
├── config/
── constants.js # Agent 定义、配置
└── hooks/ # React Hooks
├── hooks/ # React Hooks
│ ├── useWebSocketConnection.js # WebSocket 连接管理
│ ├── useRuntimeControls.js # 运行时配置管理
│ ├── useAgentDataRequests.js # Agent 数据请求
── useStockDataRequests.js # 股票数据请求
│ ├── useStockExplainData.js # 股票解释数据
── useAgentWorkspacePanel.js # Agent 工作区面板
│ ├── useWebsocketSessionSync.js # WebSocket 会话同步
│ └── useFeedProcessor.js # Feed 事件处理
├── store/ # Zustand 状态管理
│ ├── runtimeStore.js # 连接状态、运行时配置
│ ├── marketStore.js # 市场数据、股票价格
│ ├── portfolioStore.js # 组合、持仓、交易
│ ├── agentStore.js # Agent 技能、工作区
│ └── uiStore.js # UI 状态、视图切换
├── services/
│ ├── websocket.js # WebSocket 客户端
│ ├── runtimeApi.js # 运行时 API
│ ├── runtimeControls.js # 运行时控制
│ ├── newsApi.js # 新闻 API
│ └── tradingApi.js # 交易 API
├── utils/
│ ├── formatters.js # 格式化工具
│ └── modelIcons.js # 模型图标
└── config/
└── constants.js # Agent 定义、配置
```
## Agent 系统
@@ -195,108 +294,85 @@ frontend/src/
| `sentiment_analyst` | 情绪分析师 | 市场情绪、新闻情绪、内幕交易 |
| `valuation_analyst` | 估值分析师 | DCF、EV/EBITDA、intrinsic value |
| `portfolio_manager` | 投资经理 | 决策执行、交易协调 |
| `risk_manager` | 风控经理 | 实时价格/波动率监控、仓位限制、多层风险预警 |
### Hook 系统 (`base/hooks.py`)
- **MemoryCompactionHook**: 基于 CoPaw 的内存压缩
- `memory_compact_ratio`: 压缩目标比例(默认 0.75
- `memory_reserve_ratio`: 保留比例(默认 0.1
- `enable_tool_result_compact`: 工具结果压缩
- `tool_result_compact_keep_n`: 保留最近 N 条工具结果
| `risk_manager` | 风控经理 | 实时价格/波动率监控、仓位限制 |
### 添加自定义分析师
1. `backend/agents/prompts/analyst/personas.yaml` 注册
2. `backend/config/constants.py``ANALYST_TYPES` 字典添加
3. 可选:在 `frontend/src/config/constants.js` 中更新前端配置
1. `backend/agents/prompts/analyst/personas.yaml` 注册
2. `backend/config/constants.py``ANALYST_TYPES` 字典添加
3. `frontend/src/config/constants.js` 可选更新
### LLM 模型封装 (`backend/llm/models.py`)
基于 CoPaw 的模型封装设计:
- **RetryChatModel**: 自动重试瞬态 LLM 错误rate limit、timeout、502/503 等),指数退避
- `max_retries`: 最大重试次数(默认 3
- `initial_delay`: 初始延迟秒数(默认 1.0
- `backoff_multiplier`: 退避倍数(默认 2.0
- **TokenRecordingModelWrapper**: 追踪每个 provider 的 token 消耗和成本
```python
from backend.llm.models import create_model, RetryChatModel
model = RetryChatModel(create_model("gpt-4o", "OPENAI"), max_retries=3)
```
- **RetryChatModel**: 自动重试瞬态 LLM 错误,指数退避
- **TokenRecordingModelWrapper**: 追踪 token 消耗和成本
## 技能系统 (`backend/skills/`)
技能定义在 `SKILL.md` 文件中,包含:
- `instructions` - 技能说明
- `triggers` - 触发条件
- `parameters` - 输入/输出 schema
- `available_tools` - 技能可使用的工具
技能由 `skills_manager.py` 加载,通过 `skill_adaptation_hook.py` 绑定到 Agent。
技能定义在 `SKILL.md`,包含 `instructions``triggers``parameters``available_tools`
技能管理器支持 6 种作用域builtin、customized、installed、active、disabled、local。
## Pipeline 执行 (`backend/core/`)
## 运行时数据布局
每日交易流程:
1. **分析阶段** - 各 Agent 基于工具和历史经验独立分析
2. **沟通阶段** - 通过私聊、通知、会议等方式交换观点1v1/1vN/NvN
3. **决策阶段** - 投资经理综合判断,给出最终交易
4. **评估阶段** - 绩效跟踪
5. **复盘阶段** - Agent 根据当日实际收益反思总结,通过 ReMe 记忆框架更新经验
## 前端状态管理
项目正在向 Zustand 状态管理过渡,已创建的 store
- `data/market_research.db` - 持久研究数据
- `runs/<run_id>/` - 每次任务运行的状态
- `runs/<run_id>/team_dashboard/*.json` - 仪表板导出层(非权威源)
- `runs/<run_id>/state/runtime_state.json` - 运行时快照
- 运行时 API 优先使用 `server_state.json``runtime.db`
```bash
frontend/src/store/
├── index.js # 导出所有 store
├── runtimeStore.js # 连接状态、运行时配置
├── marketStore.js # 市场数据、股票价格
├── portfolioStore.js # 组合、持仓、交易
├── agentStore.js # Agent 技能、工作区
└── uiStore.js # UI 状态、视图切换
RUNS_RETENTION_COUNT=20 # 时间戳格式文件夹自动清理
```
**迁移状态**
- Stores 已创建但尚未在 App.jsx 中使用
- 计划:逐步迁移 60+ 个 useState 到对应 store
## 环境配置
`.env` 必需配置:
### Backend (`env.template`)
```bash
# 金融数据源
FIN_DATA_SOURCE=finnhub|financial_datasets
# 金融数据源支持多源fallback
FIN_DATA_SOURCE=finnhub|financial_datasets|yfinance|local_csv
ENABLED_DATA_SOURCES=financial_datasets,finnhub,yfinance,local_csv
FINANCIAL_DATASETS_API_KEY= # 回测必需
FINNHUB_API_KEY= # 实盘必需
POLYGON_API_KEY= # Polygon市场库采集可选
# Agent LLM
# LLM 配置
OPENAI_API_KEY=
OPENAI_BASE_URL=
MODEL_NAME=qwen3-max-preview
# 可为不同 Agent 指定不同模型
AGENT_SENTIMENT_ANALYST_MODEL_NAME=qwen3-max-preview
AGENT_FUNDAMENTALS_ANALYST_MODEL_NAME=deepseek-chat
# Agent 特定模型
AGENT_SENTIMENT_ANALYST_MODEL_NAME=deepseek-v3.2-exp
AGENT_TECHNICAL_ANALYST_MODEL_NAME=glm-4.6
AGENT_FUNDAMENTALS_ANALYST_MODEL_NAME=qwen3-max-preview
AGENT_VALUATION_ANALYST_MODEL_NAME=Moonshot-Kimi-K2-Instruct
AGENT_RISK_MANAGER_MODEL_NAME=qwen3-max-preview
AGENT_PORTFOLIO_MANAGER_MODEL_NAME=qwen3-max-preview
# ReMe 记忆系统
MEMORY_API_KEY=
MEMORY_MODEL_NAME=qwen3-max
MEMORY_EMBEDDING_MODEL=text-embedding-v4
# 交易参数
MAX_COMM_CYCLES=2
MARGIN_REQUIREMENT=0.5
DATA_START_DATE=2022-01-01
AUTO_UPDATE_DATA=true
```
### Frontend (`frontend/env.template`)
```bash
VITE_WS_URL=ws://localhost:8765
```
## 关键依赖
- **AgentScope** - 多智能体框架
- **ReMe** - 持续学习记忆系统
- **FastAPI** + **uvicorn** - 后端 API 服务器
- **FastAPI** + **uvicorn** - 后端 API
- **websockets** - 实时通信
- **React 19** + **Vite** + **TailwindCSS** - 前端
- **React Context** - 前端状态管理App.jsx 中使用 useState + useCallback
- **Three.js** / **React-Three-Fiber** - 3D 可视化
- **Zustand** - 状态管理

View File

@@ -41,6 +41,8 @@ class SkillsManager:
)
self.runs_root = self.project_root / "runs"
self._lock = Lock()
# Instance-level pending skill changes (thread-safe via self._lock)
self._pending_skill_changes: Dict[str, Set[Path]] = {}
def get_active_root(self, config_name: str) -> Path:
return self.runs_root / config_name / "skills" / "active"
@@ -739,7 +741,7 @@ class SkillsManager:
if local_root.exists():
watched_paths.append(local_root)
handler = _SkillsChangeHandler(watched_paths, callback, self._lock)
handler = _SkillsChangeHandler(watched_paths, self._pending_skill_changes, callback, self._lock)
observer = Observer()
for path in watched_paths:
observer.schedule(handler, str(path), recursive=True)
@@ -773,6 +775,7 @@ class SkillsManager:
# -------------------------------------------------------------------------
# Internal change-tracking state (populated by _SkillsChangeHandler)
# -------------------------------------------------------------------------
# Legacy class-level reference kept for migration compatibility
_pending_skill_changes: Dict[str, Set[Path]] = {}
def _resolve_disabled_skill_names(
@@ -824,11 +827,13 @@ class _SkillsChangeHandler(FileSystemEventHandler):
def __init__(
self,
watched_paths: List[Path],
pending_changes: Dict[str, Set[Path]],
callback: Optional[Any] = None,
lock: Optional[Lock] = None,
) -> None:
super().__init__()
self._watched_paths = watched_paths
self._pending_changes = pending_changes
self._callback = callback
self._lock = lock
@@ -841,13 +846,9 @@ class _SkillsChangeHandler(FileSystemEventHandler):
run_id = self._run_id_from_path(src_path)
if self._lock:
with self._lock:
SkillsManager._pending_skill_changes.setdefault(
run_id, set()
).add(src_path)
self._pending_changes.setdefault(run_id, set()).add(src_path)
else:
SkillsManager._pending_skill_changes.setdefault(
run_id, set()
).add(src_path)
self._pending_changes.setdefault(run_id, set()).add(src_path)
if self._callback:
self._callback([src_path])
break

View File

@@ -9,6 +9,7 @@ from .runtime_service import app as runtime_app
from .runtime_service import create_app as create_runtime_app
from .trading_service import app as trading_app
from .trading_service import create_app as create_trading_app
from .cors import add_cors_middleware, get_cors_origins
app = agent_app
create_app = create_agent_app
@@ -24,4 +25,6 @@ __all__ = [
"create_runtime_app",
"trading_app",
"create_trading_app",
"add_cors_middleware",
"get_cors_origins",
]

View File

@@ -8,7 +8,8 @@ from pathlib import Path
from typing import AsyncGenerator
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.apps.cors import add_cors_middleware
from backend.api import agents_router, guard_router, workspaces_router
from backend.agents import AgentFactory, WorkspaceManager, get_registry
@@ -47,13 +48,7 @@ def create_app(project_root: Path | None = None) -> FastAPI:
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
add_cors_middleware(app)
@app.get("/health")
async def health_check() -> dict[str, object]:

View File

@@ -4,10 +4,10 @@
from __future__ import annotations
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.api import runtime_router
from backend.api.runtime import get_runtime_state
from backend.apps.cors import add_cors_middleware
def create_app() -> FastAPI:
@@ -18,13 +18,7 @@ def create_app() -> FastAPI:
version="0.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
add_cors_middleware(app)
@app.get("/health")
async def health_check() -> dict[str, object]:

View File

@@ -6,7 +6,7 @@ from __future__ import annotations
from typing import Any
from fastapi import FastAPI, Query
from fastapi.middleware.cors import CORSMiddleware
from backend.apps.cors import add_cors_middleware
from backend.domains import trading as trading_domain
from shared.schema import (
@@ -26,13 +26,7 @@ def create_app() -> FastAPI:
version="0.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
add_cors_middleware(app)
@app.get("/health")
async def health_check() -> dict[str, str]:

View File

@@ -266,10 +266,6 @@ async def run_pipeline(
set_global_runtime_manager(runtime_manager)
# Register runtime manager with API
from backend.api.runtime import register_runtime_manager
register_runtime_manager(runtime_manager)
# ======================================================================
# PHASE 1 & 2: Create infrastructure services (Market, Storage)
# These will be started by Gateway in the correct order

View File

@@ -9,7 +9,7 @@ import os
import sqlite3
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Iterable
from typing import Any, Iterable, Optional
SCHEMA = """
@@ -147,12 +147,30 @@ def _utc_timestamp() -> str:
class MarketStore:
"""SQLite-backed market research warehouse."""
"""SQLite-backed market research warehouse. Use get_instance() for the singleton."""
_instance: Optional["MarketStore"] = None
def __new__(cls, db_path: Path | None = None) -> "MarketStore":
if cls._instance is not None:
if db_path is None or cls._instance.db_path == Path(db_path or get_market_db_path()):
return cls._instance
instance = super().__new__(cls)
cls._instance = instance
return instance
def __init__(self, db_path: Path | None = None):
if getattr(self, "_initialized", False):
return
self.db_path = Path(db_path or get_market_db_path())
self.db_path.parent.mkdir(parents=True, exist_ok=True)
self._init_db()
self._initialized = True
@classmethod
def get_instance(cls, db_path: Path | None = None) -> "MarketStore":
"""Get the MarketStore singleton instance."""
return cls(db_path)
def _connect(self) -> sqlite3.Connection:
conn = sqlite3.connect(self.db_path)

View File

@@ -226,7 +226,6 @@ async def run_with_gateway(args):
)
runtime_manager.prepare_run()
set_global_runtime_manager(runtime_manager)
register_runtime_manager(runtime_manager)
# Create market service
market_service = MarketService(

View File

@@ -13,15 +13,30 @@ from .registry import RuntimeRegistry
_global_runtime_manager: Optional["TradingRuntimeManager"] = None
_shutdown_event: Optional[asyncio.Event] = None
# Lazy import to avoid circular dependency
_api_runtime = None
def _get_api_runtime():
global _api_runtime
if _api_runtime is None:
from backend.api import runtime as api_runtime_module
_api_runtime = api_runtime_module
return _api_runtime
def set_global_runtime_manager(manager: "TradingRuntimeManager") -> None:
global _global_runtime_manager
_global_runtime_manager = manager
# Sync to RuntimeState for consistency
_get_api_runtime().register_runtime_manager(manager)
def clear_global_runtime_manager() -> None:
global _global_runtime_manager
_global_runtime_manager = None
# Sync to RuntimeState for consistency
_get_api_runtime().unregister_runtime_manager()
def get_global_runtime_manager() -> Optional["TradingRuntimeManager"]:

View File

@@ -0,0 +1,4 @@
{
"status": "failed",
"failedTests": []
}

1
reference/CoPaw Submodule

Submodule reference/CoPaw added at 934cfce0a7

Submodule reference/Hyper-Alpha-Arena added at f137cff476

1
reference/openclaw Submodule

Submodule reference/openclaw added at 7b151afeeb