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/ /smoke_live_mock/
# Local tooling state # Local tooling state
/.omc/ .omc/
/.pydeps/ /.pydeps/
/referance/ /referance/
# Run outputs
/runs/
# Data files # Data files
backend/data/ret_data/ backend/data/ret_data/

View File

@@ -1,6 +1,6 @@
{ {
"version": "1.0.0", "version": "1.0.0",
"lastScanned": 1773938154948, "lastScanned": 1774313111650,
"projectRoot": "/Users/cillin/workspeace/evotraders", "projectRoot": "/Users/cillin/workspeace/evotraders",
"techStack": { "techStack": {
"languages": [ "languages": [
@@ -11,14 +11,6 @@
"markers": [ "markers": [
"pyproject.toml" "pyproject.toml"
] ]
},
{
"name": "C/C++",
"version": null,
"confidence": "high",
"markers": [
"Makefile"
]
} }
], ],
"frameworks": [ "frameworks": [
@@ -32,8 +24,8 @@
"runtime": null "runtime": null
}, },
"build": { "build": {
"buildCommand": "make build", "buildCommand": null,
"testCommand": "make test", "testCommand": "pytest",
"lintCommand": "ruff check", "lintCommand": "ruff check",
"devCommand": null, "devCommand": null,
"scripts": {} "scripts": {}
@@ -58,24 +50,13 @@
}, },
"customNotes": [], "customNotes": [],
"directoryMap": { "directoryMap": {
"agent-service": {
"path": "agent-service",
"purpose": null,
"fileCount": 2,
"lastAccessed": 1773938154941,
"keyFiles": [
"Dockerfile",
"requirements.txt"
]
},
"backend": { "backend": {
"path": "backend", "path": "backend",
"purpose": null, "purpose": null,
"fileCount": 5, "fileCount": 4,
"lastAccessed": 1773938154941, "lastAccessed": 1774313111639,
"keyFiles": [ "keyFiles": [
"__init__.py", "__init__.py",
"app.py",
"cli.py", "cli.py",
"gateway_server.py", "gateway_server.py",
"main.py" "main.py"
@@ -85,37 +66,41 @@
"path": "backtest", "path": "backtest",
"purpose": null, "purpose": null,
"fileCount": 0, "fileCount": 0,
"lastAccessed": 1773938154941, "lastAccessed": 1774313111640,
"keyFiles": [] "keyFiles": []
}, },
"data": { "data": {
"path": "data", "path": "data",
"purpose": "Data files", "purpose": "Data files",
"fileCount": 1, "fileCount": 3,
"lastAccessed": 1773938154941, "lastAccessed": 1774313111640,
"keyFiles": [ "keyFiles": [
"market_research.db" "market_research.db",
"market_research.db-shm",
"market_research.db-wal"
] ]
}, },
"deploy": { "deploy": {
"path": "deploy", "path": "deploy",
"purpose": null, "purpose": null,
"fileCount": 0, "fileCount": 0,
"lastAccessed": 1773938154942, "lastAccessed": 1774313111640,
"keyFiles": [] "keyFiles": []
}, },
"docs": { "docs": {
"path": "docs", "path": "docs",
"purpose": "Documentation", "purpose": "Documentation",
"fileCount": 0, "fileCount": 1,
"lastAccessed": 1773938154942, "lastAccessed": 1774313111641,
"keyFiles": [] "keyFiles": [
"compat-removal-plan.md"
]
}, },
"evotraders.egg-info": { "evotraders.egg-info": {
"path": "evotraders.egg-info", "path": "evotraders.egg-info",
"purpose": null, "purpose": null,
"fileCount": 6, "fileCount": 6,
"lastAccessed": 1773938154942, "lastAccessed": 1774313111641,
"keyFiles": [ "keyFiles": [
"PKG-INFO", "PKG-INFO",
"SOURCES.txt", "SOURCES.txt",
@@ -128,7 +113,7 @@
"path": "frontend", "path": "frontend",
"purpose": null, "purpose": null,
"fileCount": 13, "fileCount": 13,
"lastAccessed": 1773938154942, "lastAccessed": 1774313111641,
"keyFiles": [ "keyFiles": [
"README.md", "README.md",
"components.json", "components.json",
@@ -141,51 +126,41 @@
"path": "live", "path": "live",
"purpose": null, "purpose": null,
"fileCount": 0, "fileCount": 0,
"lastAccessed": 1773938154943, "lastAccessed": 1774313111642,
"keyFiles": [] "keyFiles": []
}, },
"logs": { "logs": {
"path": "logs", "path": "logs",
"purpose": null, "purpose": null,
"fileCount": 7, "fileCount": 6,
"lastAccessed": 1773938154943, "lastAccessed": 1774313111642,
"keyFiles": [ "keyFiles": [
"2026-03-16_00-48-03.log", "2026-03-16_00-48-03.log",
"2026-03-18_23-17-29.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-18_23-17-30.log",
"2026-03-19_00-18-04.log" "2026-03-19_00-18-04.log",
] "2026-03-19_00-34-21.log"
},
"news-service": {
"path": "news-service",
"purpose": null,
"fileCount": 3,
"lastAccessed": 1773938154943,
"keyFiles": [
"Dockerfile",
"requirements.txt"
] ]
}, },
"reference": { "reference": {
"path": "reference", "path": "reference",
"purpose": null, "purpose": null,
"fileCount": 0, "fileCount": 0,
"lastAccessed": 1773938154943, "lastAccessed": 1774313111643,
"keyFiles": [] "keyFiles": []
}, },
"runs": { "runs": {
"path": "runs", "path": "runs",
"purpose": null, "purpose": null,
"fileCount": 0, "fileCount": 0,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111643,
"keyFiles": [] "keyFiles": []
}, },
"scripts": { "scripts": {
"path": "scripts", "path": "scripts",
"purpose": "Build/utility scripts", "purpose": "Build/utility scripts",
"fileCount": 1, "fileCount": 1,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111644,
"keyFiles": [ "keyFiles": [
"run_prod.sh" "run_prod.sh"
] ]
@@ -194,7 +169,7 @@
"path": "services", "path": "services",
"purpose": "Business logic services", "purpose": "Business logic services",
"fileCount": 1, "fileCount": 1,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111644,
"keyFiles": [ "keyFiles": [
"README.md" "README.md"
] ]
@@ -203,43 +178,21 @@
"path": "shared", "path": "shared",
"purpose": null, "purpose": null,
"fileCount": 0, "fileCount": 0,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111644,
"keyFiles": [] "keyFiles": []
}, },
"trading-service": {
"path": "trading-service",
"purpose": null,
"fileCount": 4,
"lastAccessed": 1773938154944,
"keyFiles": [
"Dockerfile",
"README.md",
"requirements.txt"
]
},
"workspaces": { "workspaces": {
"path": "workspaces", "path": "workspaces",
"purpose": null, "purpose": null,
"fileCount": 0, "fileCount": 0,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111645,
"keyFiles": [] "keyFiles": []
}, },
"agent-service/src": {
"path": "agent-service/src",
"purpose": "Source code",
"fileCount": 5,
"lastAccessed": 1773938154944,
"keyFiles": [
"__init__.py",
"config.py",
"main.py"
]
},
"backend/api": { "backend/api": {
"path": "backend/api", "path": "backend/api",
"purpose": "API routes", "purpose": "API routes",
"fileCount": 5, "fileCount": 5,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111645,
"keyFiles": [ "keyFiles": [
"__init__.py", "__init__.py",
"agents.py", "agents.py",
@@ -250,7 +203,7 @@
"path": "backend/config", "path": "backend/config",
"purpose": "Configuration files", "purpose": "Configuration files",
"fileCount": 6, "fileCount": 6,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111646,
"keyFiles": [ "keyFiles": [
"__init__.py", "__init__.py",
"agent_profiles.yaml", "agent_profiles.yaml",
@@ -261,7 +214,7 @@
"path": "backend/data", "path": "backend/data",
"purpose": "Data files", "purpose": "Data files",
"fileCount": 13, "fileCount": 13,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111647,
"keyFiles": [ "keyFiles": [
"__init__.py", "__init__.py",
"cache.py", "cache.py",
@@ -272,7 +225,7 @@
"path": "docs/assets", "path": "docs/assets",
"purpose": "Static assets", "purpose": "Static assets",
"fileCount": 5, "fileCount": 5,
"lastAccessed": 1773938154944, "lastAccessed": 1774313111647,
"keyFiles": [ "keyFiles": [
"dashboard.jpg", "dashboard.jpg",
"evotraders_demo.gif", "evotraders_demo.gif",
@@ -283,7 +236,7 @@
"path": "frontend/dist", "path": "frontend/dist",
"purpose": "Distribution/build output", "purpose": "Distribution/build output",
"fileCount": 2, "fileCount": 2,
"lastAccessed": 1773938154945, "lastAccessed": 1774313111647,
"keyFiles": [ "keyFiles": [
"index.html", "index.html",
"trading_logo.png" "trading_logo.png"
@@ -293,331 +246,261 @@
"path": "frontend/node_modules", "path": "frontend/node_modules",
"purpose": "Dependencies", "purpose": "Dependencies",
"fileCount": 1, "fileCount": 1,
"lastAccessed": 1773938154947, "lastAccessed": 1774313111650,
"keyFiles": [] "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": [ "hotPaths": [
{ {
"path": "backend/agents/factory.py", "path": "CLAUDE.md",
"accessCount": 17, "accessCount": 15,
"lastAccessed": 1773939950376, "lastAccessed": 1774342728155,
"type": "directory"
},
{
"path": "frontend/src/App.jsx",
"accessCount": 10,
"lastAccessed": 1774339397617,
"type": "file" "type": "file"
}, },
{ {
"path": "backend", "path": "frontend/src/hooks/useWebsocketSessionSync.js",
"accessCount": 16, "accessCount": 4,
"lastAccessed": 1773940042371, "lastAccessed": 1774313470024,
"type": "directory" "type": "file"
}, },
{ {
"path": "", "path": "",
"accessCount": 13, "accessCount": 4,
"lastAccessed": 1773939899611, "lastAccessed": 1774339108220,
"type": "directory" "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", "path": "backend/services/gateway.py",
"accessCount": 3, "accessCount": 3,
"lastAccessed": 1773939672930, "lastAccessed": 1774339389171,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/core/__init__.py", "path": "backend/main.py",
"accessCount": 3, "accessCount": 3,
"lastAccessed": 1773939963627, "lastAccessed": 1774342613364,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/services/trading/main.py", "path": "frontend/src/store/runtimeStore.js",
"accessCount": 2, "accessCount": 2,
"lastAccessed": 1773938360736, "lastAccessed": 1774317990919,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/services/agents/main.py", "path": "frontend/src/services/websocket.js",
"accessCount": 2, "accessCount": 2,
"lastAccessed": 1773938361040, "lastAccessed": 1774318009819,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/services/trading/data/__init__.py", "path": "backend/core/pipeline_runner.py",
"accessCount": 2, "accessCount": 2,
"lastAccessed": 1773938402496, "lastAccessed": 1774339367538,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/services/news/explain/__init__.py", "path": "backend/runtime/manager.py",
"accessCount": 2, "accessCount": 2,
"lastAccessed": 1773938460019, "lastAccessed": 1774339367572,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/services/news/enrich/__init__.py", "path": "frontend/src/store/marketStore.js",
"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",
"accessCount": 1, "accessCount": 1,
"lastAccessed": 1773938226307, "lastAccessed": 1774313140483,
"type": "file" "type": "file"
}, },
{ {
"path": "docker-compose.yml", "path": "frontend/src/hooks/useFeedProcessor.js",
"accessCount": 1, "accessCount": 1,
"lastAccessed": 1773938226360, "lastAccessed": 1774313148279,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/services/news/shared/trading_client.py", "path": "frontend/src/components/Header.jsx",
"accessCount": 1, "accessCount": 1,
"lastAccessed": 1773938370618, "lastAccessed": 1774313156696,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/services/agents", "path": "frontend/src/components/TraderView.jsx",
"accessCount": 1, "accessCount": 1,
"lastAccessed": 1773938397772, "lastAccessed": 1774313156753,
"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,
"type": "file" "type": "file"
}, },
{ {
"path": "shared/client/news_client.py", "path": "frontend/src/store/uiStore.js",
"accessCount": 1, "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" "type": "file"
}, },
{ {
"path": "shared/client/trading_client.py", "path": "shared/client/trading_client.py",
"accessCount": 1, "accessCount": 1,
"lastAccessed": 1773938638770, "lastAccessed": 1774317984365,
"type": "file" "type": "file"
}, },
{ {
"path": "backend/api", "path": "backend/apps/trading_service.py",
"accessCount": 1, "accessCount": 1,
"lastAccessed": 1773938669143, "lastAccessed": 1774317984408,
"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,
"type": "file" "type": "file"
}, },
{ {
"path": "pyproject.toml", "path": "pyproject.toml",
"accessCount": 1, "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" "type": "file"
} }
], ],

View File

@@ -1,6 +1,6 @@
{ {
"timestamp": "2026-03-19T16:36:52.471Z", "timestamp": "2026-03-24T07:58:12.123Z",
"backgroundTasks": [], "backgroundTasks": [],
"sessionStartTimestamp": "2026-03-19T16:36:42.224Z", "sessionStartTimestamp": "2026-03-24T07:58:09.417Z",
"sessionId": "ef02339a-1eec-4c7a-95ac-c8cfa0b5067d" "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": [ "agents": [
{ {
"agent_id": "a8305a91e192b2196", "agent_id": "abeaf609b74a2b7ee",
"agent_type": "Explore", "agent_type": "Explore",
"started_at": "2026-03-19T17:00:33.284Z", "started_at": "2026-03-24T08:01:40.015Z",
"parent_mode": "none", "parent_mode": "none",
"status": "completed", "status": "completed",
"completed_at": "2026-03-19T17:02:19.439Z", "completed_at": "2026-03-24T08:02:31.822Z",
"duration_ms": 106155 "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_spawned": 2,
"total_completed": 1, "total_completed": 2,
"total_failed": 0, "total_failed": 0,
"last_updated": "2026-03-19T17:02:39.175Z" "last_updated": "2026-03-24T08:59:06.380Z"
} }

392
CLAUDE.md
View File

@@ -1,5 +1,7 @@
# CLAUDE.md # CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
本文件为 Claude Code (claude.ai/code) 在此代码库中工作时提供指导。 本文件为 Claude Code (claude.ai/code) 在此代码库中工作时提供指导。
## 项目概述 ## 项目概述
@@ -23,18 +25,20 @@ evotraders live -t 22:30 # 定时每日交易
evotraders frontend # 启动可视化界面 evotraders frontend # 启动可视化界面
# 开发服务器 # 开发服务器
./start-dev.sh # 启动全部 4 个微服务 ./start-dev.sh # 启动全部 4 个微服务 (agent, runtime, trading, news)
# 单独启动某个服务 # Gateway WebSocket 服务
python -m uvicorn backend.apps.agent_service:app --host 0.0.0.0 --port 8000 --reload 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.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.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 python -m uvicorn backend.apps.news_service:app --host 0.0.0.0 --port 8002 --reload
# 测试 # 测试
pytest backend/tests # 运行全部测试 pytest backend/tests # 运行全部测试
pytest backend/tests/test_news_service_app.py -v # 运行单个测试文件 pytest backend/tests/test_news_service_app.py -v # 运行单个测试
pytest backend/tests/test_news_service_app.py::test_news_service_routes_are_exposed -v # 运行单个测试
``` ```
### Frontend (React) ### Frontend (React)
@@ -46,142 +50,237 @@ npm run build # 生产构建
npm run lint # ESLint 检查 npm run lint # ESLint 检查
npm run lint:fix # ESLint 自动修复 npm run lint:fix # ESLint 自动修复
npm run test # Vitest 单元测试 npm run test # Vitest 单元测试
npm run test:watch # 监听模式
``` ```
## 架构概览 ## 架构概览
### 微服务架构 (`backend/apps/`) ### 系统分层
项目采用 split-first 微服务架构4 个独立的 FastAPI 服务: ```
┌─────────────────────────────────────────────────────────────┐
| 服务 | 入口 | 端口 | 职责 | │ Frontend (React) │
|------|------|------|------| │ WebSocket ws://localhost:8765 连接 Gateway │
| 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 | 新闻、新闻富化、解释功能 | ┌─────────────────────────────────────────────────────────────┐
│ Gateway (backend/services/gateway.py) │
服务间通过环境变量通信(详见 `start-dev.sh` │ WebSocket 服务器,编排 Pipeline4 阶段启动 │
```bash └─────────────────────────────────────────────────────────────┘
export TRADING_SERVICE_URL=http://localhost:8001 │ │ │ │
export NEWS_SERVICE_URL=http://localhost:8002 ▼ ▼ ▼ ▼
export RUNTIME_SERVICE_URL=http://localhost:8003 ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ 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 | runtime_service | 8003 | 运行时配置、任务启动、Pipeline Runner |
- `/trading/*` → trading_service | agent_service | 8000 | Agent 生命周期、工作区管理 |
- `/news/*` → news_service | 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 服务通信 | | `manager.py` | TradingRuntimeManager - 全局运行时管理器agent 注册、会话、事件快照 |
| `RuntimeServiceClient` | 运行时服务通信 | | `agent_runtime.py` | AgentRuntimeState - 单 agent 状态status、last_session |
| `TradingServiceClient` | 交易服务通信 | | `context.py` | TradingRunContext - 运行上下文 |
| `NewsServiceClient` | 新闻服务通信 | | `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/ backend/
├── agents/ # 多智能体实现 ├── agents/ # 多智能体实现
│ ├── base/ # 核心类、Hooks、评估 │ ├── analyst.py # AnalystAgent 基类
│ ├── evo_agent.py # 基于 AgentScope 的核心实现 │ ├── portfolio_manager.py # PMAgent 投资经理
│ ├── hooks.py # 生命周期 Hooks │ ├── risk_manager.py # RiskAgent 风控经理
│ │ │ ├── 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 # 任务分发
│ ├── factory.py # Agent 实例工厂 │ ├── factory.py # Agent 实例工厂
│ ├── skills_manager.py # 技能加载管理6 种作用域) │ ├── toolkit_factory.py # 工具集工厂
── toolkit_factory.py # 工具集工厂 ── skills_manager.py # 技能加载管理
├── apps/ # 微服务入口split-first │ ├── workspace_manager.py # 工作区管理
│ ├── agent_service.py │ ├── skill_loader.py # 技能加载器
│ ├── runtime_service.py │ ├── agent_workspace.py # Agent 工作区
│ ├── trading_service.py │ ├── prompt_loader.py # Prompt 加载器
── news_service.py ── 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/ # 领域业务逻辑 ├── domains/ # 领域业务逻辑
│ ├── news.py │ ├── news.py
│ └── trading.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 集成 ├── llm/ # LLM 集成
│ └── models.py # RetryChatModel、TokenRecordingModelWrapper │ └── models.py # RetryChatModel、TokenRecordingModelWrapper
├── skills/ # 技能定义(内置 + 自定义)
├── skills/ # 技能定义
├── tools/ # 交易和分析工具 ├── 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/ frontend/src/
├── App.jsx # React 主应用 ├── App.jsx # 主应用LiveTradingApp
├── components/ # React 组件 ├── AppShell.jsx # App 外壳(布局、侧边栏)
├── components/
│ ├── RuntimeView.jsx # 交易运行时 UI │ ├── RuntimeView.jsx # 交易运行时 UI
│ ├── TraderView.jsx # 交易员界面 │ ├── TraderView.jsx # 交易员界面
│ ├── RoomView.jsx # 聊天室视图 │ ├── RoomView.jsx # 聊天室视图
│ ├── StockExplainView.jsx # 股票解释视图 │ ├── StockExplainView.jsx # 股票解释视图
│ ├── RuntimeSettingsPanel.jsx # 运行时设置面板 │ ├── RuntimeSettingsPanel.jsx # 运行时设置面板
│ ├── RuntimeLogsModal.jsx # 运行时日志弹窗
│ ├── WatchlistPanel.jsx # 关注列表 │ ├── WatchlistPanel.jsx # 关注列表
│ ├── PerformanceView.jsx # 绩效视图 │ ├── PerformanceView.jsx # 绩效视图
│ ├── StatisticsView.jsx # 统计视图 │ ├── StatisticsView.jsx # 统计视图
│ ├── NetValueChart.jsx # 净值曲线图 │ ├── NetValueChart.jsx # 净值曲线图
│ ├── AgentCard.jsx # Agent 卡片 │ ├── AgentCard.jsx # Agent 卡片
│ ├── AgentFeed.jsx # Agent 动态 │ ├── AgentFeed.jsx # Agent 动态
── explain/ # 解释相关组件 ── Header.jsx # 头部
│ ├── MarkdownModal.jsx # Markdown 弹窗
│ ├── StockLogo.jsx # 股票 Logo
│ └── explain/ # 解释组件
│ ├── ExplainNewsSection.jsx │ ├── ExplainNewsSection.jsx
│ ├── ExplainRangeSection.jsx │ ├── ExplainRangeSection.jsx
│ ├── ExplainSimilarDaysSection.jsx │ ├── ExplainSimilarDaysSection.jsx
│ ├── ExplainStorySection.jsx │ ├── ExplainStorySection.jsx
│ └── useExplainModel.js │ └── useExplainModel.js
├── services/ # API 服务 ├── hooks/ # React Hooks
│ ├── runtimeApi.js # 运行时 API 调用 │ ├── useWebSocketConnection.js # WebSocket 连接管理
│ ├── websocket.js # WebSocket 实时通信 │ ├── useRuntimeControls.js # 运行时配置管理
│ ├── newsApi.js # 新闻服务客户端 │ ├── useAgentDataRequests.js # Agent 数据请求
── tradingApi.js # 交易服务客户端 ── useStockDataRequests.js # 股票数据请求
├── config/ │ ├── useStockExplainData.js # 股票解释数据
── constants.js # Agent 定义、配置 ── useAgentWorkspacePanel.js # Agent 工作区面板
└── hooks/ # React Hooks │ ├── 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 系统 ## Agent 系统
@@ -193,110 +292,87 @@ frontend/src/
| `fundamentals_analyst` | 基本面分析师 | 财务健康、盈利能力、成长质量 | | `fundamentals_analyst` | 基本面分析师 | 财务健康、盈利能力、成长质量 |
| `technical_analyst` | 技术分析师 | 价格趋势、技术指标、动量分析 | | `technical_analyst` | 技术分析师 | 价格趋势、技术指标、动量分析 |
| `sentiment_analyst` | 情绪分析师 | 市场情绪、新闻情绪、内幕交易 | | `sentiment_analyst` | 情绪分析师 | 市场情绪、新闻情绪、内幕交易 |
| `valuation_analyst` | 估值分析师 | DCF、EV/EBITDA、 intrinsic value | | `valuation_analyst` | 估值分析师 | DCF、EV/EBITDA、intrinsic value |
| `portfolio_manager` | 投资经理 | 决策执行、交易协调 | | `portfolio_manager` | 投资经理 | 决策执行、交易协调 |
| `risk_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 条工具结果
### 添加自定义分析师 ### 添加自定义分析师
1. `backend/agents/prompts/analyst/personas.yaml` 注册 1. `backend/agents/prompts/analyst/personas.yaml` 注册
2. `backend/config/constants.py``ANALYST_TYPES` 字典添加 2. `backend/config/constants.py``ANALYST_TYPES` 字典添加
3. 可选:在 `frontend/src/config/constants.js` 中更新前端配置 3. `frontend/src/config/constants.js` 可选更新
### LLM 模型封装 (`backend/llm/models.py`) ### LLM 模型封装 (`backend/llm/models.py`)
基于 CoPaw 的模型封装设计: - **RetryChatModel**: 自动重试瞬态 LLM 错误,指数退避
- **TokenRecordingModelWrapper**: 追踪 token 消耗和成本
- **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)
```
## 技能系统 (`backend/skills/`) ## 技能系统 (`backend/skills/`)
技能定义在 `SKILL.md` 文件中,包含: 技能定义在 `SKILL.md`,包含 `instructions``triggers``parameters``available_tools`
- `instructions` - 技能说明
- `triggers` - 触发条件
- `parameters` - 输入/输出 schema
- `available_tools` - 技能可使用的工具
技能由 `skills_manager.py` 加载,通过 `skill_adaptation_hook.py` 绑定到 Agent。
技能管理器支持 6 种作用域builtin、customized、installed、active、disabled、local。 技能管理器支持 6 种作用域builtin、customized、installed、active、disabled、local。
## Pipeline 执行 (`backend/core/`) ## 运行时数据布局
每日交易流程: - `data/market_research.db` - 持久研究数据
- `runs/<run_id>/` - 每次任务运行的状态
1. **分析阶段** - 各 Agent 基于工具和历史经验独立分析 - `runs/<run_id>/team_dashboard/*.json` - 仪表板导出层(非权威源)
2. **沟通阶段** - 通过私聊、通知、会议等方式交换观点1v1/1vN/NvN - `runs/<run_id>/state/runtime_state.json` - 运行时快照
3. **决策阶段** - 投资经理综合判断,给出最终交易 - 运行时 API 优先使用 `server_state.json``runtime.db`
4. **评估阶段** - 绩效跟踪
5. **复盘阶段** - Agent 根据当日实际收益反思总结,通过 ReMe 记忆框架更新经验
## 前端状态管理
项目正在向 Zustand 状态管理过渡,已创建的 store
```bash ```bash
frontend/src/store/ RUNS_RETENTION_COUNT=20 # 时间戳格式文件夹自动清理
├── index.js # 导出所有 store
├── runtimeStore.js # 连接状态、运行时配置
├── marketStore.js # 市场数据、股票价格
├── portfolioStore.js # 组合、持仓、交易
├── agentStore.js # Agent 技能、工作区
└── uiStore.js # UI 状态、视图切换
``` ```
**迁移状态**
- Stores 已创建但尚未在 App.jsx 中使用
- 计划:逐步迁移 60+ 个 useState 到对应 store
## 环境配置 ## 环境配置
`.env` 必需配置: ### Backend (`env.template`)
```bash ```bash
# 金融数据源 # 金融数据源支持多源fallback
FIN_DATA_SOURCE=finnhub|financial_datasets FIN_DATA_SOURCE=finnhub|financial_datasets|yfinance|local_csv
ENABLED_DATA_SOURCES=financial_datasets,finnhub,yfinance,local_csv
FINANCIAL_DATASETS_API_KEY= # 回测必需 FINANCIAL_DATASETS_API_KEY= # 回测必需
FINNHUB_API_KEY= # 实盘必需 FINNHUB_API_KEY= # 实盘必需
POLYGON_API_KEY= # Polygon市场库采集可选
# Agent LLM # LLM 配置
OPENAI_API_KEY= OPENAI_API_KEY=
OPENAI_BASE_URL= OPENAI_BASE_URL=
MODEL_NAME=qwen3-max-preview MODEL_NAME=qwen3-max-preview
# 可为不同 Agent 指定不同模型 # Agent 特定模型
AGENT_SENTIMENT_ANALYST_MODEL_NAME=qwen3-max-preview AGENT_SENTIMENT_ANALYST_MODEL_NAME=deepseek-v3.2-exp
AGENT_FUNDAMENTALS_ANALYST_MODEL_NAME=deepseek-chat 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 记忆系统 # ReMe 记忆系统
MEMORY_API_KEY= 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** - 多智能体框架 - **AgentScope** - 多智能体框架
- **ReMe** - 持续学习记忆系统 - **ReMe** - 持续学习记忆系统
- **FastAPI** + **uvicorn** - 后端 API 服务器 - **FastAPI** + **uvicorn** - 后端 API
- **websockets** - 实时通信 - **websockets** - 实时通信
- **React 19** + **Vite** + **TailwindCSS** - 前端 - **React 19** + **Vite** + **TailwindCSS** - 前端
- **React Context** - 前端状态管理App.jsx 中使用 useState + useCallback - **Zustand** - 状态管理
- **Three.js** / **React-Three-Fiber** - 3D 可视化

View File

@@ -41,6 +41,8 @@ class SkillsManager:
) )
self.runs_root = self.project_root / "runs" self.runs_root = self.project_root / "runs"
self._lock = Lock() 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: def get_active_root(self, config_name: str) -> Path:
return self.runs_root / config_name / "skills" / "active" return self.runs_root / config_name / "skills" / "active"
@@ -739,7 +741,7 @@ class SkillsManager:
if local_root.exists(): if local_root.exists():
watched_paths.append(local_root) 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() observer = Observer()
for path in watched_paths: for path in watched_paths:
observer.schedule(handler, str(path), recursive=True) observer.schedule(handler, str(path), recursive=True)
@@ -773,6 +775,7 @@ class SkillsManager:
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Internal change-tracking state (populated by _SkillsChangeHandler) # Internal change-tracking state (populated by _SkillsChangeHandler)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Legacy class-level reference kept for migration compatibility
_pending_skill_changes: Dict[str, Set[Path]] = {} _pending_skill_changes: Dict[str, Set[Path]] = {}
def _resolve_disabled_skill_names( def _resolve_disabled_skill_names(
@@ -824,11 +827,13 @@ class _SkillsChangeHandler(FileSystemEventHandler):
def __init__( def __init__(
self, self,
watched_paths: List[Path], watched_paths: List[Path],
pending_changes: Dict[str, Set[Path]],
callback: Optional[Any] = None, callback: Optional[Any] = None,
lock: Optional[Lock] = None, lock: Optional[Lock] = None,
) -> None: ) -> None:
super().__init__() super().__init__()
self._watched_paths = watched_paths self._watched_paths = watched_paths
self._pending_changes = pending_changes
self._callback = callback self._callback = callback
self._lock = lock self._lock = lock
@@ -841,13 +846,9 @@ class _SkillsChangeHandler(FileSystemEventHandler):
run_id = self._run_id_from_path(src_path) run_id = self._run_id_from_path(src_path)
if self._lock: if self._lock:
with self._lock: with self._lock:
SkillsManager._pending_skill_changes.setdefault( self._pending_changes.setdefault(run_id, set()).add(src_path)
run_id, set()
).add(src_path)
else: else:
SkillsManager._pending_skill_changes.setdefault( self._pending_changes.setdefault(run_id, set()).add(src_path)
run_id, set()
).add(src_path)
if self._callback: if self._callback:
self._callback([src_path]) self._callback([src_path])
break 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 .runtime_service import create_app as create_runtime_app
from .trading_service import app as trading_app from .trading_service import app as trading_app
from .trading_service import create_app as create_trading_app from .trading_service import create_app as create_trading_app
from .cors import add_cors_middleware, get_cors_origins
app = agent_app app = agent_app
create_app = create_agent_app create_app = create_agent_app
@@ -24,4 +25,6 @@ __all__ = [
"create_runtime_app", "create_runtime_app",
"trading_app", "trading_app",
"create_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 typing import AsyncGenerator
from fastapi import FastAPI 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.api import agents_router, guard_router, workspaces_router
from backend.agents import AgentFactory, WorkspaceManager, get_registry from backend.agents import AgentFactory, WorkspaceManager, get_registry
@@ -47,13 +48,7 @@ def create_app(project_root: Path | None = None) -> FastAPI:
lifespan=lifespan, lifespan=lifespan,
) )
app.add_middleware( add_cors_middleware(app)
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health") @app.get("/health")
async def health_check() -> dict[str, object]: async def health_check() -> dict[str, object]:

View File

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

View File

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

View File

@@ -266,10 +266,6 @@ async def run_pipeline(
set_global_runtime_manager(runtime_manager) 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) # PHASE 1 & 2: Create infrastructure services (Market, Storage)
# These will be started by Gateway in the correct order # These will be started by Gateway in the correct order

View File

@@ -9,7 +9,7 @@ import os
import sqlite3 import sqlite3
from datetime import datetime, timezone from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
from typing import Any, Iterable from typing import Any, Iterable, Optional
SCHEMA = """ SCHEMA = """
@@ -147,12 +147,30 @@ def _utc_timestamp() -> str:
class MarketStore: 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): 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 = Path(db_path or get_market_db_path())
self.db_path.parent.mkdir(parents=True, exist_ok=True) self.db_path.parent.mkdir(parents=True, exist_ok=True)
self._init_db() 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: def _connect(self) -> sqlite3.Connection:
conn = sqlite3.connect(self.db_path) conn = sqlite3.connect(self.db_path)

View File

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

View File

@@ -13,15 +13,30 @@ from .registry import RuntimeRegistry
_global_runtime_manager: Optional["TradingRuntimeManager"] = None _global_runtime_manager: Optional["TradingRuntimeManager"] = None
_shutdown_event: Optional[asyncio.Event] = 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: def set_global_runtime_manager(manager: "TradingRuntimeManager") -> None:
global _global_runtime_manager global _global_runtime_manager
_global_runtime_manager = manager _global_runtime_manager = manager
# Sync to RuntimeState for consistency
_get_api_runtime().register_runtime_manager(manager)
def clear_global_runtime_manager() -> None: def clear_global_runtime_manager() -> None:
global _global_runtime_manager global _global_runtime_manager
_global_runtime_manager = None _global_runtime_manager = None
# Sync to RuntimeState for consistency
_get_api_runtime().unregister_runtime_manager()
def get_global_runtime_manager() -> Optional["TradingRuntimeManager"]: 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