Skip to content

性能监控面板 #9

@realm520

Description

@realm520

## 概述

扩展现有的监控系统,为Strategy-21创建专门的性能监控面板,提供实时策略表现追踪、VATSM指标可视化、风险监控告警等功能,确保策略运行状态的透明化和可控性。

## 目标

- 扩展现有监控系统,添加Strategy-21专用指标
- 创建实时性能监控面板
- 实现多维度风险监控和告警
- 提供策略优化决策支持数据

## 技术要求

### 核心功能

1. **VATSM指标监控**
   - Volume、ATR、Trend、Strength、Momentum实时追踪
   - 指标相关性分析
   - 信号质量评估
   - 指标异常检测

2. **交易性能监控**
   - 实时P&L追踪
   - 胜率和盈亏比分析
   - 持仓分布监控
   - 资金使用率追踪

3. **风险监控告警**
   - 回撤监控和预警
   - 仓位集中度检查
   - 流动性风险评估
   - 异常交易行为检测

4. **策略健康度评估**
   - 策略运行状态监控
   - 性能稳定性分析
   - 参数漂移检测
   - 市场适应性评估

### 实现细节

#### 监控数据模型 (monitoring/models.py)

```python
from dataclasses import dataclass
from datetime import datetime
from typing import Dict, List, Optional, Any
from enum import Enum

class AlertLevel(Enum):
    INFO = "info"
    WARNING = "warning"
    CRITICAL = "critical"
    EMERGENCY = "emergency"

@dataclass
class VATSMMetrics:
    """VATSM技术指标监控数据"""
    timestamp: datetime
    pair: str
    
    # Volume指标
    volume_ratio: float
    volume_sma: float
    volume_anomaly_score: float
    
    # ATR指标
    atr_value: float
    atr_percentile: float
    volatility_regime: str
    
    # Trend指标
    trend_strength: float
    trend_direction: str
    trend_consistency: float
    
    # Strength指标
    rsi_value: float
    macd_signal: float
    strength_score: float
    
    # Momentum指标
    momentum_value: float
    momentum_acceleration: float
    momentum_divergence: float
    
    # 综合信号
    vatsm_score: float
    signal_quality: str
    confidence_level: float

@dataclass
class TradingPerformance:
    """交易性能监控数据"""
    timestamp: datetime
    
    # P&L指标
    unrealized_pnl: float
    realized_pnl: float
    total_pnl: float
    pnl_percentage: float
    
    # 交易统计
    total_trades: int
    winning_trades: int
    losing_trades: int
    win_rate: float
    profit_factor: float
    avg_win: float
    avg_loss: float
    
    # 持仓信息
    active_positions: int
    total_exposure: float
    max_position_size: float
    position_distribution: Dict[str, float]
    
    # 资金使用
    available_balance: float
    used_margin: float
    margin_utilization: float
    daily_volume: float

@dataclass
class RiskMetrics:
    """风险监控数据"""
    timestamp: datetime
    
    # 回撤指标
    current_drawdown: float
    max_drawdown: float
    drawdown_duration: int
    peak_timestamp: datetime
    
    # 风险度量
    var_1day: float
    var_1week: float
    expected_shortfall: float
    sharpe_ratio: float
    
    # 集中度风险
    position_concentration: Dict[str, float]
    sector_concentration: Dict[str, float]
    correlation_risk: float
    
    # 流动性风险
    liquidity_score: float
    slippage_impact: float
    market_depth_risk: float

@dataclass
class StrategyHealth:
    """策略健康度数据"""
    timestamp: datetime
    
    # 运行状态
    strategy_status: str
    uptime_hours: float
    last_signal_time: datetime
    signal_frequency: float
    
    # 性能稳定性
    performance_consistency: float
    parameter_drift_score: float
    market_adaptation_score: float
    
    # 异常检测
    anomaly_count_24h: int
    error_rate: float
    latency_p95: float
    
    # 健康评级
    overall_health_score: float
    health_grade: str
    recommended_actions: List[str]

@dataclass
class MonitoringAlert:
    """监控告警数据"""
    timestamp: datetime
    alert_id: str
    level: AlertLevel
    category: str
    title: str
    message: str
    affected_pairs: List[str]
    metrics: Dict[str, Any]
    is_resolved: bool = False
    resolved_at: Optional[datetime] = None

监控数据收集器 (monitoring/collectors.py)

import asyncio
from typing import Dict, List
from datetime import datetime, timedelta
from freqtrade.data.dataprovider import DataProvider
from freqtrade.persistence import Trade

class VATSMMetricsCollector:
    """VATSM指标数据收集器"""
    
    def __init__(self, dataprovider: DataProvider):
        self.dataprovider = dataprovider
        
    async def collect_metrics(self, pairs: List[str]) -> Dict[str, VATSMMetrics]:
        """收集VATSM指标数据"""
        metrics = {}
        
        for pair in pairs:
            try:
                # 获取OHLCV数据
                ohlcv = self.dataprovider.get_pair_dataframe(pair, '5m')
                if ohlcv.empty:
                    continue
                
                latest = ohlcv.iloc[-1]
                
                # 计算VATSM指标
                volume_ratio = self._calculate_volume_ratio(ohlcv)
                atr_data = self._calculate_atr_metrics(ohlcv)
                trend_data = self._calculate_trend_metrics(ohlcv)
                strength_data = self._calculate_strength_metrics(ohlcv)
                momentum_data = self._calculate_momentum_metrics(ohlcv)
                
                # 计算综合评分
                vatsm_score = self._calculate_vatsm_score(
                    volume_ratio, atr_data, trend_data, 
                    strength_data, momentum_data
                )
                
                metrics[pair] = VATSMMetrics(
                    timestamp=datetime.now(),
                    pair=pair,
                    volume_ratio=volume_ratio['ratio'],
                    volume_sma=volume_ratio['sma'],
                    volume_anomaly_score=volume_ratio['anomaly'],
                    atr_value=atr_data['value'],
                    atr_percentile=atr_data['percentile'],
                    volatility_regime=atr_data['regime'],
                    trend_strength=trend_data['strength'],
                    trend_direction=trend_data['direction'],
                    trend_consistency=trend_data['consistency'],
                    rsi_value=strength_data['rsi'],
                    macd_signal=strength_data['macd'],
                    strength_score=strength_data['score'],
                    momentum_value=momentum_data['value'],
                    momentum_acceleration=momentum_data['acceleration'],
                    momentum_divergence=momentum_data['divergence'],
                    vatsm_score=vatsm_score['score'],
                    signal_quality=vatsm_score['quality'],
                    confidence_level=vatsm_score['confidence']
                )
                
            except Exception as e:
                print(f"Error collecting metrics for {pair}: {e}")
                continue
                
        return metrics
    
    def _calculate_volume_ratio(self, ohlcv) -> Dict[str, float]:
        """计算成交量指标"""
        volume_sma = ohlcv['volume'].rolling(20).mean().iloc[-1]
        current_volume = ohlcv['volume'].iloc[-1]
        volume_ratio = current_volume / volume_sma if volume_sma > 0 else 1.0
        
        # 异常检测(基于z-score)
        volume_std = ohlcv['volume'].rolling(20).std().iloc[-1]
        z_score = abs(current_volume - volume_sma) / volume_std if volume_std > 0 else 0
        
        return {
            'ratio': volume_ratio,
            'sma': volume_sma,
            'anomaly': min(z_score / 3.0, 1.0)  # 标准化到0-1
        }
    
    def _calculate_atr_metrics(self, ohlcv) -> Dict[str, Any]:
        """计算ATR指标"""
        # 计算ATR
        high_low = ohlcv['high'] - ohlcv['low']
        high_close = abs(ohlcv['high'] - ohlcv['close'].shift())
        low_close = abs(ohlcv['low'] - ohlcv['close'].shift())
        
        true_range = pd.concat([high_low, high_close, low_close], axis=1).max(axis=1)
        atr = true_range.rolling(14).mean().iloc[-1]
        
        # 计算ATR百分位数
        atr_series = true_range.rolling(14).mean()
        atr_percentile = (atr_series <= atr).sum() / len(atr_series) * 100
        
        # 波动性制度分类
        if atr_percentile < 30:
            regime = "low_volatility"
        elif atr_percentile > 70:
            regime = "high_volatility"
        else:
            regime = "medium_volatility"
            
        return {
            'value': atr,
            'percentile': atr_percentile,
            'regime': regime
        }

class PerformanceCollector:
    """交易性能数据收集器"""
    
    def __init__(self, freqtrade_bot):
        self.bot = freqtrade_bot
        
    async def collect_performance(self) -> TradingPerformance:
        """收集交易性能数据"""
        # 获取当前余额
        balance = self.bot.wallets.get_total_stake_amount()
        
        # 获取开仓交易
        open_trades = Trade.get_open_trades()
        
        # 计算未实现盈亏
        unrealized_pnl = sum([trade.calc_profit_ratio() * trade.stake_amount 
                             for trade in open_trades])
        
        # 获取历史交易统计
        all_trades = Trade.get_trades([]).order_by(Trade.close_date.desc()).limit(1000)
        closed_trades = [t for t in all_trades if t.is_closed]
        
        # 计算统计指标
        winning_trades = len([t for t in closed_trades if t.profit_abs > 0])
        total_closed = len(closed_trades)
        win_rate = winning_trades / total_closed if total_closed > 0 else 0
        
        profits = [t.profit_abs for t in closed_trades if t.profit_abs > 0]
        losses = [abs(t.profit_abs) for t in closed_trades if t.profit_abs < 0]
        
        avg_win = sum(profits) / len(profits) if profits else 0
        avg_loss = sum(losses) / len(losses) if losses else 0
        profit_factor = (avg_win * len(profits)) / (avg_loss * len(losses)) if losses else float('inf')
        
        return TradingPerformance(
            timestamp=datetime.now(),
            unrealized_pnl=unrealized_pnl,
            realized_pnl=sum([t.profit_abs for t in closed_trades]),
            total_pnl=unrealized_pnl + sum([t.profit_abs for t in closed_trades]),
            pnl_percentage=(unrealized_pnl + sum([t.profit_abs for t in closed_trades])) / balance * 100,
            total_trades=len(open_trades) + total_closed,
            winning_trades=winning_trades,
            losing_trades=total_closed - winning_trades,
            win_rate=win_rate,
            profit_factor=profit_factor,
            avg_win=avg_win,
            avg_loss=avg_loss,
            active_positions=len(open_trades),
            total_exposure=sum([t.stake_amount for t in open_trades]),
            max_position_size=max([t.stake_amount for t in open_trades], default=0),
            position_distribution={t.pair: t.stake_amount for t in open_trades},
            available_balance=balance - sum([t.stake_amount for t in open_trades]),
            used_margin=sum([t.stake_amount for t in open_trades]),
            margin_utilization=sum([t.stake_amount for t in open_trades]) / balance * 100,
            daily_volume=self._calculate_daily_volume(closed_trades)
        )
    
    def _calculate_daily_volume(self, trades: List[Trade]) -> float:
        """计算日交易量"""
        today = datetime.now().date()
        today_trades = [t for t in trades 
                       if t.close_date and t.close_date.date() == today]
        return sum([t.amount * t.close_rate for t in today_trades])

监控面板后端API (monitoring/api.py)

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse
import json
import asyncio
from typing import List, Dict
from datetime import datetime, timedelta

app = FastAPI(title="Strategy21 Monitoring API")

class ConnectionManager:
    def __init__(self):
        self.active_connections: List[WebSocket] = []
    
    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)
    
    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)
    
    async def broadcast(self, message: str):
        for connection in self.active_connections:
            try:
                await connection.send_text(message)
            except:
                self.active_connections.remove(connection)

manager = ConnectionManager()

@app.websocket("/ws/monitoring")
async def websocket_endpoint(websocket: WebSocket):
    await manager.connect(websocket)
    try:
        while True:
            # 发送实时监控数据
            monitoring_data = await collect_all_monitoring_data()
            await websocket.send_text(json.dumps(monitoring_data, default=str))
            await asyncio.sleep(5)  # 每5秒更新一次
    except WebSocketDisconnect:
        manager.disconnect(websocket)

@app.get("/api/metrics/vatsm")
async def get_vatsm_metrics():
    """获取VATSM指标数据"""
    collector = VATSMMetricsCollector(dataprovider)
    pairs = ["BTC/USDT", "ETH/USDT", "BNB/USDT"]  # 配置化
    metrics = await collector.collect_metrics(pairs)
    return {
        "timestamp": datetime.now().isoformat(),
        "metrics": metrics
    }

@app.get("/api/performance/summary")
async def get_performance_summary():
    """获取性能摘要"""
    collector = PerformanceCollector(bot)
    performance = await collector.collect_performance()
    return performance

@app.get("/api/risk/assessment")
async def get_risk_assessment():
    """获取风险评估"""
    collector = RiskMetricsCollector(bot)
    risk_metrics = await collector.collect_risk_metrics()
    return risk_metrics

@app.get("/api/alerts/active")
async def get_active_alerts():
    """获取活跃告警"""
    alert_manager = AlertManager()
    alerts = alert_manager.get_active_alerts()
    return {
        "timestamp": datetime.now().isoformat(),
        "alerts": alerts,
        "count": len(alerts)
    }

监控面板前端 (monitoring/dashboard.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Strategy21 监控面板</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        .alert-critical { @apply bg-red-100 border-red-400 text-red-700; }
        .alert-warning { @apply bg-yellow-100 border-yellow-400 text-yellow-700; }
        .alert-info { @apply bg-blue-100 border-blue-400 text-blue-700; }
        .metric-card { @apply bg-white rounded-lg shadow-md p-6 mb-4; }
        .status-healthy { @apply text-green-600; }
        .status-warning { @apply text-yellow-600; }
        .status-critical { @apply text-red-600; }
    </style>
</head>
<body class="bg-gray-100">
    <div class="container mx-auto px-4 py-8">
        <!-- 页面标题 -->
        <div class="mb-8">
            <h1 class="text-3xl font-bold text-gray-800">Strategy21 监控面板</h1>
            <p class="text-gray-600">实时策略性能与风险监控</p>
        </div>

        <!-- 告警区域 -->
        <div id="alerts-section" class="mb-8">
            <!-- 告警会动态加载 -->
        </div>

        <!-- 核心指标卡片 -->
        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-2">总盈亏</h3>
                <div class="text-2xl font-bold" id="total-pnl">--</div>
                <div class="text-sm text-gray-600" id="pnl-percentage">--</div>
            </div>
            
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-2">活跃仓位</h3>
                <div class="text-2xl font-bold" id="active-positions">--</div>
                <div class="text-sm text-gray-600" id="total-exposure">--</div>
            </div>
            
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-2">胜率</h3>
                <div class="text-2xl font-bold" id="win-rate">--</div>
                <div class="text-sm text-gray-600" id="profit-factor">--</div>
            </div>
            
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-2">当前回撤</h3>
                <div class="text-2xl font-bold" id="current-drawdown">--</div>
                <div class="text-sm text-gray-600" id="max-drawdown">--</div>
            </div>
        </div>

        <!-- 图表区域 -->
        <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
            <!-- VATSM指标图表 -->
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-4">VATSM指标趋势</h3>
                <canvas id="vatsm-chart" width="400" height="200"></canvas>
            </div>
            
            <!-- 盈亏曲线图表 -->
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-4">盈亏曲线</h3>
                <canvas id="pnl-chart" width="400" height="200"></canvas>
            </div>
        </div>

        <!-- 详细数据表格 -->
        <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
            <!-- VATSM详细指标 -->
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-4">VATSM详细指标</h3>
                <div id="vatsm-table" class="overflow-x-auto">
                    <!-- 表格内容动态加载 -->
                </div>
            </div>
            
            <!-- 仓位分布 -->
            <div class="metric-card">
                <h3 class="text-lg font-semibold mb-4">仓位分布</h3>
                <div id="positions-table" class="overflow-x-auto">
                    <!-- 表格内容动态加载 -->
                </div>
            </div>
        </div>
    </div>

    <script>
        // WebSocket连接
        const ws = new WebSocket('ws://localhost:8000/ws/monitoring');
        
        // 图表实例
        let vatsmChart = null;
        let pnlChart = null;
        
        // 初始化图表
        function initCharts() {
            // VATSM指标图表
            const vatsmCtx = document.getElementById('vatsm-chart').getContext('2d');
            vatsmChart = new Chart(vatsmCtx, {
                type: 'line',
                data: {
                    labels: [],
                    datasets: [{
                        label: 'VATSM Score',
                        data: [],
                        borderColor: 'rgb(59, 130, 246)',
                        backgroundColor: 'rgba(59, 130, 246, 0.1)',
                        tension: 0.4
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            max: 1
                        }
                    }
                }
            });
            
            // 盈亏曲线图表
            const pnlCtx = document.getElementById('pnl-chart').getContext('2d');
            pnlChart = new Chart(pnlCtx, {
                type: 'line',
                data: {
                    labels: [],
                    datasets: [{
                        label: '累计盈亏',
                        data: [],
                        borderColor: 'rgb(16, 185, 129)',
                        backgroundColor: 'rgba(16, 185, 129, 0.1)',
                        tension: 0.4
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false
                }
            });
        }
        
        // 更新核心指标
        function updateMetrics(data) {
            if (data.performance) {
                const perf = data.performance;
                document.getElementById('total-pnl').textContent = `$${perf.total_pnl.toFixed(2)}`;
                document.getElementById('pnl-percentage').textContent = `${perf.pnl_percentage.toFixed(2)}%`;
                document.getElementById('active-positions').textContent = perf.active_positions;
                document.getElementById('total-exposure').textContent = `$${perf.total_exposure.toFixed(2)}`;
                document.getElementById('win-rate').textContent = `${(perf.win_rate * 100).toFixed(1)}%`;
                document.getElementById('profit-factor').textContent = `${perf.profit_factor.toFixed(2)}`;
            }
            
            if (data.risk_metrics) {
                const risk = data.risk_metrics;
                document.getElementById('current-drawdown').textContent = `${(risk.current_drawdown * 100).toFixed(2)}%`;
                document.getElementById('max-drawdown').textContent = `Max: ${(risk.max_drawdown * 100).toFixed(2)}%`;
            }
        }
        
        // 更新告警
        function updateAlerts(alerts) {
            const alertsSection = document.getElementById('alerts-section');
            alertsSection.innerHTML = '';
            
            alerts.forEach(alert => {
                const alertDiv = document.createElement('div');
                alertDiv.className = `alert-${alert.level} border-l-4 p-4 mb-4`;
                alertDiv.innerHTML = `
                    <div class="flex">
                        <div>
                            <p class="font-bold">${alert.title}</p>
                            <p>${alert.message}</p>
                            <p class="text-xs mt-1">${new Date(alert.timestamp).toLocaleString()}</p>
                        </div>
                    </div>
                `;
                alertsSection.appendChild(alertDiv);
            });
        }
        
        // WebSocket消息处理
        ws.onmessage = function(event) {
            const data = JSON.parse(event.data);
            
            updateMetrics(data);
            
            if (data.alerts) {
                updateAlerts(data.alerts);
            }
            
            // 更新图表数据
            if (data.vatsm_metrics && vatsmChart) {
                const now = new Date().toLocaleTimeString();
                vatsmChart.data.labels.push(now);
                
                // 计算平均VATSM得分
                const avgScore = Object.values(data.vatsm_metrics)
                    .reduce((sum, metric) => sum + metric.vatsm_score, 0) / Object.keys(data.vatsm_metrics).length;
                
                vatsmChart.data.datasets[0].data.push(avgScore);
                
                // 保持最近30个数据点
                if (vatsmChart.data.labels.length > 30) {
                    vatsmChart.data.labels.shift();
                    vatsmChart.data.datasets[0].data.shift();
                }
                
                vatsmChart.update();
            }
            
            if (data.performance && pnlChart) {
                const now = new Date().toLocaleTimeString();
                pnlChart.data.labels.push(now);
                pnlChart.data.datasets[0].data.push(data.performance.total_pnl);
                
                // 保持最近30个数据点
                if (pnlChart.data.labels.length > 30) {
                    pnlChart.data.labels.shift();
                    pnlChart.data.datasets[0].data.shift();
                }
                
                pnlChart.update();
            }
        };
        
        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', function() {
            initCharts();
        });
        
        // WebSocket错误处理
        ws.onerror = function(error) {
            console.error('WebSocket error:', error);
        };
        
        ws.onclose = function() {
            console.log('WebSocket连接已关闭,尝试重连...');
            setTimeout(() => {
                location.reload();
            }, 5000);
        };
    </script>
</body>
</html>

告警系统 (monitoring/alerts.py)

class AlertManager:
    """告警管理器"""
    
    def __init__(self):
        self.active_alerts: Dict[str, MonitoringAlert] = {}
        self.alert_rules = self._load_alert_rules()
    
    def _load_alert_rules(self) -> Dict[str, Dict]:
        """加载告警规则"""
        return {
            "high_drawdown": {
                "threshold": 0.10,
                "level": AlertLevel.WARNING,
                "message": "回撤超过10%警戒线"
            },
            "critical_drawdown": {
                "threshold": 0.15,
                "level": AlertLevel.CRITICAL,
                "message": "回撤超过15%危险线"
            },
            "low_liquidity": {
                "threshold": 0.3,
                "level": AlertLevel.WARNING,
                "message": "市场流动性不足"
            },
            "position_concentration": {
                "threshold": 0.6,
                "level": AlertLevel.WARNING,
                "message": "仓位过度集中"
            },
            "signal_quality_degradation": {
                "threshold": 0.3,
                "level": AlertLevel.INFO,
                "message": "信号质量下降"
            }
        }
    
    async def check_alerts(self, monitoring_data: Dict[str, Any]):
        """检查告警条件"""
        new_alerts = []
        
        # 检查回撤告警
        if 'risk_metrics' in monitoring_data:
            risk = monitoring_data['risk_metrics']
            new_alerts.extend(self._check_drawdown_alerts(risk))
            new_alerts.extend(self._check_concentration_alerts(risk))
        
        # 检查性能告警
        if 'performance' in monitoring_data:
            performance = monitoring_data['performance']
            new_alerts.extend(self._check_performance_alerts(performance))
        
        # 检查VATSM指标告警
        if 'vatsm_metrics' in monitoring_data:
            vatsm = monitoring_data['vatsm_metrics']
            new_alerts.extend(self._check_vatsm_alerts(vatsm))
        
        # 更新告警状态
        for alert in new_alerts:
            self.active_alerts[alert.alert_id] = alert
        
        return new_alerts
    
    def _check_drawdown_alerts(self, risk_metrics: RiskMetrics) -> List[MonitoringAlert]:
        """检查回撤告警"""
        alerts = []
        
        if risk_metrics.current_drawdown > self.alert_rules["critical_drawdown"]["threshold"]:
            alerts.append(MonitoringAlert(
                timestamp=datetime.now(),
                alert_id=f"drawdown_critical_{datetime.now().timestamp()}",
                level=AlertLevel.CRITICAL,
                category="risk_management",
                title="严重回撤告警",
                message=f"当前回撤{risk_metrics.current_drawdown:.2%}超过危险阈值",
                affected_pairs=[],
                metrics={"current_drawdown": risk_metrics.current_drawdown}
            ))
        elif risk_metrics.current_drawdown > self.alert_rules["high_drawdown"]["threshold"]:
            alerts.append(MonitoringAlert(
                timestamp=datetime.now(),
                alert_id=f"drawdown_warning_{datetime.now().timestamp()}",
                level=AlertLevel.WARNING,
                category="risk_management",
                title="回撤告警",
                message=f"当前回撤{risk_metrics.current_drawdown:.2%}超过警戒阈值",
                affected_pairs=[],
                metrics={"current_drawdown": risk_metrics.current_drawdown}
            ))
        
        return alerts

验收标准

功能测试

  1. 监控数据收集

    • VATSM指标数据准确收集和计算
    • 交易性能数据实时更新
    • 风险指标正确评估
    • 策略健康度准确评估
  2. 监控面板

    • 实时数据展示正常
    • 图表更新及时准确
    • 告警系统响应及时
    • 界面友好易用
  3. 告警系统

    • 告警规则触发准确
    • 告警级别分类正确
    • 告警通知及时送达
    • 告警历史记录完整

性能测试

  1. 数据处理性能
    • 监控数据收集延迟 < 1秒
    • 面板数据更新频率稳定
    • 大量历史数据查询响应及时

实现计划

阶段1:数据收集器 (1.5小时)

  • 实现VATSM指标收集器
  • 实现交易性能收集器
  • 实现风险指标收集器
  • 添加数据验证和异常处理

阶段2:监控面板后端 (1小时)

  • 创建FastAPI监控接口
  • 实现WebSocket实时数据推送
  • 添加数据缓存和优化

阶段3:监控面板前端 (1小时)

  • 创建响应式监控界面
  • 实现实时图表展示
  • 添加交互功能和数据导出

阶段4:告警系统 (0.5小时)

  • 实现告警规则引擎
  • 创建告警通知机制
  • 添加告警历史管理

依赖关系

  • 前置依赖: 006 (配置文件模板) - 需要监控参数配置
  • 并行执行: 不可与其他任务并行执行
  • 后续集成: 为任务009和010提供监控数据

风险与缓解

技术风险

  1. 监控性能影响

    • 风险:监控系统影响策略执行性能
    • 缓解:异步数据收集,控制监控频率
  2. 数据存储压力

    • 风险:监控数据量过大影响系统性能
    • 缓解:实现数据采样和定期清理机制

业务风险

  1. 告警疲劳
    • 风险:过多告警导致关键告警被忽略
    • 缓解:优化告警规则,实现智能告警聚合

成功指标

  1. 监控覆盖率 - 覆盖所有关键策略指标
  2. 数据准确性 - 监控数据与实际一致性 > 99%
  3. 响应及时性 - 关键告警响应时间 < 30秒
  4. 系统稳定性 - 监控系统可用性 > 99.5%
  5. 用户满意度 - 面板易用性和信息价值

交付物

  1. 监控数据收集器

    • vatsm_metrics_collector.py
    • performance_collector.py
    • risk_metrics_collector.py
    • strategy_health_collector.py
  2. 监控面板

    • monitoring_api.py (后端API)
    • dashboard.html (前端界面)
    • monitoring_config.json (监控配置)
  3. 告警系统

    • alert_manager.py (告警管理器)
    • alert_rules.json (告警规则配置)
    • notification_handlers.py (通知处理器)
  4. 文档

    • 监控面板使用指南
    • 告警规则配置说明
    • 监控指标说明文档

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions