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
mcli/ml/dashboard/cli.py CHANGED
@@ -3,12 +3,14 @@
3
3
  import subprocess
4
4
  import sys
5
5
  from pathlib import Path
6
+
6
7
  import typer
7
8
  from rich.console import Console
8
9
 
9
10
  app = typer.Typer()
10
11
  console = Console()
11
12
 
13
+
12
14
  @app.command()
13
15
  def launch(
14
16
  port: int = typer.Option(8501, "--port", "-p", help="Port to run dashboard on"),
@@ -26,11 +28,17 @@ def launch(
26
28
 
27
29
  # Build streamlit command
28
30
  cmd = [
29
- sys.executable, "-m", "streamlit", "run",
31
+ sys.executable,
32
+ "-m",
33
+ "streamlit",
34
+ "run",
30
35
  str(dashboard_path),
31
- "--server.port", str(port),
32
- "--server.address", host,
33
- "--browser.gatherUsageStats", "false"
36
+ "--server.port",
37
+ str(port),
38
+ "--server.address",
39
+ host,
40
+ "--browser.gatherUsageStats",
41
+ "false",
34
42
  ]
35
43
 
36
44
  if debug:
@@ -47,5 +55,6 @@ def launch(
47
55
  console.print(f"[red]Failed to start dashboard: {e}[/red]")
48
56
  raise typer.Exit(1)
49
57
 
58
+
50
59
  if __name__ == "__main__":
51
- app()
60
+ app()
@@ -1,18 +1,19 @@
1
1
  """API connectors for real-time data ingestion"""
2
2
 
3
- import requests
4
3
  import asyncio
5
- import aiohttp
6
- import websockets
7
4
  import json
8
- import pandas as pd
9
- from typing import Dict, Any, Optional, List, Callable, AsyncIterator
10
- from datetime import datetime, timedelta
11
- from dataclasses import dataclass
12
5
  import logging
13
- from abc import ABC, abstractmethod
14
6
  import time
7
+ from abc import ABC, abstractmethod
8
+ from dataclasses import dataclass
9
+ from datetime import datetime, timedelta
10
+ from typing import Any, AsyncIterator, Callable, Dict, List, Optional
15
11
  from urllib.parse import urljoin
12
+
13
+ import aiohttp
14
+ import pandas as pd
15
+ import requests
16
+ import websockets
16
17
  import yfinance as yf
17
18
 
18
19
  logger = logging.getLogger(__name__)
@@ -21,6 +22,7 @@ logger = logging.getLogger(__name__)
21
22
  @dataclass
22
23
  class APIConfig:
23
24
  """API configuration"""
25
+
24
26
  api_key: Optional[str] = None
25
27
  base_url: str = ""
26
28
  rate_limit: int = 100 # requests per minute
@@ -59,10 +61,7 @@ class BaseAPIConnector(ABC):
59
61
  self.session = aiohttp.ClientSession()
60
62
 
61
63
  async with self.session.get(
62
- url,
63
- params=params,
64
- headers=headers,
65
- timeout=self.config.timeout
64
+ url, params=params, headers=headers, timeout=self.config.timeout
66
65
  ) as response:
67
66
  response.raise_for_status()
68
67
  return await response.json()
@@ -110,10 +109,7 @@ class CongressionalDataAPI(BaseAPIConnector):
110
109
 
111
110
  def __init__(self, config: Optional[APIConfig] = None):
112
111
  if not config:
113
- config = APIConfig(
114
- base_url="https://api.capitoltrades.com/v1/",
115
- rate_limit=60
116
- )
112
+ config = APIConfig(base_url="https://api.capitoltrades.com/v1/", rate_limit=60)
117
113
  super().__init__(config)
118
114
 
119
115
  async def fetch_recent_trades(self, days: int = 30) -> List[Dict[str, Any]]:
@@ -121,7 +117,7 @@ class CongressionalDataAPI(BaseAPIConnector):
121
117
  params = {
122
118
  "from_date": (datetime.now() - timedelta(days=days)).isoformat(),
123
119
  "to_date": datetime.now().isoformat(),
124
- "limit": 1000
120
+ "limit": 1000,
125
121
  }
126
122
 
127
123
  try:
@@ -142,19 +138,24 @@ class CongressionalDataAPI(BaseAPIConnector):
142
138
  def _generate_mock_trades(self) -> List[Dict[str, Any]]:
143
139
  """Generate mock trades for testing"""
144
140
  import random
141
+
145
142
  trades = []
146
143
  politicians = ["Nancy Pelosi", "Mitch McConnell", "Chuck Schumer", "Kevin McCarthy"]
147
144
  tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "TSLA", "META", "NVDA"]
148
145
 
149
146
  for _ in range(50):
150
- trades.append({
151
- "politician": random.choice(politicians),
152
- "ticker": random.choice(tickers),
153
- "transaction_type": random.choice(["buy", "sell"]),
154
- "amount": random.randint(1000, 1000000),
155
- "transaction_date": (datetime.now() - timedelta(days=random.randint(1, 30))).isoformat(),
156
- "disclosure_date": datetime.now().isoformat()
157
- })
147
+ trades.append(
148
+ {
149
+ "politician": random.choice(politicians),
150
+ "ticker": random.choice(tickers),
151
+ "transaction_type": random.choice(["buy", "sell"]),
152
+ "amount": random.randint(1000, 1000000),
153
+ "transaction_date": (
154
+ datetime.now() - timedelta(days=random.randint(1, 30))
155
+ ).isoformat(),
156
+ "disclosure_date": datetime.now().isoformat(),
157
+ }
158
+ )
158
159
 
159
160
  return trades
160
161
 
@@ -166,7 +167,7 @@ class CongressionalDataAPI(BaseAPIConnector):
166
167
  "party": "Independent",
167
168
  "state": "CA",
168
169
  "chamber": "House",
169
- "committees": ["Finance", "Technology"]
170
+ "committees": ["Finance", "Technology"],
170
171
  }
171
172
 
172
173
 
@@ -193,17 +194,13 @@ class AlphaVantageConnector(StockMarketAPI):
193
194
  config = APIConfig(
194
195
  api_key=api_key,
195
196
  base_url="https://www.alphavantage.co/query",
196
- rate_limit=5 # Free tier: 5 requests per minute
197
+ rate_limit=5, # Free tier: 5 requests per minute
197
198
  )
198
199
  super().__init__(config)
199
200
 
200
201
  async def fetch_quote(self, symbol: str) -> Dict[str, Any]:
201
202
  """Fetch current quote from Alpha Vantage"""
202
- params = {
203
- "function": "GLOBAL_QUOTE",
204
- "symbol": symbol,
205
- "apikey": self.config.api_key
206
- }
203
+ params = {"function": "GLOBAL_QUOTE", "symbol": symbol, "apikey": self.config.api_key}
207
204
 
208
205
  data = await self._make_request("", params)
209
206
  return self._parse_quote(data.get("Global Quote", {}))
@@ -214,16 +211,16 @@ class AlphaVantageConnector(StockMarketAPI):
214
211
  "function": "TIME_SERIES_DAILY",
215
212
  "symbol": symbol,
216
213
  "outputsize": "full" if period == "max" else "compact",
217
- "apikey": self.config.api_key
214
+ "apikey": self.config.api_key,
218
215
  }
