mcli-framework 7.12.1__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 +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.1.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.1.dist-info/RECORD +0 -279
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/WHEEL +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.12.1.dist-info → mcli_framework-7.12.3.dist-info}/top_level.txt +0 -0
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
"""Advanced portfolio optimization for stock recommendations"""
|
|
1
|
+
"""Advanced portfolio optimization for stock recommendations."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from abc import ABC, abstractmethod
|
|
5
5
|
from dataclasses import dataclass, field
|
|
6
|
-
from datetime import datetime
|
|
6
|
+
from datetime import datetime
|
|
7
7
|
from enum import Enum
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
9
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
10
10
|
|
|
11
11
|
# Optimization libraries
|
|
12
12
|
import cvxpy as cp
|
|
13
13
|
import matplotlib.pyplot as plt
|
|
14
14
|
import numpy as np
|
|
15
15
|
import pandas as pd
|
|
16
|
-
import seaborn as sns
|
|
17
16
|
from scipy.optimize import minimize
|
|
18
17
|
from scipy.stats import norm
|
|
19
18
|
|
|
@@ -21,7 +20,7 @@ logger = logging.getLogger(__name__)
|
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
class OptimizationObjective(Enum):
|
|
24
|
-
"""Portfolio optimization objectives"""
|
|
23
|
+
"""Portfolio optimization objectives."""
|
|
25
24
|
|
|
26
25
|
MEAN_VARIANCE = "mean_variance"
|
|
27
26
|
RISK_PARITY = "risk_parity"
|
|
@@ -35,7 +34,7 @@ class OptimizationObjective(Enum):
|
|
|
35
34
|
|
|
36
35
|
@dataclass
|
|
37
36
|
class OptimizationConstraints:
|
|
38
|
-
"""Portfolio optimization constraints"""
|
|
37
|
+
"""Portfolio optimization constraints."""
|
|
39
38
|
|
|
40
39
|
# Weight constraints
|
|
41
40
|
min_weight: float = 0.0
|
|
@@ -66,7 +65,7 @@ class OptimizationConstraints:
|
|
|
66
65
|
|
|
67
66
|
@dataclass
|
|
68
67
|
class PortfolioAllocation:
|
|
69
|
-
"""Portfolio allocation result"""
|
|
68
|
+
"""Portfolio allocation result."""
|
|
70
69
|
|
|
71
70
|
weights: Dict[str, float]
|
|
72
71
|
expected_return: float
|
|
@@ -93,7 +92,7 @@ class PortfolioAllocation:
|
|
|
93
92
|
|
|
94
93
|
|
|
95
94
|
class BaseOptimizer(ABC):
|
|
96
|
-
"""Base class for portfolio optimizers"""
|
|
95
|
+
"""Base class for portfolio optimizers."""
|
|
97
96
|
|
|
98
97
|
def __init__(self, constraints: OptimizationConstraints):
|
|
99
98
|
self.constraints = constraints
|
|
@@ -102,8 +101,7 @@ class BaseOptimizer(ABC):
|
|
|
102
101
|
def optimize(
|
|
103
102
|
self, expected_returns: pd.Series, covariance_matrix: pd.DataFrame, **kwargs
|
|
104
103
|
) -> PortfolioAllocation:
|
|
105
|
-
"""Optimize portfolio allocation"""
|
|
106
|
-
pass
|
|
104
|
+
"""Optimize portfolio allocation."""
|
|
107
105
|
|
|
108
106
|
def _calculate_portfolio_metrics(
|
|
109
107
|
self,
|
|
@@ -112,7 +110,7 @@ class BaseOptimizer(ABC):
|
|
|
112
110
|
covariance_matrix: pd.DataFrame,
|
|
113
111
|
risk_free_rate: float = 0.02,
|
|
114
112
|
) -> Tuple[float, float, float]:
|
|
115
|
-
"""Calculate portfolio return, volatility, and Sharpe ratio"""
|
|
113
|
+
"""Calculate portfolio return, volatility, and Sharpe ratio."""
|
|
116
114
|
portfolio_return = np.dot(weights, expected_returns)
|
|
117
115
|
portfolio_variance = np.dot(weights.T, np.dot(covariance_matrix, weights))
|
|
118
116
|
portfolio_volatility = np.sqrt(portfolio_variance)
|
|
@@ -132,7 +130,7 @@ class BaseOptimizer(ABC):
|
|
|
132
130
|
covariance_matrix: pd.DataFrame,
|
|
133
131
|
confidence_level: float = 0.95,
|
|
134
132
|
) -> Tuple[float, float]:
|
|
135
|
-
"""Calculate Value at Risk and Conditional Value at Risk"""
|
|
133
|
+
"""Calculate Value at Risk and Conditional Value at Risk."""
|
|
136
134
|
portfolio_return = np.dot(weights, expected_returns)
|
|
137
135
|
portfolio_volatility = np.sqrt(np.dot(weights.T, np.dot(covariance_matrix, weights)))
|
|
138
136
|
|
|
@@ -147,7 +145,7 @@ class BaseOptimizer(ABC):
|
|
|
147
145
|
|
|
148
146
|
|
|
149
147
|
class MeanVarianceOptimizer(BaseOptimizer):
|
|
150
|
-
"""Modern Portfolio Theory mean-variance optimizer"""
|
|
148
|
+
"""Modern Portfolio Theory mean-variance optimizer."""
|
|
151
149
|
|
|
152
150
|
def optimize(
|
|
153
151
|
self,
|
|
@@ -156,7 +154,7 @@ class MeanVarianceOptimizer(BaseOptimizer):
|
|
|
156
154
|
risk_aversion: float = 1.0,
|
|
157
155
|
**kwargs,
|
|
158
156
|
) -> PortfolioAllocation:
|
|
159
|
-
"""Optimize using mean-variance framework"""
|
|
157
|
+
"""Optimize using mean-variance framework."""
|
|
160
158
|
n_assets = len(expected_returns)
|
|
161
159
|
|
|
162
160
|
# Decision variable: portfolio weights
|
|
@@ -213,15 +211,15 @@ class MeanVarianceOptimizer(BaseOptimizer):
|
|
|
213
211
|
|
|
214
212
|
|
|
215
213
|
class RiskParityOptimizer(BaseOptimizer):
|
|
216
|
-
"""Risk parity portfolio optimizer"""
|
|
214
|
+
"""Risk parity portfolio optimizer."""
|
|
217
215
|
|
|
218
216
|
def optimize(
|
|
219
217
|
self, expected_returns: pd.Series, covariance_matrix: pd.DataFrame, **kwargs
|
|
220
218
|
) -> PortfolioAllocation:
|
|
221
|
-
"""Optimize using risk parity approach"""
|
|
219
|
+
"""Optimize using risk parity approach."""
|
|
222
220
|
|
|
223
221
|
def risk_parity_objective(weights, cov_matrix):
|
|
224
|
-
"""Risk parity objective function"""
|
|
222
|
+
"""Risk parity objective function."""
|
|
225
223
|
portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
|
|
226
224
|
marginal_contrib = np.dot(cov_matrix, weights) / portfolio_vol
|
|
227
225
|
contrib = weights * marginal_contrib
|
|
@@ -282,7 +280,7 @@ class RiskParityOptimizer(BaseOptimizer):
|
|
|
282
280
|
|
|
283
281
|
|
|
284
282
|
class BlackLittermanOptimizer(BaseOptimizer):
|
|
285
|
-
"""Black-Litterman portfolio optimizer"""
|
|
283
|
+
"""Black-Litterman portfolio optimizer."""
|
|
286
284
|
|
|
287
285
|
def optimize(
|
|
288
286
|
self,
|
|
@@ -295,7 +293,7 @@ class BlackLittermanOptimizer(BaseOptimizer):
|
|
|
295
293
|
risk_aversion: float = 3.0,
|
|
296
294
|
**kwargs,
|
|
297
295
|
) -> PortfolioAllocation:
|
|
298
|
-
"""Optimize using Black-Litterman model"""
|
|
296
|
+
"""Optimize using Black-Litterman model."""
|
|
299
297
|
|
|
300
298
|
# Market capitalization weights (if not provided, use equal weights)
|
|
301
299
|
if market_caps is None:
|
|
@@ -354,7 +352,7 @@ class BlackLittermanOptimizer(BaseOptimizer):
|
|
|
354
352
|
|
|
355
353
|
|
|
356
354
|
class CVaROptimizer(BaseOptimizer):
|
|
357
|
-
"""Conditional Value at Risk optimizer"""
|
|
355
|
+
"""Conditional Value at Risk optimizer."""
|
|
358
356
|
|
|
359
357
|
def optimize(
|
|
360
358
|
self,
|
|
@@ -364,7 +362,7 @@ class CVaROptimizer(BaseOptimizer):
|
|
|
364
362
|
confidence_level: float = 0.95,
|
|
365
363
|
**kwargs,
|
|
366
364
|
) -> PortfolioAllocation:
|
|
367
|
-
"""Optimize portfolio to minimize CVaR"""
|
|
365
|
+
"""Optimize portfolio to minimize CVaR."""
|
|
368
366
|
|
|
369
367
|
if scenarios is None:
|
|
370
368
|
# Generate scenarios from normal distribution
|
|
@@ -434,12 +432,12 @@ class CVaROptimizer(BaseOptimizer):
|
|
|
434
432
|
|
|
435
433
|
|
|
436
434
|
class KellyCriterionOptimizer(BaseOptimizer):
|
|
437
|
-
"""Kelly Criterion optimizer for growth-optimal portfolios"""
|
|
435
|
+
"""Kelly Criterion optimizer for growth-optimal portfolios."""
|
|
438
436
|
|
|
439
437
|
def optimize(
|
|
440
438
|
self, expected_returns: pd.Series, covariance_matrix: pd.DataFrame, **kwargs
|
|
441
439
|
) -> PortfolioAllocation:
|
|
442
|
-
"""Optimize using Kelly Criterion"""
|
|
440
|
+
"""Optimize using Kelly Criterion."""
|
|
443
441
|
|
|
444
442
|
# Kelly optimal weights: w* = Σ^(-1) * μ
|
|
445
443
|
# where μ is expected excess returns and Σ is covariance matrix
|
|
@@ -510,7 +508,7 @@ class KellyCriterionOptimizer(BaseOptimizer):
|
|
|
510
508
|
|
|
511
509
|
|
|
512
510
|
class AdvancedPortfolioOptimizer:
|
|
513
|
-
"""Advanced portfolio optimization system"""
|
|
511
|
+
"""Advanced portfolio optimization system."""
|
|
514
512
|
|
|
515
513
|
def __init__(self, constraints: Optional[OptimizationConstraints] = None):
|
|
516
514
|
self.constraints = constraints or OptimizationConstraints()
|
|
@@ -533,7 +531,7 @@ class AdvancedPortfolioOptimizer:
|
|
|
533
531
|
objective: OptimizationObjective = OptimizationObjective.MEAN_VARIANCE,
|
|
534
532
|
**optimizer_kwargs,
|
|
535
533
|
) -> PortfolioAllocation:
|
|
536
|
-
"""Optimize portfolio using specified objective"""
|
|
534
|
+
"""Optimize portfolio using specified objective."""
|
|
537
535
|
|
|
538
536
|
if objective not in self.optimizers:
|
|
539
537
|
raise ValueError(f"Unsupported optimization objective: {objective}")
|
|
@@ -558,7 +556,7 @@ class AdvancedPortfolioOptimizer:
|
|
|
558
556
|
objectives: List[OptimizationObjective],
|
|
559
557
|
weights: Optional[List[float]] = None,
|
|
560
558
|
) -> PortfolioAllocation:
|
|
561
|
-
"""Combine multiple optimization objectives"""
|
|
559
|
+
"""Combine multiple optimization objectives."""
|
|
562
560
|
|
|
563
561
|
if weights is None:
|
|
564
562
|
weights = [1.0 / len(objectives)] * len(objectives)
|
|
@@ -608,7 +606,7 @@ class AdvancedPortfolioOptimizer:
|
|
|
608
606
|
def efficient_frontier(
|
|
609
607
|
self, expected_returns: pd.Series, covariance_matrix: pd.DataFrame, n_points: int = 20
|
|
610
608
|
) -> pd.DataFrame:
|
|
611
|
-
"""Generate efficient frontier"""
|
|
609
|
+
"""Generate efficient frontier."""
|
|
612
610
|
|
|
613
611
|
min_vol_allocation = self.optimize_portfolio(
|
|
614
612
|
expected_returns,
|
|
@@ -676,7 +674,7 @@ class AdvancedPortfolioOptimizer:
|
|
|
676
674
|
target_allocation: PortfolioAllocation,
|
|
677
675
|
rebalance_threshold: float = 0.05,
|
|
678
676
|
) -> Dict[str, Any]:
|
|
679
|
-
"""Calculate rebalancing trades"""
|
|
677
|
+
"""Calculate rebalancing trades."""
|
|
680
678
|
|
|
681
679
|
trades = {}
|
|
682
680
|
total_deviation = 0
|
|
@@ -709,9 +707,9 @@ class AdvancedPortfolioOptimizer:
|
|
|
709
707
|
expected_returns: pd.Series,
|
|
710
708
|
covariance_matrix: pd.DataFrame,
|
|
711
709
|
) -> PortfolioAllocation:
|
|
712
|
-
"""Add additional metrics to allocation"""
|
|
710
|
+
"""Add additional metrics to allocation."""
|
|
713
711
|
|
|
714
|
-
|
|
712
|
+
_weights_array = np.array( # noqa: F841
|
|
715
713
|
[allocation.weights.get(asset, 0) for asset in expected_returns.index]
|
|
716
714
|
)
|
|
717
715
|
|
|
@@ -727,7 +725,7 @@ class AdvancedPortfolioOptimizer:
|
|
|
727
725
|
def plot_allocation(
|
|
728
726
|
self, allocation: PortfolioAllocation, save_path: Optional[Path] = None
|
|
729
727
|
) -> None:
|
|
730
|
-
"""Plot portfolio allocation"""
|
|
728
|
+
"""Plot portfolio allocation."""
|
|
731
729
|
|
|
732
730
|
# Filter out zero weights
|
|
733
731
|
non_zero_weights = {k: v for k, v in allocation.weights.items() if abs(v) > 0.001}
|
|
@@ -854,7 +852,7 @@ if __name__ == "__main__":
|
|
|
854
852
|
weights=[0.7, 0.3],
|
|
855
853
|
)
|
|
856
854
|
|
|
857
|
-
print(
|
|
855
|
+
print("\nMULTI-OBJECTIVE Optimization:")
|
|
858
856
|
print(f"Expected Return: {multi_obj_allocation.expected_return:.3f}")
|
|
859
857
|
print(f"Volatility: {multi_obj_allocation.expected_volatility:.3f}")
|
|
860
858
|
print(f"Sharpe Ratio: {multi_obj_allocation.sharpe_ratio:.3f}")
|
mcli/ml/predictions/__init__.py
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
"""Data cleaning utilities for ML preprocessing"""
|
|
1
|
+
"""Data cleaning utilities for ML preprocessing."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
from typing import Any, Dict, List, Optional, Tuple
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
import pandas as pd
|
|
@@ -14,7 +14,7 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
|
|
15
15
|
@dataclass
|
|
16
16
|
class CleaningStats:
|
|
17
|
-
"""Statistics about data cleaning operations"""
|
|
17
|
+
"""Statistics about data cleaning operations."""
|
|
18
18
|
|
|
19
19
|
total_records: int
|
|
20
20
|
cleaned_records: int
|
|
@@ -25,7 +25,7 @@ class CleaningStats:
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
class TradingDataCleaner:
|
|
28
|
-
"""Cleans and standardizes politician trading data for ML"""
|
|
28
|
+
"""Cleans and standardizes politician trading data for ML."""
|
|
29
29
|
|
|
30
30
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
31
31
|
self.config = config or {}
|
|
@@ -41,7 +41,7 @@ class TradingDataCleaner:
|
|
|
41
41
|
def clean_trading_records(
|
|
42
42
|
self, records: List[Dict[str, Any]]
|
|
43
43
|
) -> Tuple[List[Dict[str, Any]], CleaningStats]:
|
|
44
|
-
"""Clean a batch of trading records"""
|
|
44
|
+
"""Clean a batch of trading records."""
|
|
45
45
|
self.cleaning_stats.total_records = len(records)
|
|
46
46
|
cleaned_records = []
|
|
47
47
|
|
|
@@ -56,7 +56,7 @@ class TradingDataCleaner:
|
|
|
56
56
|
return cleaned_records, self.cleaning_stats
|
|
57
57
|
|
|
58
58
|
def _clean_single_record(self, record: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
59
|
-
"""Clean a single trading record"""
|
|
59
|
+
"""Clean a single trading record."""
|
|
60
60
|
try:
|
|
61
61
|
cleaned = record.copy()
|
|
62
62
|
|
|
@@ -86,7 +86,7 @@ class TradingDataCleaner:
|
|
|
86
86
|
return None
|
|
87
87
|
|
|
88
88
|
def _clean_politician_name(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
89
|
-
"""Clean and standardize politician names"""
|
|
89
|
+
"""Clean and standardize politician names."""
|
|
90
90
|
name_fields = ["politician_name", "name", "representative_name", "senator_name"]
|
|
91
91
|
|
|
92
92
|
for field in name_fields:
|
|
@@ -113,7 +113,7 @@ class TradingDataCleaner:
|
|
|
113
113
|
return record
|
|
114
114
|
|
|
115
115
|
def _clean_transaction_amount(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
116
|
-
"""Clean and standardize transaction amounts"""
|
|
116
|
+
"""Clean and standardize transaction amounts."""
|
|
117
117
|
amount_fields = ["transaction_amount", "amount", "value", "transaction_value"]
|
|
118
118
|
|
|
119
119
|
for field in amount_fields:
|
|
@@ -148,7 +148,7 @@ class TradingDataCleaner:
|
|
|
148
148
|
return record
|
|
149
149
|
|
|
150
150
|
def _clean_transaction_date(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
151
|
-
"""Clean and standardize transaction dates"""
|
|
151
|
+
"""Clean and standardize transaction dates."""
|
|
152
152
|
date_fields = ["transaction_date", "date", "trade_date", "disclosure_date"]
|
|
153
153
|
|
|
154
154
|
for field in date_fields:
|
|
@@ -183,7 +183,7 @@ class TradingDataCleaner:
|
|
|
183
183
|
date_obj = pd.to_datetime(date_str)
|
|
184
184
|
record["transaction_date_cleaned"] = date_obj.strftime("%Y-%m-%d")
|
|
185
185
|
self._increment_cleaning_operation("transaction_date_cleaned")
|
|
186
|
-
except:
|
|
186
|
+
except Exception:
|
|
187
187
|
continue
|
|
188
188
|
|
|
189
189
|
if "transaction_date_cleaned" in record:
|
|
@@ -192,8 +192,7 @@ class TradingDataCleaner:
|
|
|
192
192
|
return record
|
|
193
193
|
|
|
194
194
|
def _clean_asset_info(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
195
|
-
"""Clean and standardize asset information"""
|
|
196
|
-
asset_fields = ["asset_name", "stock_symbol", "ticker", "security_name"]
|
|
195
|
+
"""Clean and standardize asset information."""
|
|
197
196
|
|
|
198
197
|
# Clean ticker/symbol
|
|
199
198
|
for field in ["stock_symbol", "ticker", "symbol"]:
|
|
@@ -230,7 +229,7 @@ class TradingDataCleaner:
|
|
|
230
229
|
return record
|
|
231
230
|
|
|
232
231
|
def _clean_transaction_type(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
233
|
-
"""Clean and standardize transaction types"""
|
|
232
|
+
"""Clean and standardize transaction types."""
|
|
234
233
|
type_fields = ["transaction_type", "type", "action", "trade_type"]
|
|
235
234
|
|
|
236
235
|
for field in type_fields:
|
|
@@ -254,7 +253,7 @@ class TradingDataCleaner:
|
|
|
254
253
|
return record
|
|
255
254
|
|
|
256
255
|
def _validate_required_fields(self, record: Dict[str, Any]) -> bool:
|
|
257
|
-
"""Validate that required fields exist after cleaning"""
|
|
256
|
+
"""Validate that required fields exist after cleaning."""
|
|
258
257
|
required_fields = [
|
|
259
258
|
"politician_name_cleaned",
|
|
260
259
|
"transaction_date_cleaned",
|
|
@@ -272,14 +271,14 @@ class TradingDataCleaner:
|
|
|
272
271
|
return has_required and amount_or_asset
|
|
273
272
|
|
|
274
273
|
def _increment_cleaning_operation(self, operation: str):
|
|
275
|
-
"""Track cleaning operations"""
|
|
274
|
+
"""Track cleaning operations."""
|
|
276
275
|
if operation not in self.cleaning_stats.cleaning_operations:
|
|
277
276
|
self.cleaning_stats.cleaning_operations[operation] = 0
|
|
278
277
|
self.cleaning_stats.cleaning_operations[operation] += 1
|
|
279
278
|
|
|
280
279
|
|
|
281
280
|
class OutlierDetector:
|
|
282
|
-
"""Detects and handles outliers in trading data"""
|
|
281
|
+
"""Detects and handles outliers in trading data."""
|
|
283
282
|
|
|
284
283
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
285
284
|
self.config = config or {}
|
|
@@ -297,7 +296,7 @@ class OutlierDetector:
|
|
|
297
296
|
}
|
|
298
297
|
|
|
299
298
|
def detect_outliers(self, df: pd.DataFrame) -> Tuple[pd.DataFrame, Dict[str, Any]]:
|
|
300
|
-
"""Detect outliers in the dataset"""
|
|
299
|
+
"""Detect outliers in the dataset."""
|
|
301
300
|
outlier_info = {"total_outliers": 0, "outliers_by_field": {}, "outlier_indices": set()}
|
|
302
301
|
|
|
303
302
|
# Amount-based outliers
|
|
@@ -328,7 +327,7 @@ class OutlierDetector:
|
|
|
328
327
|
return df, outlier_info
|
|
329
328
|
|
|
330
329
|
def _detect_amount_outliers(self, df: pd.DataFrame) -> List[int]:
|
|
331
|
-
"""Detect amount-based outliers"""
|
|
330
|
+
"""Detect amount-based outliers."""
|
|
332
331
|
outliers = []
|
|
333
332
|
amount_col = "transaction_amount_cleaned"
|
|
334
333
|
|
|
@@ -344,7 +343,7 @@ class OutlierDetector:
|
|
|
344
343
|
return list(set(outliers))
|
|
345
344
|
|
|
346
345
|
def _detect_date_outliers(self, df: pd.DataFrame) -> List[int]:
|
|
347
|
-
"""Detect date-based outliers"""
|
|
346
|
+
"""Detect date-based outliers."""
|
|
348
347
|
outliers = []
|
|
349
348
|
date_col = "transaction_date_cleaned"
|
|
350
349
|
|
|
@@ -365,7 +364,7 @@ class OutlierDetector:
|
|
|
365
364
|
return list(set(outliers))
|
|
366
365
|
|
|
367
366
|
def _detect_statistical_outliers(self, df: pd.DataFrame, column: str) -> List[int]:
|
|
368
|
-
"""Detect statistical outliers using Z-score"""
|
|
367
|
+
"""Detect statistical outliers using Z-score."""
|
|
369
368
|
outliers = []
|
|
370
369
|
|
|
371
370
|
if column not in df.columns or df[column].dtype not in [np.number, "float64", "int64"]:
|
|
@@ -387,7 +386,7 @@ class OutlierDetector:
|
|
|
387
386
|
|
|
388
387
|
|
|
389
388
|
class MissingValueHandler:
|
|
390
|
-
"""Handles missing values in trading data"""
|
|
389
|
+
"""Handles missing values in trading data."""
|
|
391
390
|
|
|
392
391
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
393
392
|
self.config = config or {}
|
|
@@ -401,7 +400,7 @@ class MissingValueHandler:
|
|
|
401
400
|
}
|
|
402
401
|
|
|
403
402
|
def handle_missing_values(self, df: pd.DataFrame) -> Tuple[pd.DataFrame, Dict[str, Any]]:
|
|
404
|
-
"""Handle missing values according to strategies"""
|
|
403
|
+
"""Handle missing values according to strategies."""
|
|
405
404
|
missing_info = {
|
|
406
405
|
"original_shape": df.shape,
|
|
407
406
|
"missing_counts": df.isnull().sum().to_dict(),
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
"""Feature extraction utilities for ML preprocessing"""
|
|
1
|
+
"""Feature extraction utilities for ML preprocessing."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import re
|
|
5
|
-
from collections import Counter, defaultdict
|
|
6
4
|
from dataclasses import dataclass
|
|
7
|
-
from
|
|
8
|
-
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
9
6
|
|
|
10
7
|
import numpy as np
|
|
11
8
|
import pandas as pd
|
|
@@ -15,7 +12,7 @@ logger = logging.getLogger(__name__)
|
|
|
15
12
|
|
|
16
13
|
@dataclass
|
|
17
14
|
class FeatureExtractionStats:
|
|
18
|
-
"""Statistics about feature extraction operations"""
|
|
15
|
+
"""Statistics about feature extraction operations."""
|
|
19
16
|
|
|
20
17
|
total_records: int
|
|
21
18
|
features_extracted: int
|
|
@@ -25,7 +22,7 @@ class FeatureExtractionStats:
|
|
|
25
22
|
|
|
26
23
|
|
|
27
24
|
class PoliticianFeatureExtractor:
|
|
28
|
-
"""Extracts features related to politicians"""
|
|
25
|
+
"""Extracts features related to politicians."""
|
|
29
26
|
|
|
30
27
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
31
28
|
self.config = config or {}
|
|
@@ -39,7 +36,7 @@ class PoliticianFeatureExtractor:
|
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
def extract_politician_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
42
|
-
"""Extract politician-related features"""
|
|
39
|
+
"""Extract politician-related features."""
|
|
43
40
|
df_features = df.copy()
|
|
44
41
|
|
|
45
42
|
# Basic politician features
|
|
@@ -51,7 +48,7 @@ class PoliticianFeatureExtractor:
|
|
|
51
48
|
return df_features
|
|
52
49
|
|
|
53
50
|
def _extract_name_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
54
|
-
"""Extract features from politician names"""
|
|
51
|
+
"""Extract features from politician names."""
|
|
55
52
|
if "politician_name_cleaned" not in df.columns:
|
|
56
53
|
return df
|
|
57
54
|
|
|
@@ -72,7 +69,7 @@ class PoliticianFeatureExtractor:
|
|
|
72
69
|
return df
|
|
73
70
|
|
|
74
71
|
def _extract_trading_patterns(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
75
|
-
"""Extract trading pattern features for each politician"""
|
|
72
|
+
"""Extract trading pattern features for each politician."""
|
|
76
73
|
if "politician_name_cleaned" not in df.columns:
|
|
77
74
|
return df
|
|
78
75
|
|
|
@@ -147,7 +144,7 @@ class PoliticianFeatureExtractor:
|
|
|
147
144
|
return df
|
|
148
145
|
|
|
149
146
|
def _extract_frequency_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
150
|
-
"""Extract trading frequency features"""
|
|
147
|
+
"""Extract trading frequency features."""
|
|
151
148
|
if not all(
|
|
152
149
|
col in df.columns for col in ["politician_name_cleaned", "transaction_date_cleaned"]
|
|
153
150
|
):
|
|
@@ -189,7 +186,7 @@ class PoliticianFeatureExtractor:
|
|
|
189
186
|
return df
|
|
190
187
|
|
|
191
188
|
def _extract_timing_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
192
|
-
"""Extract timing-related features"""
|
|
189
|
+
"""Extract timing-related features."""
|
|
193
190
|
if "transaction_date_dt" not in df.columns:
|
|
194
191
|
return df
|
|
195
192
|
|
|
@@ -220,14 +217,14 @@ class PoliticianFeatureExtractor:
|
|
|
220
217
|
|
|
221
218
|
|
|
222
219
|
class MarketFeatureExtractor:
|
|
223
|
-
"""Extracts market-related features"""
|
|
220
|
+
"""Extracts market-related features."""
|
|
224
221
|
|
|
225
222
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
226
223
|
self.config = config or {}
|
|
227
224
|
self.sector_mapping = self._load_sector_mapping()
|
|
228
225
|
|
|
229
226
|
def extract_market_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
230
|
-
"""Extract market-related features"""
|
|
227
|
+
"""Extract market-related features."""
|
|
231
228
|
df_features = df.copy()
|
|
232
229
|
|
|
233
230
|
# Asset features
|
|
@@ -238,7 +235,7 @@ class MarketFeatureExtractor:
|
|
|
238
235
|
return df_features
|
|
239
236
|
|
|
240
237
|
def _extract_asset_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
241
|
-
"""Extract features from asset names"""
|
|
238
|
+
"""Extract features from asset names."""
|
|
242
239
|
if "asset_name_cleaned" not in df.columns:
|
|
243
240
|
return df
|
|
244
241
|
|
|
@@ -270,7 +267,7 @@ class MarketFeatureExtractor:
|
|
|
270
267
|
return df
|
|
271
268
|
|
|
272
269
|
def _extract_ticker_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
273
|
-
"""Extract features from stock tickers"""
|
|
270
|
+
"""Extract features from stock tickers."""
|
|
274
271
|
if "ticker_cleaned" not in df.columns:
|
|
275
272
|
return df
|
|
276
273
|
|
|
@@ -288,7 +285,7 @@ class MarketFeatureExtractor:
|
|
|
288
285
|
return df
|
|
289
286
|
|
|
290
287
|
def _extract_market_cap_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
291
|
-
"""Extract market cap related features (placeholder)"""
|
|
288
|
+
"""Extract market cap related features (placeholder)."""
|
|
292
289
|
# This would typically connect to external APIs
|
|
293
290
|
# For now, create estimated features based on transaction amounts
|
|
294
291
|
|
|
@@ -311,7 +308,7 @@ class MarketFeatureExtractor:
|
|
|
311
308
|
return df
|
|
312
309
|
|
|
313
310
|
def _load_sector_mapping(self) -> Dict[str, str]:
|
|
314
|
-
"""Load ticker to sector mapping (simplified)"""
|
|
311
|
+
"""Load ticker to sector mapping (simplified)."""
|
|
315
312
|
# This would typically be loaded from a data file or API
|
|
316
313
|
return {
|
|
317
314
|
"AAPL": "technology",
|
|
@@ -333,14 +330,14 @@ class MarketFeatureExtractor:
|
|
|
333
330
|
|
|
334
331
|
|
|
335
332
|
class TemporalFeatureExtractor:
|
|
336
|
-
"""Extracts temporal features for time series analysis"""
|
|
333
|
+
"""Extracts temporal features for time series analysis."""
|
|
337
334
|
|
|
338
335
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
339
336
|
self.config = config or {}
|
|
340
337
|
self.lookback_periods = config.get("lookback_periods", [7, 30, 90, 365])
|
|
341
338
|
|
|
342
339
|
def extract_temporal_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
343
|
-
"""Extract temporal features"""
|
|
340
|
+
"""Extract temporal features."""
|
|
344
341
|
df_features = df.copy()
|
|
345
342
|
|
|
346
343
|
if "transaction_date_dt" not in df.columns:
|
|
@@ -357,7 +354,7 @@ class TemporalFeatureExtractor:
|
|
|
357
354
|
return df_features
|
|
358
355
|
|
|
359
356
|
def _extract_rolling_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
360
|
-
"""Extract rolling window features"""
|
|
357
|
+
"""Extract rolling window features."""
|
|
361
358
|
# Set date as index temporarily
|
|
362
359
|
df_indexed = df.set_index("transaction_date_dt")
|
|
363
360
|
|
|
@@ -389,7 +386,7 @@ class TemporalFeatureExtractor:
|
|
|
389
386
|
return df
|
|
390
387
|
|
|
391
388
|
def _extract_lag_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
392
|
-
"""Extract lagged features"""
|
|
389
|
+
"""Extract lagged features."""
|
|
393
390
|
lag_periods = [1, 7, 30]
|
|
394
391
|
|
|
395
392
|
for lag in lag_periods:
|
|
@@ -406,7 +403,7 @@ class TemporalFeatureExtractor:
|
|
|
406
403
|
return df
|
|
407
404
|
|
|
408
405
|
def _extract_trend_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
409
|
-
"""Extract trend features"""
|
|
406
|
+
"""Extract trend features."""
|
|
410
407
|
# Calculate percentage changes
|
|
411
408
|
df["amount_pct_change_1d"] = df.groupby("politician_name_cleaned")[
|
|
412
409
|
"transaction_amount_cleaned"
|
|
@@ -439,7 +436,7 @@ class TemporalFeatureExtractor:
|
|
|
439
436
|
|
|
440
437
|
|
|
441
438
|
class SentimentFeatureExtractor:
|
|
442
|
-
"""Extracts sentiment and text-based features"""
|
|
439
|
+
"""Extracts sentiment and text-based features."""
|
|
443
440
|
|
|
444
441
|
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
445
442
|
self.config = config or {}
|
|
@@ -447,7 +444,7 @@ class SentimentFeatureExtractor:
|
|
|
447
444
|
self.negative_words = ["loss", "down", "bear", "decline", "weak", "fall", "drop"]
|
|
448
445
|
|
|
449
446
|
def extract_sentiment_features(self, df: pd.DataFrame) -> pd.DataFrame:
|
|
450
|
-
"""Extract sentiment features from text fields"""
|
|
447
|
+
"""Extract sentiment features from text fields."""
|
|
451
448
|
df_features = df.copy()
|
|
452
449
|
|
|
453
450
|
# Asset name sentiment
|
|
@@ -465,7 +462,7 @@ class SentimentFeatureExtractor:
|
|
|
465
462
|
def _extract_text_sentiment(
|
|
466
463
|
self, df: pd.DataFrame, text_column: str, prefix: str
|
|
467
464
|
) -> pd.DataFrame:
|
|
468
|
-
"""Extract sentiment from text column"""
|
|
465
|
+
"""Extract sentiment from text column."""
|
|
469
466
|
if text_column not in df.columns:
|
|
470
467
|
return df
|
|
471
468
|
|