mcli-framework 7.0.0__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/chat_cmd.py +42 -0
- mcli/app/commands_cmd.py +226 -0
- mcli/app/completion_cmd.py +216 -0
- mcli/app/completion_helpers.py +288 -0
- mcli/app/cron_test_cmd.py +697 -0
- mcli/app/logs_cmd.py +419 -0
- mcli/app/main.py +492 -0
- mcli/app/model/model.py +1060 -0
- mcli/app/model_cmd.py +227 -0
- mcli/app/redis_cmd.py +269 -0
- mcli/app/video/video.py +1114 -0
- mcli/app/visual_cmd.py +303 -0
- mcli/chat/chat.py +2409 -0
- mcli/chat/command_rag.py +514 -0
- mcli/chat/enhanced_chat.py +652 -0
- mcli/chat/system_controller.py +1010 -0
- mcli/chat/system_integration.py +1016 -0
- mcli/cli.py +25 -0
- mcli/config.toml +20 -0
- mcli/lib/api/api.py +586 -0
- mcli/lib/api/daemon_client.py +203 -0
- mcli/lib/api/daemon_client_local.py +44 -0
- mcli/lib/api/daemon_decorator.py +217 -0
- mcli/lib/api/mcli_decorators.py +1032 -0
- mcli/lib/auth/auth.py +85 -0
- mcli/lib/auth/aws_manager.py +85 -0
- mcli/lib/auth/azure_manager.py +91 -0
- mcli/lib/auth/credential_manager.py +192 -0
- mcli/lib/auth/gcp_manager.py +93 -0
- mcli/lib/auth/key_manager.py +117 -0
- mcli/lib/auth/mcli_manager.py +93 -0
- mcli/lib/auth/token_manager.py +75 -0
- mcli/lib/auth/token_util.py +1011 -0
- mcli/lib/config/config.py +47 -0
- mcli/lib/discovery/__init__.py +1 -0
- mcli/lib/discovery/command_discovery.py +274 -0
- mcli/lib/erd/erd.py +1345 -0
- mcli/lib/erd/generate_graph.py +453 -0
- mcli/lib/files/files.py +76 -0
- mcli/lib/fs/fs.py +109 -0
- mcli/lib/lib.py +29 -0
- mcli/lib/logger/logger.py +611 -0
- mcli/lib/performance/optimizer.py +409 -0
- mcli/lib/performance/rust_bridge.py +502 -0
- mcli/lib/performance/uvloop_config.py +154 -0
- mcli/lib/pickles/pickles.py +50 -0
- mcli/lib/search/cached_vectorizer.py +479 -0
- mcli/lib/services/data_pipeline.py +460 -0
- mcli/lib/services/lsh_client.py +441 -0
- mcli/lib/services/redis_service.py +387 -0
- mcli/lib/shell/shell.py +137 -0
- mcli/lib/toml/toml.py +33 -0
- mcli/lib/ui/styling.py +47 -0
- mcli/lib/ui/visual_effects.py +634 -0
- mcli/lib/watcher/watcher.py +185 -0
- mcli/ml/api/app.py +215 -0
- mcli/ml/api/middleware.py +224 -0
- mcli/ml/api/routers/admin_router.py +12 -0
- mcli/ml/api/routers/auth_router.py +244 -0
- mcli/ml/api/routers/backtest_router.py +12 -0
- mcli/ml/api/routers/data_router.py +12 -0
- mcli/ml/api/routers/model_router.py +302 -0
- mcli/ml/api/routers/monitoring_router.py +12 -0
- mcli/ml/api/routers/portfolio_router.py +12 -0
- mcli/ml/api/routers/prediction_router.py +267 -0
- mcli/ml/api/routers/trade_router.py +12 -0
- mcli/ml/api/routers/websocket_router.py +76 -0
- mcli/ml/api/schemas.py +64 -0
- mcli/ml/auth/auth_manager.py +425 -0
- mcli/ml/auth/models.py +154 -0
- mcli/ml/auth/permissions.py +302 -0
- mcli/ml/backtesting/backtest_engine.py +502 -0
- mcli/ml/backtesting/performance_metrics.py +393 -0
- mcli/ml/cache.py +400 -0
- mcli/ml/cli/main.py +398 -0
- mcli/ml/config/settings.py +394 -0
- mcli/ml/configs/dvc_config.py +230 -0
- mcli/ml/configs/mlflow_config.py +131 -0
- mcli/ml/configs/mlops_manager.py +293 -0
- mcli/ml/dashboard/app.py +532 -0
- mcli/ml/dashboard/app_integrated.py +738 -0
- mcli/ml/dashboard/app_supabase.py +560 -0
- mcli/ml/dashboard/app_training.py +615 -0
- mcli/ml/dashboard/cli.py +51 -0
- mcli/ml/data_ingestion/api_connectors.py +501 -0
- mcli/ml/data_ingestion/data_pipeline.py +567 -0
- mcli/ml/data_ingestion/stream_processor.py +512 -0
- mcli/ml/database/migrations/env.py +94 -0
- mcli/ml/database/models.py +667 -0
- mcli/ml/database/session.py +200 -0
- mcli/ml/experimentation/ab_testing.py +845 -0
- mcli/ml/features/ensemble_features.py +607 -0
- mcli/ml/features/political_features.py +676 -0
- mcli/ml/features/recommendation_engine.py +809 -0
- mcli/ml/features/stock_features.py +573 -0
- mcli/ml/features/test_feature_engineering.py +346 -0
- mcli/ml/logging.py +85 -0
- mcli/ml/mlops/data_versioning.py +518 -0
- mcli/ml/mlops/experiment_tracker.py +377 -0
- mcli/ml/mlops/model_serving.py +481 -0
- mcli/ml/mlops/pipeline_orchestrator.py +614 -0
- mcli/ml/models/base_models.py +324 -0
- mcli/ml/models/ensemble_models.py +675 -0
- mcli/ml/models/recommendation_models.py +474 -0
- mcli/ml/models/test_models.py +487 -0
- mcli/ml/monitoring/drift_detection.py +676 -0
- mcli/ml/monitoring/metrics.py +45 -0
- mcli/ml/optimization/portfolio_optimizer.py +834 -0
- mcli/ml/preprocessing/data_cleaners.py +451 -0
- mcli/ml/preprocessing/feature_extractors.py +491 -0
- mcli/ml/preprocessing/ml_pipeline.py +382 -0
- mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
- mcli/ml/preprocessing/test_preprocessing.py +294 -0
- mcli/ml/scripts/populate_sample_data.py +200 -0
- mcli/ml/tasks.py +400 -0
- mcli/ml/tests/test_integration.py +429 -0
- mcli/ml/tests/test_training_dashboard.py +387 -0
- mcli/public/oi/oi.py +15 -0
- mcli/public/public.py +4 -0
- mcli/self/self_cmd.py +1246 -0
- mcli/workflow/daemon/api_daemon.py +800 -0
- mcli/workflow/daemon/async_command_database.py +681 -0
- mcli/workflow/daemon/async_process_manager.py +591 -0
- mcli/workflow/daemon/client.py +530 -0
- mcli/workflow/daemon/commands.py +1196 -0
- mcli/workflow/daemon/daemon.py +905 -0
- mcli/workflow/daemon/daemon_api.py +59 -0
- mcli/workflow/daemon/enhanced_daemon.py +571 -0
- mcli/workflow/daemon/process_cli.py +244 -0
- mcli/workflow/daemon/process_manager.py +439 -0
- mcli/workflow/daemon/test_daemon.py +275 -0
- mcli/workflow/dashboard/dashboard_cmd.py +113 -0
- mcli/workflow/docker/docker.py +0 -0
- mcli/workflow/file/file.py +100 -0
- mcli/workflow/gcloud/config.toml +21 -0
- mcli/workflow/gcloud/gcloud.py +58 -0
- mcli/workflow/git_commit/ai_service.py +328 -0
- mcli/workflow/git_commit/commands.py +430 -0
- mcli/workflow/lsh_integration.py +355 -0
- mcli/workflow/model_service/client.py +594 -0
- mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
- mcli/workflow/model_service/lightweight_embedder.py +397 -0
- mcli/workflow/model_service/lightweight_model_server.py +714 -0
- mcli/workflow/model_service/lightweight_test.py +241 -0
- mcli/workflow/model_service/model_service.py +1955 -0
- mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
- mcli/workflow/model_service/pdf_processor.py +386 -0
- mcli/workflow/model_service/test_efficient_runner.py +234 -0
- mcli/workflow/model_service/test_example.py +315 -0
- mcli/workflow/model_service/test_integration.py +131 -0
- mcli/workflow/model_service/test_new_features.py +149 -0
- mcli/workflow/openai/openai.py +99 -0
- mcli/workflow/politician_trading/commands.py +1790 -0
- mcli/workflow/politician_trading/config.py +134 -0
- mcli/workflow/politician_trading/connectivity.py +490 -0
- mcli/workflow/politician_trading/data_sources.py +395 -0
- mcli/workflow/politician_trading/database.py +410 -0
- mcli/workflow/politician_trading/demo.py +248 -0
- mcli/workflow/politician_trading/models.py +165 -0
- mcli/workflow/politician_trading/monitoring.py +413 -0
- mcli/workflow/politician_trading/scrapers.py +966 -0
- mcli/workflow/politician_trading/scrapers_california.py +412 -0
- mcli/workflow/politician_trading/scrapers_eu.py +377 -0
- mcli/workflow/politician_trading/scrapers_uk.py +350 -0
- mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
- mcli/workflow/politician_trading/supabase_functions.py +354 -0
- mcli/workflow/politician_trading/workflow.py +852 -0
- mcli/workflow/registry/registry.py +180 -0
- mcli/workflow/repo/repo.py +223 -0
- mcli/workflow/scheduler/commands.py +493 -0
- mcli/workflow/scheduler/cron_parser.py +238 -0
- mcli/workflow/scheduler/job.py +182 -0
- mcli/workflow/scheduler/monitor.py +139 -0
- mcli/workflow/scheduler/persistence.py +324 -0
- mcli/workflow/scheduler/scheduler.py +679 -0
- mcli/workflow/sync/sync_cmd.py +437 -0
- mcli/workflow/sync/test_cmd.py +314 -0
- mcli/workflow/videos/videos.py +242 -0
- mcli/workflow/wakatime/wakatime.py +11 -0
- mcli/workflow/workflow.py +37 -0
- mcli_framework-7.0.0.dist-info/METADATA +479 -0
- mcli_framework-7.0.0.dist-info/RECORD +186 -0
- mcli_framework-7.0.0.dist-info/WHEEL +5 -0
- mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
- mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
- mcli_framework-7.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""Test script for feature engineering system"""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "../../.."))
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import numpy as np
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
import logging
|
|
12
|
+
|
|
13
|
+
# Set up logging
|
|
14
|
+
logging.basicConfig(level=logging.INFO)
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_mock_trading_data(n_records: int = 100) -> pd.DataFrame:
|
|
19
|
+
"""Generate mock politician trading data"""
|
|
20
|
+
np.random.seed(42)
|
|
21
|
+
|
|
22
|
+
politicians = ["Nancy Pelosi", "Mitch McConnell", "Chuck Schumer", "Kevin McCarthy"]
|
|
23
|
+
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "META", "JPM", "BAC"]
|
|
24
|
+
companies = [
|
|
25
|
+
"Apple Inc",
|
|
26
|
+
"Microsoft Corp",
|
|
27
|
+
"Alphabet Inc",
|
|
28
|
+
"Amazon.com Inc",
|
|
29
|
+
"Tesla Inc",
|
|
30
|
+
"Meta Platforms Inc",
|
|
31
|
+
"JPMorgan Chase",
|
|
32
|
+
"Bank of America",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
records = []
|
|
36
|
+
start_date = datetime.now() - timedelta(days=365)
|
|
37
|
+
|
|
38
|
+
for i in range(n_records):
|
|
39
|
+
days_ago = np.random.randint(0, 365)
|
|
40
|
+
trade_date = start_date + timedelta(days=days_ago)
|
|
41
|
+
|
|
42
|
+
ticker_idx = np.random.randint(0, len(tickers))
|
|
43
|
+
|
|
44
|
+
record = {
|
|
45
|
+
"politician_name_cleaned": np.random.choice(politicians),
|
|
46
|
+
"transaction_date_cleaned": trade_date.strftime("%Y-%m-%d"),
|
|
47
|
+
"transaction_date_dt": trade_date,
|
|
48
|
+
"transaction_amount_cleaned": np.random.uniform(1000, 500000),
|
|
49
|
+
"transaction_type_cleaned": np.random.choice(["buy", "sell"]),
|
|
50
|
+
"asset_name_cleaned": companies[ticker_idx],
|
|
51
|
+
"ticker_cleaned": tickers[ticker_idx],
|
|
52
|
+
"disclosure_date": (trade_date + timedelta(days=np.random.randint(1, 60))).strftime(
|
|
53
|
+
"%Y-%m-%d"
|
|
54
|
+
),
|
|
55
|
+
}
|
|
56
|
+
records.append(record)
|
|
57
|
+
|
|
58
|
+
return pd.DataFrame(records)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def generate_mock_stock_data(tickers: list, days: int = 365) -> pd.DataFrame:
|
|
62
|
+
"""Generate mock stock price data"""
|
|
63
|
+
np.random.seed(42)
|
|
64
|
+
|
|
65
|
+
stock_data = []
|
|
66
|
+
start_date = datetime.now() - timedelta(days=days)
|
|
67
|
+
|
|
68
|
+
for ticker in tickers:
|
|
69
|
+
base_price = np.random.uniform(50, 300)
|
|
70
|
+
|
|
71
|
+
for day in range(days):
|
|
72
|
+
date = start_date + timedelta(days=day)
|
|
73
|
+
|
|
74
|
+
# Random walk for price
|
|
75
|
+
change = np.random.normal(0, 0.02)
|
|
76
|
+
base_price *= 1 + change
|
|
77
|
+
|
|
78
|
+
# Generate OHLCV data
|
|
79
|
+
open_price = base_price * (1 + np.random.normal(0, 0.005))
|
|
80
|
+
high_price = max(open_price, base_price) * (1 + abs(np.random.normal(0, 0.01)))
|
|
81
|
+
low_price = min(open_price, base_price) * (1 - abs(np.random.normal(0, 0.01)))
|
|
82
|
+
close_price = base_price
|
|
83
|
+
volume = np.random.randint(1000000, 10000000)
|
|
84
|
+
|
|
85
|
+
stock_data.append(
|
|
86
|
+
{
|
|
87
|
+
"symbol": ticker,
|
|
88
|
+
"date": date,
|
|
89
|
+
"open": open_price,
|
|
90
|
+
"high": high_price,
|
|
91
|
+
"low": low_price,
|
|
92
|
+
"close": close_price,
|
|
93
|
+
"volume": volume,
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return pd.DataFrame(stock_data)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_stock_features():
|
|
101
|
+
"""Test stock feature extraction"""
|
|
102
|
+
logger.info("Testing stock features...")
|
|
103
|
+
|
|
104
|
+
from stock_features import StockRecommendationFeatures, TechnicalIndicatorFeatures
|
|
105
|
+
|
|
106
|
+
# Generate mock stock data
|
|
107
|
+
stock_data = generate_mock_stock_data(["AAPL"], 100)
|
|
108
|
+
|
|
109
|
+
# Test basic stock features
|
|
110
|
+
stock_extractor = StockRecommendationFeatures()
|
|
111
|
+
features_df = stock_extractor.extract_features(stock_data)
|
|
112
|
+
|
|
113
|
+
logger.info(f"Stock features shape: {features_df.shape}")
|
|
114
|
+
|
|
115
|
+
# Check for expected features
|
|
116
|
+
expected_features = ["sma_20", "rsi", "macd", "volatility_20", "bb_position"]
|
|
117
|
+
|
|
118
|
+
found_features = 0
|
|
119
|
+
for feature in expected_features:
|
|
120
|
+
if feature in features_df.columns:
|
|
121
|
+
logger.info(f"✅ Found feature: {feature}")
|
|
122
|
+
found_features += 1
|
|
123
|
+
else:
|
|
124
|
+
logger.warning(f"⚠️ Missing feature: {feature}")
|
|
125
|
+
|
|
126
|
+
# Test technical indicators
|
|
127
|
+
tech_extractor = TechnicalIndicatorFeatures()
|
|
128
|
+
tech_features = tech_extractor.extract_advanced_indicators(features_df)
|
|
129
|
+
|
|
130
|
+
logger.info(f"Technical features shape: {tech_features.shape}")
|
|
131
|
+
|
|
132
|
+
assert found_features > 0, "Should have extracted some stock features"
|
|
133
|
+
logger.info("✅ Stock features test passed")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_political_features():
|
|
137
|
+
"""Test political feature extraction"""
|
|
138
|
+
logger.info("Testing political features...")
|
|
139
|
+
|
|
140
|
+
from political_features import PoliticalInfluenceFeatures
|
|
141
|
+
|
|
142
|
+
# Generate mock trading data
|
|
143
|
+
trading_data = generate_mock_trading_data(50)
|
|
144
|
+
|
|
145
|
+
# Test political features
|
|
146
|
+
political_extractor = PoliticalInfluenceFeatures()
|
|
147
|
+
features_df = political_extractor.extract_influence_features(trading_data)
|
|
148
|
+
|
|
149
|
+
logger.info(f"Political features shape: {features_df.shape}")
|
|
150
|
+
|
|
151
|
+
# Check for expected features
|
|
152
|
+
expected_features = [
|
|
153
|
+
"total_influence",
|
|
154
|
+
"trading_frequency_score",
|
|
155
|
+
"committee_sector_alignment",
|
|
156
|
+
"disclosure_compliance_score",
|
|
157
|
+
"seniority_influence",
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
found_features = 0
|
|
161
|
+
for feature in expected_features:
|
|
162
|
+
if feature in features_df.columns:
|
|
163
|
+
logger.info(f"✅ Found feature: {feature}")
|
|
164
|
+
found_features += 1
|
|
165
|
+
else:
|
|
166
|
+
logger.warning(f"⚠️ Missing feature: {feature}")
|
|
167
|
+
|
|
168
|
+
assert found_features > 0, "Should have extracted some political features"
|
|
169
|
+
logger.info("✅ Political features test passed")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def test_ensemble_features():
|
|
173
|
+
"""Test ensemble feature engineering"""
|
|
174
|
+
logger.info("Testing ensemble features...")
|
|
175
|
+
|
|
176
|
+
from ensemble_features import EnsembleFeatureBuilder
|
|
177
|
+
|
|
178
|
+
# Generate base features
|
|
179
|
+
trading_data = generate_mock_trading_data(50)
|
|
180
|
+
|
|
181
|
+
# Add some numerical features for ensemble processing
|
|
182
|
+
trading_data["feature_1"] = np.random.random(len(trading_data))
|
|
183
|
+
trading_data["feature_2"] = np.random.random(len(trading_data))
|
|
184
|
+
trading_data["feature_3"] = np.random.random(len(trading_data))
|
|
185
|
+
|
|
186
|
+
# Test ensemble features
|
|
187
|
+
ensemble_builder = EnsembleFeatureBuilder()
|
|
188
|
+
ensemble_df = ensemble_builder.build_ensemble_features(trading_data)
|
|
189
|
+
|
|
190
|
+
logger.info(f"Ensemble features shape: {ensemble_df.shape}")
|
|
191
|
+
|
|
192
|
+
# Check for ensemble-specific features
|
|
193
|
+
ensemble_feature_types = ["rolling", "interaction", "cluster", "poly"]
|
|
194
|
+
found_types = 0
|
|
195
|
+
|
|
196
|
+
for feature_type in ensemble_feature_types:
|
|
197
|
+
matching_features = [col for col in ensemble_df.columns if feature_type in col.lower()]
|
|
198
|
+
if matching_features:
|
|
199
|
+
logger.info(f"✅ Found {feature_type} features: {len(matching_features)}")
|
|
200
|
+
found_types += 1
|
|
201
|
+
else:
|
|
202
|
+
logger.warning(f"⚠️ No {feature_type} features found")
|
|
203
|
+
|
|
204
|
+
assert found_types > 0, "Should have created some ensemble features"
|
|
205
|
+
logger.info("✅ Ensemble features test passed")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_recommendation_engine():
|
|
209
|
+
"""Test the full recommendation engine"""
|
|
210
|
+
logger.info("Testing recommendation engine...")
|
|
211
|
+
|
|
212
|
+
from recommendation_engine import StockRecommendationEngine, RecommendationConfig
|
|
213
|
+
|
|
214
|
+
# Generate comprehensive test data
|
|
215
|
+
trading_data = generate_mock_trading_data(100)
|
|
216
|
+
stock_data = generate_mock_stock_data(["AAPL", "MSFT", "GOOGL"], 100)
|
|
217
|
+
|
|
218
|
+
# Create recommendation engine
|
|
219
|
+
config = RecommendationConfig(
|
|
220
|
+
enable_technical_features=True,
|
|
221
|
+
enable_political_features=True,
|
|
222
|
+
enable_ensemble_features=True,
|
|
223
|
+
max_features=50, # Limit for testing
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
engine = StockRecommendationEngine(config)
|
|
227
|
+
|
|
228
|
+
# Generate recommendations
|
|
229
|
+
recommendations = engine.generate_recommendation(
|
|
230
|
+
trading_data=trading_data,
|
|
231
|
+
stock_price_data=stock_data,
|
|
232
|
+
politician_metadata=None,
|
|
233
|
+
market_data=None,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
logger.info(f"Generated {len(recommendations)} recommendations")
|
|
237
|
+
|
|
238
|
+
# Validate recommendations
|
|
239
|
+
assert len(recommendations) > 0, "Should generate at least one recommendation"
|
|
240
|
+
|
|
241
|
+
for rec in recommendations:
|
|
242
|
+
logger.info(f"Recommendation for {rec.ticker}:")
|
|
243
|
+
logger.info(f" Score: {rec.recommendation_score}")
|
|
244
|
+
logger.info(f" Confidence: {rec.confidence}")
|
|
245
|
+
logger.info(f" Risk: {rec.risk_level}")
|
|
246
|
+
logger.info(f" Reason: {rec.recommendation_reason}")
|
|
247
|
+
|
|
248
|
+
# Validate recommendation structure
|
|
249
|
+
assert 0 <= rec.recommendation_score <= 1, "Score should be between 0 and 1"
|
|
250
|
+
assert 0 <= rec.confidence <= 1, "Confidence should be between 0 and 1"
|
|
251
|
+
assert rec.risk_level in ["low", "medium", "high"], "Risk level should be valid"
|
|
252
|
+
assert len(rec.key_features) >= 0, "Should have key features list"
|
|
253
|
+
assert isinstance(rec.warnings, list), "Warnings should be a list"
|
|
254
|
+
|
|
255
|
+
logger.info("✅ Recommendation engine test passed")
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def test_feature_integration():
|
|
259
|
+
"""Test integration of all feature components"""
|
|
260
|
+
logger.info("Testing feature integration...")
|
|
261
|
+
|
|
262
|
+
from stock_features import StockRecommendationFeatures
|
|
263
|
+
from political_features import PoliticalInfluenceFeatures
|
|
264
|
+
from ensemble_features import EnsembleFeatureBuilder
|
|
265
|
+
|
|
266
|
+
# Generate test data
|
|
267
|
+
trading_data = generate_mock_trading_data(30)
|
|
268
|
+
|
|
269
|
+
# Apply features sequentially
|
|
270
|
+
logger.info("Applying political features...")
|
|
271
|
+
political_extractor = PoliticalInfluenceFeatures()
|
|
272
|
+
features_df = political_extractor.extract_influence_features(trading_data)
|
|
273
|
+
|
|
274
|
+
logger.info("Applying ensemble features...")
|
|
275
|
+
ensemble_builder = EnsembleFeatureBuilder()
|
|
276
|
+
final_df = ensemble_builder.build_ensemble_features(features_df)
|
|
277
|
+
|
|
278
|
+
logger.info(f"Final integrated features shape: {final_df.shape}")
|
|
279
|
+
|
|
280
|
+
# Check that we have a reasonable number of features
|
|
281
|
+
original_cols = len(trading_data.columns)
|
|
282
|
+
final_cols = len(final_df.columns)
|
|
283
|
+
|
|
284
|
+
logger.info(f"Features increased from {original_cols} to {final_cols}")
|
|
285
|
+
assert final_cols > original_cols, "Should have added new features"
|
|
286
|
+
|
|
287
|
+
# Check for feature diversity
|
|
288
|
+
feature_types = set()
|
|
289
|
+
for col in final_df.columns:
|
|
290
|
+
if "rolling" in col:
|
|
291
|
+
feature_types.add("rolling")
|
|
292
|
+
elif "interaction" in col or "_x_" in col:
|
|
293
|
+
feature_types.add("interaction")
|
|
294
|
+
elif "cluster" in col:
|
|
295
|
+
feature_types.add("cluster")
|
|
296
|
+
elif "influence" in col:
|
|
297
|
+
feature_types.add("political")
|
|
298
|
+
|
|
299
|
+
logger.info(f"Feature types found: {feature_types}")
|
|
300
|
+
assert len(feature_types) > 1, "Should have multiple types of features"
|
|
301
|
+
|
|
302
|
+
logger.info("✅ Feature integration test passed")
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def main():
|
|
306
|
+
"""Run all feature engineering tests"""
|
|
307
|
+
logger.info("Starting feature engineering tests...")
|
|
308
|
+
|
|
309
|
+
try:
|
|
310
|
+
# Test individual components
|
|
311
|
+
test_stock_features()
|
|
312
|
+
test_political_features()
|
|
313
|
+
test_ensemble_features()
|
|
314
|
+
|
|
315
|
+
# Test integration
|
|
316
|
+
test_feature_integration()
|
|
317
|
+
test_recommendation_engine()
|
|
318
|
+
|
|
319
|
+
logger.info("🎉 All feature engineering tests passed!")
|
|
320
|
+
|
|
321
|
+
# Print summary
|
|
322
|
+
logger.info("\n" + "=" * 60)
|
|
323
|
+
logger.info("FEATURE ENGINEERING SYSTEM SUMMARY")
|
|
324
|
+
logger.info("=" * 60)
|
|
325
|
+
logger.info("✅ Stock technical features: RSI, MACD, Bollinger Bands, etc.")
|
|
326
|
+
logger.info("✅ Political influence features: Committee alignment, trading patterns")
|
|
327
|
+
logger.info("✅ Ensemble features: Interactions, clustering, polynomial")
|
|
328
|
+
logger.info("✅ Market regime features: Volatility, trend, volume regimes")
|
|
329
|
+
logger.info("✅ Advanced interactions: Conditional, non-linear, min/max")
|
|
330
|
+
logger.info("✅ Feature selection: Multiple criteria, dynamic selection")
|
|
331
|
+
logger.info("✅ Recommendation engine: Comprehensive scoring and explanations")
|
|
332
|
+
logger.info("=" * 60)
|
|
333
|
+
|
|
334
|
+
return True
|
|
335
|
+
|
|
336
|
+
except Exception as e:
|
|
337
|
+
logger.error(f"❌ Feature engineering tests failed: {e}")
|
|
338
|
+
import traceback
|
|
339
|
+
|
|
340
|
+
traceback.print_exc()
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
if __name__ == "__main__":
|
|
345
|
+
success = main()
|
|
346
|
+
sys.exit(0 if success else 1)
|
mcli/ml/logging.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Logging configuration for ML system"""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler
|
|
7
|
+
import json
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from mcli.ml.config import settings
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class StructuredFormatter(logging.Formatter):
|
|
14
|
+
"""JSON structured logging formatter"""
|
|
15
|
+
|
|
16
|
+
def format(self, record):
|
|
17
|
+
log_obj = {
|
|
18
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
19
|
+
"level": record.levelname,
|
|
20
|
+
"logger": record.name,
|
|
21
|
+
"message": record.getMessage(),
|
|
22
|
+
"module": record.module,
|
|
23
|
+
"function": record.funcName,
|
|
24
|
+
"line": record.lineno,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if hasattr(record, 'request_id'):
|
|
28
|
+
log_obj['request_id'] = record.request_id
|
|
29
|
+
|
|
30
|
+
if record.exc_info:
|
|
31
|
+
log_obj['exception'] = self.formatException(record.exc_info)
|
|
32
|
+
|
|
33
|
+
return json.dumps(log_obj)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def setup_logging():
|
|
37
|
+
"""Configure logging for the application"""
|
|
38
|
+
|
|
39
|
+
# Create logs directory
|
|
40
|
+
log_dir = Path("logs")
|
|
41
|
+
log_dir.mkdir(exist_ok=True)
|
|
42
|
+
|
|
43
|
+
# Root logger configuration
|
|
44
|
+
root_logger = logging.getLogger()
|
|
45
|
+
root_logger.setLevel(logging.DEBUG if settings.debug else logging.INFO)
|
|
46
|
+
|
|
47
|
+
# Console handler
|
|
48
|
+
console_handler = logging.StreamHandler(sys.stdout)
|
|
49
|
+
console_handler.setLevel(logging.DEBUG if settings.debug else logging.INFO)
|
|
50
|
+
|
|
51
|
+
if settings.monitoring.log_format == "structured":
|
|
52
|
+
console_formatter = StructuredFormatter()
|
|
53
|
+
else:
|
|
54
|
+
console_formatter = logging.Formatter(
|
|
55
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
console_handler.setFormatter(console_formatter)
|
|
59
|
+
root_logger.addHandler(console_handler)
|
|
60
|
+
|
|
61
|
+
# File handler for all logs
|
|
62
|
+
file_handler = TimedRotatingFileHandler(
|
|
63
|
+
log_dir / "mcli_ml.log",
|
|
64
|
+
when="midnight",
|
|
65
|
+
interval=1,
|
|
66
|
+
backupCount=30
|
|
67
|
+
)
|
|
68
|
+
file_handler.setLevel(logging.DEBUG)
|
|
69
|
+
file_handler.setFormatter(StructuredFormatter())
|
|
70
|
+
root_logger.addHandler(file_handler)
|
|
71
|
+
|
|
72
|
+
# Error file handler
|
|
73
|
+
error_handler = RotatingFileHandler(
|
|
74
|
+
log_dir / "mcli_ml_errors.log",
|
|
75
|
+
maxBytes=10 * 1024 * 1024, # 10MB
|
|
76
|
+
backupCount=5
|
|
77
|
+
)
|
|
78
|
+
error_handler.setLevel(logging.ERROR)
|
|
79
|
+
error_handler.setFormatter(StructuredFormatter())
|
|
80
|
+
root_logger.addHandler(error_handler)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_logger(name: str) -> logging.Logger:
|
|
84
|
+
"""Get a logger instance"""
|
|
85
|
+
return logging.getLogger(name)
|