diff --git a/CLAUDE.md b/CLAUDE.md index d6b22fb..c2142fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,108 +1,225 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +本文件为 Claude Code (claude.ai/code) 在此代码库中工作时提供指导。 -## Project Overview +## 项目概述 -EvoTraders is a self-evolving multi-agent trading system where 6 AI agents (4 analysts + portfolio manager + risk manager) collaborate to make trading decisions. Agents use the AgentScope framework with a ReMe memory system for continuous learning. +EvoTraders 是一个自进化多智能体交易系统,由 6 个 AI Agent(4 名分析师 + 投资经理 + 风控经理)协作完成交易决策。Agent 基于 AgentScope 框架构建,配合 ReMe 记忆系统实现持续学习。 -## Development Commands +## 常用命令 ### Backend (Python) ```bash -# Install dependencies +# 安装依赖 uv pip install -e . -# Run commands -evotraders backtest --start 2025-11-01 --end 2025-12-01 # Backtest mode -evotraders backtest --start 2025-11-01 --end 2025-12-01 --enable-memory -evotraders live # Live trading -evotraders live --mock # Mock/testing mode -evotraders live -t 22:30 # Scheduled daily trading -evotraders frontend # Launch visualization UI +# 运行命令 +evotraders backtest --start 2025-11-01 --end 2025-12-01 # 回测模式 +evotraders backtest --start 2025-11-01 --end 2025-12-01 --enable-memory # 带记忆回测 +evotraders live # 实盘交易 +evotraders live --mock # 模拟/测试模式 +evotraders live -t 22:30 # 定时每日交易 +evotraders frontend # 启动可视化界面 -# Dev server (starts FastAPI on port 8000) -./start-dev.sh -# Or manually: -python -m uvicorn backend.app:app --host 0.0.0.0 --port 8000 --reload --reload-dir backend +# 开发服务器 +./start-dev.sh # 启动全部 4 个微服务 -# Testing -pytest backend/tests +# 单独启动某个服务 +python -m uvicorn backend.apps.agent_service:app --host 0.0.0.0 --port 8000 --reload +python -m uvicorn backend.apps.runtime_service:app --host 0.0.0.0 --port 8003 --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 # 运行单个测试 ``` ### Frontend (React) ```bash cd frontend -npm run dev # Vite dev server (http://localhost:5173) -npm run build # Production build -npm run lint # ESLint -npm run test # Vitest -npm run test:watch # Watch mode +npm run dev # Vite 开发服务器 (http://localhost:5173) +npm run build # 生产构建 +npm run lint # ESLint 检查 +npm run lint:fix # ESLint 自动修复 +npm run test # Vitest 单元测试 +npm run test:watch # 监听模式 ``` -## Architecture +## 架构概览 -### Multi-Agent System (`backend/agents/`) +### 微服务架构 (`backend/apps/`) -**6 Agent Roles** (configured in `prompts/analyst/personas.yaml`): -- **fundamentals_analyst** - Financial health, profitability, growth quality -- **technical_analyst** - Price trends, indicators, momentum -- **sentiment_analyst** - Market sentiment, news, insider trading -- **valuation_analyst** - DCF, EV/EBITDA, intrinsic value -- **portfolio_manager** - Decision execution, trade coordination -- **risk_manager** - Real-time risk monitoring, position limits +项目采用 split-first 微服务架构,4 个独立的 FastAPI 服务: -**Key Agent Files**: -- `base/evo_agent.py` - Core agent implementation extending AgentScope -- `base/hooks.py` - Lifecycle hooks for agent execution (BootstrapHook, MemoryCompactionHook, HeartbeatHook, WorkspaceWatchHook) -- `base/evaluation_hook.py` - Post-execution evaluation -- `base/skill_adaptation_hook.py` - Dynamic skill adaptation -- `factory.py` - Agent factory for creating agent instances -- `skills_manager.py` - Skill loading and management (6 scopes: builtin/customized/installed/active/disabled/local) -- `toolkit_factory.py` - Tool collection factory for agents -- `team/` - Team coordination (registry, coordinator, messenger, task_delegator) +| 服务 | 入口 | 端口 | 职责 | +|------|------|------|------| +| 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 | 新闻、新闻富化、解释功能 | -**Hook System** (`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 条工具结果 +服务间通过环境变量通信(详见 `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 +``` -**Adding Custom Analysts**: -1. Register in `backend/agents/prompts/analyst/personas.yaml` -2. Add to `ANALYST_TYPES` dict in `backend/config/constants.py` -3. Optionally update frontend config in `frontend/src/config/constants.js` +### Gateway 网关 (`backend/services/gateway.py`) -### Backend Structure +Gateway 是统一的请求路由器,根据路径前缀将请求转发到对应的微服务: +- `/control/*` → agent_service +- `/runtime/*` → runtime_service +- `/trading/*` → trading_service +- `/news/*` → news_service + +新增接口时应注册到对应的 service app,而非直接添加到 gateway。 + +### 共享客户端 (`shared/client/`) + +统一的服务客户端库,所有前端和后端服务间通信都使用此处定义的客户端: + +| 客户端 | 用途 | +|--------|------| +| `ControlPlaneClient` | Agent 服务通信 | +| `RuntimeServiceClient` | 运行时服务通信 | +| `TradingServiceClient` | 交易服务通信 | +| `NewsServiceClient` | 新闻服务通信 | + +### 领域层 (`backend/domains/`) + +业务逻辑按领域分离: + +- `news.py` - 新闻领域操作 +- `trading.py` - 交易领域操作 + +## 后端结构 ``` backend/ -├── agents/ # Multi-agent implementation -│ ├── base/ # Base classes, hooks, evaluation -│ ├── prompts/ # Agent prompts and personas -│ └── team/ # Team coordination logic -├── api/ # FastAPI endpoints -├── config/ # Constants and configuration -├── core/ # Pipeline execution logic -├── data/ # Market data handling -├── enrich/ # LLM response enrichment -├── explain/ # Decision explanation -├── llm/ # LLM integrations (with RetryChatModel, TokenRecordingModelWrapper) -├── services/ # Gateway, WebSocket services -├── skills/ # Skill definitions (builtin + custom) -└── tools/ # Trading and analysis tools +├── 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/ # 领域业务逻辑 +│ ├── 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 Model Wrappers (`backend/llm/models.py`) +## 前端结构 + +``` +frontend/src/ +├── App.jsx # React 主应用 +├── components/ # React 组件 +│ ├── 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/ # 解释相关组件 +│ ├── 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 +``` + +## Agent 系统 + +### 6 种 Agent 角色 + +| 角色 ID | 名称 | 职责 | +|---------|------|------| +| `fundamentals_analyst` | 基本面分析师 | 财务健康、盈利能力、成长质量 | +| `technical_analyst` | 技术分析师 | 价格趋势、技术指标、动量分析 | +| `sentiment_analyst` | 情绪分析师 | 市场情绪、新闻情绪、内幕交易 | +| `valuation_analyst` | 估值分析师 | DCF、EV/EBITDA、 intrinsic value | +| `portfolio_manager` | 投资经理 | 决策执行、交易协调 | +| `risk_manager` | 风控经理 | 实时价格/波动率监控、仓位限制、多层风险预警 | + +### Hook 系统 (`base/hooks.py`) + +- **MemoryCompactionHook**: 基于 CoPaw 的内存压缩 + - `memory_compact_ratio`: 压缩目标比例(默认 0.75) + - `memory_reserve_ratio`: 保留比例(默认 0.1) + - `enable_tool_result_compact`: 工具结果压缩 + - `tool_result_compact_keep_n`: 保留最近 N 条工具结果 + +### 添加自定义分析师 + +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 的模型封装设计: -Based on CoPaw's model wrapper design: - **RetryChatModel**: 自动重试瞬态 LLM 错误(rate limit、timeout、502/503 等),指数退避 - - `max_retries`: 最大重试次数 (默认 3) - - `initial_delay`: 初始延迟秒数 (默认 1.0) - - `backoff_multiplier`: 退避倍数 (默认 2.0) + - `max_retries`: 最大重试次数(默认 3) + - `initial_delay`: 初始延迟秒数(默认 1.0) + - `backoff_multiplier`: 退避倍数(默认 2.0) + - **TokenRecordingModelWrapper**: 追踪每个 provider 的 token 消耗和成本 ```python @@ -111,60 +228,75 @@ from backend.llm.models import create_model, RetryChatModel model = RetryChatModel(create_model("gpt-4o", "OPENAI"), max_retries=3) ``` -### Frontend Structure +## 技能系统 (`backend/skills/`) -``` -frontend/src/ -├── App.jsx # Main React application -├── components/ # React components -│ ├── RuntimeView.jsx # Trading runtime UI -│ ├── TraderView.jsx # Trader interface -│ └── RuntimeSettingsPanel.jsx -├── services/ # API and WebSocket services -│ ├── runtimeApi.js # Backend API calls -│ └── websocket.js # Real-time communication -└── config/ - └── constants.js # Agent definitions, configuration -``` +技能定义在 `SKILL.md` 文件中,包含: +- `instructions` - 技能说明 +- `triggers` - 触发条件 +- `parameters` - 输入/输出 schema +- `available_tools` - 技能可使用的工具 -### Skill System (`backend/skills/`) +技能由 `skills_manager.py` 加载,通过 `skill_adaptation_hook.py` 绑定到 Agent。 -Skills are defined in `SKILL.md` files with: -- `instructions` - What the skill does -- `triggers` - When to invoke -- `parameters` - Input/output schema -- `available_tools` - Tools the skill can use +技能管理器支持 6 种作用域:builtin、customized、installed、active、disabled、local。 -Skills are loaded by `skills_manager.py` and attached to agents via `skill_adaptation_hook.py`. +## Pipeline 执行 (`backend/core/`) -### Pipeline Execution (`backend/core/`) +每日交易流程: -The daily trading flow: -1. **Analysis Stage** - Each agent analyzes independently -2. **Communication Stage** - Agent-to-agent messaging (1v1, 1vN, NvN) -3. **Decision Stage** - Portfolio manager makes final trades -4. **Evaluation Stage** - Performance tracking -5. **Review Stage** - Memory updates via ReMe +1. **分析阶段** - 各 Agent 基于工具和历史经验独立分析 +2. **沟通阶段** - 通过私聊、通知、会议等方式交换观点(1v1/1vN/NvN) +3. **决策阶段** - 投资经理综合判断,给出最终交易 +4. **评估阶段** - 绩效跟踪 +5. **复盘阶段** - Agent 根据当日实际收益反思总结,通过 ReMe 记忆框架更新经验 -## Environment Configuration +## 前端状态管理 + +项目正在向 Zustand 状态管理过渡,已创建的 store: -Required in `.env`: ```bash +frontend/src/store/ +├── index.js # 导出所有 store +├── runtimeStore.js # 连接状态、运行时配置 +├── marketStore.js # 市场数据、股票价格 +├── portfolioStore.js # 组合、持仓、交易 +├── agentStore.js # Agent 技能、工作区 +└── uiStore.js # UI 状态、视图切换 +``` + +**迁移状态**: +- Stores 已创建但尚未在 App.jsx 中使用 +- 计划:逐步迁移 60+ 个 useState 到对应 store + +## 环境配置 + +`.env` 必需配置: + +```bash +# 金融数据源 FIN_DATA_SOURCE=finnhub|financial_datasets -FINANCIAL_DATASETS_API_KEY= # Required for backtest -FINNHUB_API_KEY= # Required for live trading -OPENAI_API_KEY= # Agent LLM +FINANCIAL_DATASETS_API_KEY= # 回测必需 +FINNHUB_API_KEY= # 实盘必需 + +# Agent LLM +OPENAI_API_KEY= OPENAI_BASE_URL= MODEL_NAME=qwen3-max-preview -MEMORY_API_KEY= # For ReMe memory system + +# 可为不同 Agent 指定不同模型 +AGENT_SENTIMENT_ANALYST_MODEL_NAME=qwen3-max-preview +AGENT_FUNDAMENTALS_ANALYST_MODEL_NAME=deepseek-chat + +# ReMe 记忆系统 +MEMORY_API_KEY= ``` -## Key Dependencies +## 关键依赖 -- **AgentScope** - Multi-agent framework -- **ReMe** - Memory system for continuous learning -- **FastAPI** + **uvicorn** - Backend API server -- **websockets** - Real-time communication -- **React 19** + **Vite** + **TailwindCSS** - Frontend -- **Zustand** - Frontend state management -- **Three.js** / **React-Three-Fiber** - 3D visualizations +- **AgentScope** - 多智能体框架 +- **ReMe** - 持续学习记忆系统 +- **FastAPI** + **uvicorn** - 后端 API 服务器 +- **websockets** - 实时通信 +- **React 19** + **Vite** + **TailwindCSS** - 前端 +- **React Context** - 前端状态管理(App.jsx 中使用 useState + useCallback) +- **Three.js** / **React-Three-Fiber** - 3D 可视化 diff --git a/frontend/src/store/agentStore.js b/frontend/src/store/agentStore.js new file mode 100644 index 0000000..6f2d3ac --- /dev/null +++ b/frontend/src/store/agentStore.js @@ -0,0 +1,58 @@ +import { create } from 'zustand'; + +/** + * Agent Store - Agent skills, profiles, workspaces + */ +export const useAgentStore = create((set) => ({ + // Selected agent for skill/workspace editing + selectedSkillAgentId: null, + setSelectedSkillAgentId: (selectedSkillAgentId) => set({ selectedSkillAgentId }), + + // Agent profiles + agentProfilesByAgent: {}, + setAgentProfilesByAgent: (agentProfilesByAgent) => set({ agentProfilesByAgent }), + + // Agent skills + agentSkillsByAgent: {}, + setAgentSkillsByAgent: (agentSkillsByAgent) => set({ agentSkillsByAgent }), + + // Skill details + skillDetailsByName: {}, + setSkillDetailsByName: (skillDetailsByName) => set({ skillDetailsByName }), + + // Local skill drafts + localSkillDraftsByKey: {}, + setLocalSkillDraftsByKey: (localSkillDraftsByKey) => set({ localSkillDraftsByKey }), + + // Loading states + isAgentSkillsLoading: false, + setIsAgentSkillsLoading: (isAgentSkillsLoading) => set({ isAgentSkillsLoading }), + + skillDetailLoadingKey: null, + setSkillDetailLoadingKey: (skillDetailLoadingKey) => set({ skillDetailLoadingKey }), + + agentSkillsSavingKey: null, + setAgentSkillsSavingKey: (agentSkillsSavingKey) => set({ agentSkillsSavingKey }), + + agentSkillsFeedback: null, + setAgentSkillsFeedback: (agentSkillsFeedback) => set({ agentSkillsFeedback }), + + // Workspace files + selectedWorkspaceFile: null, + setSelectedWorkspaceFile: (selectedWorkspaceFile) => set({ selectedWorkspaceFile }), + + workspaceFilesByAgent: {}, + setWorkspaceFilesByAgent: (workspaceFilesByAgent) => set({ workspaceFilesByAgent }), + + workspaceDraftContent: '', + setWorkspaceDraftContent: (workspaceDraftContent) => set({ workspaceDraftContent }), + + isWorkspaceFileLoading: false, + setIsWorkspaceFileLoading: (isWorkspaceFileLoading) => set({ isWorkspaceFileLoading }), + + workspaceFileSavingKey: null, + setWorkspaceFileSavingKey: (workspaceFileSavingKey) => set({ workspaceFileSavingKey }), + + workspaceFileFeedback: null, + setWorkspaceFileFeedback: (workspaceFileFeedback) => set({ workspaceFileFeedback }), +})); diff --git a/frontend/src/store/index.js b/frontend/src/store/index.js new file mode 100644 index 0000000..c6e2951 --- /dev/null +++ b/frontend/src/store/index.js @@ -0,0 +1,5 @@ +export { useRuntimeStore } from './runtimeStore'; +export { useMarketStore } from './marketStore'; +export { usePortfolioStore } from './portfolioStore'; +export { useAgentStore } from './agentStore'; +export { useUIStore } from './uiStore'; diff --git a/frontend/src/store/marketStore.js b/frontend/src/store/marketStore.js new file mode 100644 index 0000000..250e9c3 --- /dev/null +++ b/frontend/src/store/marketStore.js @@ -0,0 +1,44 @@ +import { create } from 'zustand'; + +/** + * Market Store - Market data, stock prices, news + */ +export const useMarketStore = create((set) => ({ + // Ticker prices + tickers: [], + setTickers: (tickers) => set({ tickers }), + rollingTickers: {}, + setRollingTickers: (rollingTickers) => set({ rollingTickers }), + + // Price history + priceHistoryByTicker: {}, + setPriceHistoryByTicker: (priceHistoryByTicker) => set({ priceHistoryByTicker }), + + // OHLC history + ohlcHistoryByTicker: {}, + setOhlcHistoryByTicker: (ohlcHistoryByTicker) => set({ ohlcHistoryByTicker }), + + // History source tracking + historySourceByTicker: {}, + setHistorySourceByTicker: (historySourceByTicker) => set({ historySourceByTicker }), + + // Explain events + explainEventsByTicker: {}, + setExplainEventsByTicker: (explainEventsByTicker) => set({ explainEventsByTicker }), + + // Selected explain symbol + selectedExplainSymbol: '', + setSelectedExplainSymbol: (selectedExplainSymbol) => set({ selectedExplainSymbol }), + + // News by ticker + newsByTicker: {}, + setNewsByTicker: (newsByTicker) => set({ newsByTicker }), + + // Insider trades + insiderTradesByTicker: {}, + setInsiderTradesByTicker: (insiderTradesByTicker) => set({ insiderTradesByTicker }), + + // Technical indicators + technicalIndicatorsByTicker: {}, + setTechnicalIndicatorsByTicker: (technicalIndicatorsByTicker) => set({ technicalIndicatorsByTicker }), +})); diff --git a/frontend/src/store/portfolioStore.js b/frontend/src/store/portfolioStore.js new file mode 100644 index 0000000..c185dc4 --- /dev/null +++ b/frontend/src/store/portfolioStore.js @@ -0,0 +1,38 @@ +import { create } from 'zustand'; + +/** + * Portfolio Store - Portfolio data, holdings, trades, statistics + */ +export const usePortfolioStore = create((set) => ({ + // Portfolio data + portfolioData: { + netValue: 10000, + pnl: 0, + equity: [], + baseline: [], + baseline_vw: [], + momentum: [], + strategies: [], + equity_return: 0, + baseline_return: 0, + baseline_vw_return: 0, + momentum_return: 0, + }, + setPortfolioData: (portfolioData) => set({ portfolioData }), + + // Holdings + holdings: [], + setHoldings: (holdings) => set({ holdings }), + + // Trades + trades: [], + setTrades: (trades) => set({ trades }), + + // Statistics + stats: null, + setStats: (stats) => set({ stats }), + + // Leaderboard + leaderboard: [], + setLeaderboard: (leaderboard) => set({ leaderboard }), +})); diff --git a/frontend/src/store/runtimeStore.js b/frontend/src/store/runtimeStore.js new file mode 100644 index 0000000..5f864e8 --- /dev/null +++ b/frontend/src/store/runtimeStore.js @@ -0,0 +1,90 @@ +import { create } from 'zustand'; + +/** + * Runtime Store - Connection state and runtime configuration + */ +export const useRuntimeStore = create((set) => ({ + // Connection state + isConnected: false, + connectionStatus: 'connecting', // 'connecting' | 'connected' | 'disconnected' + setIsConnected: (isConnected) => set({ isConnected }), + setConnectionStatus: (connectionStatus) => set({ connectionStatus }), + + // System state + systemStatus: 'initializing', // 'initializing' | 'running' | 'completed' + currentDate: null, + setSystemStatus: (systemStatus) => set({ systemStatus }), + setCurrentDate: (currentDate) => set({ currentDate }), + + // Progress + progress: { current: 0, total: 0 }, + setProgress: (progress) => set({ progress }), + + // Server mode + serverMode: null, // 'live' | 'backtest' | null + setServerMode: (serverMode) => set({ serverMode }), + + // Market status + marketStatus: null, + virtualTime: null, + setMarketStatus: (marketStatus) => set({ marketStatus }), + setVirtualTime: (virtualTime) => set({ virtualTime }), + + // Data sources + dataSources: null, + setDataSources: (dataSources) => set({ dataSources }), + + // Runtime config + runtimeConfig: null, + setRuntimeConfig: (runtimeConfig) => set({ runtimeConfig }), + + // Watchlist panel + isWatchlistPanelOpen: false, + setIsWatchlistPanelOpen: (isWatchlistPanelOpen) => set({ isWatchlistPanelOpen }), + + // Watchlist draft + watchlistDraftSymbols: [], + watchlistInputValue: '', + watchlistFeedback: null, + isWatchlistSaving: false, + setWatchlistDraftSymbols: (watchlistDraftSymbols) => set({ watchlistDraftSymbols }), + setWatchlistInputValue: (watchlistInputValue) => set({ watchlistInputValue }), + setWatchlistFeedback: (watchlistFeedback) => set({ watchlistFeedback }), + setIsWatchlistSaving: (isWatchlistSaving) => set({ isWatchlistSaving }), + + // Runtime settings panel + isRuntimeSettingsOpen: false, + setIsRuntimeSettingsOpen: (isRuntimeSettingsOpen) => set({ isRuntimeSettingsOpen }), + + // Runtime config drafts + scheduleModeDraft: 'daily', + intervalMinutesDraft: '60', + triggerTimeDraft: '09:30', + maxCommCyclesDraft: '2', + initialCashDraft: '100000', + marginRequirementDraft: '0', + enableMemoryDraft: false, + modeDraft: 'live', + pollIntervalDraft: '10', + startDateDraft: '', + endDateDraft: '', + enableMockDraft: false, + setScheduleModeDraft: (scheduleModeDraft) => set({ scheduleModeDraft }), + setIntervalMinutesDraft: (intervalMinutesDraft) => set({ intervalMinutesDraft }), + setTriggerTimeDraft: (triggerTimeDraft) => set({ triggerTimeDraft }), + setMaxCommCyclesDraft: (maxCommCyclesDraft) => set({ maxCommCyclesDraft }), + setInitialCashDraft: (initialCashDraft) => set({ initialCashDraft }), + setMarginRequirementDraft: (marginRequirementDraft) => set({ marginRequirementDraft }), + setEnableMemoryDraft: (enableMemoryDraft) => set({ enableMemoryDraft }), + setModeDraft: (modeDraft) => set({ modeDraft }), + setPollIntervalDraft: (pollIntervalDraft) => set({ pollIntervalDraft }), + setStartDateDraft: (startDateDraft) => set({ startDateDraft }), + setEndDateDraft: (endDateDraft) => set({ endDateDraft }), + setEnableMockDraft: (enableMockDraft) => set({ enableMockDraft }), + + // Runtime config feedback + runtimeConfigFeedback: null, + isRuntimeConfigSaving: false, + setRuntimeConfigFeedback: (runtimeConfigFeedback) => set({ runtimeConfigFeedback }), + setIsRuntimeConfigSaving: (isRuntimeConfigSaving) => set({ isRuntimeConfigSaving }), +})); diff --git a/frontend/src/store/uiStore.js b/frontend/src/store/uiStore.js new file mode 100644 index 0000000..daf7f75 --- /dev/null +++ b/frontend/src/store/uiStore.js @@ -0,0 +1,40 @@ +import { create } from 'zustand'; + +/** + * UI Store - UI state, view management, layout + */ +export const useUIStore = create((set) => ({ + // Current view + currentView: 'traders', // 'traders' | 'room' | 'explain' | 'chart' | 'statistics' | 'runtime' + setCurrentView: (currentView) => set({ currentView }), + + // Chart tab + chartTab: 'all', + setChartTab: (chartTab) => set({ chartTab }), + + // Initial animation + isInitialAnimating: true, + setIsInitialAnimating: (isInitialAnimating) => set({ isInitialAnimating }), + + // Last update timestamp + lastUpdate: new Date(), + setLastUpdate: (lastUpdate) => set({ lastUpdate }), + + // Is updating + isUpdating: false, + setIsUpdating: (isUpdating) => set({ isUpdating }), + + // Room bubbles + bubbles: {}, + setBubbles: (bubbles) => set({ bubbles }), + + // Resizable panels + leftWidth: 70, + setLeftWidth: (leftWidth) => set({ leftWidth }), + isResizing: false, + setIsResizing: (isResizing) => set({ isResizing }), + + // Now timestamp (for current time display) + now: new Date(), + setNow: (now) => set({ now }), +}));