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.

Files changed (186) hide show
  1. mcli/app/chat_cmd.py +42 -0
  2. mcli/app/commands_cmd.py +226 -0
  3. mcli/app/completion_cmd.py +216 -0
  4. mcli/app/completion_helpers.py +288 -0
  5. mcli/app/cron_test_cmd.py +697 -0
  6. mcli/app/logs_cmd.py +419 -0
  7. mcli/app/main.py +492 -0
  8. mcli/app/model/model.py +1060 -0
  9. mcli/app/model_cmd.py +227 -0
  10. mcli/app/redis_cmd.py +269 -0
  11. mcli/app/video/video.py +1114 -0
  12. mcli/app/visual_cmd.py +303 -0
  13. mcli/chat/chat.py +2409 -0
  14. mcli/chat/command_rag.py +514 -0
  15. mcli/chat/enhanced_chat.py +652 -0
  16. mcli/chat/system_controller.py +1010 -0
  17. mcli/chat/system_integration.py +1016 -0
  18. mcli/cli.py +25 -0
  19. mcli/config.toml +20 -0
  20. mcli/lib/api/api.py +586 -0
  21. mcli/lib/api/daemon_client.py +203 -0
  22. mcli/lib/api/daemon_client_local.py +44 -0
  23. mcli/lib/api/daemon_decorator.py +217 -0
  24. mcli/lib/api/mcli_decorators.py +1032 -0
  25. mcli/lib/auth/auth.py +85 -0
  26. mcli/lib/auth/aws_manager.py +85 -0
  27. mcli/lib/auth/azure_manager.py +91 -0
  28. mcli/lib/auth/credential_manager.py +192 -0
  29. mcli/lib/auth/gcp_manager.py +93 -0
  30. mcli/lib/auth/key_manager.py +117 -0
  31. mcli/lib/auth/mcli_manager.py +93 -0
  32. mcli/lib/auth/token_manager.py +75 -0
  33. mcli/lib/auth/token_util.py +1011 -0
  34. mcli/lib/config/config.py +47 -0
  35. mcli/lib/discovery/__init__.py +1 -0
  36. mcli/lib/discovery/command_discovery.py +274 -0
  37. mcli/lib/erd/erd.py +1345 -0
  38. mcli/lib/erd/generate_graph.py +453 -0
  39. mcli/lib/files/files.py +76 -0
  40. mcli/lib/fs/fs.py +109 -0
  41. mcli/lib/lib.py +29 -0
  42. mcli/lib/logger/logger.py +611 -0
  43. mcli/lib/performance/optimizer.py +409 -0
  44. mcli/lib/performance/rust_bridge.py +502 -0
  45. mcli/lib/performance/uvloop_config.py +154 -0
  46. mcli/lib/pickles/pickles.py +50 -0
  47. mcli/lib/search/cached_vectorizer.py +479 -0
  48. mcli/lib/services/data_pipeline.py +460 -0
  49. mcli/lib/services/lsh_client.py +441 -0
  50. mcli/lib/services/redis_service.py +387 -0
  51. mcli/lib/shell/shell.py +137 -0
  52. mcli/lib/toml/toml.py +33 -0
  53. mcli/lib/ui/styling.py +47 -0
  54. mcli/lib/ui/visual_effects.py +634 -0
  55. mcli/lib/watcher/watcher.py +185 -0
  56. mcli/ml/api/app.py +215 -0
  57. mcli/ml/api/middleware.py +224 -0
  58. mcli/ml/api/routers/admin_router.py +12 -0
  59. mcli/ml/api/routers/auth_router.py +244 -0
  60. mcli/ml/api/routers/backtest_router.py +12 -0
  61. mcli/ml/api/routers/data_router.py +12 -0
  62. mcli/ml/api/routers/model_router.py +302 -0
  63. mcli/ml/api/routers/monitoring_router.py +12 -0
  64. mcli/ml/api/routers/portfolio_router.py +12 -0
  65. mcli/ml/api/routers/prediction_router.py +267 -0
  66. mcli/ml/api/routers/trade_router.py +12 -0
  67. mcli/ml/api/routers/websocket_router.py +76 -0
  68. mcli/ml/api/schemas.py +64 -0
  69. mcli/ml/auth/auth_manager.py +425 -0
  70. mcli/ml/auth/models.py +154 -0
  71. mcli/ml/auth/permissions.py +302 -0
  72. mcli/ml/backtesting/backtest_engine.py +502 -0
  73. mcli/ml/backtesting/performance_metrics.py +393 -0
  74. mcli/ml/cache.py +400 -0
  75. mcli/ml/cli/main.py +398 -0
  76. mcli/ml/config/settings.py +394 -0
  77. mcli/ml/configs/dvc_config.py +230 -0
  78. mcli/ml/configs/mlflow_config.py +131 -0
  79. mcli/ml/configs/mlops_manager.py +293 -0
  80. mcli/ml/dashboard/app.py +532 -0
  81. mcli/ml/dashboard/app_integrated.py +738 -0
  82. mcli/ml/dashboard/app_supabase.py +560 -0
  83. mcli/ml/dashboard/app_training.py +615 -0
  84. mcli/ml/dashboard/cli.py +51 -0
  85. mcli/ml/data_ingestion/api_connectors.py +501 -0
  86. mcli/ml/data_ingestion/data_pipeline.py +567 -0
  87. mcli/ml/data_ingestion/stream_processor.py +512 -0
  88. mcli/ml/database/migrations/env.py +94 -0
  89. mcli/ml/database/models.py +667 -0
  90. mcli/ml/database/session.py +200 -0
  91. mcli/ml/experimentation/ab_testing.py +845 -0
  92. mcli/ml/features/ensemble_features.py +607 -0
  93. mcli/ml/features/political_features.py +676 -0
  94. mcli/ml/features/recommendation_engine.py +809 -0
  95. mcli/ml/features/stock_features.py +573 -0
  96. mcli/ml/features/test_feature_engineering.py +346 -0
  97. mcli/ml/logging.py +85 -0
  98. mcli/ml/mlops/data_versioning.py +518 -0
  99. mcli/ml/mlops/experiment_tracker.py +377 -0
  100. mcli/ml/mlops/model_serving.py +481 -0
  101. mcli/ml/mlops/pipeline_orchestrator.py +614 -0
  102. mcli/ml/models/base_models.py +324 -0
  103. mcli/ml/models/ensemble_models.py +675 -0
  104. mcli/ml/models/recommendation_models.py +474 -0
  105. mcli/ml/models/test_models.py +487 -0
  106. mcli/ml/monitoring/drift_detection.py +676 -0
  107. mcli/ml/monitoring/metrics.py +45 -0
  108. mcli/ml/optimization/portfolio_optimizer.py +834 -0
  109. mcli/ml/preprocessing/data_cleaners.py +451 -0
  110. mcli/ml/preprocessing/feature_extractors.py +491 -0
  111. mcli/ml/preprocessing/ml_pipeline.py +382 -0
  112. mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
  113. mcli/ml/preprocessing/test_preprocessing.py +294 -0
  114. mcli/ml/scripts/populate_sample_data.py +200 -0
  115. mcli/ml/tasks.py +400 -0
  116. mcli/ml/tests/test_integration.py +429 -0
  117. mcli/ml/tests/test_training_dashboard.py +387 -0
  118. mcli/public/oi/oi.py +15 -0
  119. mcli/public/public.py +4 -0
  120. mcli/self/self_cmd.py +1246 -0
  121. mcli/workflow/daemon/api_daemon.py +800 -0
  122. mcli/workflow/daemon/async_command_database.py +681 -0
  123. mcli/workflow/daemon/async_process_manager.py +591 -0
  124. mcli/workflow/daemon/client.py +530 -0
  125. mcli/workflow/daemon/commands.py +1196 -0
  126. mcli/workflow/daemon/daemon.py +905 -0
  127. mcli/workflow/daemon/daemon_api.py +59 -0
  128. mcli/workflow/daemon/enhanced_daemon.py +571 -0
  129. mcli/workflow/daemon/process_cli.py +244 -0
  130. mcli/workflow/daemon/process_manager.py +439 -0
  131. mcli/workflow/daemon/test_daemon.py +275 -0
  132. mcli/workflow/dashboard/dashboard_cmd.py +113 -0
  133. mcli/workflow/docker/docker.py +0 -0
  134. mcli/workflow/file/file.py +100 -0
  135. mcli/workflow/gcloud/config.toml +21 -0
  136. mcli/workflow/gcloud/gcloud.py +58 -0
  137. mcli/workflow/git_commit/ai_service.py +328 -0
  138. mcli/workflow/git_commit/commands.py +430 -0
  139. mcli/workflow/lsh_integration.py +355 -0
  140. mcli/workflow/model_service/client.py +594 -0
  141. mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
  142. mcli/workflow/model_service/lightweight_embedder.py +397 -0
  143. mcli/workflow/model_service/lightweight_model_server.py +714 -0
  144. mcli/workflow/model_service/lightweight_test.py +241 -0
  145. mcli/workflow/model_service/model_service.py +1955 -0
  146. mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
  147. mcli/workflow/model_service/pdf_processor.py +386 -0
  148. mcli/workflow/model_service/test_efficient_runner.py +234 -0
  149. mcli/workflow/model_service/test_example.py +315 -0
  150. mcli/workflow/model_service/test_integration.py +131 -0
  151. mcli/workflow/model_service/test_new_features.py +149 -0
  152. mcli/workflow/openai/openai.py +99 -0
  153. mcli/workflow/politician_trading/commands.py +1790 -0
  154. mcli/workflow/politician_trading/config.py +134 -0
  155. mcli/workflow/politician_trading/connectivity.py +490 -0
  156. mcli/workflow/politician_trading/data_sources.py +395 -0
  157. mcli/workflow/politician_trading/database.py +410 -0
  158. mcli/workflow/politician_trading/demo.py +248 -0
  159. mcli/workflow/politician_trading/models.py +165 -0
  160. mcli/workflow/politician_trading/monitoring.py +413 -0
  161. mcli/workflow/politician_trading/scrapers.py +966 -0
  162. mcli/workflow/politician_trading/scrapers_california.py +412 -0
  163. mcli/workflow/politician_trading/scrapers_eu.py +377 -0
  164. mcli/workflow/politician_trading/scrapers_uk.py +350 -0
  165. mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
  166. mcli/workflow/politician_trading/supabase_functions.py +354 -0
  167. mcli/workflow/politician_trading/workflow.py +852 -0
  168. mcli/workflow/registry/registry.py +180 -0
  169. mcli/workflow/repo/repo.py +223 -0
  170. mcli/workflow/scheduler/commands.py +493 -0
  171. mcli/workflow/scheduler/cron_parser.py +238 -0
  172. mcli/workflow/scheduler/job.py +182 -0
  173. mcli/workflow/scheduler/monitor.py +139 -0
  174. mcli/workflow/scheduler/persistence.py +324 -0
  175. mcli/workflow/scheduler/scheduler.py +679 -0
  176. mcli/workflow/sync/sync_cmd.py +437 -0
  177. mcli/workflow/sync/test_cmd.py +314 -0
  178. mcli/workflow/videos/videos.py +242 -0
  179. mcli/workflow/wakatime/wakatime.py +11 -0
  180. mcli/workflow/workflow.py +37 -0
  181. mcli_framework-7.0.0.dist-info/METADATA +479 -0
  182. mcli_framework-7.0.0.dist-info/RECORD +186 -0
  183. mcli_framework-7.0.0.dist-info/WHEEL +5 -0
  184. mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
  185. mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
  186. 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)