feat(strategy_manager): 添加策略自动启动的白名单管理
实现全面的白名单管理系统,支持策略自动启动: - 添加 WhitelistManager 类用于持久化白名单配置存储 - 将白名单功能集成到 StrategyManager 核心模块 - 添加用于白名单 CRUD 操作的 REST API 端点 - 通过 start.py 创建用于白名单管理的 CLI 命令 - 更新前端 UI,添加白名单状态指示器和批量操作功能 - 实现每日 08:58 的自动启动调度 - 为白名单操作添加配置验证和日志记录 同时添加项目文档: - 代码格式规则(缩进、行长度、导入、命名) - 文件、变量、常量、函数、类的命名规范 - 受限制文件和目录的保护规则
This commit is contained in:
493
plans/strategy_manager_optimization_plan.md
Normal file
493
plans/strategy_manager_optimization_plan.md
Normal file
@@ -0,0 +1,493 @@
|
||||
# 策略管理优化方案
|
||||
|
||||
## 1. 需求概述
|
||||
|
||||
### 1.1 问题背景
|
||||
- 策略启动后如果出现异常会停止进程
|
||||
- 需要机制确保重要策略能够自动重启
|
||||
|
||||
### 1.2 优化目标
|
||||
1. **白名单机制**: 本地文件保存希望自动启动的策略列表
|
||||
2. **Web管理**: 界面可以管理白名单策略
|
||||
3. **定时启动**: 每个工作日 8:58 自动尝试启动白名单中未运行的策略
|
||||
4. **单日单次**: 每天只尝试一次,避免重复尝试
|
||||
5. **手动控制**: 手动停止后不自动重启,需要用户重新操作
|
||||
6. **双状态展示**: 每个策略显示"是否在白名单"和"是否运行中"
|
||||
|
||||
---
|
||||
|
||||
## 2. 系统架构
|
||||
|
||||
### 2.1 新增/修改文件
|
||||
|
||||
| 文件 | 操作 | 说明 |
|
||||
|------|------|------|
|
||||
| `config/whitelist.json` | 新增 | 白名单配置文件 |
|
||||
| `core/whitelist_manager.py` | 新增 | 白名单管理器 |
|
||||
| `core/manager.py` | 修改 | 集成白名单功能 |
|
||||
| `web_backend.py` | 修改 | 新增白名单管理 API |
|
||||
| `frontend/` | 修改 | 前端界面添加白名单管理 |
|
||||
|
||||
### 2.2 核心数据结构
|
||||
|
||||
#### 白名单配置 (`config/whitelist.json`)
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"last_auto_start_date": "2024-01-25",
|
||||
"strategies": {
|
||||
"DualModeTrendlineHawkesStrategy2_FG": {
|
||||
"enabled": true,
|
||||
"added_at": "2024-01-25T10:00:00",
|
||||
"added_by": "web"
|
||||
},
|
||||
"SpectralTrendStrategy_rb": {
|
||||
"enabled": true,
|
||||
"added_at": "2024-01-25T10:00:00",
|
||||
"added_by": "web"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 策略状态结构扩展
|
||||
```python
|
||||
# 在 StrategyManager.strategies 字典中新增字段
|
||||
{
|
||||
"strategy_key": {
|
||||
# ... 现有字段 ...
|
||||
"in_whitelist": True, # 是否在白名单中
|
||||
"whitelist_enabled": True, # 白名单中是否启用
|
||||
"last_auto_start_attempt": "2024-01-25T08:58:00", # 上次自动启动尝试
|
||||
"auto_start_success": False # 上次自动启动是否成功
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 详细设计
|
||||
|
||||
### 3.1 白名单管理器 (`core/whitelist_manager.py`)
|
||||
|
||||
```python
|
||||
class WhitelistManager:
|
||||
"""白名单管理器"""
|
||||
|
||||
def __init__(self, config_path: str = "config/whitelist.json"):
|
||||
self.config_path = Path(config_path)
|
||||
self.data = self._load()
|
||||
|
||||
def _load(self) -> Dict:
|
||||
"""加载白名单配置"""
|
||||
if not self.config_path.exists():
|
||||
return {"version": "1.0", "strategies": {}}
|
||||
with open(self.config_path, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
def _save(self):
|
||||
"""保存白名单配置"""
|
||||
self.config_path.parent.mkdir(exist_ok=True)
|
||||
with open(self.config_path, 'w') as f:
|
||||
json.dump(self.data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
def add(self, strategy_key: str, enabled: bool = True) -> bool:
|
||||
"""添加策略到白名单"""
|
||||
if strategy_key in self.data["strategies"]:
|
||||
return False # 已存在
|
||||
self.data["strategies"][strategy_key] = {
|
||||
"enabled": enabled,
|
||||
"added_at": datetime.now().isoformat(),
|
||||
"added_by": "web"
|
||||
}
|
||||
self._save()
|
||||
return True
|
||||
|
||||
def remove(self, strategy_key: str) -> bool:
|
||||
"""从白名单移除策略"""
|
||||
if strategy_key not in self.data["strategies"]:
|
||||
return False
|
||||
del self.data["strategies"][strategy_key]
|
||||
self._save()
|
||||
return True
|
||||
|
||||
def set_enabled(self, strategy_key: str, enabled: bool) -> bool:
|
||||
"""设置策略在白名单中的启用状态"""
|
||||
if strategy_key not in self.data["strategies"]:
|
||||
return False
|
||||
self.data["strategies"][strategy_key]["enabled"] = enabled
|
||||
self._save()
|
||||
return True
|
||||
|
||||
def get_all(self) -> Dict[str, Dict]:
|
||||
"""获取所有白名单策略"""
|
||||
return self.data.get("strategies", {})
|
||||
|
||||
def is_in_whitelist(self, strategy_key: str) -> bool:
|
||||
"""检查策略是否在白名单中"""
|
||||
return strategy_key in self.data.get("strategies", {})
|
||||
|
||||
def is_enabled_in_whitelist(self, strategy_key: str) -> bool:
|
||||
"""检查策略是否在白名单中且已启用"""
|
||||
if strategy_key not in self.data.get("strategies", {}):
|
||||
return False
|
||||
return self.data["strategies"][strategy_key].get("enabled", False)
|
||||
|
||||
def update_last_auto_start_date(self, date_str: str):
|
||||
"""更新最后自动启动日期"""
|
||||
self.data["last_auto_start_date"] = date_str
|
||||
self._save()
|
||||
|
||||
def should_auto_start_today(self) -> bool:
|
||||
"""检查今天是否应该自动启动"""
|
||||
today = datetime.now().date().isoformat()
|
||||
return self.data.get("last_auto_start_date") != today
|
||||
```
|
||||
|
||||
### 3.2 修改 `core/manager.py`
|
||||
|
||||
#### 新增功能
|
||||
```python
|
||||
class StrategyManager:
|
||||
def __init__(self, config_path: str = "config/main.json"):
|
||||
# ... 现有代码 ...
|
||||
self.whitelist_manager = WhitelistManager()
|
||||
|
||||
def get_status(self) -> Dict[str, Any]:
|
||||
"""获取完整状态(扩展白名单信息)"""
|
||||
status = super().get_status()
|
||||
|
||||
# 添加白名单信息
|
||||
for name, info in status["strategies"].items():
|
||||
info["in_whitelist"] = self.whitelist_manager.is_in_whitelist(name)
|
||||
info["whitelist_enabled"] = self.whitelist_manager.is_enabled_in_whitelist(name)
|
||||
|
||||
status["whitelist_auto_start_today"] = self.whitelist_manager.should_auto_start_today()
|
||||
|
||||
return status
|
||||
|
||||
def add_to_whitelist(self, name: str) -> bool:
|
||||
"""添加策略到白名单"""
|
||||
return self.whitelist_manager.add(name, enabled=True)
|
||||
|
||||
def remove_from_whitelist(self, name: str) -> bool:
|
||||
"""从白名单移除策略"""
|
||||
return self.whitelist_manager.remove(name)
|
||||
|
||||
def set_whitelist_enabled(self, name: str, enabled: bool) -> bool:
|
||||
"""设置白名单中策略的启用状态"""
|
||||
return self.whitelist_manager.set_enabled(name, enabled)
|
||||
|
||||
def start_strategy(self, name: str, auto_start: bool = False) -> bool:
|
||||
"""
|
||||
启动策略
|
||||
|
||||
Args:
|
||||
name: 策略标识符
|
||||
auto_start: 是否为自动启动(自动启动不会更新最后尝试日期)
|
||||
"""
|
||||
# ... 现有启动逻辑 ...
|
||||
|
||||
# 如果是手动启动,清除自动启动相关的标记
|
||||
if not auto_start:
|
||||
if name in self.strategies:
|
||||
self.strategies[name]["last_auto_start_attempt"] = None
|
||||
self.strategies[name]["auto_start_success"] = None
|
||||
|
||||
return success
|
||||
|
||||
def auto_start_whitelist_strategies(self) -> Dict[str, bool]:
|
||||
"""
|
||||
自动启动白名单中所有未运行的策略
|
||||
一天只执行一次
|
||||
|
||||
Returns:
|
||||
Dict[str, bool]: 每个策略的启动结果
|
||||
"""
|
||||
if not self.whitelist_manager.should_auto_start_today():
|
||||
return {}
|
||||
|
||||
results = {}
|
||||
whitelist = self.whitelist_manager.get_all()
|
||||
|
||||
for name, config in whitelist.items():
|
||||
if not config.get("enabled", True):
|
||||
continue
|
||||
|
||||
if name not in self.strategies:
|
||||
continue
|
||||
|
||||
# 检查是否已在运行
|
||||
if self._is_running(name):
|
||||
results[name] = True
|
||||
continue
|
||||
|
||||
# 尝试启动
|
||||
success = self.start_strategy(name, auto_start=True)
|
||||
results[name] = success
|
||||
|
||||
# 更新策略状态
|
||||
if name in self.strategies:
|
||||
self.strategies[name]["last_auto_start_attempt"] = datetime.now().isoformat()
|
||||
self.strategies[name]["auto_start_success"] = success
|
||||
|
||||
# 更新日期
|
||||
self.whitelist_manager.update_last_auto_start_date(
|
||||
datetime.now().date().isoformat()
|
||||
)
|
||||
|
||||
return results
|
||||
```
|
||||
|
||||
### 3.3 修改 `web_backend.py`
|
||||
|
||||
#### 新增 API 端点
|
||||
```python
|
||||
# ============ 白名单管理 API ============
|
||||
|
||||
@app.get("/api/whitelist")
|
||||
def get_whitelist():
|
||||
"""获取白名单列表"""
|
||||
whitelist = manager.whitelist_manager.get_all()
|
||||
return {
|
||||
"whitelist": whitelist,
|
||||
"auto_start_today": manager.whitelist_manager.should_auto_start_today()
|
||||
}
|
||||
|
||||
@app.post("/api/whitelist/{name}/add")
|
||||
def add_to_whitelist(name: str):
|
||||
"""添加策略到白名单"""
|
||||
if manager.add_to_whitelist(name):
|
||||
return {"success": True}
|
||||
raise HTTPException(400, "添加失败,策略可能已存在")
|
||||
|
||||
@app.post("/api/whitelist/{name}/remove")
|
||||
def remove_from_whitelist(name: str):
|
||||
"""从白名单移除策略"""
|
||||
if manager.remove_from_whitelist(name):
|
||||
return {"success": True}
|
||||
raise HTTPException(400, "移除失败,策略可能不在白名单中")
|
||||
|
||||
@app.post("/api/whitelist/{name}/enable")
|
||||
def enable_in_whitelist(name: str):
|
||||
"""启用白名单中的策略"""
|
||||
if manager.set_whitelist_enabled(name, True):
|
||||
return {"success": True}
|
||||
raise HTTPException(400, "操作失败")
|
||||
|
||||
@app.post("/api/whitelist/{name}/disable")
|
||||
def disable_in_whitelist(name: str):
|
||||
"""禁用白名单中的策略"""
|
||||
if manager.set_whitelist_enabled(name, False):
|
||||
return {"success": True}
|
||||
raise HTTPException(400, "操作失败")
|
||||
|
||||
@app.post("/api/whitelist/auto-start")
|
||||
def trigger_auto_start():
|
||||
"""手动触发白名单自动启动(用于测试)"""
|
||||
results = manager.auto_start_whitelist_strategies()
|
||||
return {
|
||||
"success": True,
|
||||
"results": results,
|
||||
"count": len(results)
|
||||
}
|
||||
|
||||
# ============ 修改现有的状态 API ============
|
||||
|
||||
@app.get("/api/status")
|
||||
def get_status():
|
||||
"""获取策略状态(包含白名单信息)"""
|
||||
status_data = manager.get_status()
|
||||
status_data['git_info'] = get_git_commit_info()
|
||||
return status_data
|
||||
```
|
||||
|
||||
#### 修改定时任务
|
||||
```python
|
||||
@app.on_event("startup")
|
||||
async def start_scheduler():
|
||||
# ... 现有的 08:58, 20:58 重启任务 ...
|
||||
|
||||
# 新增:白名单自动启动任务(仅 08:58)
|
||||
scheduler.add_job(
|
||||
auto_start_whitelist_task,
|
||||
CronTrigger(hour=8, minute=58),
|
||||
id="whitelist_auto_start",
|
||||
replace_existing=True
|
||||
)
|
||||
logger.info("📅 白名单自动启动任务已添加 (计划时间: 08:58)")
|
||||
|
||||
|
||||
def auto_start_whitelist_task():
|
||||
"""
|
||||
白名单自动启动任务
|
||||
"""
|
||||
logger.info("⏰ [白名单任务] 触发自动启动...")
|
||||
|
||||
results = manager.auto_start_whitelist_strategies()
|
||||
|
||||
if not results:
|
||||
logger.info("⏰ [白名单任务] 今天已执行过或无需启动")
|
||||
return
|
||||
|
||||
success_count = sum(1 for v in results.values() if v)
|
||||
fail_count = len(results) - success_count
|
||||
|
||||
logger.info(f"⏰ [白名单任务] 完成: 成功 {success_count}, 失败 {fail_count}")
|
||||
|
||||
for name, success in results.items():
|
||||
if success:
|
||||
logger.info(f"✅ [白名单任务] {name} 启动成功")
|
||||
else:
|
||||
logger.warning(f"❌ [白名单任务] {name} 启动失败")
|
||||
```
|
||||
|
||||
### 3.4 修改 `start.py`
|
||||
|
||||
#### 新增 CLI 命令
|
||||
```python
|
||||
def main():
|
||||
# ... 现有的参数解析 ...
|
||||
|
||||
parser.add_argument("--whitelist", action="store_true", help="白名单操作模式")
|
||||
|
||||
# 白名单子命令
|
||||
whitelist_parser = subparsers.add_parser("whitelist", help="白名单管理")
|
||||
whitelist_parser.add_argument("action", choices=["add", "remove", "list", "enable", "disable"])
|
||||
whitelist_parser.add_argument("-n", "--name", help="策略标识符")
|
||||
```
|
||||
|
||||
### 3.5 前端界面修改
|
||||
|
||||
#### 状态表格新增列
|
||||
| 列名 | 说明 | 示例值 |
|
||||
|------|------|--------|
|
||||
| 白名单 | 是否在白名单中 | ✅ / ❌ |
|
||||
| 白名单状态 | 白名单中是否启用 | 启用 / 禁用 |
|
||||
| 自动启动 | 今天是否已尝试自动启动 | 是 / 否 |
|
||||
| 自动启动结果 | 上次自动启动是否成功 | 成功 / 失败 / - |
|
||||
|
||||
#### 新增管理操作
|
||||
1. **添加到白名单**: 勾选策略后点击"添加到白名单"
|
||||
2. **从白名单移除**: 勾选策略后点击"从白名单移除"
|
||||
3. **启用/禁用**: 在白名单中启用或禁用某个策略
|
||||
4. **手动触发**: 按钮"立即执行白名单启动"
|
||||
|
||||
---
|
||||
|
||||
## 4. 工作流程
|
||||
|
||||
### 4.1 自动启动流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant T as 定时任务 (08:58)
|
||||
participant W as Web Backend
|
||||
participant M as StrategyManager
|
||||
participant WM as WhitelistManager
|
||||
participant S as Strategy Process
|
||||
|
||||
T->>W: 触发定时任务
|
||||
W->>M: auto_start_whitelist_strategies()
|
||||
M->>WM: should_auto_start_today()?
|
||||
|
||||
alt 今天未执行过
|
||||
WM-->>M: True
|
||||
M->>WM: get_all() 获取白名单
|
||||
loop 遍历白名单策略
|
||||
M->>M: 检查策略是否运行
|
||||
alt 未运行
|
||||
M->>S: 启动策略进程 (auto_start=True)
|
||||
M->>M: 记录启动结果
|
||||
else 已在运行
|
||||
M->>M: 标记为成功
|
||||
end
|
||||
end
|
||||
M->>WM: update_last_auto_start_date(今天)
|
||||
M-->>W: 返回启动结果
|
||||
W->>T: 记录日志
|
||||
else 今天已执行过
|
||||
WM-->>M: False
|
||||
M-->>W: 返回空结果
|
||||
W->>T: 跳过(今天已执行)
|
||||
end
|
||||
```
|
||||
|
||||
### 4.2 手动管理流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant U as 用户
|
||||
participant W as Web界面
|
||||
participant API as Web Backend API
|
||||
participant M as StrategyManager
|
||||
participant WM as WhitelistManager
|
||||
participant F as 白名单配置文件
|
||||
|
||||
U->>W: 点击"添加到白名单"
|
||||
W->>API: POST /api/whitelist/{name}/add
|
||||
API->>M: add_to_whitelist(name)
|
||||
M->>WM: add(name)
|
||||
WM->>F: 写入配置
|
||||
F-->>WM: 保存成功
|
||||
WM-->>M: True
|
||||
M-->>API: True
|
||||
API-->>W: {"success": True}
|
||||
W->>U: 显示成功提示
|
||||
W->>API: GET /api/status
|
||||
API->>M: get_status()
|
||||
M->>WM: 获取白名单状态
|
||||
M-->>API: 状态数据
|
||||
API-->>W: 状态数据
|
||||
W->>U: 更新表格(显示白名单状态)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 兼容性设计
|
||||
|
||||
### 5.1 向后兼容
|
||||
- 现有 `start.py status` 命令保持不变
|
||||
- 现有 API `/api/status` 保持兼容(新增字段不影响现有功能)
|
||||
- 现有日志查看功能保持不变
|
||||
|
||||
### 5.2 数据迁移
|
||||
- 首次启动时自动创建 `config/whitelist.json`
|
||||
- 无需手动迁移现有配置
|
||||
|
||||
---
|
||||
|
||||
## 6. 实施计划
|
||||
|
||||
### 阶段一:后端实现
|
||||
1. 创建 `config/whitelist.json` 模板
|
||||
2. 实现 `core/whitelist_manager.py`
|
||||
3. 修改 `core/manager.py` 集成白名单功能
|
||||
4. 修改 `web_backend.py` 添加 API
|
||||
5. 修改 `start.py` 添加 CLI 命令
|
||||
|
||||
### 阶段二:前端实现
|
||||
1. 修改状态表格,添加白名单列
|
||||
2. 添加白名单管理操作按钮
|
||||
3. 添加手动触发按钮
|
||||
4. 美化界面交互
|
||||
|
||||
### 阶段三:测试
|
||||
1. 测试白名单添加/移除
|
||||
2. 测试自动启动功能
|
||||
3. 测试手动停止不自动重启
|
||||
4. 测试一天只启动一次
|
||||
5. 测试重启后状态恢复
|
||||
|
||||
---
|
||||
|
||||
## 7. 文件修改清单
|
||||
|
||||
| 文件路径 | 操作 | 说明 |
|
||||
|----------|------|------|
|
||||
| `config/whitelist.json` | 新增 | 白名单配置文件 |
|
||||
| `core/whitelist_manager.py` | 新增 | 白名单管理器类 |
|
||||
| `core/manager.py` | 修改 | 集成白名单功能,扩展状态结构 |
|
||||
| `web_backend.py` | 修改 | 添加白名单管理 API,修改定时任务 |
|
||||
| `start.py` | 修改 | 添加白名单 CLI 命令 |
|
||||
| `frontend/` | 修改 | 前端界面添加白名单管理功能 |
|
||||
Reference in New Issue
Block a user