254 lines
8.5 KiB
Python
254 lines
8.5 KiB
Python
|
|
"""
|
||
|
|
XAUBot AI - Centralized Version Management
|
||
|
|
==========================================
|
||
|
|
|
||
|
|
Semantic Versioning (SemVer): MAJOR.MINOR.PATCH
|
||
|
|
|
||
|
|
MAJOR: Incompatible API changes, breaking changes
|
||
|
|
MINOR: New features, backward compatible
|
||
|
|
PATCH: Bug fixes, backward compatible
|
||
|
|
|
||
|
|
Author: AI Assistant
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Dict, Tuple
|
||
|
|
from loguru import logger
|
||
|
|
|
||
|
|
|
||
|
|
class VersionManager:
|
||
|
|
"""
|
||
|
|
Centralized version management for XAUBot AI.
|
||
|
|
|
||
|
|
Auto-detects version based on enabled features and components.
|
||
|
|
Reads base version from VERSION file, calculates effective version.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.base_version = self._read_version_file()
|
||
|
|
self.features = self._detect_features()
|
||
|
|
self.effective_version = self._calculate_version()
|
||
|
|
|
||
|
|
def _read_version_file(self) -> Tuple[int, int, int]:
|
||
|
|
"""Read version from VERSION file."""
|
||
|
|
version_file = Path(__file__).parent.parent / "VERSION"
|
||
|
|
try:
|
||
|
|
with open(version_file, 'r') as f:
|
||
|
|
version_str = f.read().strip()
|
||
|
|
parts = version_str.split('.')
|
||
|
|
if len(parts) != 3:
|
||
|
|
raise ValueError(f"Invalid version format: {version_str}")
|
||
|
|
return tuple(int(p) for p in parts)
|
||
|
|
except Exception as e:
|
||
|
|
logger.warning(f"Could not read VERSION file: {e}, using default 0.0.0")
|
||
|
|
return (0, 0, 0)
|
||
|
|
|
||
|
|
def _detect_features(self) -> Dict[str, bool]:
|
||
|
|
"""
|
||
|
|
Auto-detect enabled features from environment and imports.
|
||
|
|
"""
|
||
|
|
features = {}
|
||
|
|
|
||
|
|
# Core features (always enabled)
|
||
|
|
features['mt5_integration'] = True
|
||
|
|
features['smc_analysis'] = True
|
||
|
|
features['ml_prediction'] = True
|
||
|
|
features['hmm_regime'] = True
|
||
|
|
|
||
|
|
# Advanced exit features (from environment flags)
|
||
|
|
features['kalman_filter'] = os.environ.get("KALMAN_ENABLED", "1") == "1"
|
||
|
|
features['advanced_exits'] = os.environ.get("ADVANCED_EXITS_ENABLED", "1") == "1"
|
||
|
|
features['predictive_intelligence'] = os.environ.get("PREDICTIVE_ENABLED", "1") == "1"
|
||
|
|
|
||
|
|
# Component detection
|
||
|
|
try:
|
||
|
|
# Fuzzy Logic
|
||
|
|
from src.fuzzy_exit_logic import FuzzyExitController
|
||
|
|
features['fuzzy_logic'] = True
|
||
|
|
except ImportError:
|
||
|
|
features['fuzzy_logic'] = False
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Kelly Criterion
|
||
|
|
from src.kelly_position_scaler import KellyPositionScaler
|
||
|
|
features['kelly_criterion'] = True
|
||
|
|
except ImportError:
|
||
|
|
features['kelly_criterion'] = False
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Trajectory Predictor
|
||
|
|
from src.trajectory_predictor import TrajectoryPredictor
|
||
|
|
features['trajectory_predictor'] = True
|
||
|
|
except ImportError:
|
||
|
|
features['trajectory_predictor'] = False
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Momentum Persistence
|
||
|
|
from src.momentum_persistence import MomentumPersistence
|
||
|
|
features['momentum_persistence'] = True
|
||
|
|
except ImportError:
|
||
|
|
features['momentum_persistence'] = False
|
||
|
|
|
||
|
|
try:
|
||
|
|
# Recovery Detector
|
||
|
|
from src.recovery_detector import RecoveryDetector
|
||
|
|
features['recovery_detector'] = True
|
||
|
|
except ImportError:
|
||
|
|
features['recovery_detector'] = False
|
||
|
|
|
||
|
|
return features
|
||
|
|
|
||
|
|
def _calculate_version(self) -> Tuple[int, int, int]:
|
||
|
|
"""
|
||
|
|
Calculate effective version based on base + features.
|
||
|
|
|
||
|
|
Version increments:
|
||
|
|
- Kalman Filter: +0.1.0 (MINOR)
|
||
|
|
- Fuzzy Logic: +0.1.0 (MINOR)
|
||
|
|
- Kelly Criterion: +0.1.0 (MINOR)
|
||
|
|
- Predictive Intelligence (all 3): +0.3.0 (MINOR)
|
||
|
|
- Each predictor separately: +0.1.0 (MINOR)
|
||
|
|
"""
|
||
|
|
major, minor, patch = self.base_version
|
||
|
|
|
||
|
|
# MINOR increments for features
|
||
|
|
if self.features.get('kalman_filter', False):
|
||
|
|
minor += 1 # v0.1.0
|
||
|
|
|
||
|
|
if self.features.get('fuzzy_logic', False):
|
||
|
|
minor += 1 # v0.2.0
|
||
|
|
|
||
|
|
if self.features.get('kelly_criterion', False):
|
||
|
|
minor += 1 # v0.3.0
|
||
|
|
|
||
|
|
# Predictive Intelligence components
|
||
|
|
predictive_count = sum([
|
||
|
|
self.features.get('trajectory_predictor', False),
|
||
|
|
self.features.get('momentum_persistence', False),
|
||
|
|
self.features.get('recovery_detector', False)
|
||
|
|
])
|
||
|
|
|
||
|
|
if predictive_count > 0:
|
||
|
|
minor += predictive_count # Each predictor = +0.1.0
|
||
|
|
|
||
|
|
return (major, minor, patch)
|
||
|
|
|
||
|
|
def get_version_string(self) -> str:
|
||
|
|
"""Get version as string (MAJOR.MINOR.PATCH)."""
|
||
|
|
major, minor, patch = self.effective_version
|
||
|
|
return f"{major}.{minor}.{patch}"
|
||
|
|
|
||
|
|
def get_detailed_version(self) -> str:
|
||
|
|
"""
|
||
|
|
Get detailed version with feature breakdown.
|
||
|
|
|
||
|
|
Example: "v0.6.0 (Kalman + Fuzzy + Kelly + Predictive)"
|
||
|
|
"""
|
||
|
|
major, minor, patch = self.effective_version
|
||
|
|
version_str = f"v{major}.{minor}.{patch}"
|
||
|
|
|
||
|
|
# Build feature list
|
||
|
|
feature_list = []
|
||
|
|
|
||
|
|
if self.features.get('kalman_filter', False):
|
||
|
|
feature_list.append("Kalman")
|
||
|
|
|
||
|
|
if self.features.get('fuzzy_logic', False):
|
||
|
|
feature_list.append("Fuzzy")
|
||
|
|
|
||
|
|
if self.features.get('kelly_criterion', False):
|
||
|
|
feature_list.append("Kelly")
|
||
|
|
|
||
|
|
# Check if all 3 predictive components enabled
|
||
|
|
predictive_all = all([
|
||
|
|
self.features.get('trajectory_predictor', False),
|
||
|
|
self.features.get('momentum_persistence', False),
|
||
|
|
self.features.get('recovery_detector', False)
|
||
|
|
])
|
||
|
|
|
||
|
|
if predictive_all:
|
||
|
|
feature_list.append("Predictive")
|
||
|
|
else:
|
||
|
|
# Add individual predictive components
|
||
|
|
if self.features.get('trajectory_predictor', False):
|
||
|
|
feature_list.append("Trajectory")
|
||
|
|
if self.features.get('momentum_persistence', False):
|
||
|
|
feature_list.append("Momentum")
|
||
|
|
if self.features.get('recovery_detector', False):
|
||
|
|
feature_list.append("Recovery")
|
||
|
|
|
||
|
|
if feature_list:
|
||
|
|
features_str = " + ".join(feature_list)
|
||
|
|
return f"{version_str} ({features_str})"
|
||
|
|
else:
|
||
|
|
return f"{version_str} (Core)"
|
||
|
|
|
||
|
|
def get_exit_strategy_version(self) -> str:
|
||
|
|
"""Get exit strategy version label."""
|
||
|
|
if self.features.get('predictive_intelligence', False):
|
||
|
|
return "Exit v6.3 Predictive Intelligence"
|
||
|
|
elif self.features.get('advanced_exits', False):
|
||
|
|
return "Exit v6.2 Advanced"
|
||
|
|
elif self.features.get('kalman_filter', False):
|
||
|
|
return "Exit v6.0 Kalman"
|
||
|
|
else:
|
||
|
|
return "Exit v5.0 Dynamic"
|
||
|
|
|
||
|
|
def print_version_info(self):
|
||
|
|
"""Print comprehensive version information."""
|
||
|
|
logger.info("=" * 60)
|
||
|
|
logger.info(f"XAUBot AI {self.get_detailed_version()}")
|
||
|
|
logger.info(f"Exit Strategy: {self.get_exit_strategy_version()}")
|
||
|
|
logger.info("=" * 60)
|
||
|
|
logger.info("Enabled Features:")
|
||
|
|
|
||
|
|
for feature, enabled in sorted(self.features.items()):
|
||
|
|
status = "✓" if enabled else "✗"
|
||
|
|
feature_name = feature.replace('_', ' ').title()
|
||
|
|
logger.info(f" [{status}] {feature_name}")
|
||
|
|
|
||
|
|
logger.info("=" * 60)
|
||
|
|
|
||
|
|
def get_component_versions(self) -> Dict[str, str]:
|
||
|
|
"""Get version info for each component."""
|
||
|
|
return {
|
||
|
|
"core": self.get_version_string(),
|
||
|
|
"exit_strategy": self.get_exit_strategy_version(),
|
||
|
|
"detailed": self.get_detailed_version(),
|
||
|
|
"base": f"{self.base_version[0]}.{self.base_version[1]}.{self.base_version[2]}",
|
||
|
|
"effective": self.get_version_string()
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
# Global version instance
|
||
|
|
__version_manager__ = VersionManager()
|
||
|
|
|
||
|
|
# Expose convenient module-level attributes
|
||
|
|
__version__ = __version_manager__.get_version_string()
|
||
|
|
__version_detailed__ = __version_manager__.get_detailed_version()
|
||
|
|
__exit_strategy__ = __version_manager__.get_exit_strategy_version()
|
||
|
|
|
||
|
|
|
||
|
|
def get_version() -> str:
|
||
|
|
"""Get version string."""
|
||
|
|
return __version__
|
||
|
|
|
||
|
|
|
||
|
|
def get_detailed_version() -> str:
|
||
|
|
"""Get detailed version with features."""
|
||
|
|
return __version_detailed__
|
||
|
|
|
||
|
|
|
||
|
|
def print_version_info():
|
||
|
|
"""Print version information."""
|
||
|
|
__version_manager__.print_version_info()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
# Test version detection
|
||
|
|
print_version_info()
|
||
|
|
print(f"\nVersion: {get_version()}")
|
||
|
|
print(f"Detailed: {get_detailed_version()}")
|
||
|
|
print(f"\nComponents: {__version_manager__.get_component_versions()}")
|