283 lines
11 KiB
Markdown
283 lines
11 KiB
Markdown
# 配置架构验证文档
|
||
|
||
## 📋 验证目标
|
||
|
||
确认:
|
||
1. ✅ **所有用户下的账户都使用全局策略配置**
|
||
2. ✅ **普通用户无法通过自己的配置直接影响核心策略参数**
|
||
|
||
---
|
||
|
||
## 🏗️ 配置架构设计
|
||
|
||
### 1. 配置层级
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ 全局策略账号 (account_id=1, 默认) │
|
||
│ - 存储所有核心策略参数 │
|
||
│ - 例如:ATR_STOP_LOSS_MULTIPLIER, ATR_TAKE_PROFIT_... │
|
||
└─────────────────────────────────────────────────────────┘
|
||
↓ 读取
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ 用户账户 (account_id=2, 3, 4...) │
|
||
│ - 存储风险旋钮(每个账户独立) │
|
||
│ - 例如:MAX_POSITION_PERCENT, AUTO_TRADE_ENABLED... │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 2. 配置读取逻辑
|
||
|
||
**位置**:`backend/config_manager.py` 的 `get_trading_config()` 方法
|
||
|
||
```python
|
||
def eff_get(key: str, default: Any):
|
||
"""
|
||
策略核心:默认从全局账号读取(GLOBAL_STRATEGY_ACCOUNT_ID)。
|
||
风险旋钮:从当前账号读取。
|
||
"""
|
||
# API key/secret/testnet 永远按账号读取
|
||
if key in RISK_KNOBS_KEYS or global_mgr is None:
|
||
return self.get(key, default) # 从当前账号读取
|
||
try:
|
||
# 从全局账号读取
|
||
return global_mgr.get(key, default)
|
||
except Exception:
|
||
return self.get(key, default)
|
||
```
|
||
|
||
**风险旋钮列表**(`RISK_KNOBS_KEYS`):
|
||
- `MIN_MARGIN_USDT`
|
||
- `MIN_POSITION_PERCENT`
|
||
- `MAX_POSITION_PERCENT`
|
||
- `MAX_TOTAL_POSITION_PERCENT`
|
||
- `AUTO_TRADE_ENABLED`
|
||
- `MAX_OPEN_POSITIONS`
|
||
- `MAX_DAILY_ENTRIES`
|
||
|
||
**核心策略参数**(从全局账号读取):
|
||
- `ATR_STOP_LOSS_MULTIPLIER`
|
||
- `ATR_TAKE_PROFIT_MULTIPLIER`
|
||
- `RISK_REWARD_RATIO`
|
||
- `USE_FIXED_RISK_SIZING`
|
||
- `FIXED_RISK_PERCENT`
|
||
- `USE_DYNAMIC_ATR_MULTIPLIER`
|
||
- `MIN_SIGNAL_STRENGTH`
|
||
- `SCAN_INTERVAL`
|
||
- `TOP_N_SYMBOLS`
|
||
- ... 等等所有非风险旋钮的配置
|
||
|
||
---
|
||
|
||
## 🔒 权限控制
|
||
|
||
### 1. 后端API权限控制
|
||
|
||
**位置**:`backend/api/routes/config.py`
|
||
|
||
#### GET `/api/config` - 获取配置列表
|
||
|
||
```python
|
||
# 普通用户:只展示风险旋钮 + 账号密钥
|
||
# 管理员:若当前不是"全局策略账号",同样只展示风险旋钮
|
||
is_admin = (user.get("role") or "user") == "admin"
|
||
gid = _global_strategy_account_id()
|
||
if (not is_admin) or (is_admin and int(account_id) != int(gid)):
|
||
allowed = set(USER_RISK_KNOBS) | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}
|
||
result = {k: v for k, v in result.items() if k in allowed}
|
||
```
|
||
|
||
**验证**:
|
||
- ✅ 普通用户只能看到 `USER_RISK_KNOBS` + API密钥
|
||
- ✅ 管理员在非全局策略账号时,也只能看到风险旋钮
|
||
- ✅ 只有管理员在全局策略账号时,才能看到所有配置
|
||
|
||
#### PUT `/api/config/{key}` - 更新单个配置
|
||
|
||
```python
|
||
# 管理员:若不是全局策略账号,则禁止修改策略核心
|
||
if (user.get("role") or "user") == "admin":
|
||
gid = _global_strategy_account_id()
|
||
if int(account_id) != int(gid):
|
||
if key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
|
||
raise HTTPException(status_code=403, detail=f"该配置由全局策略账号 #{gid} 统一管理")
|
||
|
||
# 产品模式:普通用户只能改"风险旋钮"与账号私有密钥/测试网
|
||
if (user.get("role") or "user") != "admin":
|
||
if key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
|
||
raise HTTPException(status_code=403, detail="该配置由平台统一管理(仅管理员可修改)")
|
||
```
|
||
|
||
**验证**:
|
||
- ✅ 普通用户尝试修改核心策略参数会返回 403 错误
|
||
- ✅ 管理员在非全局策略账号时,也无法修改核心策略参数
|
||
- ✅ 只有管理员在全局策略账号时,才能修改核心策略参数
|
||
|
||
#### POST `/api/config/batch` - 批量更新配置
|
||
|
||
```python
|
||
for item in configs:
|
||
# 管理员:若不是全局策略账号,则批量只允许风险旋钮/密钥
|
||
if (user.get("role") or "user") == "admin":
|
||
gid = _global_strategy_account_id()
|
||
if int(account_id) != int(gid):
|
||
if item.key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
|
||
errors.append(f"{item.key}: 该配置由全局策略账号 #{gid} 统一管理,请切换账号修改")
|
||
continue
|
||
|
||
# 产品模式:普通用户只能改"风险旋钮"与账号私有密钥/测试网
|
||
if (user.get("role") or "user") != "admin":
|
||
if item.key not in (USER_RISK_KNOBS | {"BINANCE_API_KEY", "BINANCE_API_SECRET", "USE_TESTNET"}):
|
||
errors.append(f"{item.key}: 该配置由平台统一管理(仅管理员可修改)")
|
||
continue
|
||
```
|
||
|
||
**验证**:
|
||
- ✅ 普通用户批量更新时,核心策略参数会被过滤并返回错误
|
||
- ✅ 管理员在非全局策略账号时,核心策略参数也会被过滤
|
||
|
||
---
|
||
|
||
## ✅ 验证结果
|
||
|
||
### 1. 所有账户使用全局策略配置 ✅
|
||
|
||
**验证点**:
|
||
- `config_manager.py` 的 `get_trading_config()` 方法中,所有非风险旋钮的配置都通过 `eff_get()` 从全局账号读取
|
||
- 即使普通用户在自己的账户中设置了核心策略参数,也不会生效(因为读取时从全局账号读取)
|
||
|
||
**代码位置**:
|
||
- `backend/config_manager.py:509-522`
|
||
|
||
**结论**:✅ **所有账户都使用全局策略配置**
|
||
|
||
---
|
||
|
||
### 2. 普通用户无法修改核心策略参数 ✅
|
||
|
||
**验证点**:
|
||
- **前端限制**:普通用户在配置页面只能看到风险旋钮(通过API过滤)
|
||
- **后端限制**:
|
||
- GET `/api/config`:只返回风险旋钮
|
||
- PUT `/api/config/{key}`:尝试修改核心参数返回 403
|
||
- POST `/api/config/batch`:核心参数被过滤并返回错误
|
||
|
||
**代码位置**:
|
||
- `backend/api/routes/config.py:273-280` (GET)
|
||
- `backend/api/routes/config.py:645-655` (PUT)
|
||
- `backend/api/routes/config.py:765-776` (POST)
|
||
|
||
**结论**:✅ **普通用户无法通过自己的配置直接影响核心策略参数**
|
||
|
||
---
|
||
|
||
## 📊 配置分类总结
|
||
|
||
### 风险旋钮(每个账户独立)
|
||
- `MIN_MARGIN_USDT` - 最小保证金(USDT)
|
||
- `MIN_POSITION_PERCENT` - 最小仓位占比
|
||
- `MAX_POSITION_PERCENT` - 最大仓位占比
|
||
- `MAX_TOTAL_POSITION_PERCENT` - 总仓位占比上限
|
||
- `AUTO_TRADE_ENABLED` - 自动交易开关
|
||
- `MAX_OPEN_POSITIONS` - 同时持仓数量上限
|
||
- `MAX_DAILY_ENTRIES` - 每日最多开仓次数
|
||
|
||
### 核心策略参数(全局统一)
|
||
- `ATR_STOP_LOSS_MULTIPLIER` - ATR止损倍数
|
||
- `ATR_TAKE_PROFIT_MULTIPLIER` - ATR止盈倍数
|
||
- `RISK_REWARD_RATIO` - 盈亏比
|
||
- `USE_FIXED_RISK_SIZING` - 使用固定风险百分比
|
||
- `FIXED_RISK_PERCENT` - 固定风险百分比
|
||
- `USE_DYNAMIC_ATR_MULTIPLIER` - 动态ATR倍数
|
||
- `MIN_SIGNAL_STRENGTH` - 最小信号强度
|
||
- `SCAN_INTERVAL` - 扫描间隔
|
||
- `TOP_N_SYMBOLS` - 每次扫描处理的交易对数量
|
||
- ... 等等所有非风险旋钮的配置
|
||
|
||
### 账号私有配置(每个账户独立)
|
||
- `BINANCE_API_KEY` - 币安API密钥
|
||
- `BINANCE_API_SECRET` - 币安API密钥
|
||
- `USE_TESTNET` - 是否使用测试网
|
||
|
||
---
|
||
|
||
## 🎯 实际运行验证
|
||
|
||
### 测试场景1:普通用户查看配置
|
||
1. 普通用户登录
|
||
2. 进入配置页面
|
||
3. **预期**:只能看到风险旋钮 + API密钥配置
|
||
4. **验证**:前端只显示允许的配置项
|
||
|
||
### 测试场景2:普通用户尝试修改核心策略参数
|
||
1. 普通用户登录
|
||
2. 尝试通过API修改 `ATR_STOP_LOSS_MULTIPLIER`
|
||
3. **预期**:返回 403 错误:"该配置由平台统一管理(仅管理员可修改)"
|
||
4. **验证**:后端拒绝修改请求
|
||
|
||
### 测试场景3:管理员在非全局策略账号修改核心策略参数
|
||
1. 管理员登录
|
||
2. 切换到非全局策略账号(如 account_id=2)
|
||
3. 尝试修改 `ATR_STOP_LOSS_MULTIPLIER`
|
||
4. **预期**:返回 403 错误:"该配置由全局策略账号 #1 统一管理,请切换到该账号修改"
|
||
5. **验证**:后端拒绝修改请求
|
||
|
||
### 测试场景4:管理员在全局策略账号修改核心策略参数
|
||
1. 管理员登录
|
||
2. 切换到全局策略账号(account_id=1)
|
||
3. 修改 `ATR_STOP_LOSS_MULTIPLIER = 2.5`
|
||
4. **预期**:修改成功
|
||
5. **验证**:所有账户的交易系统都会使用新的值(通过 `config_manager.get_trading_config()` 读取)
|
||
|
||
---
|
||
|
||
## 🔍 代码检查清单
|
||
|
||
- [x] `backend/config_manager.py` - 配置读取逻辑使用全局账号
|
||
- [x] `backend/api/routes/config.py` - API权限控制
|
||
- [x] `frontend/src/components/ConfigPanel.jsx` - 前端配置页面(依赖后端过滤)
|
||
- [x] `frontend/src/components/GlobalConfig.jsx` - 管理员全局配置页面
|
||
|
||
---
|
||
|
||
## ✅ 最终结论
|
||
|
||
1. ✅ **所有用户下的账户都使用全局策略配置**
|
||
- 通过 `config_manager.get_trading_config()` 的 `eff_get()` 函数实现
|
||
- 核心策略参数从全局账号(account_id=1)读取
|
||
- 风险旋钮从当前账号读取
|
||
|
||
2. ✅ **普通用户无法通过自己的配置直接影响核心策略参数**
|
||
- 前端:只能看到风险旋钮
|
||
- 后端:尝试修改核心参数会返回 403 错误
|
||
- 即使数据库中有值,读取时也会从全局账号读取
|
||
|
||
3. ✅ **管理员权限控制**
|
||
- 管理员在非全局策略账号时,也只能修改风险旋钮
|
||
- 只有管理员在全局策略账号时,才能修改核心策略参数
|
||
|
||
---
|
||
|
||
## 📝 注意事项
|
||
|
||
1. **全局策略账号ID**:默认是 `account_id=1`,可通过环境变量 `ATS_GLOBAL_STRATEGY_ACCOUNT_ID` 修改
|
||
|
||
2. **配置缓存**:配置存储在 Redis 中,修改后需要确保 Redis 缓存已更新
|
||
|
||
3. **配置生效**:修改全局策略配置后,所有账户的交易系统会在下次 `reload_from_redis()` 时读取新值
|
||
|
||
4. **风险旋钮的作用**:虽然核心策略参数是全局的,但每个账户可以通过风险旋钮控制:
|
||
- 仓位大小(MAX_POSITION_PERCENT)
|
||
- 交易频率(MAX_DAILY_ENTRIES)
|
||
- 同时持仓数量(MAX_OPEN_POSITIONS)
|
||
- 是否启用自动交易(AUTO_TRADE_ENABLED)
|
||
|
||
---
|
||
|
||
## 🎯 建议
|
||
|
||
1. **定期检查**:定期验证全局策略账号的配置是否正确
|
||
2. **配置快照**:在修改全局策略配置前,先导出配置快照作为备份
|
||
3. **测试环境**:在测试环境验证配置修改的效果,再应用到生产环境
|
||
4. **文档更新**:修改配置后,及时更新相关文档
|