mcli-framework 7.12.2__py3-none-any.whl → 7.12.4__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 +30 -26
- mcli/app/completion_helpers.py +5 -5
- mcli/app/init_cmd.py +10 -10
- mcli/app/lock_cmd.py +29 -24
- mcli/app/main.py +2 -8
- 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 +10 -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.2.dist-info → mcli_framework-7.12.4.dist-info}/METADATA +1 -1
- mcli_framework-7.12.4.dist-info/RECORD +279 -0
- mcli_framework-7.12.2.dist-info/RECORD +0 -279
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/WHEEL +0 -0
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.12.2.dist-info → mcli_framework-7.12.4.dist-info}/top_level.txt +0 -0
mcli/ml/models/base_models.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Base classes for ML models"""
|
|
1
|
+
"""Base classes for ML models."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
|
|
|
15
15
|
|
|
16
16
|
@dataclass
|
|
17
17
|
class ModelMetrics:
|
|
18
|
-
"""Container for model performance metrics"""
|
|
18
|
+
"""Container for model performance metrics."""
|
|
19
19
|
|
|
20
20
|
accuracy: float
|
|
21
21
|
precision: float
|
|
@@ -30,7 +30,7 @@ class ModelMetrics:
|
|
|
30
30
|
avg_loss: Optional[float] = None
|
|
31
31
|
|
|
32
32
|
def to_dict(self) -> Dict[str, float]:
|
|
33
|
-
"""Convert metrics to dictionary"""
|
|
33
|
+
"""Convert metrics to dictionary."""
|
|
34
34
|
return {
|
|
35
35
|
"accuracy": self.accuracy,
|
|
36
36
|
"precision": self.precision,
|
|
@@ -48,7 +48,7 @@ class ModelMetrics:
|
|
|
48
48
|
|
|
49
49
|
@dataclass
|
|
50
50
|
class ValidationResult:
|
|
51
|
-
"""Container for validation results"""
|
|
51
|
+
"""Container for validation results."""
|
|
52
52
|
|
|
53
53
|
train_metrics: ModelMetrics
|
|
54
54
|
val_metrics: ModelMetrics
|
|
@@ -60,7 +60,7 @@ class ValidationResult:
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class BaseStockModel(nn.Module, ABC):
|
|
63
|
-
"""Abstract base class for all stock prediction models"""
|
|
63
|
+
"""Abstract base class for all stock prediction models."""
|
|
64
64
|
|
|
65
65
|
def __init__(self, input_dim: int, config: Optional[Dict[str, Any]] = None):
|
|
66
66
|
super().__init__()
|
|
@@ -73,21 +73,21 @@ class BaseStockModel(nn.Module, ABC):
|
|
|
73
73
|
|
|
74
74
|
@abstractmethod
|
|
75
75
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
76
|
-
"""Forward pass through the model"""
|
|
76
|
+
"""Forward pass through the model."""
|
|
77
77
|
pass
|
|
78
78
|
|
|
79
79
|
@abstractmethod
|
|
80
80
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
81
|
-
"""Predict class probabilities"""
|
|
81
|
+
"""Predict class probabilities."""
|
|
82
82
|
pass
|
|
83
83
|
|
|
84
84
|
def predict(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
85
|
-
"""Make binary predictions"""
|
|
85
|
+
"""Make binary predictions."""
|
|
86
86
|
probas = self.predict_proba(X)
|
|
87
87
|
return (probas[:, 1] > 0.5).astype(int)
|
|
88
88
|
|
|
89
89
|
def preprocess_input(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> torch.Tensor:
|
|
90
|
-
"""Preprocess input data for model"""
|
|
90
|
+
"""Preprocess input data for model."""
|
|
91
91
|
if isinstance(X, pd.DataFrame):
|
|
92
92
|
X = X.values
|
|
93
93
|
|
|
@@ -102,13 +102,13 @@ class BaseStockModel(nn.Module, ABC):
|
|
|
102
102
|
return X.to(self.device)
|
|
103
103
|
|
|
104
104
|
def get_feature_importance(self) -> Optional[Dict[str, float]]:
|
|
105
|
-
"""Get feature importance scores"""
|
|
105
|
+
"""Get feature importance scores."""
|
|
106
106
|
# Base implementation returns None
|
|
107
107
|
# Override in specific models that support feature importance
|
|
108
108
|
return None
|
|
109
109
|
|
|
110
110
|
def save_model(self, path: str) -> None:
|
|
111
|
-
"""Save model state"""
|
|
111
|
+
"""Save model state."""
|
|
112
112
|
state = {
|
|
113
113
|
"model_state_dict": self.state_dict(),
|
|
114
114
|
"config": self.config,
|
|
@@ -121,7 +121,7 @@ class BaseStockModel(nn.Module, ABC):
|
|
|
121
121
|
logger.info(f"Model saved to {path}")
|
|
122
122
|
|
|
123
123
|
def load_model(self, path: str) -> None:
|
|
124
|
-
"""Load model state"""
|
|
124
|
+
"""Load model state."""
|
|
125
125
|
state = torch.load(path, map_location=self.device)
|
|
126
126
|
self.load_state_dict(state["model_state_dict"])
|
|
127
127
|
self.config = state["config"]
|
|
@@ -134,7 +134,7 @@ class BaseStockModel(nn.Module, ABC):
|
|
|
134
134
|
def calculate_metrics(
|
|
135
135
|
self, y_true: np.ndarray, y_pred: np.ndarray, y_proba: Optional[np.ndarray] = None
|
|
136
136
|
) -> ModelMetrics:
|
|
137
|
-
"""Calculate comprehensive model metrics"""
|
|
137
|
+
"""Calculate comprehensive model metrics."""
|
|
138
138
|
from sklearn.metrics import (
|
|
139
139
|
accuracy_score,
|
|
140
140
|
f1_score,
|
|
@@ -173,13 +173,13 @@ class BaseStockModel(nn.Module, ABC):
|
|
|
173
173
|
)
|
|
174
174
|
|
|
175
175
|
def to(self, device):
|
|
176
|
-
"""Move model to device and update internal device reference"""
|
|
176
|
+
"""Move model to device and update internal device reference."""
|
|
177
177
|
self.device = device
|
|
178
178
|
return super().to(device)
|
|
179
179
|
|
|
180
180
|
|
|
181
181
|
class MLPBaseModel(BaseStockModel):
|
|
182
|
-
"""Basic Multi-Layer Perceptron base model"""
|
|
182
|
+
"""Basic Multi-Layer Perceptron base model."""
|
|
183
183
|
|
|
184
184
|
def __init__(
|
|
185
185
|
self,
|
|
@@ -220,18 +220,18 @@ class MLPBaseModel(BaseStockModel):
|
|
|
220
220
|
self.apply(self._init_weights)
|
|
221
221
|
|
|
222
222
|
def _init_weights(self, module):
|
|
223
|
-
"""Initialize model weights"""
|
|
223
|
+
"""Initialize model weights."""
|
|
224
224
|
if isinstance(module, nn.Linear):
|
|
225
225
|
torch.nn.init.xavier_uniform_(module.weight)
|
|
226
226
|
if module.bias is not None:
|
|
227
227
|
torch.nn.init.zeros_(module.bias)
|
|
228
228
|
|
|
229
229
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
230
|
-
"""Forward pass"""
|
|
230
|
+
"""Forward pass."""
|
|
231
231
|
return self.network(x)
|
|
232
232
|
|
|
233
233
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
234
|
-
"""Predict class probabilities"""
|
|
234
|
+
"""Predict class probabilities."""
|
|
235
235
|
self.eval()
|
|
236
236
|
with torch.no_grad():
|
|
237
237
|
X_tensor = self.preprocess_input(X)
|
|
@@ -241,7 +241,7 @@ class MLPBaseModel(BaseStockModel):
|
|
|
241
241
|
|
|
242
242
|
|
|
243
243
|
class ResidualBlock(nn.Module):
|
|
244
|
-
"""Residual block for deeper networks"""
|
|
244
|
+
"""Residual block for deeper networks."""
|
|
245
245
|
|
|
246
246
|
def __init__(self, dim: int, dropout_rate: float = 0.1):
|
|
247
247
|
super().__init__()
|
|
@@ -263,7 +263,7 @@ class ResidualBlock(nn.Module):
|
|
|
263
263
|
|
|
264
264
|
|
|
265
265
|
class ResNetModel(BaseStockModel):
|
|
266
|
-
"""ResNet-style model for tabular data"""
|
|
266
|
+
"""ResNet-style model for tabular data."""
|
|
267
267
|
|
|
268
268
|
def __init__(
|
|
269
269
|
self,
|
|
@@ -300,14 +300,14 @@ class ResNetModel(BaseStockModel):
|
|
|
300
300
|
self.apply(self._init_weights)
|
|
301
301
|
|
|
302
302
|
def _init_weights(self, module):
|
|
303
|
-
"""Initialize weights"""
|
|
303
|
+
"""Initialize weights."""
|
|
304
304
|
if isinstance(module, nn.Linear):
|
|
305
305
|
torch.nn.init.kaiming_normal_(module.weight, mode="fan_out", nonlinearity="relu")
|
|
306
306
|
if module.bias is not None:
|
|
307
307
|
torch.nn.init.zeros_(module.bias)
|
|
308
308
|
|
|
309
309
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
310
|
-
"""Forward pass"""
|
|
310
|
+
"""Forward pass."""
|
|
311
311
|
x = self.input_proj(x)
|
|
312
312
|
|
|
313
313
|
for block in self.blocks:
|
|
@@ -316,7 +316,7 @@ class ResNetModel(BaseStockModel):
|
|
|
316
316
|
return self.output_layer(x)
|
|
317
317
|
|
|
318
318
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
319
|
-
"""Predict class probabilities"""
|
|
319
|
+
"""Predict class probabilities."""
|
|
320
320
|
self.eval()
|
|
321
321
|
with torch.no_grad():
|
|
322
322
|
X_tensor = self.preprocess_input(X)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Ensemble models for stock prediction"""
|
|
1
|
+
"""Ensemble models for stock prediction."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import dataclass
|
|
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
|
|
17
17
|
|
|
18
18
|
@dataclass
|
|
19
19
|
class ModelConfig:
|
|
20
|
-
"""Configuration for individual models"""
|
|
20
|
+
"""Configuration for individual models."""
|
|
21
21
|
|
|
22
22
|
model_type: str
|
|
23
23
|
hidden_dims: List[int]
|
|
@@ -30,7 +30,7 @@ class ModelConfig:
|
|
|
30
30
|
|
|
31
31
|
@dataclass
|
|
32
32
|
class EnsembleConfig:
|
|
33
|
-
"""Configuration for ensemble model"""
|
|
33
|
+
"""Configuration for ensemble model."""
|
|
34
34
|
|
|
35
35
|
base_models: List[ModelConfig]
|
|
36
36
|
ensemble_method: str = "weighted_average" # weighted_average, stacking, voting
|
|
@@ -41,7 +41,7 @@ class EnsembleConfig:
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class AttentionStockPredictor(BaseStockModel):
|
|
44
|
-
"""Attention-based stock predictor"""
|
|
44
|
+
"""Attention-based stock predictor."""
|
|
45
45
|
|
|
46
46
|
def __init__(
|
|
47
47
|
self,
|
|
@@ -104,7 +104,7 @@ class AttentionStockPredictor(BaseStockModel):
|
|
|
104
104
|
self.softmax = nn.Softmax(dim=1)
|
|
105
105
|
|
|
106
106
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
107
|
-
"""Forward pass with attention mechanism"""
|
|
107
|
+
"""Forward pass with attention mechanism."""
|
|
108
108
|
# Project input
|
|
109
109
|
x = self.input_proj(x)
|
|
110
110
|
|
|
@@ -131,7 +131,7 @@ class AttentionStockPredictor(BaseStockModel):
|
|
|
131
131
|
return self.output_layer(x)
|
|
132
132
|
|
|
133
133
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
134
|
-
"""Predict class probabilities"""
|
|
134
|
+
"""Predict class probabilities."""
|
|
135
135
|
self.eval()
|
|
136
136
|
with torch.no_grad():
|
|
137
137
|
X_tensor = self.preprocess_input(X)
|
|
@@ -141,7 +141,7 @@ class AttentionStockPredictor(BaseStockModel):
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
class TransformerStockModel(BaseStockModel):
|
|
144
|
-
"""Transformer model for stock prediction"""
|
|
144
|
+
"""Transformer model for stock prediction."""
|
|
145
145
|
|
|
146
146
|
def __init__(
|
|
147
147
|
self,
|
|
@@ -185,7 +185,7 @@ class TransformerStockModel(BaseStockModel):
|
|
|
185
185
|
self.softmax = nn.Softmax(dim=1)
|
|
186
186
|
|
|
187
187
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
188
|
-
"""Forward pass through transformer"""
|
|
188
|
+
"""Forward pass through transformer."""
|
|
189
189
|
# Embed input
|
|
190
190
|
x = self.input_embedding(x)
|
|
191
191
|
|
|
@@ -205,7 +205,7 @@ class TransformerStockModel(BaseStockModel):
|
|
|
205
205
|
return self.classifier(x)
|
|
206
206
|
|
|
207
207
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
208
|
-
"""Predict class probabilities"""
|
|
208
|
+
"""Predict class probabilities."""
|
|
209
209
|
self.eval()
|
|
210
210
|
with torch.no_grad():
|
|
211
211
|
X_tensor = self.preprocess_input(X)
|
|
@@ -215,7 +215,7 @@ class TransformerStockModel(BaseStockModel):
|
|
|
215
215
|
|
|
216
216
|
|
|
217
217
|
class PositionalEncoding(nn.Module):
|
|
218
|
-
"""Positional encoding for transformer"""
|
|
218
|
+
"""Positional encoding for transformer."""
|
|
219
219
|
|
|
220
220
|
def __init__(self, d_model: int, dropout: float = 0.1, max_len: int = 5000):
|
|
221
221
|
super().__init__()
|
|
@@ -235,7 +235,7 @@ class PositionalEncoding(nn.Module):
|
|
|
235
235
|
|
|
236
236
|
|
|
237
237
|
class LSTMStockPredictor(BaseStockModel):
|
|
238
|
-
"""LSTM-based stock predictor"""
|
|
238
|
+
"""LSTM-based stock predictor."""
|
|
239
239
|
|
|
240
240
|
def __init__(
|
|
241
241
|
self,
|
|
@@ -272,7 +272,7 @@ class LSTMStockPredictor(BaseStockModel):
|
|
|
272
272
|
self.softmax = nn.Softmax(dim=1)
|
|
273
273
|
|
|
274
274
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
275
|
-
"""Forward pass through LSTM"""
|
|
275
|
+
"""Forward pass through LSTM."""
|
|
276
276
|
# Add sequence dimension if needed
|
|
277
277
|
if x.dim() == 2:
|
|
278
278
|
x = x.unsqueeze(1)
|
|
@@ -286,7 +286,7 @@ class LSTMStockPredictor(BaseStockModel):
|
|
|
286
286
|
return self.classifier(x)
|
|
287
287
|
|
|
288
288
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
289
|
-
"""Predict class probabilities"""
|
|
289
|
+
"""Predict class probabilities."""
|
|
290
290
|
self.eval()
|
|
291
291
|
with torch.no_grad():
|
|
292
292
|
X_tensor = self.preprocess_input(X)
|
|
@@ -296,7 +296,7 @@ class LSTMStockPredictor(BaseStockModel):
|
|
|
296
296
|
|
|
297
297
|
|
|
298
298
|
class CNNFeatureExtractor(BaseStockModel):
|
|
299
|
-
"""CNN-based feature extractor for tabular data"""
|
|
299
|
+
"""CNN-based feature extractor for tabular data."""
|
|
300
300
|
|
|
301
301
|
def __init__(
|
|
302
302
|
self,
|
|
@@ -350,8 +350,8 @@ class CNNFeatureExtractor(BaseStockModel):
|
|
|
350
350
|
self.softmax = nn.Softmax(dim=1)
|
|
351
351
|
|
|
352
352
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
353
|
-
"""Forward pass through CNN"""
|
|
354
|
-
batch_size = x.size(0)
|
|
353
|
+
"""Forward pass through CNN."""
|
|
354
|
+
batch_size = x.size(0) # noqa: F841
|
|
355
355
|
|
|
356
356
|
# Apply padding if needed
|
|
357
357
|
if self.input_padding is not None:
|
|
@@ -373,7 +373,7 @@ class CNNFeatureExtractor(BaseStockModel):
|
|
|
373
373
|
return self.classifier(x)
|
|
374
374
|
|
|
375
375
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
376
|
-
"""Predict class probabilities"""
|
|
376
|
+
"""Predict class probabilities."""
|
|
377
377
|
self.eval()
|
|
378
378
|
with torch.no_grad():
|
|
379
379
|
X_tensor = self.preprocess_input(X)
|
|
@@ -383,7 +383,7 @@ class CNNFeatureExtractor(BaseStockModel):
|
|
|
383
383
|
|
|
384
384
|
|
|
385
385
|
class DeepEnsembleModel(BaseStockModel):
|
|
386
|
-
"""Deep ensemble combining multiple models"""
|
|
386
|
+
"""Deep ensemble combining multiple models."""
|
|
387
387
|
|
|
388
388
|
def __init__(self, input_dim: int, config: EnsembleConfig):
|
|
389
389
|
super().__init__(input_dim, config.__dict__)
|
|
@@ -406,7 +406,7 @@ class DeepEnsembleModel(BaseStockModel):
|
|
|
406
406
|
self.softmax = nn.Softmax(dim=1)
|
|
407
407
|
|
|
408
408
|
def _create_model(self, model_config: ModelConfig, input_dim: int) -> BaseStockModel:
|
|
409
|
-
"""Create individual model based on configuration"""
|
|
409
|
+
"""Create individual model based on configuration."""
|
|
410
410
|
if model_config.model_type == "attention":
|
|
411
411
|
return AttentionStockPredictor(
|
|
412
412
|
input_dim=input_dim,
|
|
@@ -442,7 +442,7 @@ class DeepEnsembleModel(BaseStockModel):
|
|
|
442
442
|
)
|
|
443
443
|
|
|
444
444
|
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
|
445
|
-
"""Forward pass through ensemble"""
|
|
445
|
+
"""Forward pass through ensemble."""
|
|
446
446
|
model_outputs = []
|
|
447
447
|
|
|
448
448
|
for model in self.models:
|
|
@@ -480,7 +480,7 @@ class DeepEnsembleModel(BaseStockModel):
|
|
|
480
480
|
return ensemble_output
|
|
481
481
|
|
|
482
482
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
483
|
-
"""Predict class probabilities"""
|
|
483
|
+
"""Predict class probabilities."""
|
|
484
484
|
self.eval()
|
|
485
485
|
with torch.no_grad():
|
|
486
486
|
X_tensor = self.preprocess_input(X)
|
|
@@ -489,13 +489,13 @@ class DeepEnsembleModel(BaseStockModel):
|
|
|
489
489
|
return probas.cpu().numpy()
|
|
490
490
|
|
|
491
491
|
def set_model_weights(self, weights: List[float]):
|
|
492
|
-
"""Set weights for weighted ensemble"""
|
|
492
|
+
"""Set weights for weighted ensemble."""
|
|
493
493
|
self.model_weights = torch.FloatTensor(weights)
|
|
494
494
|
|
|
495
495
|
def get_individual_predictions(
|
|
496
496
|
self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]
|
|
497
497
|
) -> List[np.ndarray]:
|
|
498
|
-
"""Get predictions from individual models"""
|
|
498
|
+
"""Get predictions from individual models."""
|
|
499
499
|
predictions = []
|
|
500
500
|
self.eval()
|
|
501
501
|
|
|
@@ -508,7 +508,7 @@ class DeepEnsembleModel(BaseStockModel):
|
|
|
508
508
|
|
|
509
509
|
|
|
510
510
|
class EnsembleTrainer:
|
|
511
|
-
"""Trainer for ensemble models"""
|
|
511
|
+
"""Trainer for ensemble models."""
|
|
512
512
|
|
|
513
513
|
def __init__(self, ensemble_model: DeepEnsembleModel, config: EnsembleConfig):
|
|
514
514
|
self.ensemble_model = ensemble_model
|
|
@@ -523,7 +523,7 @@ class EnsembleTrainer:
|
|
|
523
523
|
X_val: Optional[np.ndarray] = None,
|
|
524
524
|
y_val: Optional[np.ndarray] = None,
|
|
525
525
|
) -> ValidationResult:
|
|
526
|
-
"""Train the ensemble model"""
|
|
526
|
+
"""Train the ensemble model."""
|
|
527
527
|
logger.info("Training ensemble model...")
|
|
528
528
|
|
|
529
529
|
# Train individual models first
|
|
@@ -568,7 +568,7 @@ class EnsembleTrainer:
|
|
|
568
568
|
def _create_subset(
|
|
569
569
|
self, X: np.ndarray, y: np.ndarray, model_config: ModelConfig
|
|
570
570
|
) -> Tuple[np.ndarray, np.ndarray]:
|
|
571
|
-
"""Create data subset for individual model training"""
|
|
571
|
+
"""Create data subset for individual model training."""
|
|
572
572
|
if self.config.bootstrap_samples:
|
|
573
573
|
# Bootstrap sampling
|
|
574
574
|
n_samples = len(X)
|
|
@@ -586,7 +586,7 @@ class EnsembleTrainer:
|
|
|
586
586
|
X_val: Optional[np.ndarray],
|
|
587
587
|
y_val: Optional[np.ndarray],
|
|
588
588
|
) -> Dict[str, Any]:
|
|
589
|
-
"""Train individual model"""
|
|
589
|
+
"""Train individual model."""
|
|
590
590
|
from torch.utils.data import DataLoader, TensorDataset
|
|
591
591
|
|
|
592
592
|
# Convert to tensors
|
|
@@ -629,7 +629,7 @@ class EnsembleTrainer:
|
|
|
629
629
|
return {"train_losses": train_losses}
|
|
630
630
|
|
|
631
631
|
def _calculate_ensemble_weights(self, X_val: np.ndarray, y_val: np.ndarray):
|
|
632
|
-
"""Calculate optimal ensemble weights based on validation performance"""
|
|
632
|
+
"""Calculate optimal ensemble weights based on validation performance."""
|
|
633
633
|
individual_predictions = self.ensemble_model.get_individual_predictions(X_val)
|
|
634
634
|
|
|
635
635
|
# Calculate individual model accuracies
|
|
@@ -653,21 +653,21 @@ class EnsembleTrainer:
|
|
|
653
653
|
X_val: Optional[np.ndarray],
|
|
654
654
|
y_val: Optional[np.ndarray],
|
|
655
655
|
):
|
|
656
|
-
"""Train meta-learner for stacking ensemble"""
|
|
656
|
+
"""Train meta-learner for stacking ensemble."""
|
|
657
657
|
# Get predictions from base models
|
|
658
658
|
train_predictions = self.ensemble_model.get_individual_predictions(X_train)
|
|
659
659
|
meta_X_train = np.concatenate(train_predictions, axis=1)
|
|
660
660
|
|
|
661
661
|
# Train meta-learner
|
|
662
662
|
meta_config = self.config.meta_learner_config
|
|
663
|
-
result = self._train_individual_model(
|
|
663
|
+
result = self._train_individual_model( # noqa: F841
|
|
664
664
|
self.ensemble_model.meta_learner, meta_config, meta_X_train, y_train, None, None
|
|
665
665
|
)
|
|
666
666
|
|
|
667
667
|
logger.info("Meta-learner training completed")
|
|
668
668
|
|
|
669
669
|
def _evaluate(self, X: np.ndarray, y: np.ndarray) -> ModelMetrics:
|
|
670
|
-
"""Evaluate ensemble model"""
|
|
670
|
+
"""Evaluate ensemble model."""
|
|
671
671
|
if X is None or y is None:
|
|
672
672
|
return ModelMetrics(0, 0, 0, 0, 0)
|
|
673
673
|
|
|
@@ -1,25 +1,24 @@
|
|
|
1
|
-
"""Stock recommendation models"""
|
|
1
|
+
"""Stock recommendation models."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import dataclass
|
|
5
5
|
from datetime import datetime
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Dict, List, Optional, Union
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
import pandas as pd
|
|
10
10
|
import torch
|
|
11
11
|
import torch.nn as nn
|
|
12
|
-
import torch.nn.functional as F
|
|
13
12
|
|
|
14
13
|
from mcli.ml.models.base_models import BaseStockModel, ModelMetrics, ValidationResult
|
|
15
|
-
from mcli.ml.models.ensemble_models import DeepEnsembleModel, EnsembleConfig
|
|
14
|
+
from mcli.ml.models.ensemble_models import DeepEnsembleModel, EnsembleConfig
|
|
16
15
|
|
|
17
16
|
logger = logging.getLogger(__name__)
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
@dataclass
|
|
21
20
|
class RecommendationConfig:
|
|
22
|
-
"""Configuration for recommendation model"""
|
|
21
|
+
"""Configuration for recommendation model."""
|
|
23
22
|
|
|
24
23
|
ensemble_config: EnsembleConfig
|
|
25
24
|
risk_adjustment: bool = True
|
|
@@ -32,7 +31,7 @@ class RecommendationConfig:
|
|
|
32
31
|
|
|
33
32
|
@dataclass
|
|
34
33
|
class PortfolioRecommendation:
|
|
35
|
-
"""Portfolio recommendation result"""
|
|
34
|
+
"""Portfolio recommendation result."""
|
|
36
35
|
|
|
37
36
|
ticker: str
|
|
38
37
|
recommendation_score: float
|
|
@@ -59,7 +58,7 @@ class PortfolioRecommendation:
|
|
|
59
58
|
|
|
60
59
|
|
|
61
60
|
class StockRecommendationModel(BaseStockModel):
|
|
62
|
-
"""Main stock recommendation model combining ensemble prediction with portfolio optimization"""
|
|
61
|
+
"""Main stock recommendation model combining ensemble prediction with portfolio optimization."""
|
|
63
62
|
|
|
64
63
|
def __init__(self, input_dim: int, config: RecommendationConfig):
|
|
65
64
|
super().__init__(input_dim, config.__dict__)
|
|
@@ -104,7 +103,7 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
104
103
|
self.softmax = nn.Softmax(dim=1)
|
|
105
104
|
|
|
106
105
|
def forward(self, x: torch.Tensor) -> Dict[str, torch.Tensor]:
|
|
107
|
-
"""Forward pass returning multiple outputs"""
|
|
106
|
+
"""Forward pass returning multiple outputs."""
|
|
108
107
|
# Main prediction
|
|
109
108
|
main_prediction = self.ensemble_model(x)
|
|
110
109
|
|
|
@@ -132,7 +131,7 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
132
131
|
}
|
|
133
132
|
|
|
134
133
|
def predict_proba(self, X: Union[torch.Tensor, np.ndarray, pd.DataFrame]) -> np.ndarray:
|
|
135
|
-
"""Predict class probabilities"""
|
|
134
|
+
"""Predict class probabilities."""
|
|
136
135
|
self.eval()
|
|
137
136
|
with torch.no_grad():
|
|
138
137
|
X_tensor = self.preprocess_input(X)
|
|
@@ -146,7 +145,7 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
146
145
|
tickers: List[str],
|
|
147
146
|
market_data: Optional[pd.DataFrame] = None,
|
|
148
147
|
) -> List[PortfolioRecommendation]:
|
|
149
|
-
"""Generate portfolio recommendations"""
|
|
148
|
+
"""Generate portfolio recommendations."""
|
|
150
149
|
self.eval()
|
|
151
150
|
recommendations = []
|
|
152
151
|
|
|
@@ -187,7 +186,7 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
187
186
|
features: np.ndarray,
|
|
188
187
|
market_data: Optional[pd.DataFrame],
|
|
189
188
|
) -> PortfolioRecommendation:
|
|
190
|
-
"""Create individual stock recommendation"""
|
|
189
|
+
"""Create individual stock recommendation."""
|
|
191
190
|
|
|
192
191
|
# Basic recommendation score (probability of positive outcome)
|
|
193
192
|
recommendation_score = main_prob[1] # Assuming class 1 is positive
|
|
@@ -253,7 +252,7 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
253
252
|
def _generate_recommendation_reason(
|
|
254
253
|
self, score: float, risk: str, confidence: float, expected_return: float
|
|
255
254
|
) -> str:
|
|
256
|
-
"""Generate human-readable recommendation reason"""
|
|
255
|
+
"""Generate human-readable recommendation reason."""
|
|
257
256
|
if score > 0.7:
|
|
258
257
|
strength = "Strong"
|
|
259
258
|
elif score > 0.6:
|
|
@@ -267,12 +266,12 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
267
266
|
)
|
|
268
267
|
|
|
269
268
|
def _extract_key_features(self, features: np.ndarray) -> List[str]:
|
|
270
|
-
"""Extract key features driving the recommendation"""
|
|
269
|
+
"""Extract key features driving the recommendation."""
|
|
271
270
|
# Simplified implementation - would use feature importance in practice
|
|
272
271
|
return ["technical_indicators", "political_influence", "market_regime"]
|
|
273
272
|
|
|
274
273
|
def _generate_warnings(self, risk_level: str, confidence: float, score: float) -> List[str]:
|
|
275
|
-
"""Generate warnings for the recommendation"""
|
|
274
|
+
"""Generate warnings for the recommendation."""
|
|
276
275
|
warnings = []
|
|
277
276
|
|
|
278
277
|
if confidence < 0.5:
|
|
@@ -289,7 +288,7 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
289
288
|
def _optimize_portfolio(
|
|
290
289
|
self, recommendations: List[PortfolioRecommendation]
|
|
291
290
|
) -> List[PortfolioRecommendation]:
|
|
292
|
-
"""Apply portfolio-level optimization"""
|
|
291
|
+
"""Apply portfolio-level optimization."""
|
|
293
292
|
# Sort by risk-adjusted score
|
|
294
293
|
recommendations.sort(key=lambda x: x.risk_adjusted_score, reverse=True)
|
|
295
294
|
|
|
@@ -319,7 +318,7 @@ class StockRecommendationModel(BaseStockModel):
|
|
|
319
318
|
|
|
320
319
|
|
|
321
320
|
class RecommendationTrainer:
|
|
322
|
-
"""Trainer for recommendation model"""
|
|
321
|
+
"""Trainer for recommendation model."""
|
|
323
322
|
|
|
324
323
|
def __init__(self, model: StockRecommendationModel, config: RecommendationConfig):
|
|
325
324
|
self.model = model
|
|
@@ -340,7 +339,7 @@ class RecommendationTrainer:
|
|
|
340
339
|
epochs: int = 100,
|
|
341
340
|
batch_size: int = 64,
|
|
342
341
|
) -> ValidationResult:
|
|
343
|
-
"""Train the recommendation model"""
|
|
342
|
+
"""Train the recommendation model."""
|
|
344
343
|
|
|
345
344
|
from torch.utils.data import DataLoader, TensorDataset
|
|
346
345
|
|
|
@@ -442,7 +441,7 @@ class RecommendationTrainer:
|
|
|
442
441
|
returns_val: np.ndarray,
|
|
443
442
|
risk_labels_val: np.ndarray,
|
|
444
443
|
) -> float:
|
|
445
|
-
"""Validate model during training"""
|
|
444
|
+
"""Validate model during training."""
|
|
446
445
|
self.model.eval()
|
|
447
446
|
|
|
448
447
|
with torch.no_grad():
|
|
@@ -466,7 +465,7 @@ class RecommendationTrainer:
|
|
|
466
465
|
return total_loss.item()
|
|
467
466
|
|
|
468
467
|
def _evaluate(self, X: np.ndarray, y: np.ndarray) -> ModelMetrics:
|
|
469
|
-
"""Evaluate model performance"""
|
|
468
|
+
"""Evaluate model performance."""
|
|
470
469
|
if X is None or y is None:
|
|
471
470
|
return ModelMetrics(0, 0, 0, 0, 0)
|
|
472
471
|
|