diff --git a/.omc/project-memory.json b/.omc/project-memory.json
index 38b9f47..5669ed0 100644
--- a/.omc/project-memory.json
+++ b/.omc/project-memory.json
@@ -1,6 +1,6 @@
{
"version": "1.0.0",
- "lastScanned": 1774313111650,
+ "lastScanned": 1774515151036,
"projectRoot": "/Users/cillin/workspeace/evotraders",
"techStack": {
"languages": [
@@ -54,7 +54,7 @@
"path": "backend",
"purpose": null,
"fileCount": 4,
- "lastAccessed": 1774313111639,
+ "lastAccessed": 1774515151025,
"keyFiles": [
"__init__.py",
"cli.py",
@@ -66,14 +66,14 @@
"path": "backtest",
"purpose": null,
"fileCount": 0,
- "lastAccessed": 1774313111640,
+ "lastAccessed": 1774515151026,
"keyFiles": []
},
"data": {
"path": "data",
"purpose": "Data files",
"fileCount": 3,
- "lastAccessed": 1774313111640,
+ "lastAccessed": 1774515151027,
"keyFiles": [
"market_research.db",
"market_research.db-shm",
@@ -84,14 +84,14 @@
"path": "deploy",
"purpose": null,
"fileCount": 0,
- "lastAccessed": 1774313111640,
+ "lastAccessed": 1774515151027,
"keyFiles": []
},
"docs": {
"path": "docs",
"purpose": "Documentation",
"fileCount": 1,
- "lastAccessed": 1774313111641,
+ "lastAccessed": 1774515151027,
"keyFiles": [
"compat-removal-plan.md"
]
@@ -100,7 +100,7 @@
"path": "evotraders.egg-info",
"purpose": null,
"fileCount": 6,
- "lastAccessed": 1774313111641,
+ "lastAccessed": 1774515151028,
"keyFiles": [
"PKG-INFO",
"SOURCES.txt",
@@ -113,7 +113,7 @@
"path": "frontend",
"purpose": null,
"fileCount": 13,
- "lastAccessed": 1774313111641,
+ "lastAccessed": 1774515151028,
"keyFiles": [
"README.md",
"components.json",
@@ -126,41 +126,28 @@
"path": "live",
"purpose": null,
"fileCount": 0,
- "lastAccessed": 1774313111642,
+ "lastAccessed": 1774515151028,
"keyFiles": []
},
- "logs": {
- "path": "logs",
- "purpose": null,
- "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.log",
- "2026-03-19_00-18-04.log",
- "2026-03-19_00-34-21.log"
- ]
- },
"reference": {
"path": "reference",
"purpose": null,
"fileCount": 0,
- "lastAccessed": 1774313111643,
+ "lastAccessed": 1774515151028,
"keyFiles": []
},
"runs": {
"path": "runs",
"purpose": null,
"fileCount": 0,
- "lastAccessed": 1774313111643,
+ "lastAccessed": 1774515151029,
"keyFiles": []
},
"scripts": {
"path": "scripts",
"purpose": "Build/utility scripts",
"fileCount": 1,
- "lastAccessed": 1774313111644,
+ "lastAccessed": 1774515151030,
"keyFiles": [
"run_prod.sh"
]
@@ -169,7 +156,7 @@
"path": "services",
"purpose": "Business logic services",
"fileCount": 1,
- "lastAccessed": 1774313111644,
+ "lastAccessed": 1774515151030,
"keyFiles": [
"README.md"
]
@@ -178,21 +165,14 @@
"path": "shared",
"purpose": null,
"fileCount": 0,
- "lastAccessed": 1774313111644,
- "keyFiles": []
- },
- "workspaces": {
- "path": "workspaces",
- "purpose": null,
- "fileCount": 0,
- "lastAccessed": 1774313111645,
+ "lastAccessed": 1774515151030,
"keyFiles": []
},
"backend/api": {
"path": "backend/api",
"purpose": "API routes",
"fileCount": 5,
- "lastAccessed": 1774313111645,
+ "lastAccessed": 1774515151030,
"keyFiles": [
"__init__.py",
"agents.py",
@@ -203,7 +183,7 @@
"path": "backend/config",
"purpose": "Configuration files",
"fileCount": 6,
- "lastAccessed": 1774313111646,
+ "lastAccessed": 1774515151030,
"keyFiles": [
"__init__.py",
"agent_profiles.yaml",
@@ -213,8 +193,8 @@
"backend/data": {
"path": "backend/data",
"purpose": "Data files",
- "fileCount": 13,
- "lastAccessed": 1774313111647,
+ "fileCount": 12,
+ "lastAccessed": 1774515151031,
"keyFiles": [
"__init__.py",
"cache.py",
@@ -225,7 +205,7 @@
"path": "docs/assets",
"purpose": "Static assets",
"fileCount": 5,
- "lastAccessed": 1774313111647,
+ "lastAccessed": 1774515151031,
"keyFiles": [
"dashboard.jpg",
"evotraders_demo.gif",
@@ -236,7 +216,7 @@
"path": "frontend/dist",
"purpose": "Distribution/build output",
"fileCount": 2,
- "lastAccessed": 1774313111647,
+ "lastAccessed": 1774515151031,
"keyFiles": [
"index.html",
"trading_logo.png"
@@ -246,261 +226,309 @@
"path": "frontend/node_modules",
"purpose": "Dependencies",
"fileCount": 1,
- "lastAccessed": 1774313111650,
+ "lastAccessed": 1774515151036,
"keyFiles": []
}
},
"hotPaths": [
{
- "path": "CLAUDE.md",
- "accessCount": 15,
- "lastAccessed": 1774342728155,
- "type": "directory"
- },
- {
- "path": "frontend/src/App.jsx",
- "accessCount": 10,
- "lastAccessed": 1774339397617,
+ "path": "frontend/src/hooks/useWebSocketConnection.js",
+ "accessCount": 100,
+ "lastAccessed": 1774550862686,
"type": "file"
},
- {
- "path": "frontend/src/hooks/useWebsocketSessionSync.js",
- "accessCount": 4,
- "lastAccessed": 1774313470024,
- "type": "file"
- },
- {
- "path": "",
- "accessCount": 4,
- "lastAccessed": 1774339108220,
- "type": "directory"
- },
{
"path": "backend/services/gateway.py",
- "accessCount": 3,
- "lastAccessed": 1774339389171,
+ "accessCount": 98,
+ "lastAccessed": 1774550272354,
+ "type": "file"
+ },
+ {
+ "path": "backend/services/gateway_openclaw_handlers.py",
+ "accessCount": 91,
+ "lastAccessed": 1774550256325,
+ "type": "file"
+ },
+ {
+ "path": "backend/api/openclaw.py",
+ "accessCount": 48,
+ "lastAccessed": 1774545375555,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/hooks/useOpenClawPanel.js",
+ "accessCount": 42,
+ "lastAccessed": 1774550688926,
+ "type": "file"
+ },
+ {
+ "path": "shared/client/openclaw_client.py",
+ "accessCount": 39,
+ "lastAccessed": 1774545484770,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src",
+ "accessCount": 35,
+ "lastAccessed": 1774550715529,
+ "type": "directory"
+ },
+ {
+ "path": "reference/openclaw/src",
+ "accessCount": 33,
+ "lastAccessed": 1774550840611,
+ "type": "directory"
+ },
+ {
+ "path": "backend/services/openclaw_cli.py",
+ "accessCount": 31,
+ "lastAccessed": 1774545484887,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/components/TraderView.jsx",
+ "accessCount": 23,
+ "lastAccessed": 1774543366574,
+ "type": "file"
+ },
+ {
+ "path": "shared/models/openclaw.py",
+ "accessCount": 22,
+ "lastAccessed": 1774545419541,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/store/openclawStore.js",
+ "accessCount": 20,
+ "lastAccessed": 1774550319533,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/App.jsx",
+ "accessCount": 18,
+ "lastAccessed": 1774544542524,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/services/websocket.js",
+ "accessCount": 18,
+ "lastAccessed": 1774549669596,
+ "type": "file"
+ },
+ {
+ "path": "start-dev.sh",
+ "accessCount": 15,
+ "lastAccessed": 1774548224246,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/components/RuntimeView.jsx",
+ "accessCount": 14,
+ "lastAccessed": 1774518525793,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/components/AppShell.jsx",
+ "accessCount": 13,
+ "lastAccessed": 1774533781725,
"type": "file"
},
{
"path": "backend/main.py",
+ "accessCount": 13,
+ "lastAccessed": 1774548236340,
+ "type": "directory"
+ },
+ {
+ "path": "backend/apps/openclaw_service.py",
+ "accessCount": 10,
+ "lastAccessed": 1774547900186,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/components/OpenClawStatusPanel.jsx",
+ "accessCount": 8,
+ "lastAccessed": 1774533622019,
+ "type": "file"
+ },
+ {
+ "path": "reference/openclaw/src/commands",
+ "accessCount": 7,
+ "lastAccessed": 1774530402019,
+ "type": "directory"
+ },
+ {
+ "path": "frontend/src/config/constants.js",
+ "accessCount": 7,
+ "lastAccessed": 1774544689658,
+ "type": "file"
+ },
+ {
+ "path": "",
+ "accessCount": 6,
+ "lastAccessed": 1774550700047,
+ "type": "directory"
+ },
+ {
+ "path": "backend/services",
+ "accessCount": 5,
+ "lastAccessed": 1774550692490,
+ "type": "directory"
+ },
+ {
+ "path": "frontend/src/store/uiStore.js",
+ "accessCount": 4,
+ "lastAccessed": 1774533747700,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/styles/GlobalStyles.jsx",
+ "accessCount": 4,
+ "lastAccessed": 1774533753657,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/store/agentStore.js",
"accessCount": 3,
- "lastAccessed": 1774342613364,
+ "lastAccessed": 1774517930592,
+ "type": "file"
+ },
+ {
+ "path": "reference/openclaw/src/cli/skills-cli.ts",
+ "accessCount": 3,
+ "lastAccessed": 1774527140107,
+ "type": "file"
+ },
+ {
+ "path": "reference/openclaw/src/commands/agents.commands.list.ts",
+ "accessCount": 3,
+ "lastAccessed": 1774533427441,
"type": "file"
},
{
"path": "frontend/src/store/runtimeStore.js",
"accessCount": 2,
- "lastAccessed": 1774317990919,
+ "lastAccessed": 1774517930660,
"type": "file"
},
{
- "path": "frontend/src/services/websocket.js",
+ "path": "frontend/src/hooks/useAgentWorkspacePanel.js",
"accessCount": 2,
- "lastAccessed": 1774318009819,
+ "lastAccessed": 1774518021290,
"type": "file"
},
{
- "path": "backend/core/pipeline_runner.py",
+ "path": "frontend/src/services/runtimeApi.js",
"accessCount": 2,
- "lastAccessed": 1774339367538,
+ "lastAccessed": 1774518025465,
"type": "file"
},
{
- "path": "backend/runtime/manager.py",
+ "path": "reference/openclaw/src/commands/agents.commands.delete.ts",
"accessCount": 2,
- "lastAccessed": 1774339367572,
+ "lastAccessed": 1774530389553,
+ "type": "file"
+ },
+ {
+ "path": "reference/openclaw/src/commands/agents.commands.add.ts",
+ "accessCount": 2,
+ "lastAccessed": 1774530389605,
+ "type": "file"
+ },
+ {
+ "path": "backend/api/__init__.py",
+ "accessCount": 2,
+ "lastAccessed": 1774542416191,
+ "type": "file"
+ },
+ {
+ "path": "frontend/vite.config.js",
+ "accessCount": 2,
+ "lastAccessed": 1774544772960,
+ "type": "file"
+ },
+ {
+ "path": "frontend/src/store/index.js",
+ "accessCount": 1,
+ "lastAccessed": 1774515811752,
"type": "file"
},
{
"path": "frontend/src/store/marketStore.js",
"accessCount": 1,
- "lastAccessed": 1774313140483,
- "type": "file"
- },
- {
- "path": "frontend/src/hooks/useFeedProcessor.js",
- "accessCount": 1,
- "lastAccessed": 1774313148279,
- "type": "file"
- },
- {
- "path": "frontend/src/components/Header.jsx",
- "accessCount": 1,
- "lastAccessed": 1774313156696,
- "type": "file"
- },
- {
- "path": "frontend/src/components/TraderView.jsx",
- "accessCount": 1,
- "lastAccessed": 1774313156753,
- "type": "file"
- },
- {
- "path": "frontend/src/store/uiStore.js",
- "accessCount": 1,
- "lastAccessed": 1774313187460,
+ "lastAccessed": 1774515838923,
"type": "file"
},
{
"path": "frontend/src/store/portfolioStore.js",
"accessCount": 1,
- "lastAccessed": 1774313187511,
+ "lastAccessed": 1774515839687,
"type": "file"
},
{
- "path": "frontend/src/store/agentStore.js",
+ "path": "frontend/src/index.css",
"accessCount": 1,
- "lastAccessed": 1774313187573,
+ "lastAccessed": 1774515988837,
"type": "file"
},
{
- "path": "frontend/src/hooks/useWebSocketConnection.js",
+ "path": "frontend/src/App.css",
"accessCount": 1,
- "lastAccessed": 1774313279414,
+ "lastAccessed": 1774515998423,
"type": "file"
},
{
- "path": "frontend/src/hooks/useStockDataRequests.js",
+ "path": "frontend/package.json",
"accessCount": 1,
- "lastAccessed": 1774313319716,
+ "lastAccessed": 1774516005569,
"type": "file"
},
{
"path": "frontend/src/hooks/useAgentDataRequests.js",
"accessCount": 1,
- "lastAccessed": 1774313347455,
+ "lastAccessed": 1774517930219,
"type": "file"
},
{
- "path": "frontend/src/components/AppShell.jsx",
+ "path": "backend/services/gateway_admin_handlers.py",
"accessCount": 1,
- "lastAccessed": 1774313396331,
- "type": "file"
- },
- {
- "path": "start-dev.sh",
- "accessCount": 1,
- "lastAccessed": 1774317979859,
+ "lastAccessed": 1774517937966,
"type": "file"
},
{
"path": "backend/apps/agent_service.py",
"accessCount": 1,
- "lastAccessed": 1774317984348,
+ "lastAccessed": 1774517946208,
"type": "file"
},
{
- "path": "shared/client/trading_client.py",
+ "path": "frontend/src/hooks",
"accessCount": 1,
- "lastAccessed": 1774317984365,
+ "lastAccessed": 1774517946260,
+ "type": "directory"
+ },
+ {
+ "path": "frontend/src/hooks/useFeedProcessor.js",
+ "accessCount": 1,
+ "lastAccessed": 1774517952115,
"type": "file"
},
{
- "path": "backend/apps/trading_service.py",
+ "path": "reference/openclaw/src/commands/models/set.ts",
"accessCount": 1,
- "lastAccessed": 1774317984408,
+ "lastAccessed": 1774526963526,
"type": "file"
},
{
- "path": "pyproject.toml",
+ "path": "reference/openclaw/src/commands/models/list.ts",
"accessCount": 1,
- "lastAccessed": 1774317990970,
+ "lastAccessed": 1774526963632,
"type": "file"
},
{
- "path": "backend/agents/factory.py",
+ "path": "reference/openclaw/src/cli/skills-cli.format.ts",
"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,
+ "lastAccessed": 1774526963684,
"type": "file"
}
],
diff --git a/.omc/state/hud-state.json b/.omc/state/hud-state.json
index 1eedef0..8aae63d 100644
--- a/.omc/state/hud-state.json
+++ b/.omc/state/hud-state.json
@@ -1,6 +1,6 @@
{
- "timestamp": "2026-03-24T07:58:12.123Z",
+ "timestamp": "2026-03-26T17:14:45.135Z",
"backgroundTasks": [],
- "sessionStartTimestamp": "2026-03-24T07:58:09.417Z",
- "sessionId": "fda34772-7bd2-402e-86b2-d656296416f3"
+ "sessionStartTimestamp": "2026-03-26T17:13:16.686Z",
+ "sessionId": "83f172c1-eb0f-4418-87a5-b9d4b6ce5b61"
}
\ No newline at end of file
diff --git a/.omc/state/hud-stdin-cache.json b/.omc/state/hud-stdin-cache.json
index d6b2010..c44d454 100644
--- a/.omc/state/hud-stdin-cache.json
+++ b/.omc/state/hud-stdin-cache.json
@@ -1 +1 @@
-{"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
+{"session_id":"83f172c1-eb0f-4418-87a5-b9d4b6ce5b61","transcript_path":"/Users/cillin/.claude/projects/-Users-cillin-workspeace-evotraders/83f172c1-eb0f-4418-87a5-b9d4b6ce5b61.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":98.95595149999994,"total_duration_ms":43461876,"total_api_duration_ms":7482894,"total_lines_added":2289,"total_lines_removed":1132},"context_window":{"total_input_tokens":949049,"total_output_tokens":356074,"context_window_size":200000,"current_usage":{"input_tokens":507,"output_tokens":72,"cache_creation_input_tokens":346,"cache_read_input_tokens":82368},"used_percentage":42,"remaining_percentage":58},"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 412c4a2..4b87c5f 100644
--- a/.omc/state/idle-notif-cooldown.json
+++ b/.omc/state/idle-notif-cooldown.json
@@ -1,3 +1,3 @@
{
- "lastSentAt": "2026-03-24T08:58:57.965Z"
+ "lastSentAt": "2026-03-27T03:08:22.675Z"
}
\ No newline at end of file
diff --git a/.omc/state/subagent-tracking.json b/.omc/state/subagent-tracking.json
index d2ec92f..3f33a62 100644
--- a/.omc/state/subagent-tracking.json
+++ b/.omc/state/subagent-tracking.json
@@ -1,26 +1,17 @@
{
"agents": [
{
- "agent_id": "abeaf609b74a2b7ee",
+ "agent_id": "ace758bdbd117358d",
"agent_type": "Explore",
- "started_at": "2026-03-24T08:01:40.015Z",
+ "started_at": "2026-03-26T17:16:09.450Z",
"parent_mode": "none",
"status": "completed",
- "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
+ "completed_at": "2026-03-26T17:17:33.704Z",
+ "duration_ms": 84254
}
],
- "total_spawned": 2,
- "total_completed": 2,
+ "total_spawned": 1,
+ "total_completed": 1,
"total_failed": 0,
- "last_updated": "2026-03-24T08:59:06.380Z"
+ "last_updated": "2026-03-27T03:08:25.014Z"
}
\ No newline at end of file
diff --git a/.playwright-mcp/page-2026-03-26T12-28-14-006Z.png b/.playwright-mcp/page-2026-03-26T12-28-14-006Z.png
new file mode 100644
index 0000000..38fdb42
Binary files /dev/null and b/.playwright-mcp/page-2026-03-26T12-28-14-006Z.png differ
diff --git a/README.md b/README.md
index df8a281..54461aa 100644
--- a/README.md
+++ b/README.md
@@ -5,32 +5,28 @@
EvoTraders: A Self-Evolving Multi-Agent Trading System
- 📌 Visit us at EvoTraders website !
+ 📌 Visit the EvoTraders website

-EvoTraders is an open-source financial trading agent framework that builds a trading system capable of continuous learning and evolution in real markets through multi-agent collaboration and memory systems.
+EvoTraders is an open-source financial trading agent framework that combines multi-agent collaboration, run-scoped workspaces, and memory to support both backtests and live trading workflows.
---
## Core Features
-**Multi-Agent Collaborative Trading**
-A team of 6 members, including 4 specialized analyst roles (fundamentals, technical, sentiment, valuation) + portfolio manager + risk management, collaborating to make decisions like a real trading team.
+**Multi-agent trading team**
+Six roles collaborate like a real desk: four specialist analysts (fundamentals, technical, sentiment, valuation), one portfolio manager, and one risk manager.
-You can customize your Agents here: [Custom Configuration](#custom-configuration)
+**Continuous learning**
+Agents can persist long-term memory with ReMe, reflect after each cycle, and evolve their decision patterns over time.
-**Continuous Learning and Evolution**
-Based on the ReMe memory framework, agents reflect and summarize after each trade, preserving experience across rounds, and forming unique investment methodologies.
+**Backtest and live modes**
+The same runtime model supports historical simulation and live execution with real-time market data.
-Through this design, we hope that when AI Agents form a team and enter the real-time market, they will gradually develop their own trading styles and decision preferences, rather than one-time random inference.
-
-**Real-Time Market Trading**
-Supports real-time market data integration, providing backtesting mode and live trading mode, allowing AI Agents to learn and make decisions in real market fluctuations.
-
-**Visualized Trading Information**
-Observe agents' analysis processes, communication records, and decision evolution in real-time, with complete tracking of return curves and analyst performance.
+**Operator-facing UI**
+The frontend exposes the trading room, runtime controls, logs, approvals, agent workspaces, and explain/news views.
@@ -39,83 +35,158 @@ Observe agents' analysis processes, communication records, and decision evolutio
---
+## Current Architecture
+
+The repository is currently in a transition from a modular monolith to split service surfaces. The split-service path is the default local development mode.
+
+Current app surfaces:
+
+- `backend.apps.agent_service` on `:8000`: control plane for workspaces, agents, skills, and guard/approval APIs
+- `backend.apps.trading_service` on `:8001`: read-only trading data APIs
+- `backend.apps.news_service` on `:8002`: read-only explain/news APIs
+- `backend.apps.runtime_service` on `:8003`: runtime lifecycle APIs
+- `backend.apps.openclaw_service` on `:8004`: read-only OpenClaw facade
+- WebSocket gateway on `:8765`: live event/feed channel for the frontend
+
+The most important runtime path today is:
+
+`frontend -> runtime_service/control APIs -> gateway/runtime manager -> market service + pipeline + storage`
+
+Reference notes for the migration live in [services/README.md](./services/README.md).
+
+---
+
## Quick Start
-### Installation
+### 1. Install
```bash
-# Clone repository
-git clone https://github.com/agentscope-ai/agentscope-samples
-cd agentscope-samples/EvoTraders
+# clone this repository, then:
+cd evotraders
-# Install dependencies (Recommend uv!)
+# recommended
uv pip install -e .
-# optional: pip install -e .
+# optional
+# uv pip install -e ".[dev]"
+# pip install -e .
+```
-# Configure environment variables
+### 2. Configure environment
+
+```bash
cp env.template .env
-# Edit .env file and add your API Keys. The following config are required:
+```
-# finance data API: At minimum, FINANCIAL_DATASETS_API_KEY is required, corresponding to FIN_DATA_SOURCE=financial_datasets; It is recommended to add FINNHUB_API_KEY, corresponding to FIN_DATA_SOURCE=finnhub; If using live mode, FINNHUB_API_KEY must be added
-FIN_DATA_SOURCE = #finnhub or financial_datasets
-FINANCIAL_DATASETS_API_KEY= #Required
-FINNHUB_API_KEY= #Optional
+The root `env.template` is the canonical local template. A `.env.example` is also kept in the repo for reference.
-# LLM API for Agents
+Minimum useful variables:
+
+```bash
+# watchlist
+TICKERS=AAPL,MSFT,GOOGL,NVDA,TSLA,META,AMZN
+
+# market data
+FIN_DATA_SOURCE=finnhub
+FINANCIAL_DATASETS_API_KEY=
+FINNHUB_API_KEY=
+POLYGON_API_KEY=
+
+# agent model
OPENAI_API_KEY=
OPENAI_BASE_URL=
MODEL_NAME=qwen3-max-preview
-# LLM & embedding API for Memory
+# memory (optional unless --enable-memory is used)
MEMORY_API_KEY=
```
-### Running
+Notes:
+
+- `FINNHUB_API_KEY` is required for live mode.
+- `POLYGON_API_KEY` enables long-lived market-store ingestion and refresh helpers.
+- `MEMORY_API_KEY` is only required when long-term memory is enabled.
+
+### 3. Start the stack
+
+Recommended local development flow:
-**Backtest Mode:**
```bash
-evotraders backtest --start 2025-11-01 --end 2025-12-01
-evotraders backtest --start 2025-11-01 --end 2025-12-01 --enable-memory # Use Memory
+./start-dev.sh
```
-If you do not have market data APIs and just want to try the backtest demo, download the offline data and unzip it into `backend/data`:
+This starts:
+
+- `agent_service` at `http://localhost:8000`
+- `trading_service` at `http://localhost:8001`
+- `news_service` at `http://localhost:8002`
+- `runtime_service` at `http://localhost:8003`
+- gateway WebSocket at `ws://localhost:8765`
+
+Then start the frontend in another terminal:
+
+```bash
+evotraders frontend
+```
+
+Open `http://localhost:5173`.
+
+You can also run services manually:
+
+```bash
+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
+python -m uvicorn backend.apps.runtime_service:app --host 0.0.0.0 --port 8003 --reload
+python -m backend.main --mode live --host 0.0.0.0 --port 8765
+```
+
+### 4. Run backtest or live mode from CLI
+
+Backtest:
+
+```bash
+evotraders backtest --start 2025-11-01 --end 2025-12-01
+evotraders backtest --start 2025-11-01 --end 2025-12-01 --enable-memory
+evotraders backtest --config-name smoke_fullstack --start 2025-11-01 --end 2025-12-01
+```
+
+Live:
+
+```bash
+evotraders live
+evotraders live --enable-memory
+evotraders live --schedule-mode intraday --interval-minutes 60
+evotraders live --trigger-time 22:30
+```
+
+Help:
+
+```bash
+evotraders --help
+evotraders backtest --help
+evotraders live --help
+evotraders frontend --help
+```
+
+### Offline backtest data
+
+If you want a quick backtest demo without external market APIs, download the offline bundle and unzip it into `backend/data`:
+
```bash
wget "https://agentscope-open.oss-cn-beijing.aliyuncs.com/ret_data.zip"
unzip ret_data.zip -d backend/data
```
-The zip includes basic stock price data so you can run the backtest demo out of the box.
-**Live Trading:**
-```bash
-evotraders live # Run immediately (default)
-evotraders live --enable-memory # Use memory
-evotraders live --mock # Mock mode (testing)
-evotraders live -t 22:30 # Run daily at 22:30 local time (auto-converts to NYSE timezone)
-```
+---
-**Get Help:**
-```bash
-evotraders --help # View global CLI help
-evotraders backtest --help # View backtest mode parameters
-evotraders live --help # View live/mock run parameters
-```
+## Runtime Data Layout
-**Launch Visualization Interface:**
-```bash
-# Ensure npm is installed, otherwise install it:
-# npm install
-evotraders frontend # Default connects to port 8765, you can modify the address in ./frontend/env.local to change the port number
-```
-
-Visit `http://localhost:5173/` to view the trading room, select a date and click Run/Replay to observe the decision-making process.
-
-### Runtime Data Layout
-
-- Long-lived research data is stored in `data/market_research.db`
-- Each task run writes run-scoped state under `runs//`
-- `runs//team_dashboard/*.json` is an export/compatibility layer for dashboard views, not the authoritative runtime source of truth
-- Runtime APIs prefer active runtime state, `server_state.json`, and `runtime.db`
+- Long-lived research data lives in `data/market_research.db`
+- Each run writes run-scoped state under `runs//`
+- `runs//BOOTSTRAP.md` stores run-specific bootstrap values and prompt body
+- `runs//state/runtime_state.json` stores runtime snapshot state
+- `runs//team_dashboard/*.json` is a compatibility/export layer for dashboard consumers, not the primary runtime source of truth
Optional retention control:
@@ -123,129 +194,147 @@ Optional retention control:
RUNS_RETENTION_COUNT=20
```
-Only timestamped run folders like `YYYYMMDD_HHMMSS` are pruned automatically when starting a new runtime. Named runs such as `smoke_fullstack` or `test_*` are preserved.
+Only timestamped run folders like `YYYYMMDD_HHMMSS` are pruned automatically. Named runs such as `live`, `smoke_fullstack`, or `reload_demo_*` are preserved.
---
-## System Architecture
+## Frontend Service Routing
-
+The frontend always uses the control plane and runtime APIs, and can optionally call split services directly for read-only data.
-### Agent Design
+Useful frontend env vars:
-**Analyst Team:**
-- **Fundamentals Analyst**: Financial health, profitability, growth quality
-- **Technical Analyst**: Price trends, technical indicators, momentum analysis
-- **Sentiment Analyst**: Market sentiment, news sentiment, insider trading
-- **Valuation Analyst**: DCF, residual income, EV/EBITDA
-
-**Decision Layer:**
-- **Portfolio Manager**: Integrates analysis signals from analysts, executes communication strategies, combines analyst and team historical performance, recent investment memories, and long-term investment experience to make final decisions
-- **Risk Management**: Real-time price and volatility monitoring, position limits, multi-layer risk warnings
-
-### Decision Process
-
-```
-Real-time Market Data → Independent Analysis → Intelligent Communication (1v1/1vN/NvN) → Decision Execution → Performance Evaluation → Learning and Evolution (Memory Update)
+```bash
+VITE_CONTROL_API_BASE_URL=http://localhost:8000/api
+VITE_RUNTIME_API_BASE_URL=http://localhost:8003/api/runtime
+VITE_NEWS_SERVICE_URL=http://localhost:8002
+VITE_TRADING_SERVICE_URL=http://localhost:8001
+VITE_WS_URL=ws://localhost:8765
```
-Each trading day goes through five stages:
-
-1. **Analysis Stage**: Each agent independently analyzes based on their respective tools and historical experience
-2. **Communication Stage**: Exchange views through private chats, notifications, meetings, etc.
-3. **Decision Stage**: Portfolio manager makes comprehensive judgments and provides final trades
-4. **Evaluation Stage**
- - **Performance Charts**: Track portfolio return curves vs. benchmark strategies (equal-weighted, market-cap weighted, momentum). Used to evaluate overall strategy effectiveness.
-
- - **Analyst Rankings**: Click on avatars in the Trading Room to view analyst performance (win rate, bull/bear market win rate). Used to understand which analysts provide the most valuable insights.
-
- - **Statistics**: Detailed position and trading history. Used for in-depth analysis of position management and execution quality.
-
-5. **Review Stage**: Agents reflect on decisions and summarize experiences based on actual returns of the day, and store them in the ReMe memory framework for continuous improvement
+If these are not set, the frontend falls back to its local defaults and compatibility paths where available.
---
-### Module Support
+## Decision Flow
-- **Agent Framework**: [AgentScope](https://github.com/agentscope-ai/agentscope)
-- **Memory System**: [ReMe](https://github.com/agentscope-ai/reme)
-- **LLM Support**: OpenAI, DeepSeek, Qwen, Moonshot, Zhipu AI, etc.
+```text
+Market data -> independent analyst work -> team communication -> portfolio decision ->
+risk review -> execution/settlement -> reflection/memory update
+```
+
+The runtime manager also tracks:
+
+- agent registration and status
+- pending approvals
+- run events
+- current session key
---
## Custom Configuration
-### Custom Analyst Roles
+### Add or change analyst roles
-1. Register role information in [./backend/agents/prompts/analyst/personas.yaml](./backend/agents/prompts/analyst/personas.yaml), for example:
+1. Define the analyst persona in [backend/agents/prompts/analyst/personas.yaml](./backend/agents/prompts/analyst/personas.yaml)
+2. Register the role in [backend/config/constants.py](./backend/config/constants.py)
+3. Optionally add/update the frontend seat metadata in [frontend/src/config/constants.js](./frontend/src/config/constants.js)
+
+Example persona entry:
```yaml
comprehensive_analyst:
name: "Comprehensive Analyst"
focus:
- - ...
- preferred_tools: # Flexibly select based on situation
+ - multi-factor synthesis
+ preferred_tools:
+ - get_stock_price
+ - get_company_financials
description: |
- As a comprehensive analyst ...
+ A generalist analyst that combines multiple signals.
```
-2. Add role definition in [./backend/config/constants.py](./backend/config/constants.py)
-```python
-ANALYST_TYPES = {
- # Add new analyst
- "comprehensive_analyst": {
- "display_name": "Comprehensive Analyst",
- "agent_id": "comprehensive_analyst",
- "description": "Uses LLM to intelligently select analysis tools, performs comprehensive analysis",
- "order": 15
- }
-}
-```
+### Configure per-agent models
-3. Introduce new role in frontend configuration [./frontend/src/config/constants.js](./frontend/src/config/constants.js) (optional)
-```javascript
-export const AGENTS = [
- // Override one of the agents
- {
- id: "comprehensive_analyst",
- name: "Comprehensive Analyst",
- role: "Comprehensive Analyst",
- avatar: `${ASSET_BASE_URL}/...`,
- colors: { bg: '#F9FDFF', text: '#1565C0', accent: '#1565C0' }
- }
-]
-```
-
-### Custom Models
-
-Configure models used by different agents in the [.env](.env) file:
+Model overrides are configured in `.env`:
```bash
-AGENT_SENTIMENT_ANALYST_MODEL_NAME=qwen3-max-preview
-AGENT_FUNDAMENTALS_ANALYST_MODEL_NAME=deepseek-chat
-AGENT_TECHNICAL_ANALYST_MODEL_NAME=glm-4-plus
-AGENT_VALUATION_ANALYST_MODEL_NAME=moonshot-v1-32k
+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
```
-### Project Structure
+### Run-scoped bootstrap config
+Each run can override defaults through `runs//BOOTSTRAP.md`. The front matter is parsed by [backend/config/bootstrap_config.py](./backend/config/bootstrap_config.py) and can define values such as:
+
+```yaml
+tickers:
+ - AAPL
+ - MSFT
+initial_cash: 100000
+margin_requirement: 0.5
+max_comm_cycles: 2
+schedule_mode: daily
+trigger_time: "09:30"
+enable_memory: false
```
-EvoTraders/
+
+Initialize a run workspace with:
+
+```bash
+evotraders init-workspace --config-name my_run
+```
+
+---
+
+## Project Structure
+
+```text
+evotraders/
├── backend/
-│ ├── agents/ # Agent implementation
-│ ├── communication/ # Communication system
-│ ├── memory/ # Memory system (ReMe)
-│ ├── tools/ # Analysis toolset
-│ ├── servers/ # WebSocket services
-│ └── cli.py # CLI entry point
-├── frontend/ # React visualization interface
-└── logs_and_memory/ # Logs and memory data
+│ ├── agents/ # agent roles, prompts, skills, workspaces
+│ ├── api/ # FastAPI routers
+│ ├── apps/ # split service surfaces
+│ ├── core/ # pipeline, scheduler, state sync
+│ ├── runtime/ # runtime manager and agent runtime state
+│ ├── services/ # gateway, market/storage/db services
+│ └── cli.py # Typer CLI entrypoint
+├── frontend/ # React + Vite UI
+├── shared/ # shared clients and schemas for split services
+├── runs/ # run-scoped state and dashboards
+├── data/ # long-lived research artifacts
+└── services/README.md
+```
+
+---
+
+## Testing
+
+Backend tests live under `backend/tests` and cover service apps, shared clients, domains, routing, enrichment, gateway support, and runtime support.
+
+Typical commands:
+
+```bash
+pytest
+pytest backend/tests/test_runtime_service_app.py
+pytest backend/tests/test_trading_service_app.py
+```
+
+Frontend tests:
+
+```bash
+cd frontend
+npm test
```
---
## License and Disclaimer
-EvoTraders is a research and educational project, open-sourced under the Apache 2.0 license.
+EvoTraders is a research and educational project. Review the repository license before redistribution or commercial use.
-**Risk Warning**: Before trading with real funds, please conduct thorough testing and risk assessment. Past performance does not guarantee future returns. Investment involves risks, and decisions should be made with caution.
+**Risk warning**: this project is not investment advice. Test thoroughly before any real-money deployment. Past performance does not guarantee future returns.
diff --git a/README_zh.md b/README_zh.md
index 479b062..5c7237a 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -4,291 +4,337 @@
EvoTraders:自我进化的多智能体交易系统
-
- 📌 Visit us at EvoTraders website !
+ 📌 访问 EvoTraders 官网

-EvoTraders是一个开源的金融交易智能体框架,通过多智能体协作和记忆系统,构建能够在真实市场中持续学习与进化的交易系统。
+EvoTraders 是一个开源的金融交易智能体框架,结合多智能体协作、run 级工作区和记忆机制,支持回测与实盘两类交易运行模式。
---
## 核心特性
-**多智能体协作交易**
-6名成员,包含4种专业分析师角色(基本面、技术面、情绪、估值)+ 投资组合经理 + 风险管理,像真实交易团队一样协作决策。
+**多智能体交易团队**
+系统默认包含 6 个角色:4 个分析师(基本面、技术面、情绪、估值)+ 投资经理 + 风控经理。
-你可以在这里自定义你的Agents,支持配置不同大模型(如 Qwen、DeepSeek、GPT、Claude等)协同分析:[自定义配置](#自定义配置)
+**持续学习**
+可选接入 ReMe 长期记忆,智能体会在每轮结束后反思、复盘并沉淀经验。
-**持续学习与进化**
-基于 ReMe 记忆框架,智能体在每次交易后反思总结,跨回合保留经验,形成独特的投资方法论。
-
-通过这样的设计,我们希望当 AI Agents 组成团队进入实时市场,它们会逐渐形成自己的交易风格和决策偏好,而不是一次性的随机推理
-
-
-**实时市场交易**
-支持实时行情接入,提供回测模式和实盘模式,让 AI Agents 在真实市场波动中学习和决策。
-
-**可视化交易信息**
-实时观察 Agents 的分析过程、沟通记录和决策演化,完整追踪收益曲线和分析师表现。
+**统一运行时**
+同一套运行时模型支持历史回测和实时行情驱动的实盘流程。
+**可操作前端**
+前端不只是展示层,还包含交易室、运行控制、日志、审批、Agent 工作区和 explain/news 视图。
+---
+
+## 当前架构
+
+仓库目前处于“模块化单体 -> 拆分服务”的迁移阶段,本地开发默认走 split-service 路径。
+
+当前 app surface:
+
+- `backend.apps.agent_service`,端口 `8000`:控制面,负责 workspaces、agents、skills、审批接口
+- `backend.apps.trading_service`,端口 `8001`:只读交易数据接口
+- `backend.apps.news_service`,端口 `8002`:只读 explain/news 接口
+- `backend.apps.runtime_service`,端口 `8003`:运行时生命周期接口
+- `backend.apps.openclaw_service`,端口 `8004`:只读 OpenClaw facade
+- WebSocket gateway,端口 `8765`:前端实时事件和 feed 通道
+
+当前最关键的主链路是:
+
+`frontend -> runtime_service/control APIs -> gateway/runtime manager -> market service + pipeline + storage`
+
+迁移背景可参考 [services/README.md](./services/README.md)。
---
## 快速开始
-### 安装
+### 1. 安装
```bash
-# 克隆仓库
-git clone https://github.com/agentscope-ai/agentscope-samples
-cd agentscope-samples/EvoTraders
+# 克隆仓库后进入项目目录
+cd evotraders
-# 安装依赖(推荐使用uv)
+# 推荐
uv pip install -e .
-# (可选)pip install -e .
-# 配置环境变量
+# 可选
+# uv pip install -e ".[dev]"
+# pip install -e .
+```
+
+### 2. 配置环境变量
+
+```bash
cp env.template .env
-# 编辑 .env 文件,添加你的 API Keys,以下的配置项为必填项
+```
-# finance data API:至少需要FINANCIAL_DATASETS_API_KEY,对应FIN_DATA_SOURCE=financial_datasets;推荐添加FINNHUB_API_KEY,对应至少需要FINANCIAL_DATASETS_API_KEY,对应FIN_DATA_SOURCE填为finnhub;如果使用live 模式必须添加FINNHUB_API_KEY
-FIN_DATA_SOURCE= #finnhub or financial_datasets
-FINANCIAL_DATASETS_API_KEY= #必需
-FINNHUB_API_KEY= #可选
+根目录 `env.template` 是当前本地开发的主模板,仓库里也保留了 `.env.example` 作为参考。
-# LLM API for Agents
+最常用的配置项:
+
+```bash
+# 自选股
+TICKERS=AAPL,MSFT,GOOGL,NVDA,TSLA,META,AMZN
+
+# 行情数据
+FIN_DATA_SOURCE=finnhub
+FINANCIAL_DATASETS_API_KEY=
+FINNHUB_API_KEY=
+POLYGON_API_KEY=
+
+# Agent 模型
OPENAI_API_KEY=
OPENAI_BASE_URL=
MODEL_NAME=qwen3-max-preview
-# LLM & embedding API for Memory
+# 长期记忆(只有启用 --enable-memory 才需要)
MEMORY_API_KEY=
```
-### 运行
+说明:
+
+- live 模式必须配置 `FINNHUB_API_KEY`
+- `POLYGON_API_KEY` 用于长期 market store 的补数和刷新
+- `MEMORY_API_KEY` 仅在启用长期记忆时需要
+
+### 3. 启动服务栈
+
+本地开发推荐直接使用:
-**回测模式:**
```bash
-evotraders backtest --start 2025-11-01 --end 2025-12-01
-evotraders backtest --start 2025-11-01 --end 2025-12-01 --enable-memory # 使用记忆
-
+./start-dev.sh
```
-如果没有可用的行情 API,想快速体验回测 demo,可直接下载离线数据并解压到 `backend/data`:
+该脚本会启动:
+
+- `agent_service`:`http://localhost:8000`
+- `trading_service`:`http://localhost:8001`
+- `news_service`:`http://localhost:8002`
+- `runtime_service`:`http://localhost:8003`
+- gateway WebSocket:`ws://localhost:8765`
+
+然后在另一个终端启动前端:
+
+```bash
+evotraders frontend
+```
+
+访问 `http://localhost:5173`。
+
+也可以手动分别启动:
+
+```bash
+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
+python -m uvicorn backend.apps.runtime_service:app --host 0.0.0.0 --port 8003 --reload
+python -m backend.main --mode live --host 0.0.0.0 --port 8765
+```
+
+### 4. 使用 CLI 运行回测或实盘
+
+回测:
+
+```bash
+evotraders backtest --start 2025-11-01 --end 2025-12-01
+evotraders backtest --start 2025-11-01 --end 2025-12-01 --enable-memory
+evotraders backtest --config-name smoke_fullstack --start 2025-11-01 --end 2025-12-01
+```
+
+实盘:
+
+```bash
+evotraders live
+evotraders live --enable-memory
+evotraders live --schedule-mode intraday --interval-minutes 60
+evotraders live --trigger-time 22:30
+```
+
+帮助:
+
+```bash
+evotraders --help
+evotraders backtest --help
+evotraders live --help
+evotraders frontend --help
+```
+
+### 离线回测数据
+
+如果只是想快速体验回测,不依赖外部行情 API,可以下载离线数据包并解压到 `backend/data`:
+
```bash
wget "https://agentscope-open.oss-cn-beijing.aliyuncs.com/ret_data.zip"
unzip ret_data.zip -d backend/data
```
-该压缩包提供基础的股票行情数据,解压后即可直接用于回测演示。
-**实盘交易:**
+---
+
+## 运行时数据布局
+
+- 长期研究数据保存在 `data/market_research.db`
+- 每次 run 的状态写入 `runs//`
+- `runs//BOOTSTRAP.md` 保存该 run 的 bootstrap 值和 prompt body
+- `runs//state/runtime_state.json` 保存运行时快照
+- `runs//team_dashboard/*.json` 主要是给 dashboard 用的兼容导出层,不是唯一真相源
+
+可选保留策略:
+
```bash
-evotraders live # 立即运行(默认)
-evotraders live --enable-memory # 使用记忆
-evotraders live --mock # Mock 模式(测试)
-evotraders live -t 22:30 # 每天本地时间 22:30 运行(自动转换为 NYSE 时区)
-evotraders live --schedule-mode intraday --interval-minutes 60 # 每隔 1 小时触发一次;仅交易时段执行交易,其他时段只分析
+RUNS_RETENTION_COUNT=20
```
-前端的“运行设置”面板也支持热更新 `schedule_mode`、`interval_minutes`、`max_comm_cycles`;其中 daily 模式时间当前按 NYSE/ET 配置。
+只有形如 `YYYYMMDD_HHMMSS` 的时间戳目录会被自动清理;`live`、`smoke_fullstack`、`reload_demo_*` 这类命名 run 会保留。
-**获取帮助:**
-```bash
-evotraders --help # 查看整体命令行帮助
-evotraders backtest --help # 查看回测模式的参数说明
-evotraders live --help # 查看实盘/Mock 运行的参数说明
-```
+---
-**启动可视化界面:**
-```bash
-# 确保已安装 npm, 否则请安装:
-# npm install
-evotraders frontend # 默认连接 8765 端口, 你可以修改 ./frontend/env.local 中的地址从而修改端口号
-```
+## 前端服务路由
-访问 `http://localhost:5173/` 查看交易大厅,选择日期并点击 Run/Replay 观察决策过程。
+前端始终会使用 control plane 和 runtime API,同时可以选择直连拆分服务读取只读数据。
-### 迁移期服务边界说明
-
-当前仓库正处于从模块化单体向独立服务迁移的阶段,当前默认开发路径已经切到独立 app surface:
-
-- `backend.apps.agent_service`
-- `backend.apps.runtime_service`
-- `backend.apps.trading_service`
-- `backend.apps.news_service`
-
-当前本地开发默认推荐直接运行拆分后的服务:
+常用前端环境变量:
```bash
-./start-dev.sh split
-
-# 或分别手动启动
-python -m uvicorn backend.apps.agent_service:app --port 8000 --reload
-python -m uvicorn backend.apps.runtime_service:app --port 8003 --reload
-python -m uvicorn backend.apps.trading_service:app --port 8001 --reload
-python -m uvicorn backend.apps.news_service:app --port 8002 --reload
-```
-
-迁移期关键环境变量:
-
-```bash
-# 后端 Gateway 优先走独立服务读取
-NEWS_SERVICE_URL=http://localhost:8002
-TRADING_SERVICE_URL=http://localhost:8001
-
-# 前端浏览器直连控制面 / 运行时面
VITE_CONTROL_API_BASE_URL=http://localhost:8000/api
VITE_RUNTIME_API_BASE_URL=http://localhost:8003/api/runtime
-
-# 前端浏览器优先直连独立服务
VITE_NEWS_SERVICE_URL=http://localhost:8002
VITE_TRADING_SERVICE_URL=http://localhost:8001
+VITE_WS_URL=ws://localhost:8765
```
-目前前端已支持直连 `news-service` 的 explain 只读路径包括:
-
-- runtime panel / gateway port 查询已可独立指向 `runtime-service`
-- story
-- similar days
-- range explain
-- news for date
-- news categories
-
-如果没有配置这些变量,系统会继续走当前保留的本地回退逻辑。
+如果不配置,前端会按本地默认值和兼容回退逻辑运行。
---
-## 系统架构
+## 决策流程
-
-
-### 智能体设计
-
-**分析师团队:**
-- **基本面分析师**:财务健康度、盈利能力、增长质量
-- **技术分析师**:价格趋势、技术指标、动量分析
-- **情绪分析师**:市场情绪、新闻舆情、内部人交易
-- **估值分析师**:DCF、剩余收益、EV/EBITDA
-
-**决策层:**
-- **投资组合经理**:整合来自分析师的分析信号,执行沟通策略,结合分析师和团队历史表现、近期投资记忆和长期投资经验,进行最终决策
-- **风险管理**:实时价格与波动率监控、头寸限制,多层风险预警
-
-### 决策流程
-
-```
-实时行情 → 独立分析 → 智能沟通 (1v1/1vN/NvN) → 决策执行 → 收益评估 → 学习与进化(记忆更新)
+```text
+市场数据 -> 分析师独立分析 -> 团队沟通 -> 投资决策 ->
+风控审核 -> 执行/结算 -> 复盘/记忆更新
```
-每个交易日经历五个阶段:
-
-1. **分析阶段**:各智能体基于各自工具和历史经验独立分析
-2. **沟通阶段**:通过私聊、通知、会议等方式交换观点
-3. **决策阶段**:投资组合经理综合判断,给出最终交易
-4. **评估阶段**
- - **业绩图表**: 追踪组合收益曲线 vs. 基准策略(等权、市值加权、动量)。用于评估整体策略有效性。
-
- - **分析师排名**: 在 Trading Room 点击头像查看分析师表现(胜率、牛/熊市胜率)。用于了解哪些分析师提供最有价值的洞察。
-
- - **统计数据**: 详细的持仓和交易历史。用于深入分析仓位管理和执行质量。
-
-4. **复盘阶段**:Agents 根据当日实际收益反思决策、总结经验,并存入 ReMe 记忆框架以持续改进
-
----
-
-### 模块支持
-
-- **智能体框架**:[AgentScope](https://github.com/agentscope-ai/agentscope)
-- **记忆系统**:[ReMe](https://github.com/agentscope-ai/reme)
-- **LLM 支持**:OpenAI、DeepSeek、Qwen、Moonshot、Zhipu AI 等
+运行时管理器还会跟踪:
+- agent 注册和状态
+- 待审批项
+- run 事件
+- 当前 session key
---
## 自定义配置
-### 自定义分析师角色
+### 新增或修改分析师角色
-1. 在 [./backend/agents/prompts/analyst/personas.yaml](./backend/agents/prompts/analyst/personas.yaml) 中注册角色信息,例如:
+1. 在 [backend/agents/prompts/analyst/personas.yaml](./backend/agents/prompts/analyst/personas.yaml) 中定义 persona
+2. 在 [backend/config/constants.py](./backend/config/constants.py) 中注册角色
+3. 如有需要,在 [frontend/src/config/constants.js](./frontend/src/config/constants.js) 中补充前端展示元数据
+
+示例:
```yaml
comprehensive_analyst:
name: "Comprehensive Analyst"
focus:
- - ...
- preferred_tools: # Flexibly select based on situation
+ - multi-factor synthesis
+ preferred_tools:
+ - get_stock_price
+ - get_company_financials
description: |
- As a comprehensive analyst ...
+ A generalist analyst that combines multiple signals.
```
-2. 在 [./backend/config/constants.py](./backend/config/constants.py) 添加角色定义
-```python
-ANALYST_TYPES = {
- # 增加新的分析师
- "comprehensive_analyst": {
- "display_name": "Comprehensive Analyst",
- "agent_id": "comprehensive_analyst",
- "description": "Uses LLM to intelligently select analysis tools, performs comprehensive analysis",
- "order": 15
- }
-}
-```
+### 配置各 Agent 使用的模型
-3. 在前端配置 [./frontend/src/config/constants.js](./frontend/src/config/constants.js) 中引入新角色(可选)
-```javascript
-export const AGENTS = [
- // 覆盖掉其中某一个agent
- {
- id: "comprehensive_analyst",
- name: "Comprehensive Analyst",
- role: "Comprehensive Analyst",
- avatar: `${ASSET_BASE_URL}/...`,
- colors: { bg: '#F9FDFF', text: '#1565C0', accent: '#1565C0' }
- }
- ]
-```
-
-
-
-### 自定义模型
-
-在 [.env](.env) 文件中配置不同智能体使用的模型:
+模型覆盖在 `.env` 中配置:
```bash
-AGENT_SENTIMENT_ANALYST_MODEL_NAME=qwen3-max-preview
-AGENT_FUNDAMENTAL_ANALYST_MODEL_NAME=deepseek-chat
-AGENT_TECHNICAL_ANALYST_MODEL_NAME=glm-4-plus
-AGENT_VALUATION_ANALYST_MODEL_NAME=moonshot-v1-32k
+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
```
-### 项目结构
+### run 级 BOOTSTRAP 配置
+每个 run 都可以通过 `runs//BOOTSTRAP.md` 覆盖默认值。该文件由 [backend/config/bootstrap_config.py](./backend/config/bootstrap_config.py) 解析,front matter 可配置:
+
+```yaml
+tickers:
+ - AAPL
+ - MSFT
+initial_cash: 100000
+margin_requirement: 0.5
+max_comm_cycles: 2
+schedule_mode: daily
+trigger_time: "09:30"
+enable_memory: false
```
-EvoTraders/
+
+初始化一个 run 工作区:
+
+```bash
+evotraders init-workspace --config-name my_run
+```
+
+---
+
+## 项目结构
+
+```text
+evotraders/
├── backend/
-│ ├── agents/ # 智能体实现
-│ ├── communication/ # 通信系统
-│ ├── memory/ # 记忆系统 (ReMe)
-│ ├── tools/ # 分析工具集
-│ ├── servers/ # WebSocket 服务
-│ └── cli.py # CLI 入口
-├── frontend/ # React 可视化界面
-└── logs_and_memory/ # 日志和记忆数据
+│ ├── agents/ # agent 角色、prompts、skills、workspaces
+│ ├── api/ # FastAPI 路由层
+│ ├── apps/ # 拆分服务 app surface
+│ ├── core/ # pipeline、scheduler、state sync
+│ ├── runtime/ # runtime manager 和 agent runtime state
+│ ├── services/ # gateway、market/storage/db 服务
+│ └── cli.py # Typer CLI 入口
+├── frontend/ # React + Vite 前端
+├── shared/ # 拆分服务共用 client 和 schema
+├── runs/ # run 级状态和 dashboard 导出
+├── data/ # 长期研究数据
+└── services/README.md
+```
+
+---
+
+## 测试
+
+后端测试位于 `backend/tests`,覆盖 service app、shared client、domain、路由、enrichment、gateway 支撑模块和 runtime 支撑模块。
+
+常用命令:
+
+```bash
+pytest
+pytest backend/tests/test_runtime_service_app.py
+pytest backend/tests/test_trading_service_app.py
+```
+
+前端测试:
+
+```bash
+cd frontend
+npm test
```
---
## 许可与免责
-EvoTraders 是一个研究和教育项目,采用 Apache 2.0 许可协议开源。
+EvoTraders 是研究和教育用途项目。再次分发或商用前,请先核对仓库中的实际 license 文件。
-**风险提示**:在实际资金交易前,请务必进行充分的测试和风险评估。历史表现不代表未来收益,投资有风险,决策需谨慎。
+**风险提示**:本项目不构成投资建议。任何实盘部署前都应进行充分测试和风险评估,历史表现不代表未来收益。
diff --git a/backend/api/__init__.py b/backend/api/__init__.py
index 9e8c0c5..535587e 100644
--- a/backend/api/__init__.py
+++ b/backend/api/__init__.py
@@ -11,11 +11,13 @@ Provides REST API endpoints for:
from .agents import router as agents_router
from .workspaces import router as workspaces_router
from .guard import router as guard_router
+from .openclaw import router as openclaw_router
from .runtime import router as runtime_router
__all__ = [
"agents_router",
"workspaces_router",
"guard_router",
+ "openclaw_router",
"runtime_router",
]
diff --git a/backend/api/runtime.py b/backend/api/runtime.py
index 24d3a9f..2a2438d 100644
--- a/backend/api/runtime.py
+++ b/backend/api/runtime.py
@@ -389,11 +389,21 @@ def _find_available_port(start_port: int = 8765, max_port: int = 9000) -> int:
def _is_gateway_running() -> bool:
- """Check if Gateway process is running."""
+ """Check if Gateway process is running.
+
+ Checks both the internally-managed gateway process and falls back to
+ port availability (for externally-managed gateway processes).
+ """
process = _runtime_state.gateway_process
- if process is None:
+ if process is not None and process.poll() is None:
+ return True
+ # Fallback: check if the gateway port is in use (for externally started gateway)
+ import socket
+ try:
+ with socket.create_connection(("127.0.0.1", _runtime_state.gateway_port), timeout=1):
+ return True
+ except OSError:
return False
- return process.poll() is None
def _stop_gateway() -> bool:
diff --git a/backend/apps/__init__.py b/backend/apps/__init__.py
index 7c71854..7084136 100644
--- a/backend/apps/__init__.py
+++ b/backend/apps/__init__.py
@@ -5,6 +5,8 @@ from .agent_service import app as agent_app
from .agent_service import create_app as create_agent_app
from .news_service import app as news_app
from .news_service import create_app as create_news_app
+from .openclaw_service import app as openclaw_app
+from .openclaw_service import create_app as create_openclaw_app
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
@@ -21,6 +23,8 @@ __all__ = [
"create_agent_app",
"news_app",
"create_news_app",
+ "openclaw_app",
+ "create_openclaw_app",
"runtime_app",
"create_runtime_app",
"trading_app",
diff --git a/backend/services/gateway_cycle_support.py b/backend/services/gateway_cycle_support.py
index a04cf36..313073b 100644
--- a/backend/services/gateway_cycle_support.py
+++ b/backend/services/gateway_cycle_support.py
@@ -388,4 +388,15 @@ def stop_gateway(gateway: Any) -> None:
gateway._market_status_task.cancel()
if gateway._watchlist_ingest_task:
gateway._watchlist_ingest_task.cancel()
+ # Close OpenClaw WebSocket connection
+ if gateway._openclaw_ws:
+ import asyncio
+ try:
+ loop = asyncio.get_event_loop()
+ if loop.is_running():
+ loop.create_task(gateway._openclaw_ws.disconnect())
+ else:
+ loop.run_until_complete(gateway._openclaw_ws.disconnect())
+ except Exception:
+ pass
gateway._dashboard.stop()
diff --git a/backend/tests/test_service_clients.py b/backend/tests/test_service_clients.py
index 19cc677..877d697 100644
--- a/backend/tests/test_service_clients.py
+++ b/backend/tests/test_service_clients.py
@@ -4,6 +4,7 @@
import pytest
from shared.client.control_client import ControlPlaneClient
+from shared.client.openclaw_client import OpenClawServiceClient
from shared.client.runtime_client import RuntimeServiceClient
@@ -105,3 +106,25 @@ async def test_runtime_service_client_hits_current_runtime_routes():
("get", "/config", None),
("put", "/config", {"schedule_mode": "intraday"}),
]
+
+
+@pytest.mark.asyncio
+async def test_openclaw_service_client_hits_current_openclaw_routes():
+ client = OpenClawServiceClient()
+ client._client = _DummyAsyncClient()
+
+ await client.fetch_status()
+ await client.list_sessions()
+ await client.get_session("main/session-1")
+ await client.get_session_history("main/session-1", limit=5)
+ await client.list_cron_jobs()
+ await client.list_approvals()
+
+ assert client._client.calls == [
+ ("get", "/status", None),
+ ("get", "/sessions", None),
+ ("get", "/sessions/main/session-1", None),
+ ("get", "/sessions/main/session-1/history", {"limit": 5}),
+ ("get", "/cron", None),
+ ("get", "/approvals", None),
+ ]
diff --git a/deploy/README.md b/deploy/README.md
new file mode 100644
index 0000000..2a1a9b3
--- /dev/null
+++ b/deploy/README.md
@@ -0,0 +1,121 @@
+# Deployment Notes
+
+This directory contains the current production-oriented deployment artifacts for
+the EvoTraders frontend site and the live gateway process.
+
+## Contents
+
+- [deploy/systemd/evotraders.service](./systemd/evotraders.service)
+ - systemd unit for the long-running EvoTraders gateway process
+- [scripts/run_prod.sh](../scripts/run_prod.sh)
+ - production launch script used by the systemd unit
+- [deploy/nginx/evotraders.cillinn.com.conf](./nginx/evotraders.cillinn.com.conf)
+ - HTTPS nginx config with WebSocket proxying
+- [deploy/nginx/evotraders.cillinn.com.http.conf](./nginx/evotraders.cillinn.com.http.conf)
+ - plain HTTP/static-site variant
+
+## Current Production Shape
+
+The checked-in production path is intentionally minimal:
+
+- nginx serves the built frontend from `/var/www/evotraders/current`
+- nginx proxies `/ws` to `127.0.0.1:8765`
+- systemd runs `scripts/run_prod.sh`
+- `scripts/run_prod.sh` starts `python3 -m backend.main` in live mode on `127.0.0.1:8765`
+
+This means the checked-in production example is centered on the gateway and
+frontend, not on exposing the split FastAPI services directly.
+
+## Important Paths And Ports
+
+- frontend root: `/var/www/evotraders/current`
+- gateway bind: `127.0.0.1:8765`
+- public WebSocket path: `/ws`
+- working directory expected by systemd: `/root/code/evotraders`
+
+## systemd
+
+The current systemd unit:
+
+- uses `WorkingDirectory=/root/code/evotraders`
+- executes [scripts/run_prod.sh](../scripts/run_prod.sh)
+- restarts automatically on failure
+
+Enable and start:
+
+```bash
+sudo cp deploy/systemd/evotraders.service /etc/systemd/system/evotraders.service
+sudo systemctl daemon-reload
+sudo systemctl enable evotraders
+sudo systemctl start evotraders
+```
+
+Check status and logs:
+
+```bash
+sudo systemctl status evotraders
+journalctl -u evotraders -f
+```
+
+## nginx
+
+The HTTPS nginx config does two things:
+
+- redirects `http://evotraders.cillinn.com` to HTTPS
+- proxies `/ws` to the local gateway process with WebSocket upgrade headers
+
+Typical install flow:
+
+```bash
+sudo cp deploy/nginx/evotraders.cillinn.com.conf /etc/nginx/sites-available/evotraders.cillinn.com.conf
+sudo ln -s /etc/nginx/sites-available/evotraders.cillinn.com.conf /etc/nginx/sites-enabled/
+sudo nginx -t
+sudo systemctl reload nginx
+```
+
+The checked-in TLS config expects Let's Encrypt assets at:
+
+- `/etc/letsencrypt/live/evotraders.cillinn.com/fullchain.pem`
+- `/etc/letsencrypt/live/evotraders.cillinn.com/privkey.pem`
+
+## Environment Expectations
+
+Before using the production scripts, ensure the runtime environment has:
+
+- a usable Python environment
+- repo dependencies installed
+- required market/model API keys
+- any desired `TICKERS` override
+
+The production script currently sets:
+
+```bash
+PYTHONPATH=/root/code/evotraders/.pydeps:.
+TICKERS=${TICKERS:-AAPL,MSFT,GOOGL,AMZN,NVDA,META,TSLA,AMD,NFLX,AVGO,PLTR,COIN}
+```
+
+It then launches:
+
+```bash
+python3 -m backend.main \
+ --mode live \
+ --config-name production \
+ --host 127.0.0.1 \
+ --port 8765 \
+ --trigger-time now \
+ --poll-interval 15
+```
+
+## What This Deployment Does Not Yet Cover
+
+The checked-in deployment artifacts do not currently document or automate:
+
+- split FastAPI service deployment on `8000` to `8004`
+- OpenClaw gateway deployment on `18789`
+- database backup/retention workflows
+- frontend build/publish steps
+- secret management
+
+If you move production fully to split-service mode, update this directory so it
+documents the new service topology explicitly instead of relying on the gateway-
+only path.
diff --git a/docs/compat-removal-plan.md b/docs/compat-removal-plan.md
index f4f960f..c86e5b9 100644
--- a/docs/compat-removal-plan.md
+++ b/docs/compat-removal-plan.md
@@ -1,28 +1,116 @@
-# Compatibility Removal Plan
+# Compatibility And Migration Status
-This document tracks the remaining migration-only surfaces that still exist
-after the move to split-first development.
+This document tracks the remaining migration-related boundaries after the
+repository switched to split-first development.
-## Migration-only Surfaces
+## Current Status
-None currently remain as dedicated compatibility wrappers.
+The repo no longer depends on a combined FastAPI compatibility wrapper for
+normal local development. The default path is now:
-## Completed Removals
+`agent_service + trading_service + news_service + runtime_service + gateway`
+
+That means compatibility is no longer a separate startup mode. What remains is
+mostly protocol-level and routing-level compatibility while the codebase
+continues to move responsibilities into clearer service surfaces.
+
+## What Was Removed
### `backend.app`
-- Removed after compatibility startup switched to
- `backend.apps.combined_service:app` directly.
+- Removed after startup paths switched away from the legacy app wrapper.
+
+### `backend.apps.combined_service`
+
+- Removed after split-service startup became the only supported local dev mode.
### `shared.client.AgentServiceClient`
- Removed after split-aware clients became the default import surface.
-- Replacement:
+- Replaced by:
- `ControlPlaneClient`
- `RuntimeServiceClient`
- `TradingServiceClient`
- `NewsServiceClient`
-### `backend.apps.combined_service`
+## What Still Exists For Compatibility
-- Removed after split-service mode became the only supported dev startup path.
+These are not legacy wrappers in the old sense, but they still preserve
+backward-compatible behavior while migration settles.
+
+### Gateway-mediated flows
+
+- The WebSocket gateway still carries a mix of:
+ - live runtime feed transport
+ - orchestration
+ - selected read flows that have not been moved to direct browser service calls
+- This is intentional for now because the frontend still depends on the gateway
+ for event streaming and some compatibility reads.
+
+### In-process fallbacks
+
+- Some read paths still support local-module fallback when split-service URLs
+ are not configured.
+- Relevant variables include:
+ - `TRADING_SERVICE_URL`
+ - `NEWS_SERVICE_URL`
+- This keeps the app resilient during migration, but it also means behavior can
+ differ depending on env configuration.
+
+### Dual OpenClaw integration surfaces
+
+- OpenClaw currently appears through two different shapes:
+ - WebSocket gateway integration on `:18789`
+ - optional REST surface at `backend.apps.openclaw_service` on `:8004`
+- These are both valid, but they are not the same surface and should not be
+ documented as interchangeable.
+
+## Remaining Migration Risks
+
+### Split service deployment is not yet the checked-in production default
+
+- The repo documents split-service local development clearly.
+- The checked-in production example still centers on `backend.main` and nginx
+ WebSocket proxying.
+- This is a topology mismatch to keep in mind when changing deploy docs or prod
+ automation.
+
+### Environment-dependent routing
+
+- The frontend and gateway can switch behavior based on configured service URLs.
+- This is helpful operationally, but it makes debugging more configuration-
+ sensitive than a fully fixed service topology.
+
+### Runtime/control-plane separation is logical, not fully operationally isolated
+
+- `runtime_service` owns lifecycle APIs.
+- `agent_service` owns control-plane APIs.
+- The gateway still hosts the live runtime orchestration path, so the split is
+ clean at the API level but not yet a completely independent service mesh.
+
+## Exit Criteria For Declaring Migration Complete
+
+Migration can be considered effectively complete when all of the following are
+true:
+
+1. Production deployment docs and scripts explicitly run the same split-service
+ topology used in development, or intentionally document a different stable
+ production topology.
+2. Critical read paths no longer require ambiguous fallback behavior to local
+ module implementations.
+3. OpenClaw integration is documented as a stable contract with clear guidance
+ on when to use the WebSocket gateway versus the REST surface.
+4. The frontend-service routing model is stable enough that direct-service and
+ gateway-mediated paths are deliberate design choices rather than migration
+ leftovers.
+
+## Practical Read Of The Current State
+
+The migration away from combined-service startup is done.
+
+What remains is not “legacy startup debt”, but:
+
+- topology clarification
+- deployment consistency
+- reduction of env-dependent fallback behavior
+- sharper documentation around gateway and OpenClaw boundaries
diff --git a/env.template b/env.template
index 569945e..ac2e31f 100644
--- a/env.template
+++ b/env.template
@@ -20,6 +20,9 @@ MARKET_DB_PATH= #optional path for long-lived market_research.db | 长期市场
OPENAI_API_KEY=
OPENAI_BASE_URL=
MODEL_NAME=qwen3-max-preview
+OPENCLAW_CMD=
+OPENCLAW_CWD=
+OPENCLAW_TIMEOUT_SECONDS=15
EXPLAIN_ENRICH_USE_LLM=false
EXPLAIN_ENRICH_MODEL_PROVIDER=
EXPLAIN_ENRICH_MODEL_NAME=
diff --git a/frontend/README.md b/frontend/README.md
index fa7fa51..abe9c87 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,31 +1,56 @@
-## QuickStart
+## Frontend Quick Start
+
```bash
cd frontend
npm install
npm run dev
```
-## Optional Direct Service Calls
+Default dev URL: `http://localhost:5173`
-The frontend still works with the compatibility backend entrypoint by default.
-In the current test-stage setup, split services are the recommended default.
-Point the frontend directly at those standalone services:
+The frontend expects the EvoTraders gateway WebSocket on `ws://localhost:8765` unless overridden.
+
+## Recommended Local Backend Stack
+
+Start the split backend services from the project root:
+
+```bash
+./start-dev.sh
+```
+
+That gives you:
+
+- control plane at `http://localhost:8000/api`
+- trading service at `http://localhost:8001`
+- news service at `http://localhost:8002`
+- runtime service at `http://localhost:8003/api/runtime`
+- gateway WebSocket at `ws://localhost:8765`
+
+## Frontend Environment Variables
+
+You can point the frontend directly at those services with:
```bash
VITE_CONTROL_API_BASE_URL=http://localhost:8000/api
VITE_RUNTIME_API_BASE_URL=http://localhost:8003/api/runtime
VITE_NEWS_SERVICE_URL=http://localhost:8002
VITE_TRADING_SERVICE_URL=http://localhost:8001
+VITE_WS_URL=ws://localhost:8765
```
-Current direct-call coverage:
+There is also a starter template at [frontend/env.template](./env.template).
-- runtime panel + gateway port discovery
+## Direct-Service Coverage
+
+Current direct-call coverage includes:
+
+- runtime panel data loading
+- gateway port/runtime discovery
- `story`
- `similar days`
- `range explain`
- `news for date`
- `news categories`
+- selected trading reads such as price history and insider trades
-If these variables are not set, the frontend falls back to the existing
-WebSocket-driven compatibility flow.
+If these variables are not set, the frontend falls back to local defaults and compatibility paths where they still exist.
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index cfa7717..7363493 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -13,6 +13,7 @@ import { useAgentStore } from './store/agentStore';
import { useMarketStore } from './store/marketStore';
import { usePortfolioStore } from './store/portfolioStore';
import { useRuntimeStore } from './store/runtimeStore';
+import { useOpenClawStore } from './store/openclawStore';
import { useUIStore } from './store/uiStore';
const EDITABLE_AGENT_WORKSPACE_FILES = [
@@ -141,6 +142,11 @@ export default function LiveTradingApp() {
addSystemMessage,
});
+ // Make clientRef available to OpenClaw panel via store
+ useEffect(() => {
+ useOpenClawStore.getState().setClientRef(clientRef);
+ }, [clientRef]);
+
const runtimeControls = useRuntimeControls({
clientRef,
currentTickers: tickers,
diff --git a/frontend/src/components/AppShell.jsx b/frontend/src/components/AppShell.jsx
index cd7b399..bae13db 100644
--- a/frontend/src/components/AppShell.jsx
+++ b/frontend/src/components/AppShell.jsx
@@ -14,6 +14,7 @@ const AgentFeed = lazy(() => import('./AgentFeed'));
const StatisticsView = lazy(() => import('./StatisticsView'));
const StockExplainView = lazy(() => import('./StockExplainView.jsx'));
const TraderView = lazy(() => import('./TraderView.jsx'));
+const OpenClawView = lazy(() => import('./OpenClawView.jsx'));
function ViewLoadingFallback({ label = '加载中...' }) {
return (
@@ -171,7 +172,8 @@ export default function AppShell({
const base = `view-slider-five ${currentView === 'traders' ? 'show-traders' :
currentView === 'room' ? 'show-room' :
currentView === 'explain' ? 'show-explain' :
- currentView === 'statistics' ? 'show-statistics' : 'show-chart'}`;
+ currentView === 'chart' ? 'show-chart' :
+ currentView === 'statistics' ? 'show-statistics' : 'show-openclaw'}`;
return base;
}, [currentView]);
@@ -382,6 +384,12 @@ export default function AppShell({
>
统计
+ setCurrentView('openclaw')}
+ >
+ OpenClaw
+
@@ -485,6 +493,13 @@ export default function AppShell({
/>
+
+ {/* OpenClaw View Panel */}
+
+ }>
+
+
+
diff --git a/frontend/src/components/TraderView.jsx b/frontend/src/components/TraderView.jsx
index 7dd403c..a3c2f49 100644
--- a/frontend/src/components/TraderView.jsx
+++ b/frontend/src/components/TraderView.jsx
@@ -127,7 +127,7 @@ export default function TraderView({
padding: '18px',
background: 'linear-gradient(180deg, #ffffff 0%, #f4f7fb 100%)',
display: 'grid',
- gridTemplateRows: 'auto minmax(0, 1fr)',
+ gridTemplateRows: 'auto auto 1fr',
gap: 18
}}>
@@ -138,82 +138,86 @@ export default function TraderView({
聚焦查看每个 Agent 的模型、工具组、技能编排和工作区记忆,不展示交易表现数据
+
-
- {agents.map((agent) => {
- const isSelected = agent.id === selectedAgentId;
- return (
-
onAgentChange(agent.id)}
- title={agent.name}
+ {/* Left: agent avatar list */}
+
+ {agents.map((agent) => {
+ const isSelected = agent.id === selectedAgentId;
+ return (
+
onAgentChange(agent.id)}
+ title={agent.name}
+ style={{
+ border: isSelected ? `2px solid ${agent.colors.accent}` : '1px solid #D9E0E7',
+ borderRadius: 16,
+ background: isSelected ? `${agent.colors.accent}10` : '#FFFFFF',
+ boxShadow: isSelected ? `0 10px 20px ${agent.colors.accent}18` : 'none',
+ padding: 8,
+ display: 'grid',
+ gap: 6,
+ justifyItems: 'center',
+ cursor: 'pointer'
+ }}
+ >
+
-
-
- {agent.name}
-
-
- );
- })}
-
+ />
+
+ {agent.name}
+
+
+ );
+ })}
+
-
+ {/* Right: agent detail content */}
+
-
+
{isSkillPickerOpen && createPortal((
(
*/
export const useUIStore = create((set) => ({
// Current view
- currentView: 'traders', // 'traders' | 'room' | 'explain' | 'chart' | 'statistics' | 'runtime'
+ currentView: 'traders', // 'traders' | 'room' | 'explain' | 'chart' | 'statistics' | 'openclaw' | 'runtime'
setCurrentView: (currentView) => set((state) => ({ currentView: resolveValue(currentView, state.currentView) })),
// Chart tab
diff --git a/frontend/src/styles/GlobalStyles.jsx b/frontend/src/styles/GlobalStyles.jsx
index acdfd06..f560c00 100644
--- a/frontend/src/styles/GlobalStyles.jsx
+++ b/frontend/src/styles/GlobalStyles.jsx
@@ -1098,6 +1098,10 @@ export default function GlobalStyles() {
transform: translateX(-80%);
}
+ .view-slider-five.show-openclaw {
+ transform: translateX(-100%);
+ }
+
.view-panel {
flex: 0 0 33.333%;
width: 33.333%;
diff --git a/frontend/trader-full.png b/frontend/trader-full.png
new file mode 100644
index 0000000..3542a67
Binary files /dev/null and b/frontend/trader-full.png differ
diff --git a/frontend/trader-view.png b/frontend/trader-view.png
new file mode 100644
index 0000000..0e77444
Binary files /dev/null and b/frontend/trader-view.png differ
diff --git a/reference/CoPaw b/reference/CoPaw
deleted file mode 160000
index 934cfce..0000000
--- a/reference/CoPaw
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 934cfce0a7b2981cb6f40bb0cafe7153e365504e
diff --git a/reference/Hyper-Alpha-Arena b/reference/Hyper-Alpha-Arena
deleted file mode 160000
index f137cff..0000000
--- a/reference/Hyper-Alpha-Arena
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f137cff4766d0fafa86ba44d4826d7ca3ea52504
diff --git a/reference/PokieTicker b/reference/PokieTicker
deleted file mode 160000
index 4fed775..0000000
--- a/reference/PokieTicker
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4fed7755e5de106b51788cbd327989d745d725e6
diff --git a/reference/openclaw b/reference/openclaw
index 7b151af..f92c925 160000
--- a/reference/openclaw
+++ b/reference/openclaw
@@ -1 +1 @@
-Subproject commit 7b151afeeb36d48f3edf495a675166e8c6fd1abb
+Subproject commit f92c92515bd439a71bd03eb1bc969c1964f17acf
diff --git a/reference/openclaw-control-center b/reference/openclaw-control-center
new file mode 160000
index 0000000..473f42b
--- /dev/null
+++ b/reference/openclaw-control-center
@@ -0,0 +1 @@
+Subproject commit 473f42bb412f3dc70ac6c6f33f59a8b53fa6cf8b
diff --git a/services/README.md b/services/README.md
index 1893da0..78d778f 100644
--- a/services/README.md
+++ b/services/README.md
@@ -1,73 +1,160 @@
-# EvoTraders Services Architecture
+# EvoTraders Service Surfaces
-This repo is currently in a **migration state** between a modular monolith and
-fully split services. Service boundaries now exist as dedicated FastAPI app
-surfaces, and local development now runs those split services directly.
+This repository is in a split-first state: local development now assumes
+separate app surfaces and a dedicated WebSocket gateway instead of a single
+combined backend entrypoint.
-## Current App Surfaces
+## Service Map
-| App surface | Default port | Responsibility |
+| Surface | Default port | Role |
| --- | --- | --- |
-| `backend.apps.agent_service` | 8000 | Control-plane only: workspaces, agents, guard. |
-| `backend.apps.runtime_service` | 8003 | Runtime lifecycle only: `/api/runtime/*`. |
-| `backend.apps.trading_service` | 8001 | Read-only trading data: prices, financials, insider trades, market status, market cap. |
-| `backend.apps.news_service` | 8002 | Read-only explain/news data: enriched news, categories, story, similar days, range explain. |
+| `backend.apps.agent_service` | `8000` | Control plane for workspaces, agents, skills, guard/approvals |
+| `backend.apps.trading_service` | `8001` | Read-only trading data APIs such as prices, financials, insider trades |
+| `backend.apps.news_service` | `8002` | Read-only explain/news APIs such as story, similar days, range explain |
+| `backend.apps.runtime_service` | `8003` | Runtime lifecycle APIs under `/api/runtime/*` |
+| `backend.apps.openclaw_service` | `8004` | Read-only OpenClaw REST facade |
+| Gateway (`backend.main`) | `8765` | WebSocket feed, runtime event stream, legacy/compat orchestration path |
+| OpenClaw Gateway | `18789` | External OpenClaw WebSocket endpoint consumed by EvoTraders gateway |
-## Local Development Modes
+## What Runs By Default In Dev
-### 1. Split-service mode
-
-This is now the default development mode.
+The supported local dev path is:
```bash
./start-dev.sh
-
-# explicit
-./start-dev.sh split
```
-Run dedicated service surfaces explicitly:
+That script starts:
+
+- `agent_service` on `8000`
+- `trading_service` on `8001`
+- `news_service` on `8002`
+- `runtime_service` on `8003`
+- EvoTraders gateway on `8765`
+
+It does **not** start `openclaw_service` on `8004`.
+
+Instead, the gateway expects an OpenClaw WebSocket server to already be
+available at `ws://localhost:18789` unless you override the OpenClaw gateway
+configuration outside the script.
+
+## Manual Startup
+
+Run split service surfaces explicitly:
```bash
-python -m uvicorn backend.apps.agent_service:app --port 8000 --reload
-python -m uvicorn backend.apps.runtime_service:app --port 8003 --reload
-python -m uvicorn backend.apps.trading_service:app --port 8001 --reload
-python -m uvicorn backend.apps.news_service:app --port 8002 --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
+python -m uvicorn backend.apps.runtime_service:app --host 0.0.0.0 --port 8003 --reload
+python -m backend.main --mode live --host 0.0.0.0 --port 8765
```
-## Migration Variables
+Optional OpenClaw REST surface:
-These env vars control whether the app still uses local-module fallbacks or
-prefers service boundaries:
+```bash
+python -m uvicorn backend.apps.openclaw_service:app --host 0.0.0.0 --port 8004 --reload
+```
-| Variable | Used by | Purpose |
+## Runtime Responsibilities
+
+The runtime path is intentionally split:
+
+- `runtime_service` handles start, stop, restart, current runtime info, logs, and runtime state APIs
+- `agent_service` handles control-plane reads and writes for agents, workspaces, files, and approvals
+- `backend.main` / gateway hosts the live WebSocket channel and coordinates market service, scheduler, and pipeline execution
+
+The practical request path looks like:
+
+`frontend -> runtime_service/control APIs -> gateway/runtime manager -> market service + pipeline + storage`
+
+## Environment Variables
+
+### Backend routing preferences
+
+These variables let the gateway or tools prefer split services over in-process fallbacks:
+
+| Variable | Used by | Meaning |
| --- | --- | --- |
-| `NEWS_SERVICE_URL` | backend Gateway | Prefer `news-service` for explain/news read paths |
-| `TRADING_SERVICE_URL` | backend Gateway | Prefer `trading-service` for trading read paths |
-| `RUNTIME_SERVICE_URL` | reserved | Future runtime/control-plane split follow-up |
-| `VITE_NEWS_SERVICE_URL` | frontend | Direct browser calls to `news-service` for selected explain paths |
-| `VITE_TRADING_SERVICE_URL` | frontend | Reserved for future direct trading reads |
+| `TRADING_SERVICE_URL` | gateway, data tools | Prefer `trading_service` for trading reads |
+| `NEWS_SERVICE_URL` | gateway, data tools | Prefer `news_service` for explain/news reads |
+| `RUNTIME_SERVICE_URL` | dev scripts / future follow-up | Reserved for runtime-service-aware flows |
+| `OPENCLAW_SERVICE_URL` | dev scripts / future follow-up | Points at the OpenClaw gateway origin in current dev setup |
-If these are empty, the repo keeps using local module fallbacks where they still exist.
+Current `start-dev.sh` defaults:
-## Current Internal Direction
+```bash
+TRADING_SERVICE_URL=http://localhost:8001
+NEWS_SERVICE_URL=http://localhost:8002
+RUNTIME_SERVICE_URL=http://localhost:8003
+OPENCLAW_SERVICE_URL=http://localhost:18789
+```
-The repository is now organized around split service surfaces:
+Note that `OPENCLAW_SERVICE_URL` currently points at the OpenClaw gateway origin used by the live WebSocket bridge, not the optional REST app on `:8004`.
+
+### Frontend service targets
+
+The frontend can directly call split services with:
+
+```bash
+VITE_CONTROL_API_BASE_URL=http://localhost:8000/api
+VITE_RUNTIME_API_BASE_URL=http://localhost:8003/api/runtime
+VITE_NEWS_SERVICE_URL=http://localhost:8002
+VITE_TRADING_SERVICE_URL=http://localhost:8001
+VITE_WS_URL=ws://localhost:8765
+```
+
+## Current Frontend Direct-Call Coverage
+
+Direct browser calls currently cover:
+
+- runtime panel loading and runtime discovery
+- story
+- similar days
+- range explain
+- news for date
+- news categories
+- selected trading reads such as stock history and insider trades
+
+Other flows still depend on the gateway WebSocket and control plane APIs.
+
+## OpenClaw Integration Notes
+
+There are two separate OpenClaw integration surfaces in this repo:
+
+- OpenClaw WebSocket gateway on `:18789`
+ - used directly by `backend/services/gateway.py`
+ - this is what `start-dev.sh` assumes exists
+- `backend.apps.openclaw_service` on `:8004`
+ - optional REST facade over OpenClaw CLI-backed reads
+ - useful for typed client access and service-level testing
+
+Do not treat those as interchangeable in docs or deployment config.
+
+## Internal Module Direction
+
+The codebase is now organized around these boundaries:
```text
frontend
- ├─ runtime/control/news/trading split endpoints
- └─ selective per-request fallbacks where still retained
+ ├─ runtime/control/news/trading API clients
+ └─ WebSocket runtime feed
backend.apps.agent_service
└─ control-plane routes
backend.apps.runtime_service
- └─ runtime lifecycle + gateway discovery
+ └─ runtime lifecycle routes
backend.apps.trading_service
└─ read-only trading contract
backend.apps.news_service
└─ read-only explain/news contract
+
+backend.apps.openclaw_service
+ └─ optional OpenClaw REST facade
+
+backend.main / backend.services.gateway
+ └─ live orchestration, feed transport, scheduler, runtime coordination
```
diff --git a/shared/client/__init__.py b/shared/client/__init__.py
index ae4f920..4836fa8 100644
--- a/shared/client/__init__.py
+++ b/shared/client/__init__.py
@@ -2,13 +2,15 @@
"""Shared client package."""
from shared.client.control_client import ControlPlaneClient
-from shared.client.trading_client import TradingServiceClient
from shared.client.news_client import NewsServiceClient
+from shared.client.openclaw_client import OpenClawServiceClient
from shared.client.runtime_client import RuntimeServiceClient
+from shared.client.trading_client import TradingServiceClient
__all__ = [
"ControlPlaneClient",
"RuntimeServiceClient",
"TradingServiceClient",
"NewsServiceClient",
+ "OpenClawServiceClient",
]
diff --git a/start-dev.sh b/start-dev.sh
index 5123e3a..7043d01 100755
--- a/start-dev.sh
+++ b/start-dev.sh
@@ -1,8 +1,8 @@
-#!/bin/bash
+#!/usr/bin/env bash
# EvoTraders Development Startup Script
# Split-service mode only
-set -e
+set -euo pipefail
echo "=========================================="
echo "EvoTraders Development Environment"
@@ -14,24 +14,84 @@ GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
-# Check virtual environment
-if [ -z "$VIRTUAL_ENV" ]; then
- echo -e "${YELLOW}Warning: Virtual environment not activated${NC}"
- echo "Activating .venv..."
- source .venv/bin/activate
-fi
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "${SCRIPT_DIR}"
-# Load environment variables
-if [ -f .env ]; then
- echo -e "${GREEN}Loading environment from .env${NC}"
- export $(grep -v '^#' .env | xargs)
-else
- echo -e "${YELLOW}Warning: .env file not found${NC}"
-fi
-
-cd /Users/cillin/workspeace/evotraders
PIDS=()
+require_command() {
+ local command_name="$1"
+ if ! command -v "${command_name}" >/dev/null 2>&1; then
+ echo -e "${RED}Missing required command: ${command_name}${NC}"
+ exit 1
+ fi
+}
+
+check_python_module() {
+ local module_name="$1"
+ if ! python -c "import ${module_name}" >/dev/null 2>&1; then
+ echo -e "${RED}Missing required Python module: ${module_name}${NC}"
+ echo "Install dependencies with one of:"
+ echo " pip install -r requirements.txt"
+ echo " pip install -r requirements-dev.txt"
+ echo " uv pip install -e '.[dev]'"
+ exit 1
+ fi
+}
+
+load_env_file() {
+ if [ -f .env ]; then
+ echo -e "${GREEN}Loading environment from .env${NC}"
+ set -a
+ source .env
+ set +a
+ else
+ echo -e "${YELLOW}Warning: .env file not found. Copy env.template to .env first if you need live credentials.${NC}"
+ fi
+}
+
+check_env_var() {
+ local var_name="$1"
+ local severity="${2:-warn}"
+ local value="${!var_name:-}"
+ if [ -z "${value}" ]; then
+ if [ "${severity}" = "error" ]; then
+ echo -e "${RED}Missing required environment variable: ${var_name}${NC}"
+ exit 1
+ fi
+ echo -e "${YELLOW}Warning: ${var_name} is not set${NC}"
+ fi
+}
+
+check_openclaw_gateway() {
+ local target_host="127.0.0.1"
+ local target_port="18789"
+ if python - <
/dev/null 2>&1
+import socket
+sock = socket.socket()
+sock.settimeout(1.0)
+sock.connect(("${target_host}", ${target_port}))
+sock.close()
+PY
+ then
+ echo -e "${GREEN}OpenClaw gateway reachable at ws://${target_host}:${target_port}${NC}"
+ else
+ echo -e "${YELLOW}Warning: OpenClaw gateway is not reachable at ws://${target_host}:${target_port}${NC}"
+ echo " OpenClaw panel features may be unavailable until it is started."
+ fi
+}
+
+print_prereq_help() {
+ echo "Environment checks:"
+ echo " - repo root: ${SCRIPT_DIR}"
+ echo " - python: $(command -v python)"
+ if [ -n "${VIRTUAL_ENV:-}" ]; then
+ echo " - virtualenv: ${VIRTUAL_ENV}"
+ else
+ echo " - virtualenv: not activated"
+ fi
+}
+
start_service() {
local name="$1"
local app_path="$2"
@@ -74,16 +134,56 @@ if [ $# -gt 0 ]; then
echo "Split-service mode is now the only supported development mode."
fi
+require_command python
+require_command lsof
+
+if [ -z "${VIRTUAL_ENV:-}" ]; then
+ if [ -f ".venv/bin/activate" ]; then
+ echo -e "${YELLOW}Virtual environment not activated; auto-activating .venv${NC}"
+ # shellcheck disable=SC1091
+ source .venv/bin/activate
+ else
+ echo -e "${YELLOW}Warning: no active virtual environment and .venv not found${NC}"
+ fi
+fi
+
+load_env_file
+
+print_prereq_help
+
+python - <<'PY'
+import sys
+if sys.version_info < (3, 9):
+ raise SystemExit("Python 3.9+ is required")
+print(f"Python version OK: {sys.version.split()[0]}")
+PY
+
+check_python_module fastapi
+check_python_module uvicorn
+check_python_module websockets
+check_python_module yaml
+check_python_module dotenv
+
+check_env_var OPENAI_API_KEY
+check_env_var FINNHUB_API_KEY
+check_env_var FIN_DATA_SOURCE
+
+if ! command -v npm >/dev/null 2>&1; then
+ echo -e "${YELLOW}Warning: npm is not installed. Frontend startup via 'evotraders frontend' will not work.${NC}"
+fi
+
export TRADING_SERVICE_URL="${TRADING_SERVICE_URL:-http://localhost:8001}"
export NEWS_SERVICE_URL="${NEWS_SERVICE_URL:-http://localhost:8002}"
export RUNTIME_SERVICE_URL="${RUNTIME_SERVICE_URL:-http://localhost:8003}"
export OPENCLAW_SERVICE_URL="${OPENCLAW_SERVICE_URL:-http://localhost:18789}"
+check_openclaw_gateway
+
echo ""
echo -e "${GREEN}Starting EvoTraders split services (default mode)...${NC}"
echo " agent_service: http://localhost:8000"
echo " runtime_service: http://localhost:8003"
-echo " openclaw_gateway: ws://localhost:18789"
+echo " openclaw_gateway: ws://localhost:18789"
echo " trading_service: http://localhost:8001"
echo " news_service: http://localhost:8002"
echo ""
diff --git a/test_openclaw_ws.py b/test_openclaw_ws.py
new file mode 100644
index 0000000..a33e638
--- /dev/null
+++ b/test_openclaw_ws.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Quick test script for OpenClaw WebSocket client."""
+
+import asyncio
+import sys
+sys.path.insert(0, '.')
+
+from shared.client.openclaw_websocket_client import (
+ OpenClawWebSocketClient,
+ DEFAULT_GATEWAY_URL,
+)
+
+
+async def test_connection():
+ """Test basic connection to OpenClaw Gateway."""
+ print(f"Connecting to {DEFAULT_GATEWAY_URL}...")
+
+ client = OpenClawWebSocketClient(
+ url=DEFAULT_GATEWAY_URL,
+ client_name="cli",
+ client_version="1.0.0",
+ gateway_token="d4b2d831b8a177b5cac07e781f438af3840bd0dfaca630ee",
+ )
+
+ try:
+ hello = await client.connect()
+ print(f"✓ Connected!")
+ print(f" Protocol version: {hello.protocol}")
+ print(f" Server version: {hello.server_version}")
+ print(f" Connection ID: {hello.conn_id}")
+
+ # List sessions
+ print("\nListing sessions...")
+ sessions = await client.list_sessions(limit=5)
+ print(f" Found {len(sessions)} sessions")
+ for session in sessions[:3]:
+ print(f" - {session.get('key', 'unknown')}")
+
+ # List agents
+ print("\nListing agents...")
+ agents = await client.list_agents()
+ print(f" Found {len(agents)} agents")
+ for agent in agents[:3]:
+ print(f" - {agent.get('id', 'unknown')}: {agent.get('name', 'unknown')}")
+
+ # Send a message to agent
+ if sessions:
+ session_key = sessions[0].get('key')
+ if session_key:
+ print(f"\nSending message to session: {session_key}")
+ result = await client.send_message(session_key, "Hello! This is a test message from Python.")
+ print(f" Message sent: {result}")
+
+ await client.disconnect()
+ print("\n✓ All tests passed!")
+ return True
+
+ except Exception as e:
+ print(f"✗ Error: {e}")
+ import traceback
+ traceback.print_exc()
+ return False
+
+
+if __name__ == "__main__":
+ success = asyncio.run(test_connection())
+ sys.exit(0 if success else 1)