2025-06-12 13:27:30 +08:00

676 lines
31 KiB
Python

"""
Configuration Validation and Error Handling System
This module provides comprehensive validation for chart configurations with
detailed error reporting, warnings, and configurable validation rules.
"""
from typing import Dict, List, Any, Optional, Union, Tuple, Set
from dataclasses import dataclass, field
from enum import Enum
import re
from datetime import datetime
from .indicator_defs import ChartIndicatorConfig, INDICATOR_SCHEMAS, validate_indicator_configuration
from .defaults import get_all_default_indicators, TradingStrategy, IndicatorCategory
from .strategy_charts import StrategyChartConfig, SubplotConfig, ChartStyle, ChartLayout, SubplotType
from utils.logger import get_logger
# Initialize logger
logger = get_logger()
class ValidationLevel(str, Enum):
"""Validation severity levels."""
ERROR = "error"
WARNING = "warning"
INFO = "info"
DEBUG = "debug"
class ValidationRule(str, Enum):
"""Available validation rules."""
REQUIRED_FIELDS = "required_fields"
HEIGHT_RATIOS = "height_ratios"
INDICATOR_EXISTENCE = "indicator_existence"
TIMEFRAME_FORMAT = "timeframe_format"
CHART_STYLE = "chart_style"
SUBPLOT_CONFIG = "subplot_config"
STRATEGY_CONSISTENCY = "strategy_consistency"
PERFORMANCE_IMPACT = "performance_impact"
INDICATOR_CONFLICTS = "indicator_conflicts"
RESOURCE_USAGE = "resource_usage"
@dataclass
class ValidationIssue:
"""Represents a validation issue."""
level: ValidationLevel
rule: ValidationRule
message: str
field_path: str = ""
suggestion: Optional[str] = None
auto_fix: Optional[str] = None
context: Dict[str, Any] = field(default_factory=dict)
def __str__(self) -> str:
"""String representation of the validation issue."""
prefix = f"[{self.level.value.upper()}]"
location = f" at {self.field_path}" if self.field_path else ""
suggestion = f" Suggestion: {self.suggestion}" if self.suggestion else ""
return f"{prefix} {self.message}{location}.{suggestion}"
@dataclass
class ValidationReport:
"""Comprehensive validation report."""
is_valid: bool
errors: List[ValidationIssue] = field(default_factory=list)
warnings: List[ValidationIssue] = field(default_factory=list)
info: List[ValidationIssue] = field(default_factory=list)
debug: List[ValidationIssue] = field(default_factory=list)
validation_time: Optional[datetime] = None
rules_applied: Set[ValidationRule] = field(default_factory=set)
def add_issue(self, issue: ValidationIssue) -> None:
"""Add a validation issue to the appropriate list."""
if issue.level == ValidationLevel.ERROR:
self.errors.append(issue)
self.is_valid = False
elif issue.level == ValidationLevel.WARNING:
self.warnings.append(issue)
elif issue.level == ValidationLevel.INFO:
self.info.append(issue)
elif issue.level == ValidationLevel.DEBUG:
self.debug.append(issue)
def get_all_issues(self) -> List[ValidationIssue]:
"""Get all validation issues sorted by severity."""
return self.errors + self.warnings + self.info + self.debug
def get_issues_by_rule(self, rule: ValidationRule) -> List[ValidationIssue]:
"""Get all issues for a specific validation rule."""
return [issue for issue in self.get_all_issues() if issue.rule == rule]
def has_errors(self) -> bool:
"""Check if there are any errors."""
return len(self.errors) > 0
def has_warnings(self) -> bool:
"""Check if there are any warnings."""
return len(self.warnings) > 0
def summary(self) -> str:
"""Get a summary of the validation report."""
total_issues = len(self.get_all_issues())
status = "INVALID" if not self.is_valid else "VALID"
return (f"Validation {status}: {len(self.errors)} errors, "
f"{len(self.warnings)} warnings, {total_issues} total issues")
class ConfigurationValidator:
"""Comprehensive configuration validator."""
def __init__(self, enabled_rules: Optional[Set[ValidationRule]] = None):
"""
Initialize validator with optional rule filtering.
Args:
enabled_rules: Set of rules to apply. If None, applies all rules.
"""
self.enabled_rules = enabled_rules or set(ValidationRule)
self.timeframe_pattern = re.compile(r'^(\d+)(m|h|d|w)$')
self.color_pattern = re.compile(r'^#[0-9a-fA-F]{6}$')
# Load indicator information for validation
self._load_indicator_info()
def _load_indicator_info(self) -> None:
"""Load indicator information for validation."""
try:
self.available_indicators = get_all_default_indicators()
self.indicator_schemas = INDICATOR_SCHEMAS
except Exception as e:
logger.warning(f"Validation: Failed to load indicator information: {e}")
self.available_indicators = {}
self.indicator_schemas = {}
def validate_strategy_config(self, config: StrategyChartConfig) -> ValidationReport:
"""
Perform comprehensive validation of a strategy configuration.
Args:
config: Strategy configuration to validate
Returns:
Detailed validation report
"""
report = ValidationReport(is_valid=True, validation_time=datetime.now())
# Apply validation rules
if ValidationRule.REQUIRED_FIELDS in self.enabled_rules:
self._validate_required_fields(config, report)
if ValidationRule.HEIGHT_RATIOS in self.enabled_rules:
self._validate_height_ratios(config, report)
if ValidationRule.INDICATOR_EXISTENCE in self.enabled_rules:
self._validate_indicator_existence(config, report)
if ValidationRule.TIMEFRAME_FORMAT in self.enabled_rules:
self._validate_timeframe_format(config, report)
if ValidationRule.CHART_STYLE in self.enabled_rules:
self._validate_chart_style(config, report)
if ValidationRule.SUBPLOT_CONFIG in self.enabled_rules:
self._validate_subplot_configs(config, report)
if ValidationRule.STRATEGY_CONSISTENCY in self.enabled_rules:
self._validate_strategy_consistency(config, report)
if ValidationRule.PERFORMANCE_IMPACT in self.enabled_rules:
self._validate_performance_impact(config, report)
if ValidationRule.INDICATOR_CONFLICTS in self.enabled_rules:
self._validate_indicator_conflicts(config, report)
if ValidationRule.RESOURCE_USAGE in self.enabled_rules:
self._validate_resource_usage(config, report)
# Update applied rules
report.rules_applied = self.enabled_rules
return report
def _validate_required_fields(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate required fields."""
# Strategy name
if not config.strategy_name or not config.strategy_name.strip():
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.REQUIRED_FIELDS,
message="Strategy name is required and cannot be empty",
field_path="strategy_name",
suggestion="Provide a descriptive name for your strategy"
))
elif len(config.strategy_name.strip()) < 3:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.REQUIRED_FIELDS,
message="Strategy name is very short",
field_path="strategy_name",
suggestion="Use a more descriptive name (at least 3 characters)"
))
# Strategy type
if not isinstance(config.strategy_type, TradingStrategy):
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.REQUIRED_FIELDS,
message="Invalid strategy type",
field_path="strategy_type",
suggestion=f"Must be one of: {[s.value for s in TradingStrategy]}"
))
# Description
if not config.description or not config.description.strip():
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.REQUIRED_FIELDS,
message="Strategy description is missing",
field_path="description",
suggestion="Add a description to help users understand the strategy"
))
# Timeframes
if not config.timeframes:
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.REQUIRED_FIELDS,
message="At least one timeframe must be specified",
field_path="timeframes",
suggestion="Add recommended timeframes for this strategy"
))
def _validate_height_ratios(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate chart height ratios."""
# Main chart height
if config.main_chart_height <= 0 or config.main_chart_height > 1.0:
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.HEIGHT_RATIOS,
message=f"Main chart height ({config.main_chart_height}) must be between 0 and 1.0",
field_path="main_chart_height",
suggestion="Set a value between 0.1 and 0.9",
auto_fix="0.7"
))
elif config.main_chart_height < 0.3:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.HEIGHT_RATIOS,
message=f"Main chart height ({config.main_chart_height}) is very small",
field_path="main_chart_height",
suggestion="Consider using at least 0.3 for better visibility"
))
# Subplot heights
total_subplot_height = sum(subplot.height_ratio for subplot in config.subplot_configs)
total_height = config.main_chart_height + total_subplot_height
if total_height > 1.0:
excess = total_height - 1.0
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.HEIGHT_RATIOS,
message=f"Total chart height ({total_height:.3f}) exceeds 1.0 by {excess:.3f}",
field_path="height_ratios",
suggestion="Reduce main chart height or subplot heights",
context={"total_height": total_height, "excess": excess}
))
elif total_height < 0.8:
unused = 1.0 - total_height
report.add_issue(ValidationIssue(
level=ValidationLevel.INFO,
rule=ValidationRule.HEIGHT_RATIOS,
message=f"Chart height ({total_height:.3f}) leaves {unused:.3f} unused space",
field_path="height_ratios",
suggestion="Consider increasing chart or subplot heights for better space utilization"
))
# Individual subplot heights
for i, subplot in enumerate(config.subplot_configs):
if subplot.height_ratio <= 0 or subplot.height_ratio > 1.0:
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.HEIGHT_RATIOS,
message=f"Subplot {i} height ratio ({subplot.height_ratio}) must be between 0 and 1.0",
field_path=f"subplot_configs[{i}].height_ratio",
suggestion="Set a value between 0.1 and 0.5"
))
elif subplot.height_ratio < 0.1:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.HEIGHT_RATIOS,
message=f"Subplot {i} height ratio ({subplot.height_ratio}) is very small",
field_path=f"subplot_configs[{i}].height_ratio",
suggestion="Consider using at least 0.1 for better readability"
))
def _validate_indicator_existence(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate that indicators exist in the available indicators."""
# Check overlay indicators
for indicator in config.overlay_indicators:
if indicator not in self.available_indicators:
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.INDICATOR_EXISTENCE,
message=f"Overlay indicator '{indicator}' not found",
field_path=f"overlay_indicators.{indicator}",
suggestion="Check indicator name or add it to defaults",
context={"available_count": len(self.available_indicators)}
))
# Check subplot indicators
for i, subplot in enumerate(config.subplot_configs):
for indicator in subplot.indicators:
if indicator not in self.available_indicators:
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.INDICATOR_EXISTENCE,
message=f"Subplot indicator '{indicator}' not found",
field_path=f"subplot_configs[{i}].indicators.{indicator}",
suggestion="Check indicator name or add it to defaults"
))
def _validate_timeframe_format(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate timeframe format."""
valid_timeframes = ['1m', '5m', '15m', '30m', '1h', '2h', '4h', '6h', '8h', '12h', '1d', '3d', '1w', '1M']
for timeframe in config.timeframes:
if timeframe not in valid_timeframes:
if self.timeframe_pattern.match(timeframe):
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.TIMEFRAME_FORMAT,
message=f"Timeframe '{timeframe}' format is valid but not in common list",
field_path=f"timeframes.{timeframe}",
suggestion=f"Consider using standard timeframes: {valid_timeframes[:8]}"
))
else:
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.TIMEFRAME_FORMAT,
message=f"Invalid timeframe format '{timeframe}'",
field_path=f"timeframes.{timeframe}",
suggestion="Use format like '1m', '5m', '1h', '4h', '1d', '1w'",
context={"valid_timeframes": valid_timeframes}
))
def _validate_chart_style(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate chart style configuration."""
style = config.chart_style
# Validate colors
color_fields = [
('background_color', style.background_color),
('grid_color', style.grid_color),
('text_color', style.text_color),
('candlestick_up_color', style.candlestick_up_color),
('candlestick_down_color', style.candlestick_down_color),
('volume_color', style.volume_color)
]
for field_name, color_value in color_fields:
if color_value and not self.color_pattern.match(color_value):
report.add_issue(ValidationIssue(
level=ValidationLevel.ERROR,
rule=ValidationRule.CHART_STYLE,
message=f"Invalid color format for {field_name}: '{color_value}'",
field_path=f"chart_style.{field_name}",
suggestion="Use hex color format like '#ffffff' or '#123456'"
))
# Validate font size
if style.font_size < 6 or style.font_size > 24:
level = ValidationLevel.ERROR if style.font_size < 1 or style.font_size > 48 else ValidationLevel.WARNING
report.add_issue(ValidationIssue(
level=level,
rule=ValidationRule.CHART_STYLE,
message=f"Font size {style.font_size} may cause readability issues",
field_path="chart_style.font_size",
suggestion="Use font size between 8 and 18 for optimal readability"
))
# Validate theme
valid_themes = ['plotly', 'plotly_white', 'plotly_dark', 'ggplot2', 'seaborn', 'simple_white']
if style.theme not in valid_themes:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.CHART_STYLE,
message=f"Theme '{style.theme}' may not be supported",
field_path="chart_style.theme",
suggestion=f"Consider using: {valid_themes[:3]}",
context={"valid_themes": valid_themes}
))
def _validate_subplot_configs(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate subplot configurations."""
subplot_types = [subplot.subplot_type for subplot in config.subplot_configs]
# Check for duplicate subplot types
seen_types = set()
for i, subplot in enumerate(config.subplot_configs):
if subplot.subplot_type in seen_types:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.SUBPLOT_CONFIG,
message=f"Duplicate subplot type '{subplot.subplot_type.value}' at position {i}",
field_path=f"subplot_configs[{i}].subplot_type",
suggestion="Consider using different subplot types or combining indicators"
))
seen_types.add(subplot.subplot_type)
# Validate subplot-specific indicators
if subplot.subplot_type == SubplotType.RSI and subplot.indicators:
for indicator in subplot.indicators:
if indicator in self.available_indicators:
indicator_config = self.available_indicators[indicator].config
indicator_type = indicator_config.indicator_type
# Handle both string and enum types
if hasattr(indicator_type, 'value'):
indicator_type_value = indicator_type.value
else:
indicator_type_value = str(indicator_type)
if indicator_type_value != 'rsi':
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.SUBPLOT_CONFIG,
message=f"Non-RSI indicator '{indicator}' in RSI subplot",
field_path=f"subplot_configs[{i}].indicators.{indicator}",
suggestion="Use RSI indicators in RSI subplots for consistency"
))
elif subplot.subplot_type == SubplotType.MACD and subplot.indicators:
for indicator in subplot.indicators:
if indicator in self.available_indicators:
indicator_config = self.available_indicators[indicator].config
indicator_type = indicator_config.indicator_type
# Handle both string and enum types
if hasattr(indicator_type, 'value'):
indicator_type_value = indicator_type.value
else:
indicator_type_value = str(indicator_type)
if indicator_type_value != 'macd':
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.SUBPLOT_CONFIG,
message=f"Non-MACD indicator '{indicator}' in MACD subplot",
field_path=f"subplot_configs[{i}].indicators.{indicator}",
suggestion="Use MACD indicators in MACD subplots for consistency"
))
def _validate_strategy_consistency(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate strategy consistency with indicator choices."""
strategy_type = config.strategy_type
timeframes = config.timeframes
# Check timeframe consistency with strategy
strategy_timeframe_recommendations = {
TradingStrategy.SCALPING: ['1m', '5m'],
TradingStrategy.DAY_TRADING: ['5m', '15m', '1h'],
TradingStrategy.SWING_TRADING: ['1h', '4h', '1d'],
TradingStrategy.POSITION_TRADING: ['4h', '1d', '1w'],
TradingStrategy.MOMENTUM: ['15m', '1h', '4h'],
TradingStrategy.MEAN_REVERSION: ['15m', '1h', '4h']
}
recommended = strategy_timeframe_recommendations.get(strategy_type, [])
if recommended:
mismatched_timeframes = [tf for tf in timeframes if tf not in recommended]
if mismatched_timeframes:
report.add_issue(ValidationIssue(
level=ValidationLevel.INFO,
rule=ValidationRule.STRATEGY_CONSISTENCY,
message=f"Timeframes {mismatched_timeframes} may not be optimal for {strategy_type.value}",
field_path="timeframes",
suggestion=f"Consider using: {recommended}",
context={"recommended": recommended, "current": timeframes}
))
def _validate_performance_impact(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate potential performance impact."""
total_indicators = len(config.overlay_indicators)
for subplot in config.subplot_configs:
total_indicators += len(subplot.indicators)
if total_indicators > 10:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.PERFORMANCE_IMPACT,
message=f"High indicator count ({total_indicators}) may impact performance",
field_path="indicators",
suggestion="Consider reducing the number of indicators for better performance",
context={"indicator_count": total_indicators}
))
# Check for complex indicators
complex_indicators = ['bollinger_bands', 'macd']
complex_count = 0
all_indicators = config.overlay_indicators.copy()
for subplot in config.subplot_configs:
all_indicators.extend(subplot.indicators)
for indicator in all_indicators:
if indicator in self.available_indicators:
indicator_config = self.available_indicators[indicator].config
indicator_type = indicator_config.indicator_type
# Handle both string and enum types
if hasattr(indicator_type, 'value'):
indicator_type_value = indicator_type.value
else:
indicator_type_value = str(indicator_type)
if indicator_type_value in complex_indicators:
complex_count += 1
if complex_count > 3:
report.add_issue(ValidationIssue(
level=ValidationLevel.INFO,
rule=ValidationRule.PERFORMANCE_IMPACT,
message=f"Multiple complex indicators ({complex_count}) detected",
field_path="indicators",
suggestion="Complex indicators may increase calculation time",
context={"complex_count": complex_count}
))
def _validate_indicator_conflicts(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate for potential indicator conflicts or redundancy."""
all_indicators = config.overlay_indicators.copy()
for subplot in config.subplot_configs:
all_indicators.extend(subplot.indicators)
# Check for similar indicators
sma_indicators = [ind for ind in all_indicators if 'sma_' in ind]
ema_indicators = [ind for ind in all_indicators if 'ema_' in ind]
rsi_indicators = [ind for ind in all_indicators if 'rsi_' in ind]
if len(sma_indicators) > 3:
report.add_issue(ValidationIssue(
level=ValidationLevel.INFO,
rule=ValidationRule.INDICATOR_CONFLICTS,
message=f"Multiple SMA indicators ({len(sma_indicators)}) may create visual clutter",
field_path="overlay_indicators",
suggestion="Consider using fewer SMA periods for cleaner charts"
))
if len(ema_indicators) > 3:
report.add_issue(ValidationIssue(
level=ValidationLevel.INFO,
rule=ValidationRule.INDICATOR_CONFLICTS,
message=f"Multiple EMA indicators ({len(ema_indicators)}) may create visual clutter",
field_path="overlay_indicators",
suggestion="Consider using fewer EMA periods for cleaner charts"
))
if len(rsi_indicators) > 2:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.INDICATOR_CONFLICTS,
message=f"Multiple RSI indicators ({len(rsi_indicators)}) provide redundant information",
field_path="subplot_indicators",
suggestion="Usually one or two RSI periods are sufficient"
))
def _validate_resource_usage(self, config: StrategyChartConfig, report: ValidationReport) -> None:
"""Validate estimated resource usage."""
# Estimate memory usage based on indicators and subplots
base_memory = 1.0 # Base chart memory in MB
indicator_memory = len(config.overlay_indicators) * 0.1 # 0.1 MB per overlay indicator
subplot_memory = len(config.subplot_configs) * 0.5 # 0.5 MB per subplot
total_memory = base_memory + indicator_memory + subplot_memory
if total_memory > 5.0:
report.add_issue(ValidationIssue(
level=ValidationLevel.WARNING,
rule=ValidationRule.RESOURCE_USAGE,
message=f"Estimated memory usage ({total_memory:.1f} MB) is high",
field_path="configuration",
suggestion="Consider reducing indicators or subplots for lower memory usage",
context={"estimated_memory_mb": total_memory}
))
# Check for potential rendering complexity
rendering_complexity = len(config.overlay_indicators) + (len(config.subplot_configs) * 2)
if rendering_complexity > 15:
report.add_issue(ValidationIssue(
level=ValidationLevel.INFO,
rule=ValidationRule.RESOURCE_USAGE,
message=f"High rendering complexity ({rendering_complexity}) detected",
field_path="configuration",
suggestion="Complex charts may have slower rendering times"
))
def validate_configuration(
config: StrategyChartConfig,
rules: Optional[Set[ValidationRule]] = None,
strict: bool = False
) -> ValidationReport:
"""
Validate a strategy configuration with comprehensive error checking.
Args:
config: Strategy configuration to validate
rules: Optional set of validation rules to apply
strict: If True, treats warnings as errors
Returns:
Comprehensive validation report
"""
validator = ConfigurationValidator(enabled_rules=rules)
report = validator.validate_strategy_config(config)
# In strict mode, treat warnings as errors
if strict and report.warnings:
for warning in report.warnings:
warning.level = ValidationLevel.ERROR
report.errors.append(warning)
report.warnings.clear()
report.is_valid = False
return report
def get_validation_rules_info() -> Dict[ValidationRule, Dict[str, str]]:
"""
Get information about available validation rules.
Returns:
Dictionary mapping rules to their descriptions
"""
return {
ValidationRule.REQUIRED_FIELDS: {
"name": "Required Fields",
"description": "Validates that all required configuration fields are present and valid"
},
ValidationRule.HEIGHT_RATIOS: {
"name": "Height Ratios",
"description": "Validates chart and subplot height ratios sum correctly"
},
ValidationRule.INDICATOR_EXISTENCE: {
"name": "Indicator Existence",
"description": "Validates that all referenced indicators exist in the defaults"
},
ValidationRule.TIMEFRAME_FORMAT: {
"name": "Timeframe Format",
"description": "Validates timeframe format and common usage patterns"
},
ValidationRule.CHART_STYLE: {
"name": "Chart Style",
"description": "Validates chart styling options like colors, fonts, and themes"
},
ValidationRule.SUBPLOT_CONFIG: {
"name": "Subplot Configuration",
"description": "Validates subplot configurations and indicator compatibility"
},
ValidationRule.STRATEGY_CONSISTENCY: {
"name": "Strategy Consistency",
"description": "Validates that configuration matches strategy type recommendations"
},
ValidationRule.PERFORMANCE_IMPACT: {
"name": "Performance Impact",
"description": "Warns about configurations that may impact performance"
},
ValidationRule.INDICATOR_CONFLICTS: {
"name": "Indicator Conflicts",
"description": "Detects redundant or conflicting indicator combinations"
},
ValidationRule.RESOURCE_USAGE: {
"name": "Resource Usage",
"description": "Estimates and warns about high resource usage configurations"
}
}