From 16bb3c4211e8b2565d29125cc8a214da6d3c6784 Mon Sep 17 00:00:00 2001 From: cillin Date: Tue, 24 Mar 2026 17:19:31 +0800 Subject: [PATCH] =?UTF-8?q?docs(README):=20=E6=9B=B4=E6=96=B0=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3=EF=BC=8C=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E6=9E=B6=E6=9E=84=E4=B8=8E=E5=90=AF=E5=8A=A8=E6=8C=87?= =?UTF-8?q?=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整 CLAUDE.md 内容,增加使用指导和架构概览 - 补充微服务启动说明及单服务启动命令 - 明确 Gateway 服务器四阶段启动流程和职责划分 - 细化后端目录结构说明,补充主要文件职责描述 - 新增系统分层结构图,优化整体架构理解 - 更新 .gitignore,添加 runs 目录忽略规则 - 同步 .omc 相关状态文件,更新项目状态跟踪信息 --- .gitignore | 5 +- .omc/project-memory.json | 557 +++++++++++---------------- .omc/state/hud-state.json | 6 +- .omc/state/hud-stdin-cache.json | 2 +- .omc/state/idle-notif-cooldown.json | 2 +- .omc/state/subagent-tracking.json | 23 +- CLAUDE.md | 426 +++++++++++--------- backend/agents/skills_manager.py | 15 +- backend/apps/__init__.py | 3 + backend/apps/agent_service.py | 11 +- backend/apps/runtime_service.py | 10 +- backend/apps/trading_service.py | 10 +- backend/core/pipeline_runner.py | 4 - backend/data/market_store.py | 22 +- backend/main.py | 1 - backend/runtime/manager.py | 15 + frontend/test-results/.last-run.json | 4 + reference/CoPaw | 1 + reference/Hyper-Alpha-Arena | 1 + reference/openclaw | 1 + 20 files changed, 556 insertions(+), 563 deletions(-) create mode 100644 frontend/test-results/.last-run.json create mode 160000 reference/CoPaw create mode 160000 reference/Hyper-Alpha-Arena create mode 160000 reference/openclaw diff --git a/.gitignore b/.gitignore index af840e7..3eca5a8 100644 --- a/.gitignore +++ b/.gitignore @@ -54,10 +54,13 @@ outputs/ /smoke_live_mock/ # Local tooling state -/.omc/ +.omc/ /.pydeps/ /referance/ +# Run outputs +/runs/ + # Data files backend/data/ret_data/ diff --git a/.omc/project-memory.json b/.omc/project-memory.json index 2923583..38b9f47 100644 --- a/.omc/project-memory.json +++ b/.omc/project-memory.json @@ -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" } ], diff --git a/.omc/state/hud-state.json b/.omc/state/hud-state.json index 6755a93..1eedef0 100644 --- a/.omc/state/hud-state.json +++ b/.omc/state/hud-state.json @@ -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" } \ No newline at end of file diff --git a/.omc/state/hud-stdin-cache.json b/.omc/state/hud-stdin-cache.json index 9cbf52b..d6b2010 100644 --- a/.omc/state/hud-stdin-cache.json +++ b/.omc/state/hud-stdin-cache.json @@ -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} \ No newline at end of file +{"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} \ No newline at end of file diff --git a/.omc/state/idle-notif-cooldown.json b/.omc/state/idle-notif-cooldown.json index f79a2da..412c4a2 100644 --- a/.omc/state/idle-notif-cooldown.json +++ b/.omc/state/idle-notif-cooldown.json @@ -1,3 +1,3 @@ { - "lastSentAt": "2026-03-19T17:02:32.170Z" + "lastSentAt": "2026-03-24T08:58:57.965Z" } \ No newline at end of file diff --git a/.omc/state/subagent-tracking.json b/.omc/state/subagent-tracking.json index f187633..d2ec92f 100644 --- a/.omc/state/subagent-tracking.json +++ b/.omc/state/subagent-tracking.json @@ -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" } \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index c2142fe..c6b3726 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 服务器,编排 Pipeline,4 阶段启动 │ +└─────────────────────────────────────────────────────────────┘ + │ │ │ │ + ▼ ▼ ▼ ▼ +┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ +│ 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//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 # 任务分发 -│ ├── 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 -├── domains/ # 领域业务逻辑 +├── agents/ # 多智能体实现 +│ ├── analyst.py # AnalystAgent 基类 +│ ├── portfolio_manager.py # PMAgent 投资经理 +│ ├── risk_manager.py # RiskAgent 风控经理 +│ ├── factory.py # Agent 实例工厂 +│ ├── 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/ # 技能定义(内置 + 自定义) -├── tools/ # 交易和分析工具 -└── utils/ # 工具函数 +│ +├── llm/ # LLM 集成 +│ └── models.py # RetryChatModel、TokenRecordingModelWrapper +│ +├── skills/ # 技能定义 +├── tools/ # 交易和分析工具 +├── 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 组件 -│ ├── RuntimeView.jsx # 交易运行时 UI -│ ├── TraderView.jsx # 交易员界面 -│ ├── RoomView.jsx # 聊天室视图 -│ ├── StockExplainView.jsx # 股票解释视图 +├── App.jsx # 主应用(LiveTradingApp) +├── AppShell.jsx # App 外壳(布局、侧边栏) +├── components/ +│ ├── RuntimeView.jsx # 交易运行时 UI +│ ├── TraderView.jsx # 交易员界面 +│ ├── RoomView.jsx # 聊天室视图 +│ ├── StockExplainView.jsx # 股票解释视图 │ ├── RuntimeSettingsPanel.jsx # 运行时设置面板 -│ ├── WatchlistPanel.jsx # 关注列表 -│ ├── PerformanceView.jsx # 绩效视图 -│ ├── StatisticsView.jsx # 统计视图 -│ ├── NetValueChart.jsx # 净值曲线图 -│ ├── AgentCard.jsx # Agent 卡片 -│ ├── AgentFeed.jsx # Agent 动态 -│ └── explain/ # 解释相关组件 +│ ├── RuntimeLogsModal.jsx # 运行时日志弹窗 +│ ├── WatchlistPanel.jsx # 关注列表 +│ ├── PerformanceView.jsx # 绩效视图 +│ ├── StatisticsView.jsx # 统计视图 +│ ├── NetValueChart.jsx # 净值曲线图 +│ ├── AgentCard.jsx # Agent 卡片 +│ ├── AgentFeed.jsx # Agent 动态 +│ ├── 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 系统 @@ -193,110 +292,87 @@ frontend/src/ | `fundamentals_analyst` | 基本面分析师 | 财务健康、盈利能力、成长质量 | | `technical_analyst` | 技术分析师 | 价格趋势、技术指标、动量分析 | | `sentiment_analyst` | 情绪分析师 | 市场情绪、新闻情绪、内幕交易 | -| `valuation_analyst` | 估值分析师 | DCF、EV/EBITDA、 intrinsic value | +| `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//` - 每次任务运行的状态 +- `runs//team_dashboard/*.json` - 仪表板导出层(非权威源) +- `runs//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= # 实盘必需 +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** - 状态管理 diff --git a/backend/agents/skills_manager.py b/backend/agents/skills_manager.py index 9271a90..bc131de 100644 --- a/backend/agents/skills_manager.py +++ b/backend/agents/skills_manager.py @@ -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 diff --git a/backend/apps/__init__.py b/backend/apps/__init__.py index 7e10106..7c71854 100644 --- a/backend/apps/__init__.py +++ b/backend/apps/__init__.py @@ -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", ] diff --git a/backend/apps/agent_service.py b/backend/apps/agent_service.py index e9812f5..dca8ab4 100644 --- a/backend/apps/agent_service.py +++ b/backend/apps/agent_service.py @@ -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]: diff --git a/backend/apps/runtime_service.py b/backend/apps/runtime_service.py index c6014a6..0838039 100644 --- a/backend/apps/runtime_service.py +++ b/backend/apps/runtime_service.py @@ -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]: diff --git a/backend/apps/trading_service.py b/backend/apps/trading_service.py index ccd8f56..3afee11 100644 --- a/backend/apps/trading_service.py +++ b/backend/apps/trading_service.py @@ -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]: diff --git a/backend/core/pipeline_runner.py b/backend/core/pipeline_runner.py index 911aee4..c875fa6 100644 --- a/backend/core/pipeline_runner.py +++ b/backend/core/pipeline_runner.py @@ -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 diff --git a/backend/data/market_store.py b/backend/data/market_store.py index 956ef31..cd54d52 100644 --- a/backend/data/market_store.py +++ b/backend/data/market_store.py @@ -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) diff --git a/backend/main.py b/backend/main.py index cbb8573..f5c75e5 100644 --- a/backend/main.py +++ b/backend/main.py @@ -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( diff --git a/backend/runtime/manager.py b/backend/runtime/manager.py index 333db96..f14b947 100644 --- a/backend/runtime/manager.py +++ b/backend/runtime/manager.py @@ -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"]: diff --git a/frontend/test-results/.last-run.json b/frontend/test-results/.last-run.json new file mode 100644 index 0000000..5fca3f8 --- /dev/null +++ b/frontend/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "failed", + "failedTests": [] +} \ No newline at end of file diff --git a/reference/CoPaw b/reference/CoPaw new file mode 160000 index 0000000..934cfce --- /dev/null +++ b/reference/CoPaw @@ -0,0 +1 @@ +Subproject commit 934cfce0a7b2981cb6f40bb0cafe7153e365504e diff --git a/reference/Hyper-Alpha-Arena b/reference/Hyper-Alpha-Arena new file mode 160000 index 0000000..f137cff --- /dev/null +++ b/reference/Hyper-Alpha-Arena @@ -0,0 +1 @@ +Subproject commit f137cff4766d0fafa86ba44d4826d7ca3ea52504 diff --git a/reference/openclaw b/reference/openclaw new file mode 160000 index 0000000..7b151af --- /dev/null +++ b/reference/openclaw @@ -0,0 +1 @@ +Subproject commit 7b151afeeb36d48f3edf495a675166e8c6fd1abb