mcli-framework 7.1.1__py3-none-any.whl → 7.1.2__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 (94) hide show
  1. mcli/app/completion_cmd.py +59 -49
  2. mcli/app/completion_helpers.py +60 -138
  3. mcli/app/logs_cmd.py +6 -2
  4. mcli/app/main.py +17 -14
  5. mcli/app/model_cmd.py +19 -4
  6. mcli/chat/chat.py +3 -2
  7. mcli/lib/search/cached_vectorizer.py +1 -0
  8. mcli/lib/services/data_pipeline.py +12 -5
  9. mcli/lib/services/lsh_client.py +68 -57
  10. mcli/ml/api/app.py +28 -36
  11. mcli/ml/api/middleware.py +8 -16
  12. mcli/ml/api/routers/admin_router.py +3 -1
  13. mcli/ml/api/routers/auth_router.py +32 -56
  14. mcli/ml/api/routers/backtest_router.py +3 -1
  15. mcli/ml/api/routers/data_router.py +3 -1
  16. mcli/ml/api/routers/model_router.py +35 -74
  17. mcli/ml/api/routers/monitoring_router.py +3 -1
  18. mcli/ml/api/routers/portfolio_router.py +3 -1
  19. mcli/ml/api/routers/prediction_router.py +60 -65
  20. mcli/ml/api/routers/trade_router.py +6 -2
  21. mcli/ml/api/routers/websocket_router.py +12 -9
  22. mcli/ml/api/schemas.py +10 -2
  23. mcli/ml/auth/auth_manager.py +49 -114
  24. mcli/ml/auth/models.py +30 -15
  25. mcli/ml/auth/permissions.py +12 -19
  26. mcli/ml/backtesting/backtest_engine.py +134 -108
  27. mcli/ml/backtesting/performance_metrics.py +142 -108
  28. mcli/ml/cache.py +12 -18
  29. mcli/ml/cli/main.py +37 -23
  30. mcli/ml/config/settings.py +29 -12
  31. mcli/ml/dashboard/app.py +122 -130
  32. mcli/ml/dashboard/app_integrated.py +216 -150
  33. mcli/ml/dashboard/app_supabase.py +176 -108
  34. mcli/ml/dashboard/app_training.py +212 -206
  35. mcli/ml/dashboard/cli.py +14 -5
  36. mcli/ml/data_ingestion/api_connectors.py +51 -81
  37. mcli/ml/data_ingestion/data_pipeline.py +127 -125
  38. mcli/ml/data_ingestion/stream_processor.py +72 -80
  39. mcli/ml/database/migrations/env.py +3 -2
  40. mcli/ml/database/models.py +112 -79
  41. mcli/ml/database/session.py +6 -5
  42. mcli/ml/experimentation/ab_testing.py +149 -99
  43. mcli/ml/features/ensemble_features.py +9 -8
  44. mcli/ml/features/political_features.py +6 -5
  45. mcli/ml/features/recommendation_engine.py +15 -14
  46. mcli/ml/features/stock_features.py +7 -6
  47. mcli/ml/features/test_feature_engineering.py +8 -7
  48. mcli/ml/logging.py +10 -15
  49. mcli/ml/mlops/data_versioning.py +57 -64
  50. mcli/ml/mlops/experiment_tracker.py +49 -41
  51. mcli/ml/mlops/model_serving.py +59 -62
  52. mcli/ml/mlops/pipeline_orchestrator.py +203 -149
  53. mcli/ml/models/base_models.py +8 -7
  54. mcli/ml/models/ensemble_models.py +6 -5
  55. mcli/ml/models/recommendation_models.py +7 -6
  56. mcli/ml/models/test_models.py +18 -14
  57. mcli/ml/monitoring/drift_detection.py +95 -74
  58. mcli/ml/monitoring/metrics.py +10 -22
  59. mcli/ml/optimization/portfolio_optimizer.py +172 -132
  60. mcli/ml/predictions/prediction_engine.py +62 -50
  61. mcli/ml/preprocessing/data_cleaners.py +6 -5
  62. mcli/ml/preprocessing/feature_extractors.py +7 -6
  63. mcli/ml/preprocessing/ml_pipeline.py +3 -2
  64. mcli/ml/preprocessing/politician_trading_preprocessor.py +11 -10
  65. mcli/ml/preprocessing/test_preprocessing.py +4 -4
  66. mcli/ml/scripts/populate_sample_data.py +36 -16
  67. mcli/ml/tasks.py +82 -83
  68. mcli/ml/tests/test_integration.py +86 -76
  69. mcli/ml/tests/test_training_dashboard.py +169 -142
  70. mcli/mygroup/test_cmd.py +2 -1
  71. mcli/self/self_cmd.py +31 -16
  72. mcli/self/test_cmd.py +2 -1
  73. mcli/workflow/dashboard/dashboard_cmd.py +13 -6
  74. mcli/workflow/lsh_integration.py +46 -58
  75. mcli/workflow/politician_trading/commands.py +576 -427
  76. mcli/workflow/politician_trading/config.py +7 -7
  77. mcli/workflow/politician_trading/connectivity.py +35 -33
  78. mcli/workflow/politician_trading/data_sources.py +72 -71
  79. mcli/workflow/politician_trading/database.py +18 -16
  80. mcli/workflow/politician_trading/demo.py +4 -3
  81. mcli/workflow/politician_trading/models.py +5 -5
  82. mcli/workflow/politician_trading/monitoring.py +13 -13
  83. mcli/workflow/politician_trading/scrapers.py +332 -224
  84. mcli/workflow/politician_trading/scrapers_california.py +116 -94
  85. mcli/workflow/politician_trading/scrapers_eu.py +70 -71
  86. mcli/workflow/politician_trading/scrapers_uk.py +118 -90
  87. mcli/workflow/politician_trading/scrapers_us_states.py +125 -92
  88. mcli/workflow/politician_trading/workflow.py +98 -71
  89. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/METADATA +1 -1
  90. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/RECORD +94 -94
  91. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/WHEEL +0 -0
  92. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/entry_points.txt +0 -0
  93. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/licenses/LICENSE +0 -0
  94. {mcli_framework-7.1.1.dist-info → mcli_framework-7.1.2.dist-info}/top_level.txt +0 -0