219
216
 
220
217
  data = await self._make_request("", params)
221
218
  time_series = data.get("Time Series (Daily)", {})
222
219
 
223
220
  # Convert to DataFrame
224
- df = pd.DataFrame.from_dict(time_series, orient='index')
221
+ df = pd.DataFrame.from_dict(time_series, orient="index")
225
222
  df.index = pd.to_datetime(df.index)
226
- df.columns = ['open', 'high', 'low', 'close', 'volume']
223
+ df.columns = ["open", "high", "low", "close", "volume"]
227
224
  df = df.astype(float)
228
225
 
229
226
  return df.sort_index()
@@ -236,7 +233,7 @@ class AlphaVantageConnector(StockMarketAPI):
236
233
  "volume": int(quote_data.get("06. volume", 0)),
237
234
  "timestamp": quote_data.get("07. latest trading day", ""),
238
235
  "change": float(quote_data.get("09. change", 0)),
239
- "change_percent": quote_data.get("10. change percent", "0%")
236
+ "change_percent": quote_data.get("10. change percent", "0%"),
240
237
  }
241
238
 
242
239
 
@@ -259,7 +256,7 @@ class YahooFinanceConnector(StockMarketAPI):
259
256
  "volume": info.get("volume", 0),
260
257
  "market_cap": info.get("marketCap", 0),
