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,267 @@
|
|
|
1
|
+
"""Prediction API routes"""
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, Depends, HTTPException, status, Query, BackgroundTasks
|
|
8
|
+
from sqlalchemy.orm import Session
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
|
|
11
|
+
from mcli.ml.auth import get_current_active_user
|
|
12
|
+
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
|
+
from mcli.ml.models import get_model_by_id
|
|
17
|
+
|
|
18
|
+
router = APIRouter()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PredictionInput(BaseModel):
|
|
22
|
+
ticker: str
|
|
23
|
+
features: dict
|
|
24
|
+
model_id: Optional[UUID] = None
|
|
25
|
+
horizon: int = 1 # Days ahead
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@router.post("/predict", response_model=PredictionResponse)
|
|
29
|
+
async def create_prediction(
|
|
30
|
+
request: PredictionInput,
|
|
31
|
+
background_tasks: BackgroundTasks,
|
|
32
|
+
current_user: User = Depends(get_current_active_user),
|
|
33
|
+
db: Session = Depends(get_db)
|
|
34
|
+
):
|
|
35
|
+
"""Create a new prediction"""
|
|
36
|
+
|
|
37
|
+
# Get model (use default if not specified)
|
|
38
|
+
if request.model_id:
|
|
39
|
+
model = db.query(Model).filter(Model.id == request.model_id).first()
|
|
40
|
+
if not model:
|
|
41
|
+
raise HTTPException(
|
|
42
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
43
|
+
detail="Model not found"
|
|
44
|
+
)
|
|
45
|
+
else:
|
|
46
|
+
# Get default deployed model
|
|
47
|
+
model = db.query(Model).filter(
|
|
48
|
+
Model.status == "deployed"
|
|
49
|
+
).order_by(Model.deployed_at.desc()).first()
|
|
50
|
+
|
|
51
|
+
if not model:
|
|
52
|
+
raise HTTPException(
|
|
53
|
+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
54
|
+
detail="No deployed model available"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Load model
|
|
58
|
+
ml_model = await get_model_by_id(str(model.id))
|
|
59
|
+
|
|
60
|
+
# Make prediction
|
|
61
|
+
import numpy as np
|
|
62
|
+
features_array = np.array(list(request.features.values())).reshape(1, -1)
|
|
63
|
+
predicted_return = float(ml_model.predict(features_array)[0])
|
|
64
|
+
|
|
65
|
+
# Calculate confidence (mock for now)
|
|
66
|
+
confidence_score = 0.75 + np.random.random() * 0.2
|
|
67
|
+
|
|
68
|
+
# Save prediction to database
|
|
69
|
+
prediction = Prediction(
|
|
70
|
+
model_id=model.id,
|
|
71
|
+
user_id=current_user.id,
|
|
72
|
+
ticker=request.ticker,
|
|
73
|
+
prediction_date=datetime.utcnow(),
|
|
74
|
+
target_date=datetime.utcnow() + timedelta(days=request.horizon),
|
|
75
|
+
predicted_return=predicted_return,
|
|
76
|
+
confidence_score=confidence_score,
|
|
77
|
+
feature_importance=request.features
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
db.add(prediction)
|
|
81
|
+
db.commit()
|
|
82
|
+
db.refresh(prediction)
|
|
83
|
+
|
|
84
|
+
# Cache prediction
|
|
85
|
+
cache_key = f"prediction:{prediction.id}"
|
|
86
|
+
await cache_set(cache_key, prediction, expire=3600)
|
|
87
|
+
|
|
88
|
+
# Background task to update stock data
|
|
89
|
+
background_tasks.add_task(update_stock_data, request.ticker, db)
|
|
90
|
+
|
|
91
|
+
return PredictionResponse(
|
|
92
|
+
id=prediction.id,
|
|
93
|
+
ticker=prediction.ticker,
|
|
94
|
+
predicted_return=prediction.predicted_return,
|
|
95
|
+
confidence_score=prediction.confidence_score,
|
|
96
|
+
target_date=prediction.target_date,
|
|
97
|
+
model_id=model.id,
|
|
98
|
+
model_name=model.name
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@router.post("/predict/batch", response_model=List[PredictionResponse])
|
|
103
|
+
async def create_batch_predictions(
|
|
104
|
+
request: BatchPredictionRequest,
|
|
105
|
+
background_tasks: BackgroundTasks,
|
|
106
|
+
current_user: User = Depends(get_current_active_user),
|
|
107
|
+
db: Session = Depends(get_db)
|
|
108
|
+
):
|
|
109
|
+
"""Create predictions for multiple tickers"""
|
|
110
|
+
predictions = []
|
|
111
|
+
|
|
112
|
+
for ticker_data in request.tickers:
|
|
113
|
+
pred_input = PredictionInput(
|
|
114
|
+
ticker=ticker_data.ticker,
|
|
115
|
+
features=ticker_data.features,
|
|
116
|
+
model_id=request.model_id,
|
|
117
|
+
horizon=request.horizon
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
pred = await create_prediction(
|
|
121
|
+
pred_input,
|
|
122
|
+
background_tasks,
|
|
123
|
+
current_user,
|
|
124
|
+
db
|
|
125
|
+
)
|
|
126
|
+
predictions.append(pred)
|
|
127
|
+
|
|
128
|
+
return predictions
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@router.get("/", response_model=List[PredictionResponse])
|
|
132
|
+
@cached(expire=60)
|
|
133
|
+
async def list_predictions(
|
|
134
|
+
skip: int = Query(0, ge=0),
|
|
135
|
+
limit: int = Query(100, le=1000),
|
|
136
|
+
ticker: Optional[str] = None,
|
|
137
|
+
start_date: Optional[datetime] = None,
|
|
138
|
+
end_date: Optional[datetime] = None,
|
|
139
|
+
current_user: User = Depends(get_current_active_user),
|
|
140
|
+
db: Session = Depends(get_db)
|
|
141
|
+
):
|
|
142
|
+
"""List user's predictions"""
|
|
143
|
+
query = db.query(Prediction).filter(Prediction.user_id == current_user.id)
|
|
144
|
+
|
|
145
|
+
if ticker:
|
|
146
|
+
query = query.filter(Prediction.ticker == ticker)
|
|
147
|
+
|
|
148
|
+
if start_date:
|
|
149
|
+
query = query.filter(Prediction.prediction_date >= start_date)
|
|
150
|
+
|
|
151
|
+
if end_date:
|
|
152
|
+
query = query.filter(Prediction.prediction_date <= end_date)
|
|
153
|
+
|
|
154
|
+
predictions = query.order_by(
|
|
155
|
+
Prediction.prediction_date.desc()
|
|
156
|
+
).offset(skip).limit(limit).all()
|
|
157
|
+
|
|
158
|
+
return [PredictionResponse.from_orm(p) for p in predictions]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@router.get("/{prediction_id}", response_model=PredictionResponse)
|
|
162
|
+
@cached(expire=300)
|
|
163
|
+
async def get_prediction(
|
|
164
|
+
prediction_id: UUID,
|
|
165
|
+
current_user: User = Depends(get_current_active_user),
|
|
166
|
+
db: Session = Depends(get_db)
|
|
167
|
+
):
|
|
168
|
+
"""Get specific prediction details"""
|
|
169
|
+
prediction = db.query(Prediction).filter(
|
|
170
|
+
Prediction.id == prediction_id,
|
|
171
|
+
Prediction.user_id == current_user.id
|
|
172
|
+
).first()
|
|
173
|
+
|
|
174
|
+
if not prediction:
|
|
175
|
+
raise HTTPException(
|
|
176
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
177
|
+
detail="Prediction not found"
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return PredictionResponse.from_orm(prediction)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@router.get("/{prediction_id}/outcome")
|
|
184
|
+
async def get_prediction_outcome(
|
|
185
|
+
prediction_id: UUID,
|
|
186
|
+
current_user: User = Depends(get_current_active_user),
|
|
187
|
+
db: Session = Depends(get_db)
|
|
188
|
+
):
|
|
189
|
+
"""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()
|
|
194
|
+
|
|
195
|
+
if not prediction:
|
|
196
|
+
raise HTTPException(
|
|
197
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
198
|
+
detail="Prediction not found"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# Check if target date has passed
|
|
202
|
+
if prediction.target_date > datetime.utcnow():
|
|
203
|
+
return {
|
|
204
|
+
"status": "pending",
|
|
205
|
+
"message": "Target date has not been reached yet",
|
|
206
|
+
"target_date": prediction.target_date
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# Get actual return (mock for now)
|
|
210
|
+
actual_return = prediction.predicted_return + np.random.randn() * 0.02
|
|
211
|
+
|
|
212
|
+
# Update prediction with actual outcome
|
|
213
|
+
prediction.actual_return = actual_return
|
|
214
|
+
prediction.outcome_date = datetime.utcnow()
|
|
215
|
+
db.commit()
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
"status": "completed",
|
|
219
|
+
"predicted_return": prediction.predicted_return,
|
|
220
|
+
"actual_return": actual_return,
|
|
221
|
+
"error": abs(prediction.predicted_return - actual_return),
|
|
222
|
+
"accuracy": 1 - abs(prediction.predicted_return - actual_return) / abs(actual_return)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@router.get("/recommendations/latest")
|
|
227
|
+
@cached(expire=300)
|
|
228
|
+
async def get_latest_recommendations(
|
|
229
|
+
limit: int = Query(10, le=50),
|
|
230
|
+
min_confidence: float = Query(0.7, ge=0, le=1),
|
|
231
|
+
current_user: User = Depends(get_current_active_user),
|
|
232
|
+
db: Session = Depends(get_db)
|
|
233
|
+
):
|
|
234
|
+
"""Get latest stock recommendations"""
|
|
235
|
+
# 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()
|
|
243
|
+
|
|
244
|
+
recommendations = []
|
|
245
|
+
for pred in predictions:
|
|
246
|
+
action = "buy" if pred.predicted_return > 0.02 else "hold"
|
|
247
|
+
if pred.predicted_return < -0.02:
|
|
248
|
+
action = "sell"
|
|
249
|
+
|
|
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
|
+
})
|
|
257
|
+
|
|
258
|
+
return recommendations
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
async def update_stock_data(ticker: str, db: Session):
|
|
262
|
+
"""Background task to update stock data"""
|
|
263
|
+
# In real implementation, fetch latest stock data
|
|
264
|
+
stock = db.query(StockData).filter(StockData.ticker == ticker).first()
|
|
265
|
+
if stock:
|
|
266
|
+
stock.last_updated = datetime.utcnow()
|
|
267
|
+
db.commit()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Trading API routes"""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, Depends
|
|
4
|
+
from mcli.ml.auth import get_current_active_user
|
|
5
|
+
from mcli.ml.database.models import User
|
|
6
|
+
|
|
7
|
+
router = APIRouter()
|
|
8
|
+
|
|
9
|
+
@router.get("/politician/{politician_id}")
|
|
10
|
+
async def get_politician_trades(politician_id: str, current_user: User = Depends(get_current_active_user)):
|
|
11
|
+
"""Get politician trades"""
|
|
12
|
+
return {"politician_id": politician_id, "trades": []}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""WebSocket API routes for real-time updates"""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Depends
|
|
4
|
+
from typing import Dict, Set
|
|
5
|
+
import json
|
|
6
|
+
import asyncio
|
|
7
|
+
|
|
8
|
+
from mcli.ml.logging import get_logger
|
|
9
|
+
|
|
10
|
+
router = APIRouter()
|
|
11
|
+
logger = get_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConnectionManager:
|
|
15
|
+
"""Manage WebSocket connections"""
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self.active_connections: Dict[str, Set[WebSocket]] = {}
|
|
19
|
+
|
|
20
|
+
async def connect(self, websocket: WebSocket, channel: str):
|
|
21
|
+
await websocket.accept()
|
|
22
|
+
if channel not in self.active_connections:
|
|
23
|
+
self.active_connections[channel] = set()
|
|
24
|
+
self.active_connections[channel].add(websocket)
|
|
25
|
+
|
|
26
|
+
def disconnect(self, websocket: WebSocket, channel: str):
|
|
27
|
+
if channel in self.active_connections:
|
|
28
|
+
self.active_connections[channel].discard(websocket)
|
|
29
|
+
if not self.active_connections[channel]:
|
|
30
|
+
del self.active_connections[channel]
|
|
31
|
+
|
|
32
|
+
async def broadcast(self, channel: str, message: dict):
|
|
33
|
+
if channel in self.active_connections:
|
|
34
|
+
disconnected = set()
|
|
35
|
+
for connection in self.active_connections[channel]:
|
|
36
|
+
try:
|
|
37
|
+
await connection.send_json(message)
|
|
38
|
+
except:
|
|
39
|
+
disconnected.add(connection)
|
|
40
|
+
|
|
41
|
+
# Remove disconnected clients
|
|
42
|
+
for conn in disconnected:
|
|
43
|
+
self.disconnect(conn, channel)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
manager = ConnectionManager()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@router.websocket("/predictions")
|
|
50
|
+
async def websocket_predictions(websocket: WebSocket):
|
|
51
|
+
"""Real-time prediction updates"""
|
|
52
|
+
await manager.connect(websocket, "predictions")
|
|
53
|
+
try:
|
|
54
|
+
while True:
|
|
55
|
+
data = await websocket.receive_text()
|
|
56
|
+
# Echo back for now
|
|
57
|
+
await websocket.send_text(f"Echo: {data}")
|
|
58
|
+
except WebSocketDisconnect:
|
|
59
|
+
manager.disconnect(websocket, "predictions")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@router.websocket("/prices")
|
|
63
|
+
async def websocket_prices(websocket: WebSocket):
|
|
64
|
+
"""Real-time price updates"""
|
|
65
|
+
await manager.connect(websocket, "prices")
|
|
66
|
+
try:
|
|
67
|
+
while True:
|
|
68
|
+
await asyncio.sleep(1)
|
|
69
|
+
# 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
|
+
})
|
|
75
|
+
except WebSocketDisconnect:
|
|
76
|
+
manager.disconnect(websocket, "prices")
|
mcli/ml/api/schemas.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""API request/response schemas"""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Optional, List, Dict, Any
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
# Model schemas
|
|
9
|
+
class ModelCreate(BaseModel):
|
|
10
|
+
name: str
|
|
11
|
+
model_type: str
|
|
12
|
+
framework: str = "pytorch"
|
|
13
|
+
description: Optional[str] = None
|
|
14
|
+
hyperparameters: Dict[str, Any] = {}
|
|
15
|
+
|
|
16
|
+
class ModelUpdate(BaseModel):
|
|
17
|
+
name: Optional[str] = None
|
|
18
|
+
description: Optional[str] = None
|
|
19
|
+
tags: Optional[List[str]] = None
|
|
20
|
+
|
|
21
|
+
class ModelResponse(BaseModel):
|
|
22
|
+
id: UUID
|
|
23
|
+
name: str
|
|
24
|
+
version: str
|
|
25
|
+
model_type: str
|
|
26
|
+
status: str
|
|
27
|
+
created_at: datetime
|
|
28
|
+
updated_at: datetime
|
|
29
|
+
|
|
30
|
+
class Config:
|
|
31
|
+
orm_mode = True
|
|
32
|
+
|
|
33
|
+
class ModelMetrics(BaseModel):
|
|
34
|
+
model_id: UUID
|
|
35
|
+
train_accuracy: Optional[float]
|
|
36
|
+
val_accuracy: Optional[float]
|
|
37
|
+
test_accuracy: Optional[float]
|
|
38
|
+
train_loss: Optional[float]
|
|
39
|
+
val_loss: Optional[float]
|
|
40
|
+
test_loss: Optional[float]
|
|
41
|
+
additional_metrics: Dict[str, Any]
|
|
42
|
+
|
|
43
|
+
# Prediction schemas
|
|
44
|
+
class PredictionRequest(BaseModel):
|
|
45
|
+
ticker: str
|
|
46
|
+
features: Dict[str, float]
|
|
47
|
+
model_id: Optional[UUID] = None
|
|
48
|
+
|
|
49
|
+
class BatchPredictionRequest(BaseModel):
|
|
50
|
+
tickers: List[PredictionRequest]
|
|
51
|
+
model_id: Optional[UUID] = None
|
|
52
|
+
horizon: int = 1
|
|
53
|
+
|
|
54
|
+
class PredictionResponse(BaseModel):
|
|
55
|
+
id: UUID
|
|
56
|
+
ticker: str
|
|
57
|
+
predicted_return: float
|
|
58
|
+
confidence_score: float
|
|
59
|
+
target_date: datetime
|
|
60
|
+
model_id: UUID
|
|
61
|
+
model_name: str
|
|
62
|
+
|
|
63
|
+
class Config:
|
|
64
|
+
orm_mode = True
|