@@ -1,18 +1,18 @@
1
1
  """Prediction API routes"""
2
2
 
3
- from typing import List, Optional
4
3
  from datetime import datetime, timedelta
4
+ from typing import List, Optional
5
5
  from uuid import UUID
6
6
 
7
- from fastapi import APIRouter, Depends, HTTPException, status, Query, BackgroundTasks
8
- from sqlalchemy.orm import Session
7
+ from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, Query, status
9
8
  from pydantic import BaseModel
9
+ from sqlalchemy.orm import Session
10
10
 
11
+ from mcli.ml.api.schemas import BatchPredictionRequest, PredictionRequest, PredictionResponse
11
12
  from mcli.ml.auth import get_current_active_user
13
+ from mcli.ml.cache import cache_set, cached
14
+ from mcli.ml.database.models import Model, Prediction, StockData, User
12
15
  from mcli.ml.database.session import get_db
13
- from mcli.ml.database.models import User, Prediction, Model, StockData
14
- from mcli.ml.api.schemas import PredictionRequest, PredictionResponse, BatchPredictionRequest
15
- from mcli.ml.cache import cached, cache_set
16
16
  from mcli.ml.models import get_model_by_id
17
17
 
18
18
  router = APIRouter()
@@ -30,7 +30,7 @@ async def create_prediction(
30
30
  request: PredictionInput,
31
31
  background_tasks: BackgroundTasks,
32
32
  current_user: User = Depends(get_current_active_user),
33
- db: Session = Depends(get_db)
33
+ db: Session = Depends(get_db),
34
34
  ):
35
35
  """Create a new prediction"""
36
36
 