261
258
  "pe_ratio": info.get("trailingPE", 0),
262
- "dividend_yield": info.get("dividendYield", 0)
259
+ "dividend_yield": info.get("dividendYield", 0),
263
260
  }
264
261
  except Exception as e:
265
262
  logger.error(f"Failed to fetch Yahoo Finance quote: {e}")
@@ -280,11 +277,7 @@ class PolygonIOConnector(StockMarketAPI):
280
277
  """Polygon.io API connector"""
281
278
 
282
279
  def __init__(self, api_key: str):
283
- config = APIConfig(
284
- api_key=api_key,
285
- base_url="https://api.polygon.io/",
286
- rate_limit=100
287
- )
280
+ config = APIConfig(api_key=api_key, base_url="https://api.polygon.io/", rate_limit=100)
288
281
  super().__init__(config)
289
282
 
290
283
  async def fetch_quote(self, symbol: str) -> Dict[str, Any]:
@@ -295,16 +288,12 @@ class PolygonIOConnector(StockMarketAPI):
295
288
  data = await self._make_request(endpoint, params)
296
289
  return self._parse_polygon_quote(data)
297
290
 
298
- async def fetch_aggregates(self, symbol: str,
299
- from_date: str, to_date: str,
300
- timespan: str = "day") -> pd.DataFrame:
291
+ async def fetch_aggregates(
292
+ self, symbol: str, from_date: str, to_date: str, timespan: str = "day"
293
+ ) -> pd.DataFrame:
301
294
  """Fetch aggregate bars from Polygon.io"""
302
295
  endpoint = f"v2/aggs/ticker/{symbol}/range/1/{timespan}/{from_date}/{to_date}"
303
- params = {
304
- "apiKey": self.config.api_key,
305
- "adjusted": "true",
306
- "sort": "asc"
307
- }
296
+ params = {"apiKey": self.config.api_key, "adjusted": "true", "sort": "asc"}
308
297
 
309
298
  data = await self._make_request(endpoint, params)
310
299
  results = data.get("results", [])
@@ -313,16 +302,10 @@ class PolygonIOConnector(StockMarketAPI):
313
302
  return pd.DataFrame()
314
303
 
315
304
  df = pd.DataFrame(results)
316
- df['timestamp'] = pd.to_datetime(df['t'], unit='ms')
317
- df = df.rename(columns={
318
- 'o': 'open',
319
- 'h': 'high',
320
- 'l': 'low',
321
- 'c': 'close',
322
- 'v': 'volume'
323
- })
305
+ df["timestamp"] = pd.to_datetime(df["t"], unit="ms")
306
+ df = df.rename(columns={"o": "open", "h": "high", "l": "low", "c": "close", "v": "volume"})
324
307
 
325
- return df.set_index('timestamp')
308
+ return df.set_index("timestamp")
326
309
 
327
310
  def _parse_polygon_quote(self, data: Dict[str, Any]) -> Dict[str, Any]:
328
311
  """Parse Polygon.io quote"""
@@ -331,7 +314,7 @@ class PolygonIOConnector(StockMarketAPI):
331
314
  "symbol": results.get("T", ""),
332
315
  "price": results.get("P", 0),
333
316
  "size": results.get("S", 0),
334
- "timestamp": results.get("t", 0)
317
+ "timestamp": results.get("t", 0),
335
318
  }
336
319
 
337
320
 
@@ -340,26 +323,20 @@ class QuiverQuantConnector(BaseAPIConnector):
340
323
 
341
324
  def __init__(self, api_key: str):
342
325
  config = APIConfig(
343
- api_key=api_key,
344
- base_url="https://api.quiverquant.com/beta/",
345
- rate_limit=100
326
+ api_key=api_key, base_url="https://api.quiverquant.com/beta/", rate_limit=100
346
327
  )
347
328
  super().__init__(config)
348
329
 
349
330
  async def fetch_congress_trades(self) -> List[Dict[str, Any]]:
350
331
  """Fetch congressional trading data"""
351
- headers = {
352
- "Authorization": f"Bearer {self.config.api_key}",
353
- "Accept": "application/json"
354
- }
332
+ headers = {"Authorization": f"Bearer {self.config.api_key}", "Accept": "application/json"}
355
333
 
356
334
  try:
357
335
  if not self.session:
358
336
  self.session = aiohttp.ClientSession()
359
337
 
360
338
  async with self.session.get(
361
- f"{self.config.base_url}historical/congresstrading",
362
- headers=headers
339
+ f"{self.config.base_url}historical/congresstrading", headers=headers
363
340
  ) as response:
364
341
  response.raise_for_status()
365
342
  data = await response.json()
@@ -370,18 +347,14 @@ class QuiverQuantConnector(BaseAPIConnector):
370
347
 
371
348
  async def fetch_lobbying(self, ticker: str) -> List[Dict[str, Any]]:
372
349
  """Fetch lobbying data for a ticker"""
373
- headers = {
374
- "Authorization": f"Bearer {self.config.api_key}",
375
- "Accept": "application/json"
376
- }
350
+ headers = {"Authorization": f"Bearer {self.config.api_key}", "Accept": "application/json"}
377
351
 
378
352
  try:
379
353
  if not self.session:
380
354
  self.session = aiohttp.ClientSession()
381
355
 
382
356
  async with self.session.get(
383
- f"{self.config.base_url}historical/lobbying/{ticker}",
384
- headers=headers
357
+ f"{self.config.base_url}historical/lobbying/{ticker}", headers=headers
385
358
  ) as response:
386
359
  response.raise_for_status()
387
360
  data = await response.json()
@@ -418,10 +391,7 @@ class WebSocketDataStream:
418
391
  if not self.websocket:
419
392
  await self.connect()
420
393
 
421
- message = {
422
- "action": "subscribe",
423
- "symbols": symbols
424
- }
394
+ message = {"action": "subscribe", "symbols": symbols}
425
395
  await self.websocket.send(json.dumps(message))
426
396
 
427
397
  async def stream(self):
@@ -473,7 +443,7 @@ class DataAggregator:
473
443
  # Fetch from all sources concurrently
474
444
  tasks = []
475
445
  for name, connector in self.sources.items():
476
- if hasattr(connector, 'fetch_quote'):
446
+ if hasattr(connector, "fetch_quote"):
477
447
  tasks.append(self._fetch_with_name(name, connector.fetch_quote(symbol)))
478
448
 
479
449
  responses = await asyncio.gather(*tasks, return_exceptions=True)
@@ -498,4 +468,4 @@ class DataAggregator:
498
468
  async def _fetch_with_name(self, name: str, coro):
499
469
  """Helper to fetch with source name"""
500
470
  result = await coro
501
- return name, result
471
+ return name, result