mcli-framework 7.12.0__py3-none-any.whl → 7.12.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mcli-framework might be problematic. Click here for more details.
- mcli/app/__init__.py +0 -2
- mcli/app/commands_cmd.py +19 -23
- mcli/app/completion_helpers.py +5 -5
- mcli/app/init_cmd.py +10 -10
- mcli/app/lock_cmd.py +82 -27
- mcli/app/main.py +4 -50
- mcli/app/model/model.py +5 -10
- mcli/app/store_cmd.py +8 -8
- mcli/app/video/__init__.py +0 -2
- mcli/app/video/video.py +1 -14
- mcli/chat/chat.py +90 -108
- mcli/chat/command_rag.py +0 -4
- mcli/chat/enhanced_chat.py +32 -41
- mcli/chat/system_controller.py +37 -37
- mcli/chat/system_integration.py +4 -5
- mcli/cli.py +2 -3
- mcli/lib/api/api.py +4 -9
- mcli/lib/api/daemon_client.py +19 -20
- mcli/lib/api/daemon_client_local.py +1 -3
- mcli/lib/api/daemon_decorator.py +6 -6
- mcli/lib/api/mcli_decorators.py +4 -8
- mcli/lib/auth/__init__.py +0 -1
- mcli/lib/auth/auth.py +4 -5
- mcli/lib/auth/mcli_manager.py +7 -12
- mcli/lib/auth/token_util.py +5 -5
- mcli/lib/config/__init__.py +29 -1
- mcli/lib/config/config.py +0 -1
- mcli/lib/custom_commands.py +1 -1
- mcli/lib/discovery/command_discovery.py +15 -15
- mcli/lib/erd/erd.py +7 -7
- mcli/lib/files/files.py +1 -1
- mcli/lib/fs/__init__.py +31 -1
- mcli/lib/fs/fs.py +12 -13
- mcli/lib/lib.py +0 -1
- mcli/lib/logger/logger.py +7 -10
- mcli/lib/performance/optimizer.py +25 -27
- mcli/lib/performance/rust_bridge.py +22 -27
- mcli/lib/performance/uvloop_config.py +0 -1
- mcli/lib/pickles/__init__.py +0 -1
- mcli/lib/pickles/pickles.py +0 -2
- mcli/lib/secrets/commands.py +0 -2
- mcli/lib/secrets/manager.py +0 -1
- mcli/lib/secrets/repl.py +2 -3
- mcli/lib/secrets/store.py +1 -2
- mcli/lib/services/data_pipeline.py +34 -34
- mcli/lib/services/lsh_client.py +38 -40
- mcli/lib/shell/shell.py +2 -2
- mcli/lib/toml/__init__.py +0 -1
- mcli/lib/ui/styling.py +0 -1
- mcli/lib/ui/visual_effects.py +33 -41
- mcli/lib/watcher/watcher.py +0 -1
- mcli/ml/__init__.py +1 -1
- mcli/ml/api/__init__.py +1 -1
- mcli/ml/api/app.py +8 -9
- mcli/ml/api/middleware.py +10 -10
- mcli/ml/api/routers/__init__.py +1 -1
- mcli/ml/api/routers/admin_router.py +3 -3
- mcli/ml/api/routers/auth_router.py +17 -18
- mcli/ml/api/routers/backtest_router.py +2 -2
- mcli/ml/api/routers/data_router.py +2 -2
- mcli/ml/api/routers/model_router.py +14 -15
- mcli/ml/api/routers/monitoring_router.py +2 -2
- mcli/ml/api/routers/portfolio_router.py +2 -2
- mcli/ml/api/routers/prediction_router.py +10 -9
- mcli/ml/api/routers/trade_router.py +2 -2
- mcli/ml/api/routers/websocket_router.py +6 -7
- mcli/ml/api/schemas.py +2 -2
- mcli/ml/auth/__init__.py +1 -1
- mcli/ml/auth/auth_manager.py +22 -23
- mcli/ml/auth/models.py +17 -17
- mcli/ml/auth/permissions.py +17 -17
- mcli/ml/backtesting/__init__.py +1 -1
- mcli/ml/backtesting/backtest_engine.py +31 -35
- mcli/ml/backtesting/performance_metrics.py +12 -14
- mcli/ml/backtesting/run.py +1 -2
- mcli/ml/cache.py +35 -36
- mcli/ml/cli/__init__.py +1 -1
- mcli/ml/cli/main.py +21 -24
- mcli/ml/config/__init__.py +1 -1
- mcli/ml/config/settings.py +28 -29
- mcli/ml/configs/__init__.py +1 -1
- mcli/ml/configs/dvc_config.py +14 -15
- mcli/ml/configs/mlflow_config.py +12 -13
- mcli/ml/configs/mlops_manager.py +19 -21
- mcli/ml/dashboard/__init__.py +4 -4
- mcli/ml/dashboard/app.py +20 -30
- mcli/ml/dashboard/app_supabase.py +16 -19
- mcli/ml/dashboard/app_training.py +11 -14
- mcli/ml/dashboard/cli.py +2 -2
- mcli/ml/dashboard/common.py +2 -3
- mcli/ml/dashboard/components/__init__.py +1 -1
- mcli/ml/dashboard/components/charts.py +13 -11
- mcli/ml/dashboard/components/metrics.py +7 -7
- mcli/ml/dashboard/components/tables.py +12 -9
- mcli/ml/dashboard/overview.py +2 -2
- mcli/ml/dashboard/pages/__init__.py +1 -1
- mcli/ml/dashboard/pages/cicd.py +15 -18
- mcli/ml/dashboard/pages/debug_dependencies.py +7 -7
- mcli/ml/dashboard/pages/monte_carlo_predictions.py +11 -18
- mcli/ml/dashboard/pages/predictions_enhanced.py +24 -32
- mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -24
- mcli/ml/dashboard/pages/test_portfolio.py +3 -6
- mcli/ml/dashboard/pages/trading.py +16 -18
- mcli/ml/dashboard/pages/workflows.py +20 -30
- mcli/ml/dashboard/utils.py +9 -9
- mcli/ml/dashboard/warning_suppression.py +3 -3
- mcli/ml/data_ingestion/__init__.py +1 -1
- mcli/ml/data_ingestion/api_connectors.py +41 -46
- mcli/ml/data_ingestion/data_pipeline.py +36 -46
- mcli/ml/data_ingestion/stream_processor.py +43 -46
- mcli/ml/database/__init__.py +1 -1
- mcli/ml/database/migrations/env.py +2 -2
- mcli/ml/database/models.py +22 -24
- mcli/ml/database/session.py +14 -14
- mcli/ml/experimentation/__init__.py +1 -1
- mcli/ml/experimentation/ab_testing.py +45 -46
- mcli/ml/features/__init__.py +1 -1
- mcli/ml/features/ensemble_features.py +22 -27
- mcli/ml/features/recommendation_engine.py +30 -30
- mcli/ml/features/stock_features.py +29 -32
- mcli/ml/features/test_feature_engineering.py +10 -11
- mcli/ml/logging.py +4 -4
- mcli/ml/mlops/__init__.py +1 -1
- mcli/ml/mlops/data_versioning.py +29 -30
- mcli/ml/mlops/experiment_tracker.py +24 -24
- mcli/ml/mlops/model_serving.py +31 -34
- mcli/ml/mlops/pipeline_orchestrator.py +27 -35
- mcli/ml/models/__init__.py +5 -6
- mcli/ml/models/base_models.py +23 -23
- mcli/ml/models/ensemble_models.py +31 -31
- mcli/ml/models/recommendation_models.py +18 -19
- mcli/ml/models/test_models.py +14 -16
- mcli/ml/monitoring/__init__.py +1 -1
- mcli/ml/monitoring/drift_detection.py +32 -36
- mcli/ml/monitoring/metrics.py +2 -2
- mcli/ml/optimization/__init__.py +1 -1
- mcli/ml/optimization/optimize.py +1 -2
- mcli/ml/optimization/portfolio_optimizer.py +30 -32
- mcli/ml/predictions/__init__.py +1 -1
- mcli/ml/preprocessing/__init__.py +1 -1
- mcli/ml/preprocessing/data_cleaners.py +22 -23
- mcli/ml/preprocessing/feature_extractors.py +23 -26
- mcli/ml/preprocessing/ml_pipeline.py +23 -23
- mcli/ml/preprocessing/test_preprocessing.py +7 -8
- mcli/ml/scripts/populate_sample_data.py +0 -4
- mcli/ml/serving/serve.py +1 -2
- mcli/ml/tasks.py +17 -17
- mcli/ml/tests/test_integration.py +29 -30
- mcli/ml/tests/test_training_dashboard.py +21 -21
- mcli/ml/trading/__init__.py +1 -1
- mcli/ml/trading/migrations.py +5 -5
- mcli/ml/trading/models.py +21 -23
- mcli/ml/trading/paper_trading.py +16 -13
- mcli/ml/trading/risk_management.py +17 -18
- mcli/ml/trading/trading_service.py +25 -28
- mcli/ml/training/__init__.py +1 -1
- mcli/ml/training/train.py +0 -1
- mcli/public/oi/oi.py +1 -2
- mcli/self/completion_cmd.py +6 -10
- mcli/self/logs_cmd.py +19 -24
- mcli/self/migrate_cmd.py +22 -20
- mcli/self/redis_cmd.py +10 -11
- mcli/self/self_cmd.py +62 -18
- mcli/self/store_cmd.py +10 -12
- mcli/self/visual_cmd.py +9 -14
- mcli/self/zsh_cmd.py +2 -4
- mcli/workflow/daemon/async_command_database.py +23 -24
- mcli/workflow/daemon/async_process_manager.py +27 -29
- mcli/workflow/daemon/client.py +27 -33
- mcli/workflow/daemon/daemon.py +32 -36
- mcli/workflow/daemon/enhanced_daemon.py +24 -33
- mcli/workflow/daemon/process_cli.py +11 -12
- mcli/workflow/daemon/process_manager.py +23 -26
- mcli/workflow/daemon/test_daemon.py +4 -5
- mcli/workflow/dashboard/dashboard_cmd.py +0 -1
- mcli/workflow/doc_convert.py +15 -17
- mcli/workflow/gcloud/__init__.py +0 -1
- mcli/workflow/gcloud/gcloud.py +11 -8
- mcli/workflow/git_commit/ai_service.py +14 -15
- mcli/workflow/lsh_integration.py +9 -11
- mcli/workflow/model_service/client.py +26 -31
- mcli/workflow/model_service/download_and_run_efficient_models.py +10 -14
- mcli/workflow/model_service/lightweight_embedder.py +25 -35
- mcli/workflow/model_service/lightweight_model_server.py +26 -32
- mcli/workflow/model_service/lightweight_test.py +7 -10
- mcli/workflow/model_service/model_service.py +80 -91
- mcli/workflow/model_service/ollama_efficient_runner.py +14 -18
- mcli/workflow/model_service/openai_adapter.py +23 -23
- mcli/workflow/model_service/pdf_processor.py +21 -26
- mcli/workflow/model_service/test_efficient_runner.py +12 -16
- mcli/workflow/model_service/test_example.py +11 -13
- mcli/workflow/model_service/test_integration.py +3 -5
- mcli/workflow/model_service/test_new_features.py +7 -8
- mcli/workflow/notebook/converter.py +1 -1
- mcli/workflow/notebook/notebook_cmd.py +5 -6
- mcli/workflow/notebook/schema.py +0 -1
- mcli/workflow/notebook/validator.py +7 -3
- mcli/workflow/openai/openai.py +1 -2
- mcli/workflow/registry/registry.py +4 -1
- mcli/workflow/repo/repo.py +6 -7
- mcli/workflow/scheduler/cron_parser.py +16 -19
- mcli/workflow/scheduler/job.py +10 -10
- mcli/workflow/scheduler/monitor.py +15 -15
- mcli/workflow/scheduler/persistence.py +17 -18
- mcli/workflow/scheduler/scheduler.py +37 -38
- mcli/workflow/secrets/__init__.py +1 -1
- mcli/workflow/sync/test_cmd.py +0 -1
- mcli/workflow/wakatime/__init__.py +5 -9
- mcli/workflow/wakatime/wakatime.py +1 -2
- {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/METADATA +1 -1
- mcli_framework-7.12.3.dist-info/RECORD +279 -0
- mcli_framework-7.12.0.dist-info/RECORD +0 -279
- {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/WHEEL +0 -0
- {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/top_level.txt +0 -0
mcli/ml/models/test_models.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Test script for ensemble models"""
|
|
1
|
+
"""Test script for ensemble models."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
@@ -6,7 +6,6 @@ import sys
|
|
|
6
6
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../.."))
|
|
7
7
|
|
|
8
8
|
import logging
|
|
9
|
-
from datetime import datetime, timedelta
|
|
10
9
|
|
|
11
10
|
import numpy as np
|
|
12
11
|
import pandas as pd
|
|
@@ -18,7 +17,7 @@ logger = logging.getLogger(__name__)
|
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
def generate_mock_features(n_samples: int = 500, n_features: int = 150) -> pd.DataFrame:
|
|
21
|
-
"""Generate mock feature data for testing"""
|
|
20
|
+
"""Generate mock feature data for testing."""
|
|
22
21
|
np.random.seed(42)
|
|
23
22
|
|
|
24
23
|
# Create realistic feature names
|
|
@@ -50,7 +49,7 @@ def generate_mock_features(n_samples: int = 500, n_features: int = 150) -> pd.Da
|
|
|
50
49
|
|
|
51
50
|
# Generate correlated features that simulate real market data
|
|
52
51
|
features = []
|
|
53
|
-
for
|
|
52
|
+
for _i in range(n_samples):
|
|
54
53
|
# Base market trend
|
|
55
54
|
market_trend = np.random.normal(0, 1)
|
|
56
55
|
|
|
@@ -85,7 +84,7 @@ def generate_mock_features(n_samples: int = 500, n_features: int = 150) -> pd.Da
|
|
|
85
84
|
|
|
86
85
|
|
|
87
86
|
def generate_mock_targets(n_samples: int) -> tuple:
|
|
88
|
-
"""Generate realistic target variables"""
|
|
87
|
+
"""Generate realistic target variables."""
|
|
89
88
|
np.random.seed(42)
|
|
90
89
|
|
|
91
90
|
# Generate correlated targets
|
|
@@ -104,7 +103,7 @@ def generate_mock_targets(n_samples: int) -> tuple:
|
|
|
104
103
|
|
|
105
104
|
|
|
106
105
|
def test_base_models():
|
|
107
|
-
"""Test base model functionality"""
|
|
106
|
+
"""Test base model functionality."""
|
|
108
107
|
logger.info("Testing base models...")
|
|
109
108
|
|
|
110
109
|
from base_models import MLPBaseModel, ResNetModel
|
|
@@ -122,7 +121,7 @@ def test_base_models():
|
|
|
122
121
|
logger.info(f"MLP output shape: {output.shape}")
|
|
123
122
|
|
|
124
123
|
# Test prediction
|
|
125
|
-
|
|
124
|
+
mlp_model.predict_proba(X)
|
|
126
125
|
predictions = mlp_model.predict(X)
|
|
127
126
|
logger.info(f"MLP predictions shape: {predictions.shape}")
|
|
128
127
|
|
|
@@ -140,7 +139,7 @@ def test_base_models():
|
|
|
140
139
|
|
|
141
140
|
|
|
142
141
|
def test_ensemble_models():
|
|
143
|
-
"""Test ensemble model functionality"""
|
|
142
|
+
"""Test ensemble model functionality."""
|
|
144
143
|
logger.info("Testing ensemble models...")
|
|
145
144
|
|
|
146
145
|
from ensemble_models import (
|
|
@@ -233,7 +232,7 @@ def test_ensemble_models():
|
|
|
233
232
|
logger.info(f"Individual predictions: {len(individual_preds)} models")
|
|
234
233
|
|
|
235
234
|
# Test prediction methods
|
|
236
|
-
|
|
235
|
+
_ensemble_probas = ensemble_model.predict_proba(X.values[:20]) # noqa: F841
|
|
237
236
|
ensemble_preds = ensemble_model.predict(X.values[:20])
|
|
238
237
|
logger.info(f"Ensemble predictions shape: {ensemble_preds.shape}")
|
|
239
238
|
|
|
@@ -241,14 +240,13 @@ def test_ensemble_models():
|
|
|
241
240
|
|
|
242
241
|
|
|
243
242
|
def test_recommendation_model():
|
|
244
|
-
"""Test recommendation model"""
|
|
243
|
+
"""Test recommendation model."""
|
|
245
244
|
logger.info("Testing recommendation model...")
|
|
246
245
|
|
|
247
246
|
from ensemble_models import EnsembleConfig, ModelConfig
|
|
248
247
|
from recommendation_models import (
|
|
249
248
|
PortfolioRecommendation,
|
|
250
249
|
RecommendationConfig,
|
|
251
|
-
RecommendationTrainer,
|
|
252
250
|
StockRecommendationModel,
|
|
253
251
|
)
|
|
254
252
|
|
|
@@ -329,7 +327,7 @@ def test_recommendation_model():
|
|
|
329
327
|
|
|
330
328
|
|
|
331
329
|
def test_model_training():
|
|
332
|
-
"""Test model training functionality"""
|
|
330
|
+
"""Test model training functionality."""
|
|
333
331
|
logger.info("Testing model training...")
|
|
334
332
|
|
|
335
333
|
from ensemble_models import EnsembleConfig, EnsembleTrainer, ModelConfig
|
|
@@ -376,7 +374,7 @@ def test_model_training():
|
|
|
376
374
|
logger.info("Training ensemble model...")
|
|
377
375
|
ensemble_result = ensemble_trainer.train(X_train.values, y_train, X_val.values, y_val)
|
|
378
376
|
|
|
379
|
-
logger.info(
|
|
377
|
+
logger.info("Ensemble training metrics:")
|
|
380
378
|
logger.info(f" Train accuracy: {ensemble_result.train_metrics.accuracy:.3f}")
|
|
381
379
|
logger.info(f" Val accuracy: {ensemble_result.val_metrics.accuracy:.3f}")
|
|
382
380
|
|
|
@@ -398,7 +396,7 @@ def test_model_training():
|
|
|
398
396
|
batch_size=32,
|
|
399
397
|
)
|
|
400
398
|
|
|
401
|
-
logger.info(
|
|
399
|
+
logger.info("Recommendation training metrics:")
|
|
402
400
|
logger.info(f" Train accuracy: {rec_result.train_metrics.accuracy:.3f}")
|
|
403
401
|
logger.info(f" Val accuracy: {rec_result.val_metrics.accuracy:.3f}")
|
|
404
402
|
|
|
@@ -413,7 +411,7 @@ def test_model_training():
|
|
|
413
411
|
|
|
414
412
|
|
|
415
413
|
def test_model_persistence():
|
|
416
|
-
"""Test model saving and loading"""
|
|
414
|
+
"""Test model saving and loading."""
|
|
417
415
|
logger.info("Testing model persistence...")
|
|
418
416
|
|
|
419
417
|
import tempfile
|
|
@@ -450,7 +448,7 @@ def test_model_persistence():
|
|
|
450
448
|
|
|
451
449
|
|
|
452
450
|
def main():
|
|
453
|
-
"""Run all model tests"""
|
|
451
|
+
"""Run all model tests."""
|
|
454
452
|
logger.info("Starting ensemble model tests...")
|
|
455
453
|
|
|
456
454
|
try:
|
mcli/ml/monitoring/__init__.py
CHANGED
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
"""Model monitoring and drift detection for ML systems"""
|
|
1
|
+
"""Model monitoring and drift detection for ML systems."""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import json
|
|
5
4
|
import logging
|
|
6
5
|
import pickle
|
|
7
|
-
import warnings
|
|
8
6
|
from dataclasses import asdict, dataclass, field
|
|
9
7
|
from datetime import datetime, timedelta
|
|
10
8
|
from enum import Enum
|
|
11
9
|
from pathlib import Path
|
|
12
|
-
from typing import Any, Callable, Dict, List, Optional
|
|
10
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
13
11
|
|
|
14
12
|
import numpy as np
|
|
15
13
|
import pandas as pd
|
|
16
|
-
from scipy import stats
|
|
17
14
|
from sklearn.ensemble import IsolationForest
|
|
18
15
|
from sklearn.metrics import ks_2samp
|
|
19
|
-
from sklearn.model_selection import train_test_split
|
|
20
16
|
|
|
21
17
|
logger = logging.getLogger(__name__)
|
|
22
18
|
|
|
@@ -37,7 +33,7 @@ class AlertSeverity(Enum):
|
|
|
37
33
|
|
|
38
34
|
@dataclass
|
|
39
35
|
class DriftAlert:
|
|
40
|
-
"""Drift detection alert"""
|
|
36
|
+
"""Drift detection alert."""
|
|
41
37
|
|
|
42
38
|
timestamp: datetime
|
|
43
39
|
drift_type: DriftType
|
|
@@ -51,7 +47,7 @@ class DriftAlert:
|
|
|
51
47
|
|
|
52
48
|
@dataclass
|
|
53
49
|
class ModelMetrics:
|
|
54
|
-
"""Model performance metrics"""
|
|
50
|
+
"""Model performance metrics."""
|
|
55
51
|
|
|
56
52
|
timestamp: datetime
|
|
57
53
|
accuracy: float
|
|
@@ -67,7 +63,7 @@ class ModelMetrics:
|
|
|
67
63
|
|
|
68
64
|
@dataclass
|
|
69
65
|
class DataProfile:
|
|
70
|
-
"""Statistical profile of data"""
|
|
66
|
+
"""Statistical profile of data."""
|
|
71
67
|
|
|
72
68
|
feature_means: Dict[str, float]
|
|
73
69
|
feature_stds: Dict[str, float]
|
|
@@ -79,7 +75,7 @@ class DataProfile:
|
|
|
79
75
|
|
|
80
76
|
|
|
81
77
|
class StatisticalDriftDetector:
|
|
82
|
-
"""Detect statistical drift in data distributions"""
|
|
78
|
+
"""Detect statistical drift in data distributions."""
|
|
83
79
|
|
|
84
80
|
def __init__(
|
|
85
81
|
self, reference_data: pd.DataFrame, significance_level: float = 0.05, min_samples: int = 100
|
|
@@ -90,7 +86,7 @@ class StatisticalDriftDetector:
|
|
|
90
86
|
self.min_samples = min_samples
|
|
91
87
|
|
|
92
88
|
def detect_drift(self, current_data: pd.DataFrame) -> Dict[str, Any]:
|
|
93
|
-
"""Detect drift between reference and current data"""
|
|
89
|
+
"""Detect drift between reference and current data."""
|
|
94
90
|
if len(current_data) < self.min_samples:
|
|
95
91
|
return {"drift_detected": False, "message": "Insufficient samples"}
|
|
96
92
|
|
|
@@ -138,7 +134,7 @@ class StatisticalDriftDetector:
|
|
|
138
134
|
return drift_results
|
|
139
135
|
|
|
140
136
|
def _create_data_profile(self, data: pd.DataFrame) -> DataProfile:
|
|
141
|
-
"""Create statistical profile of data"""
|
|
137
|
+
"""Create statistical profile of data."""
|
|
142
138
|
numeric_data = data.select_dtypes(include=[np.number])
|
|
143
139
|
|
|
144
140
|
return DataProfile(
|
|
@@ -156,7 +152,7 @@ class StatisticalDriftDetector:
|
|
|
156
152
|
def _calculate_psi(
|
|
157
153
|
self, reference_data: pd.DataFrame, current_data: pd.DataFrame
|
|
158
154
|
) -> Dict[str, float]:
|
|
159
|
-
"""Calculate Population Stability Index for each feature"""
|
|
155
|
+
"""Calculate Population Stability Index for each feature."""
|
|
160
156
|
psi_scores = {}
|
|
161
157
|
|
|
162
158
|
for feature in reference_data.columns:
|
|
@@ -173,7 +169,7 @@ class StatisticalDriftDetector:
|
|
|
173
169
|
return psi_scores
|
|
174
170
|
|
|
175
171
|
def _psi_score(self, reference: pd.Series, current: pd.Series, bins: int = 10) -> float:
|
|
176
|
-
"""Calculate PSI score between two distributions"""
|
|
172
|
+
"""Calculate PSI score between two distributions."""
|
|
177
173
|
try:
|
|
178
174
|
# Create bins based on reference data
|
|
179
175
|
ref_min, ref_max = reference.min(), reference.max()
|
|
@@ -203,7 +199,7 @@ class StatisticalDriftDetector:
|
|
|
203
199
|
def _compare_feature_distributions(
|
|
204
200
|
self, ref_profile: DataProfile, curr_profile: DataProfile
|
|
205
201
|
) -> Dict[str, Dict[str, float]]:
|
|
206
|
-
"""Compare feature distributions between profiles"""
|
|
202
|
+
"""Compare feature distributions between profiles."""
|
|
207
203
|
comparisons = {}
|
|
208
204
|
|
|
209
205
|
for feature in ref_profile.feature_means.keys():
|
|
@@ -231,7 +227,7 @@ class StatisticalDriftDetector:
|
|
|
231
227
|
|
|
232
228
|
|
|
233
229
|
class ConceptDriftDetector:
|
|
234
|
-
"""Detect concept drift in model predictions"""
|
|
230
|
+
"""Detect concept drift in model predictions."""
|
|
235
231
|
|
|
236
232
|
def __init__(self, window_size: int = 1000, detection_threshold: float = 0.05):
|
|
237
233
|
self.window_size = window_size
|
|
@@ -239,7 +235,7 @@ class ConceptDriftDetector:
|
|
|
239
235
|
self.historical_metrics = []
|
|
240
236
|
|
|
241
237
|
def add_batch_metrics(self, metrics: ModelMetrics):
|
|
242
|
-
"""Add batch metrics for drift detection"""
|
|
238
|
+
"""Add batch metrics for drift detection."""
|
|
243
239
|
self.historical_metrics.append(metrics)
|
|
244
240
|
|
|
245
241
|
# Keep only recent metrics
|
|
@@ -247,7 +243,7 @@ class ConceptDriftDetector:
|
|
|
247
243
|
self.historical_metrics = self.historical_metrics[-self.window_size :]
|
|
248
244
|
|
|
249
245
|
def detect_concept_drift(self) -> Dict[str, Any]:
|
|
250
|
-
"""Detect concept drift using model performance degradation"""
|
|
246
|
+
"""Detect concept drift using model performance degradation."""
|
|
251
247
|
if len(self.historical_metrics) < self.window_size:
|
|
252
248
|
return {"drift_detected": False, "message": "Insufficient historical data"}
|
|
253
249
|
|
|
@@ -292,7 +288,7 @@ class ConceptDriftDetector:
|
|
|
292
288
|
}
|
|
293
289
|
|
|
294
290
|
def _calculate_average_performance(self, metrics_list: List[ModelMetrics]) -> Dict[str, float]:
|
|
295
|
-
"""Calculate average performance metrics"""
|
|
291
|
+
"""Calculate average performance metrics."""
|
|
296
292
|
if not metrics_list:
|
|
297
293
|
return {}
|
|
298
294
|
|
|
@@ -312,7 +308,7 @@ class ConceptDriftDetector:
|
|
|
312
308
|
|
|
313
309
|
|
|
314
310
|
class OutlierDetector:
|
|
315
|
-
"""Detect outliers in incoming data"""
|
|
311
|
+
"""Detect outliers in incoming data."""
|
|
316
312
|
|
|
317
313
|
def __init__(self, contamination: float = 0.1):
|
|
318
314
|
self.contamination = contamination
|
|
@@ -320,7 +316,7 @@ class OutlierDetector:
|
|
|
320
316
|
self.is_fitted = False
|
|
321
317
|
|
|
322
318
|
def fit(self, reference_data: pd.DataFrame):
|
|
323
|
-
"""Fit outlier detector on reference data"""
|
|
319
|
+
"""Fit outlier detector on reference data."""
|
|
324
320
|
numeric_data = reference_data.select_dtypes(include=[np.number])
|
|
325
321
|
|
|
326
322
|
if numeric_data.empty:
|
|
@@ -332,7 +328,7 @@ class OutlierDetector:
|
|
|
332
328
|
self.is_fitted = True
|
|
333
329
|
|
|
334
330
|
def detect_outliers(self, data: pd.DataFrame) -> Dict[str, Any]:
|
|
335
|
-
"""Detect outliers in new data"""
|
|
331
|
+
"""Detect outliers in new data."""
|
|
336
332
|
if not self.is_fitted:
|
|
337
333
|
return {"outliers_detected": False, "message": "Detector not fitted"}
|
|
338
334
|
|
|
@@ -358,7 +354,7 @@ class OutlierDetector:
|
|
|
358
354
|
|
|
359
355
|
|
|
360
356
|
class ModelMonitor:
|
|
361
|
-
"""Comprehensive model monitoring system"""
|
|
357
|
+
"""Comprehensive model monitoring system."""
|
|
362
358
|
|
|
363
359
|
def __init__(self, model_name: str, storage_path: Path = Path("monitoring")):
|
|
364
360
|
self.model_name = model_name
|
|
@@ -385,7 +381,7 @@ class ModelMonitor:
|
|
|
385
381
|
self.monitoring_history = []
|
|
386
382
|
|
|
387
383
|
def setup_reference_data(self, reference_data: pd.DataFrame):
|
|
388
|
-
"""Set up reference data for drift detection"""
|
|
384
|
+
"""Set up reference data for drift detection."""
|
|
389
385
|
self.statistical_detector = StatisticalDriftDetector(reference_data)
|
|
390
386
|
self.outlier_detector.fit(reference_data)
|
|
391
387
|
|
|
@@ -398,7 +394,7 @@ class ModelMonitor:
|
|
|
398
394
|
predictions: np.ndarray,
|
|
399
395
|
true_labels: Optional[np.ndarray] = None,
|
|
400
396
|
) -> Dict[str, Any]:
|
|
401
|
-
"""Monitor a batch of data and predictions"""
|
|
397
|
+
"""Monitor a batch of data and predictions."""
|
|
402
398
|
monitoring_result = {
|
|
403
399
|
"timestamp": datetime.now(),
|
|
404
400
|
"batch_size": len(current_data),
|
|
@@ -480,11 +476,11 @@ class ModelMonitor:
|
|
|
480
476
|
return monitoring_result
|
|
481
477
|
|
|
482
478
|
def add_alert_handler(self, handler: Callable[[DriftAlert], None]):
|
|
483
|
-
"""Add alert handler function"""
|
|
479
|
+
"""Add alert handler function."""
|
|
484
480
|
self.alert_handlers.append(handler)
|
|
485
481
|
|
|
486
482
|
def get_monitoring_summary(self, days: int = 7) -> Dict[str, Any]:
|
|
487
|
-
"""Get monitoring summary for the last N days"""
|
|
483
|
+
"""Get monitoring summary for the last N days."""
|
|
488
484
|
cutoff_date = datetime.now() - timedelta(days=days)
|
|
489
485
|
recent_results = [
|
|
490
486
|
result for result in self.monitoring_history if result["timestamp"] >= cutoff_date
|
|
@@ -521,7 +517,7 @@ class ModelMonitor:
|
|
|
521
517
|
}
|
|
522
518
|
|
|
523
519
|
def _analyze_predictions(self, predictions: np.ndarray) -> Dict[str, Any]:
|
|
524
|
-
"""Analyze prediction distribution"""
|
|
520
|
+
"""Analyze prediction distribution."""
|
|
525
521
|
return {
|
|
526
522
|
"mean": float(np.mean(predictions)),
|
|
527
523
|
"std": float(np.std(predictions)),
|
|
@@ -533,7 +529,7 @@ class ModelMonitor:
|
|
|
533
529
|
def _calculate_performance_metrics(
|
|
534
530
|
self, predictions: np.ndarray, true_labels: np.ndarray
|
|
535
531
|
) -> ModelMetrics:
|
|
536
|
-
"""Calculate model performance metrics"""
|
|
532
|
+
"""Calculate model performance metrics."""
|
|
537
533
|
# Convert to binary if needed
|
|
538
534
|
if len(np.unique(true_labels)) == 2:
|
|
539
535
|
# Binary classification
|
|
@@ -575,7 +571,7 @@ class ModelMonitor:
|
|
|
575
571
|
)
|
|
576
572
|
|
|
577
573
|
def _handle_alert(self, alert: DriftAlert):
|
|
578
|
-
"""Handle drift alert"""
|
|
574
|
+
"""Handle drift alert."""
|
|
579
575
|
logger.warning(
|
|
580
576
|
f"DRIFT ALERT: {alert.description} "
|
|
581
577
|
f"(Type: {alert.drift_type.value}, Severity: {alert.severity.value})"
|
|
@@ -589,7 +585,7 @@ class ModelMonitor:
|
|
|
589
585
|
logger.error(f"Alert handler failed: {e}")
|
|
590
586
|
|
|
591
587
|
def _save_monitoring_result(self, result: Dict[str, Any]):
|
|
592
|
-
"""Save monitoring result to storage"""
|
|
588
|
+
"""Save monitoring result to storage."""
|
|
593
589
|
timestamp_str = result["timestamp"].strftime("%Y%m%d_%H%M%S")
|
|
594
590
|
filename = self.storage_path / f"monitoring_{timestamp_str}.json"
|
|
595
591
|
|
|
@@ -606,14 +602,14 @@ class ModelMonitor:
|
|
|
606
602
|
self.monitoring_history = self.monitoring_history[-500:]
|
|
607
603
|
|
|
608
604
|
def _save_reference_profile(self, reference_data: pd.DataFrame):
|
|
609
|
-
"""Save reference data profile"""
|
|
605
|
+
"""Save reference data profile."""
|
|
610
606
|
profile_file = self.storage_path / "reference_profile.pkl"
|
|
611
607
|
|
|
612
608
|
with open(profile_file, "wb") as f:
|
|
613
609
|
pickle.dump(reference_data, f)
|
|
614
610
|
|
|
615
611
|
def _make_serializable(self, obj: Any) -> Any:
|
|
616
|
-
"""Convert object to JSON-serializable format"""
|
|
612
|
+
"""Convert object to JSON-serializable format."""
|
|
617
613
|
if isinstance(obj, np.ndarray):
|
|
618
614
|
return obj.tolist()
|
|
619
615
|
elif isinstance(obj, np.integer):
|
|
@@ -622,7 +618,7 @@ class ModelMonitor:
|
|
|
622
618
|
return float(obj)
|
|
623
619
|
elif isinstance(obj, datetime):
|
|
624
620
|
return obj.isoformat()
|
|
625
|
-
elif isinstance(obj, DriftAlert):
|
|
621
|
+
elif isinstance(obj, DriftAlert): # noqa: SIM114
|
|
626
622
|
return asdict(obj)
|
|
627
623
|
elif isinstance(obj, ModelMetrics):
|
|
628
624
|
return asdict(obj)
|
|
@@ -638,13 +634,13 @@ class ModelMonitor:
|
|
|
638
634
|
|
|
639
635
|
# Example alert handlers
|
|
640
636
|
def email_alert_handler(alert: DriftAlert):
|
|
641
|
-
"""Example email alert handler"""
|
|
637
|
+
"""Example email alert handler."""
|
|
642
638
|
logger.info(f"EMAIL ALERT: {alert.description}")
|
|
643
639
|
# In production, would send actual email
|
|
644
640
|
|
|
645
641
|
|
|
646
642
|
def slack_alert_handler(alert: DriftAlert):
|
|
647
|
-
"""Example Slack alert handler"""
|
|
643
|
+
"""Example Slack alert handler."""
|
|
648
644
|
logger.info(f"SLACK ALERT: {alert.description}")
|
|
649
645
|
# In production, would send to Slack
|
|
650
646
|
|
mcli/ml/monitoring/metrics.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Prometheus metrics for monitoring"""
|
|
1
|
+
"""Prometheus metrics for monitoring."""
|
|
2
2
|
|
|
3
3
|
from prometheus_client import Counter, Gauge, Histogram, generate_latest
|
|
4
4
|
|
|
@@ -29,5 +29,5 @@ cache_hit_rate = Gauge("cache_hit_rate", "Cache hit rate")
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def get_metrics():
|
|
32
|
-
"""Get Prometheus metrics in text format"""
|
|
32
|
+
"""Get Prometheus metrics in text format."""
|
|
33
33
|
return generate_latest().decode("utf-8")
|
mcli/ml/optimization/__init__.py
CHANGED
mcli/ml/optimization/optimize.py
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
import click
|
|
5
5
|
|
|
6
|
-
from mcli.lib.ui.styling import error, info
|
|
6
|
+
from mcli.lib.ui.styling import error, info
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
@click.group(name="mcli-optimize", help="Portfolio optimization CLI for MCLI trading system")
|
|
10
10
|
def cli():
|
|
11
11
|
"""Main CLI group for portfolio optimization."""
|
|
12
|
-
pass
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
@cli.command(name="portfolio", help="Optimize portfolio allocation")
|