@@ -38,20 +38,20 @@ async def create_prediction(
38
38
  if request.model_id:
39
39
  model = db.query(Model).filter(Model.id == request.model_id).first()
40
40
  if not model:
41
- raise HTTPException(
42
- status_code=status.HTTP_404_NOT_FOUND,
43
- detail="Model not found"
44
- )
41
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Model not found")
45
42
  else:
46
43
  # Get default deployed model
47
- model = db.query(Model).filter(
48
- Model.status == "deployed"
49
- ).order_by(Model.deployed_at.desc()).first()
44
+ model = (
45
+ db.query(Model)
46
+ .filter(Model.status == "deployed")
47
+ .order_by(Model.deployed_at.desc())
48
+ .first()
49
+ )
50
50
 
51
51
  if not model:
52
52
  raise HTTPException(
53
53
  status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
54
- detail="No deployed model available"
54
+ detail="No deployed model available",
55
55
  )
56
56
 
57
57
  # Load model
@@ -59,6 +59,7 @@ async def create_prediction(
59
59
 
60
60
  # Make prediction
61
61
  import numpy as np
62
+
62
63
  features_array = np.array(list(request.features.values())).reshape(1, -1)
63
64
  predicted_return = float(ml_model.predict(features_array)[0])
64
65
 
@@ -74,7 +75,7 @@ async def create_prediction(
74
75
  target_date=datetime.utcnow() + timedelta(days=request.horizon),
75
76
  predicted_return=predicted_return,
76
77
  confidence_score=confidence_score,
77
- feature_importance=request.features
78
+ feature_importance=request.features,
78
79
  )
79
80
 
80
81
  db.add(prediction)
@@ -95,7 +96,7 @@ async def create_prediction(
95
96
  confidence_score=prediction.confidence_score,
96
97
  target_date=prediction.target_date,
97
98
  model_id=model.id,
98
- model_name=model.name
99
+ model_name=model.name,
99
100
  )
100
101
 
101
102
 
@@ -104,7 +105,7 @@ async def create_batch_predictions(
104
105
  request: BatchPredictionRequest,
105
106
  background_tasks: BackgroundTasks,
106
107
  current_user: User = Depends(get_current_active_user),
107
- db: Session = Depends(get_db)
108
+ db: Session = Depends(get_db),
108
109
  ):
109
110
  """Create predictions for multiple tickers"""
110
111
  predictions = []
@@ -114,15 +115,10 @@ async def create_batch_predictions(
114
115
  ticker=ticker_data.ticker,
115
116
  features=ticker_data.features,
116
117
  model_id=request.model_id,
117
- horizon=request.horizon
118
+ horizon=request.horizon,
118
119
  )
119
120
 
120
- pred = await create_prediction(
121
- pred_input,
122
- background_tasks,
123
- current_user,
124
- db
125
- )
121
+ pred = await create_prediction(pred_input, background_tasks, current_user, db)
126
122
  predictions.append(pred)
127
123
 
128
124
  return predictions
@@ -137,7 +133,7 @@ async def list_predictions(
137
133
  start_date: Optional[datetime] = None,
138
134
  end_date: Optional[datetime] = None,
139
135
  current_user: User = Depends(get_current_active_user),
140
- db: Session = Depends(get_db)
136
+ db: Session = Depends(get_db),
141
137
  ):
142
138
  """List user's predictions"""
143
139
  query = db.query(Prediction).filter(Prediction.user_id == current_user.id)
@@ -151,9 +147,7 @@ async def list_predictions(
151
147
  if end_date:
152
148
  query = query.filter(Prediction.prediction_date <= end_date)
153
149
 
154
- predictions = query.order_by(
155
- Prediction.prediction_date.desc()
156
- ).offset(skip).limit(limit).all()
150
+ predictions = query.order_by(Prediction.prediction_date.desc()).offset(skip).limit(limit).all()
157
151
 
158
152
  return [PredictionResponse.from_orm(p) for p in predictions]
159
153
 
@@ -163,19 +157,17 @@ async def list_predictions(
163
157
  async def get_prediction(
164
158
  prediction_id: UUID,
165
159
  current_user: User = Depends(get_current_active_user),
166
- db: Session = Depends(get_db)
160
+ db: Session = Depends(get_db),
167
161
  ):
168
162
  """Get specific prediction details"""
169
- prediction = db.query(Prediction).filter(
170
- Prediction.id == prediction_id,
171
- Prediction.user_id == current_user.id
172
- ).first()
163
+ prediction = (
164
+ db.query(Prediction)
165
+ .filter(Prediction.id == prediction_id, Prediction.user_id == current_user.id)
166
+ .first()
167
+ )
173
168
 
174
169
  if not prediction:
175
- raise HTTPException(
176
- status_code=status.HTTP_404_NOT_FOUND,
177
- detail="Prediction not found"
178
- )
170
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Prediction not found")
179
171
 
180
172
  return PredictionResponse.from_orm(prediction)
181
173
 
@@ -184,26 +176,24 @@ async def get_prediction(
184
176
  async def get_prediction_outcome(
185
177
  prediction_id: UUID,
186
178
  current_user: User = Depends(get_current_active_user),
187
- db: Session = Depends(get_db)
179
+ db: Session = Depends(get_db),
188
180
  ):
189
181
  """Get actual outcome of a prediction"""
190
- prediction = db.query(Prediction).filter(
191
- Prediction.id == prediction_id,
192
- Prediction.user_id == current_user.id
193
- ).first()
182
+ prediction = (
183
+ db.query(Prediction)
184
+ .filter(Prediction.id == prediction_id, Prediction.user_id == current_user.id)
185
+ .first()
186
+ )
194
187
 
195
188
  if not prediction:
196
- raise HTTPException(
197
- status_code=status.HTTP_404_NOT_FOUND,
198
- detail="Prediction not found"
199
- )
189
+ raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Prediction not found")
200
190
 
201
191
  # Check if target date has passed
202
192
  if prediction.target_date > datetime.utcnow():
203
193
  return {
204
194
  "status": "pending",
205
195
  "message": "Target date has not been reached yet",
206
- "target_date": prediction.target_date
196
+ "target_date": prediction.target_date,
207
197
  }
208
198
 
209
199
  # Get actual return (mock for now)
@@ -219,7 +209,7 @@ async def get_prediction_outcome(
219
209
  "predicted_return": prediction.predicted_return,
220
210
  "actual_return": actual_return,
221
211
  "error": abs(prediction.predicted_return - actual_return),
222
- "accuracy": 1 - abs(prediction.predicted_return - actual_return) / abs(actual_return)
212
+ "accuracy": 1 - abs(prediction.predicted_return - actual_return) / abs(actual_return),
223
213
  }
224
214
 
225
215
 
@@ -229,17 +219,20 @@ async def get_latest_recommendations(
229
219
  limit: int = Query(10, le=50),
230
220
  min_confidence: float = Query(0.7, ge=0, le=1),
231
221
  current_user: User = Depends(get_current_active_user),
232
- db: Session = Depends(get_db)
222
+ db: Session = Depends(get_db),
233
223
  ):
234
224
  """Get latest stock recommendations"""
235
225
  # Get recent predictions with high confidence
236
- predictions = db.query(Prediction).filter(
237
- Prediction.confidence_score >= min_confidence,
238
- Prediction.prediction_date >= datetime.utcnow() - timedelta(days=1)
239
- ).order_by(
240
- Prediction.confidence_score.desc(),
241
- Prediction.predicted_return.desc()
242
- ).limit(limit).all()
226
+ predictions = (
227
+ db.query(Prediction)
228
+ .filter(
229
+ Prediction.confidence_score >= min_confidence,
230
+ Prediction.prediction_date >= datetime.utcnow() - timedelta(days=1),
231
+ )
232
+ .order_by(Prediction.confidence_score.desc(), Prediction.predicted_return.desc())
233
+ .limit(limit)
234
+ .all()
235
+ )
243
236
 
244
237
  recommendations = []
245
238
  for pred in predictions:
@@ -247,13 +240,15 @@ async def get_latest_recommendations(
247
240
  if pred.predicted_return < -0.02:
248
241
  action = "sell"
249
242
 
250
- recommendations.append({
251
- "ticker": pred.ticker,
252
- "action": action,
253
- "predicted_return": pred.predicted_return,
254
- "confidence": pred.confidence_score,
255
- "target_date": pred.target_date
256
- })
243
+ recommendations.append(
244
+ {
245
+ "ticker": pred.ticker,
246
+ "action": action,
247
+ "predicted_return": pred.predicted_return,
248
+ "confidence": pred.confidence_score,
249
+ "target_date": pred.target_date,
250
+ }
251
+ )
257
252
 
258
253
  return recommendations
259
254
 
@@ -264,4 +259,4 @@ async def update_stock_data(ticker: str, db: Session):
264
259
  stock = db.query(StockData).filter(StockData.ticker == ticker).first()
265
260
  if stock:
266
261
  stock.last_updated = datetime.utcnow()
267
- db.commit()
262
+ db.commit()
@@ -1,12 +1,16 @@
1
1
  """Trading API routes"""
2
2
 
3
3
  from fastapi import APIRouter, Depends
4
+
4
5
  from mcli.ml.auth import get_current_active_user
5
6
  from mcli.ml.database.models import User
6
7
 
7
8
  router = APIRouter()
8
9
 
10
+
9
11
  @router.get("/politician/{politician_id}")
10
- async def get_politician_trades(politician_id: str, current_user: User = Depends(get_current_active_user)):
12
+ async def get_politician_trades(
13
+ politician_id: str, current_user: User = Depends(get_current_active_user)
14
+ ):
11
15
  """Get politician trades"""
12
- return {"politician_id": politician_id, "trades": []}
16
+ return {"politician_id": politician_id, "trades": []}
@@ -1,9 +1,10 @@
1
1
  """WebSocket API routes for real-time updates"""
2
2
 
3
- from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Depends
4
- from typing import Dict, Set
5
- import json
6
3
  import asyncio
4
+ import json
5
+ from typing import Dict, Set
6
+
7
+ from fastapi import APIRouter, Depends, WebSocket, WebSocketDisconnect
7
8
 
8
9
  from mcli.ml.logging import get_logger
9
10
 
@@ -67,10 +68,12 @@ async def websocket_prices(websocket: WebSocket):
67
68
  while True:
68
69
  await asyncio.sleep(1)
69
70
  # Send mock price update
70
- await websocket.send_json({
71
- "type": "price_update",
72
- "ticker": "AAPL",
73
- "price": 150.00 + (asyncio.get_event_loop().time() % 10)
74
- })
71
+ await websocket.send_json(
72
+ {
73
+ "type": "price_update",
74
+ "ticker": "AAPL",
75
+ "price": 150.00 + (asyncio.get_event_loop().time() % 10),
76
+ }
77
+ )
75
78
  except WebSocketDisconnect:
76
- manager.disconnect(websocket, "prices")
79
+ manager.disconnect(websocket, "prices")
mcli/ml/api/schemas.py CHANGED
@@ -1,10 +1,12 @@
1
1
  """API request/response schemas"""
2
2
 
3
3
  from datetime import datetime
4
- from typing import Optional, List, Dict, Any
4
+ from typing import Any, Dict, List, Optional
5
5
  from uuid import UUID
6
+
6
7
  from pydantic import BaseModel, Field
7
8
 
9
+
8
10
  # Model schemas
9
11
  class ModelCreate(BaseModel):
10
12
  name: str
@@ -13,11 +15,13 @@ class ModelCreate(BaseModel):
13
15
  description: Optional[str] = None
14
16
  hyperparameters: Dict[str, Any] = {}
15
17
 
18
+
16
19
  class ModelUpdate(BaseModel):
17
20
  name: Optional[str] = None
18
21
  description: Optional[str] = None
19
22
  tags: Optional[List[str]] = None
20
23
 
24
+
21
25
  class ModelResponse(BaseModel):
22
26
  id: UUID
23
27
  name: str
@@ -30,6 +34,7 @@ class ModelResponse(BaseModel):
30
34
  class Config:
31
35
  orm_mode = True
32
36
 
37
+
33
38
  class ModelMetrics(BaseModel):
34
39
  model_id: UUID
35
40
  train_accuracy: Optional[float]
@@ -40,17 +45,20 @@ class ModelMetrics(BaseModel):
40
45
  test_loss: Optional[float]
41
46
  additional_metrics: Dict[str, Any]
42
47
 
48
+
43
49
  # Prediction schemas
44
50
  class PredictionRequest(BaseModel):
45
51
  ticker: str
46
52
  features: Dict[str, float]
47
53
  model_id: Optional[UUID] = None
48
54
 
55
+
49
56
  class BatchPredictionRequest(BaseModel):
50
57
  tickers: List[PredictionRequest]
51
58
  model_id: Optional[UUID] = None
52
59
  horizon: int = 1
53
60
 
61
+
54
62
  class PredictionResponse(BaseModel):
55
63
  id: UUID
56
64
  ticker: str
@@ -61,4 +69,4 @@ class PredictionResponse(BaseModel):
61
69
  model_name: str
62
70
 
63
71
  class Config:
64
- orm_mode = True
72
+ orm_mode = True