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,667 @@
1
+ """Database models for ML system"""
2
+
3
+ from datetime import datetime
4
+ from typing import Optional, Dict, Any, List
5
+ from enum import Enum as PyEnum
6
+ from uuid import uuid4
7
+
8
+ from sqlalchemy import (
9
+ Column, String, Integer, Float, DateTime, Boolean, Text,
10
+ ForeignKey, JSON, Enum, Index, UniqueConstraint, CheckConstraint,
11
+ Table, event, BigInteger
12
+ )
13
+ from sqlalchemy.orm import declarative_base, relationship, validates
14
+ from sqlalchemy.dialects.postgresql import UUID, ARRAY
15
+ from sqlalchemy.ext.hybrid import hybrid_property
16
+ from sqlalchemy.sql import func
17
+
18
+
19
+ Base = declarative_base()
20
+
21
+
22
+ # Association tables for many-to-many relationships
23
+ portfolio_stocks = Table(
24
+ 'portfolio_stocks',
25
+ Base.metadata,
26
+ Column('portfolio_id', UUID(as_uuid=True), ForeignKey('portfolios.id')),
27
+ Column('stock_ticker', String, ForeignKey('stock_data.ticker')),
28
+ Column('weight', Float, nullable=False),
29
+ Column('shares', Integer, default=0),
30
+ Column('entry_price', Float),
31
+ Column('created_at', DateTime, default=datetime.utcnow),
32
+ )
33
+
34
+ experiment_models = Table(
35
+ 'experiment_models',
36
+ Base.metadata,
37
+ Column('experiment_id', UUID(as_uuid=True), ForeignKey('experiments.id')),
38
+ Column('model_id', UUID(as_uuid=True), ForeignKey('models.id')),
39
+ )
40
+
41
+
42
+ class UserRole(PyEnum):
43
+ """User role enumeration"""
44
+ ADMIN = "admin"
45
+ USER = "user"
46
+ ANALYST = "analyst"
47
+ VIEWER = "viewer"
48
+
49
+
50
+ class ModelStatus(PyEnum):
51
+ """Model status enumeration"""
52
+ TRAINING = "training"
53
+ TRAINED = "trained"
54
+ DEPLOYED = "deployed"
55
+ ARCHIVED = "archived"
56
+ FAILED = "failed"
57
+
58
+
59
+ class TradeType(PyEnum):
60
+ """Trade type enumeration"""
61
+ BUY = "buy"
62
+ SELL = "sell"
63
+ OPTION = "option"
64
+
65
+
66
+ class AlertType(PyEnum):
67
+ """Alert type enumeration"""
68
+ POLITICIAN_TRADE = "politician_trade"
69
+ PRICE_MOVEMENT = "price_movement"
70
+ VOLUME_SPIKE = "volume_spike"
71
+ MODEL_PREDICTION = "model_prediction"
72
+ PORTFOLIO_REBALANCE = "portfolio_rebalance"
73
+
74
+
75
+ class User(Base):
76
+ """User model for authentication and authorization"""
77
+ __tablename__ = 'users'
78
+
79
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
80
+ username = Column(String(50), unique=True, nullable=False, index=True)
81
+ email = Column(String(100), unique=True, nullable=False, index=True)
82
+ password_hash = Column(String(255), nullable=False)
83
+
84
+ first_name = Column(String(50))
85
+ last_name = Column(String(50))
86
+ role = Column(Enum(UserRole), default=UserRole.USER, nullable=False)
87
+
88
+ is_active = Column(Boolean, default=True, nullable=False)
89
+ is_verified = Column(Boolean, default=False, nullable=False)
90
+
91
+ api_key = Column(String(255), unique=True, index=True)
92
+ api_key_created_at = Column(DateTime)
93
+
94
+ created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
95
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
96
+ last_login_at = Column(DateTime)
97
+
98
+ # Relationships
99
+ portfolios = relationship("Portfolio", back_populates="user", cascade="all, delete-orphan")
100
+ alerts = relationship("Alert", back_populates="user", cascade="all, delete-orphan")
101
+ predictions = relationship("Prediction", back_populates="user")
102
+
103
+ # Constraints
104
+ __table_args__ = (
105
+ CheckConstraint("email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$'", name='valid_email'),
106
+ Index('idx_user_active', 'is_active'),
107
+ )
108
+
109
+ @validates('email')
110
+ def validate_email(self, key, email):
111
+ """Validate email format"""
112
+ import re
113
+ if not re.match(r'^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}$', email):
114
+ raise ValueError("Invalid email format")
115
+ return email.lower()
116
+
117
+
118
+ class Politician(Base):
119
+ """Politician information"""
120
+ __tablename__ = 'politicians'
121
+
122
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
123
+ name = Column(String(100), nullable=False)
124
+ party = Column(String(50))
125
+ state = Column(String(2))
126
+ position = Column(String(50)) # Senator, Representative, etc.
127
+
128
+ bioguide_id = Column(String(20), unique=True, index=True)
129
+ fec_id = Column(String(20), index=True)
130
+
131
+ active = Column(Boolean, default=True)
132
+ start_date = Column(DateTime)
133
+ end_date = Column(DateTime)
134
+
135
+ metadata_json = Column(JSON, default={})
136
+
137
+ created_at = Column(DateTime, default=datetime.utcnow)
138
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
139
+
140
+ # Relationships
141
+ trades = relationship("Trade", back_populates="politician", cascade="all, delete-orphan")
142
+
143
+ # Indexes
144
+ __table_args__ = (
145
+ Index('idx_politician_active', 'active'),
146
+ Index('idx_politician_party_state', 'party', 'state'),
147
+ )
148
+
149
+
150
+ class Trade(Base):
151
+ """Political trading records"""
152
+ __tablename__ = 'trades'
153
+
154
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
155
+ politician_id = Column(UUID(as_uuid=True), ForeignKey('politicians.id'), nullable=False)
156
+
157
+ ticker = Column(String(10), nullable=False, index=True)
158
+ trade_type = Column(Enum(TradeType), nullable=False)
159
+ amount_min = Column(Float)
160
+ amount_max = Column(Float)
161
+
162
+ disclosure_date = Column(DateTime, nullable=False, index=True)
163
+ trade_date = Column(DateTime, index=True)
164
+
165
+ asset_description = Column(Text)
166
+ source = Column(String(50)) # Data source
167
+ source_url = Column(String(500))
168
+
169
+ metadata_json = Column(JSON, default={})
170
+
171
+ created_at = Column(DateTime, default=datetime.utcnow)
172
+
173
+ # Relationships
174
+ politician = relationship("Politician", back_populates="trades")
175
+
176
+ # Indexes and constraints
177
+ __table_args__ = (
178
+ Index('idx_trade_ticker_date', 'ticker', 'disclosure_date'),
179
+ Index('idx_trade_politician_date', 'politician_id', 'disclosure_date'),
180
+ UniqueConstraint('politician_id', 'ticker', 'disclosure_date', 'trade_type',
181
+ name='unique_trade'),
182
+ )
183
+
184
+ @hybrid_property
185
+ def estimated_amount(self):
186
+ """Get estimated trade amount (midpoint)"""
187
+ if self.amount_min and self.amount_max:
188
+ return (self.amount_min + self.amount_max) / 2
189
+ return self.amount_min or self.amount_max
190
+
191
+
192
+ class StockData(Base):
193
+ """Stock market data"""
194
+ __tablename__ = 'stock_data'
195
+
196
+ ticker = Column(String(10), primary_key=True)
197
+ company_name = Column(String(200))
198
+ sector = Column(String(50), index=True)
199
+ industry = Column(String(100))
200
+
201
+ market_cap = Column(BigInteger)
202
+ shares_outstanding = Column(BigInteger)
203
+
204
+ # Current data
205
+ current_price = Column(Float)
206
+ volume = Column(BigInteger)
207
+ avg_volume_30d = Column(BigInteger)
208
+
209
+ # Performance metrics
210
+ change_1d = Column(Float)
211
+ change_7d = Column(Float)
212
+ change_30d = Column(Float)
213
+ change_90d = Column(Float)
214
+ change_1y = Column(Float)
215
+
216
+ # Technical indicators
217
+ rsi_14 = Column(Float)
218
+ macd = Column(Float)
219
+ bollinger_upper = Column(Float)
220
+ bollinger_lower = Column(Float)
221
+
222
+ # Fundamental data
223
+ pe_ratio = Column(Float)
224
+ eps = Column(Float)
225
+ dividend_yield = Column(Float)
226
+ beta = Column(Float)
227
+
228
+ metadata_json = Column(JSON, default={})
229
+
230
+ last_updated = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
231
+
232
+ # Relationships
233
+ predictions = relationship("Prediction", back_populates="stock")
234
+
235
+ # Indexes
236
+ __table_args__ = (
237
+ Index('idx_stock_sector_cap', 'sector', 'market_cap'),
238
+ Index('idx_stock_updated', 'last_updated'),
239
+ )
240
+
241
+
242
+ class Prediction(Base):
243
+ """Model predictions"""
244
+ __tablename__ = 'predictions'
245
+
246
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
247
+ model_id = Column(UUID(as_uuid=True), ForeignKey('models.id'), nullable=False)
248
+ user_id = Column(UUID(as_uuid=True), ForeignKey('users.id'))
249
+
250
+ ticker = Column(String(10), ForeignKey('stock_data.ticker'), nullable=False, index=True)
251
+
252
+ # Prediction details
253
+ prediction_date = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
254
+ target_date = Column(DateTime, nullable=False)
255
+
256
+ predicted_return = Column(Float, nullable=False)
257
+ confidence_score = Column(Float, nullable=False)
258
+ risk_score = Column(Float)
259
+
260
+ # Recommendation
261
+ action = Column(String(10)) # buy, sell, hold
262
+ position_size = Column(Float) # Recommended position size
263
+ stop_loss = Column(Float)
264
+ take_profit = Column(Float)
265
+
266
+ # Feature importance
267
+ feature_importance = Column(JSON)
268
+
269
+ # Actual outcomes (for backtesting)
270
+ actual_return = Column(Float)
271
+ outcome_date = Column(DateTime)
272
+
273
+ metadata_json = Column(JSON, default={})
274
+
275
+ created_at = Column(DateTime, default=datetime.utcnow)
276
+
277
+ # Relationships
278
+ model = relationship("Model", back_populates="predictions")
279
+ user = relationship("User", back_populates="predictions")
280
+ stock = relationship("StockData", back_populates="predictions")
281
+
282
+ # Indexes
283
+ __table_args__ = (
284
+ Index('idx_prediction_date_ticker', 'prediction_date', 'ticker'),
285
+ Index('idx_prediction_model_date', 'model_id', 'prediction_date'),
286
+ Index('idx_prediction_confidence', 'confidence_score'),
287
+ )
288
+
289
+
290
+ class Portfolio(Base):
291
+ """User portfolios"""
292
+ __tablename__ = 'portfolios'
293
+
294
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
295
+ user_id = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False)
296
+
297
+ name = Column(String(100), nullable=False)
298
+ description = Column(Text)
299
+
300
+ initial_capital = Column(Float, nullable=False)
301
+ current_value = Column(Float)
302
+ cash_balance = Column(Float)
303
+
304
+ # Performance metrics
305
+ total_return = Column(Float)
306
+ annual_return = Column(Float)
307
+ sharpe_ratio = Column(Float)
308
+ max_drawdown = Column(Float)
309
+
310
+ # Risk settings
311
+ max_position_size = Column(Float, default=0.2) # 20% max per position
312
+ stop_loss_pct = Column(Float, default=0.05) # 5% stop loss
313
+
314
+ is_active = Column(Boolean, default=True)
315
+ is_paper = Column(Boolean, default=True) # Paper trading vs real
316
+
317
+ created_at = Column(DateTime, default=datetime.utcnow)
318
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
319
+
320
+ # Relationships
321
+ user = relationship("User", back_populates="portfolios")
322
+ backtest_results = relationship("BacktestResult", back_populates="portfolio")
323
+
324
+ # Many-to-many relationship with stocks
325
+ stocks = relationship("StockData", secondary=portfolio_stocks, backref="portfolios")
326
+
327
+ # Constraints
328
+ __table_args__ = (
329
+ UniqueConstraint('user_id', 'name', name='unique_user_portfolio'),
330
+ Index('idx_portfolio_active', 'is_active'),
331
+ )
332
+
333
+
334
+ class Alert(Base):
335
+ """User alerts and notifications"""
336
+ __tablename__ = 'alerts'
337
+
338
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
339
+ user_id = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False)
340
+
341
+ alert_type = Column(Enum(AlertType), nullable=False, index=True)
342
+ severity = Column(String(10), default='info') # info, warning, critical
343
+
344
+ title = Column(String(200), nullable=False)
345
+ message = Column(Text, nullable=False)
346
+
347
+ # Related entities
348
+ ticker = Column(String(10), index=True)
349
+ politician_id = Column(UUID(as_uuid=True), ForeignKey('politicians.id'))
350
+ portfolio_id = Column(UUID(as_uuid=True), ForeignKey('portfolios.id'))
351
+
352
+ is_read = Column(Boolean, default=False, index=True)
353
+ is_sent = Column(Boolean, default=False)
354
+
355
+ metadata_json = Column(JSON, default={})
356
+
357
+ created_at = Column(DateTime, default=datetime.utcnow, index=True)
358
+ read_at = Column(DateTime)
359
+ sent_at = Column(DateTime)
360
+
361
+ # Relationships
362
+ user = relationship("User", back_populates="alerts")
363
+
364
+ # Indexes
365
+ __table_args__ = (
366
+ Index('idx_alert_user_unread', 'user_id', 'is_read'),
367
+ Index('idx_alert_created', 'created_at'),
368
+ )
369
+
370
+
371
+ class BacktestResult(Base):
372
+ """Backtesting results"""
373
+ __tablename__ = 'backtest_results'
374
+
375
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
376
+ model_id = Column(UUID(as_uuid=True), ForeignKey('models.id'), nullable=False)
377
+ portfolio_id = Column(UUID(as_uuid=True), ForeignKey('portfolios.id'))
378
+
379
+ start_date = Column(DateTime, nullable=False)
380
+ end_date = Column(DateTime, nullable=False)
381
+
382
+ # Performance metrics
383
+ total_return = Column(Float, nullable=False)
384
+ annual_return = Column(Float)
385
+ sharpe_ratio = Column(Float)
386
+ sortino_ratio = Column(Float)
387
+ max_drawdown = Column(Float)
388
+ calmar_ratio = Column(Float)
389
+
390
+ # Risk metrics
391
+ volatility = Column(Float)
392
+ var_95 = Column(Float) # Value at Risk
393
+ cvar_95 = Column(Float) # Conditional VaR
394
+
395
+ # Trading statistics
396
+ total_trades = Column(Integer)
397
+ winning_trades = Column(Integer)
398
+ losing_trades = Column(Integer)
399
+ win_rate = Column(Float)
400
+ avg_win = Column(Float)
401
+ avg_loss = Column(Float)
402
+ profit_factor = Column(Float)
403
+
404
+ # Detailed results
405
+ equity_curve = Column(JSON) # Time series of portfolio value
406
+ trade_log = Column(JSON) # Detailed trade history
407
+ metrics_json = Column(JSON) # Additional metrics
408
+
409
+ created_at = Column(DateTime, default=datetime.utcnow)
410
+
411
+ # Relationships
412
+ model = relationship("Model", back_populates="backtest_results")
413
+ portfolio = relationship("Portfolio", back_populates="backtest_results")
414
+
415
+ # Indexes
416
+ __table_args__ = (
417
+ Index('idx_backtest_model', 'model_id'),
418
+ Index('idx_backtest_sharpe', 'sharpe_ratio'),
419
+ )
420
+
421
+
422
+ class Experiment(Base):
423
+ """ML experiments tracking"""
424
+ __tablename__ = 'experiments'
425
+
426
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
427
+ name = Column(String(100), nullable=False, index=True)
428
+ description = Column(Text)
429
+
430
+ # MLflow integration
431
+ mlflow_experiment_id = Column(String(50), unique=True, index=True)
432
+ mlflow_run_id = Column(String(50), index=True)
433
+
434
+ # Hyperparameters
435
+ hyperparameters = Column(JSON, nullable=False)
436
+
437
+ # Metrics
438
+ train_metrics = Column(JSON)
439
+ val_metrics = Column(JSON)
440
+ test_metrics = Column(JSON)
441
+
442
+ # Status
443
+ status = Column(String(20), default='running') # running, completed, failed
444
+
445
+ # Timing
446
+ started_at = Column(DateTime, default=datetime.utcnow)
447
+ completed_at = Column(DateTime)
448
+ duration_seconds = Column(Integer)
449
+
450
+ # Versioning
451
+ code_version = Column(String(50)) # Git commit hash
452
+ data_version = Column(String(50)) # DVC version
453
+
454
+ metadata_json = Column(JSON, default={})
455
+
456
+ created_by = Column(String(50))
457
+ created_at = Column(DateTime, default=datetime.utcnow)
458
+
459
+ # Many-to-many relationship with models
460
+ models = relationship("Model", secondary=experiment_models, back_populates="experiments")
461
+
462
+ # Indexes
463
+ __table_args__ = (
464
+ Index('idx_experiment_status', 'status'),
465
+ Index('idx_experiment_created', 'created_at'),
466
+ )
467
+
468
+
469
+ class Model(Base):
470
+ """ML models registry"""
471
+ __tablename__ = 'models'
472
+
473
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
474
+ name = Column(String(100), nullable=False, index=True)
475
+ version = Column(String(20), nullable=False)
476
+
477
+ model_type = Column(String(50), nullable=False) # ensemble, lstm, transformer, etc.
478
+ framework = Column(String(20), default='pytorch') # pytorch, tensorflow, sklearn
479
+
480
+ # MLflow integration
481
+ mlflow_model_uri = Column(String(500))
482
+ mlflow_run_id = Column(String(50), index=True)
483
+
484
+ # Performance metrics
485
+ train_accuracy = Column(Float)
486
+ val_accuracy = Column(Float)
487
+ test_accuracy = Column(Float)
488
+
489
+ train_loss = Column(Float)
490
+ val_loss = Column(Float)
491
+ test_loss = Column(Float)
492
+
493
+ # Additional metrics (Bitcoin-style metrics)
494
+ metrics = Column(JSON) # Can include: rmse, mae, r2, mape, cv_scores, etc.
495
+ hyperparameters = Column(JSON)
496
+
497
+ # Cross-validation metrics
498
+ cv_scores = Column(ARRAY(Float))
499
+ cv_mean = Column(Float)
500
+ cv_std = Column(Float)
501
+
502
+ # Regression metrics
503
+ train_rmse = Column(Float)
504
+ train_mae = Column(Float)
505
+ train_r2 = Column(Float)
506
+ val_rmse = Column(Float)
507
+ val_mae = Column(Float)
508
+ val_r2 = Column(Float)
509
+ test_rmse = Column(Float)
510
+ test_mae = Column(Float)
511
+ test_r2 = Column(Float)
512
+ mape = Column(Float)
513
+
514
+ # Residuals analysis
515
+ residuals_stats = Column(JSON) # mean, std, normality tests, etc.
516
+
517
+ # Model artifacts
518
+ model_path = Column(String(500))
519
+ feature_names = Column(ARRAY(String))
520
+ input_shape = Column(JSON)
521
+ output_shape = Column(JSON)
522
+
523
+ # Deployment
524
+ status = Column(Enum(ModelStatus), default=ModelStatus.TRAINING, nullable=False)
525
+ deployed_at = Column(DateTime)
526
+ deployment_endpoint = Column(String(200))
527
+
528
+ # Metadata
529
+ description = Column(Text)
530
+ tags = Column(ARRAY(String))
531
+
532
+ created_by = Column(String(50))
533
+ created_at = Column(DateTime, default=datetime.utcnow)
534
+ updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
535
+
536
+ # Relationships
537
+ predictions = relationship("Prediction", back_populates="model")
538
+ backtest_results = relationship("BacktestResult", back_populates="model")
539
+ feature_sets = relationship("FeatureSet", back_populates="model")
540
+ experiments = relationship("Experiment", secondary=experiment_models, back_populates="models")
541
+
542
+ # Constraints and indexes
543
+ __table_args__ = (
544
+ UniqueConstraint('name', 'version', name='unique_model_version'),
545
+ Index('idx_model_status', 'status'),
546
+ Index('idx_model_created', 'created_at'),
547
+ )
548
+
549
+
550
+ class FeatureSet(Base):
551
+ """Feature sets for models"""
552
+ __tablename__ = 'feature_sets'
553
+
554
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
555
+ model_id = Column(UUID(as_uuid=True), ForeignKey('models.id'), nullable=False)
556
+
557
+ name = Column(String(100), nullable=False)
558
+ version = Column(String(20), nullable=False)
559
+
560
+ # Feature details
561
+ features = Column(JSON, nullable=False) # List of feature definitions
562
+ feature_count = Column(Integer, nullable=False)
563
+
564
+ # Feature engineering pipeline
565
+ pipeline_config = Column(JSON)
566
+ transformations = Column(JSON)
567
+
568
+ # Statistics
569
+ feature_stats = Column(JSON) # Mean, std, min, max per feature
570
+ correlation_matrix = Column(JSON)
571
+
572
+ # Data quality
573
+ missing_values = Column(JSON)
574
+ outlier_detection = Column(JSON)
575
+
576
+ created_at = Column(DateTime, default=datetime.utcnow)
577
+
578
+ # Relationships
579
+ model = relationship("Model", back_populates="feature_sets")
580
+
581
+ # Constraints
582
+ __table_args__ = (
583
+ UniqueConstraint('model_id', 'name', 'version', name='unique_feature_set'),
584
+ Index('idx_feature_set_model', 'model_id'),
585
+ )
586
+
587
+
588
+ class DataVersion(Base):
589
+ """Data versioning for DVC"""
590
+ __tablename__ = 'data_versions'
591
+
592
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
593
+
594
+ dataset_name = Column(String(100), nullable=False, index=True)
595
+ version = Column(String(50), nullable=False)
596
+
597
+ # DVC details
598
+ dvc_hash = Column(String(100), unique=True, index=True)
599
+ dvc_path = Column(String(500))
600
+
601
+ # Dataset info
602
+ row_count = Column(Integer)
603
+ column_count = Column(Integer)
604
+ file_size_bytes = Column(BigInteger)
605
+
606
+ # Time range
607
+ start_date = Column(DateTime)
608
+ end_date = Column(DateTime)
609
+
610
+ # Metadata
611
+ description = Column(Text)
612
+ tags = Column(ARRAY(String))
613
+ schema_json = Column(JSON)
614
+ statistics_json = Column(JSON)
615
+
616
+ created_by = Column(String(50))
617
+ created_at = Column(DateTime, default=datetime.utcnow)
618
+
619
+ # Constraints
620
+ __table_args__ = (
621
+ UniqueConstraint('dataset_name', 'version', name='unique_data_version'),
622
+ Index('idx_data_version_created', 'created_at'),
623
+ )
624
+
625
+
626
+ # Database events and triggers
627
+ @event.listens_for(User, 'before_insert')
628
+ def generate_api_key(mapper, connection, target):
629
+ """Generate API key for new users"""
630
+ if not target.api_key:
631
+ import secrets
632
+ target.api_key = secrets.token_urlsafe(32)
633
+ target.api_key_created_at = datetime.utcnow()
634
+
635
+
636
+ @event.listens_for(Portfolio, 'before_update')
637
+ def update_portfolio_metrics(mapper, connection, target):
638
+ """Update portfolio performance metrics"""
639
+ if target.current_value and target.initial_capital:
640
+ target.total_return = ((target.current_value - target.initial_capital) /
641
+ target.initial_capital) * 100
642
+
643
+
644
+ # Create indexes for better query performance
645
+ def create_indexes(engine):
646
+ """Create additional database indexes"""
647
+ from sqlalchemy import text
648
+
649
+ indexes = [
650
+ # Composite indexes for common queries
651
+ "CREATE INDEX IF NOT EXISTS idx_trade_analysis ON trades(ticker, disclosure_date, politician_id)",
652
+ "CREATE INDEX IF NOT EXISTS idx_prediction_latest ON predictions(ticker, prediction_date DESC)",
653
+ "CREATE INDEX IF NOT EXISTS idx_portfolio_performance ON portfolios(user_id, total_return DESC)",
654
+ "CREATE INDEX IF NOT EXISTS idx_alert_priority ON alerts(user_id, is_read, severity, created_at DESC)",
655
+
656
+ # Full text search indexes (PostgreSQL specific)
657
+ "CREATE INDEX IF NOT EXISTS idx_politician_name_search ON politicians USING gin(to_tsvector('english', name))",
658
+ "CREATE INDEX IF NOT EXISTS idx_stock_company_search ON stock_data USING gin(to_tsvector('english', company_name))",
659
+ ]
660
+
661
+ with engine.connect() as conn:
662
+ for index_sql in indexes:
663
+ try:
664
+ conn.execute(text(index_sql))
665
+ conn.commit()
666
+ except Exception as e:
667
+ print(f"Warning: Could not create index: {e}")