mcli-framework 7.1.3__py3-none-any.whl → 7.3.1__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 (114) hide show
  1. mcli/__init__.py +160 -0
  2. mcli/__main__.py +14 -0
  3. mcli/app/__init__.py +23 -0
  4. mcli/app/main.py +10 -0
  5. mcli/app/model/__init__.py +0 -0
  6. mcli/app/video/__init__.py +5 -0
  7. mcli/chat/__init__.py +34 -0
  8. mcli/lib/__init__.py +0 -0
  9. mcli/lib/api/__init__.py +0 -0
  10. mcli/lib/auth/__init__.py +1 -0
  11. mcli/lib/config/__init__.py +1 -0
  12. mcli/lib/custom_commands.py +424 -0
  13. mcli/lib/erd/__init__.py +25 -0
  14. mcli/lib/files/__init__.py +0 -0
  15. mcli/lib/fs/__init__.py +1 -0
  16. mcli/lib/logger/__init__.py +3 -0
  17. mcli/lib/paths.py +12 -0
  18. mcli/lib/performance/__init__.py +17 -0
  19. mcli/lib/pickles/__init__.py +1 -0
  20. mcli/lib/shell/__init__.py +0 -0
  21. mcli/lib/toml/__init__.py +1 -0
  22. mcli/lib/watcher/__init__.py +0 -0
  23. mcli/ml/__init__.py +16 -0
  24. mcli/ml/api/__init__.py +30 -0
  25. mcli/ml/api/routers/__init__.py +27 -0
  26. mcli/ml/api/schemas.py +2 -2
  27. mcli/ml/auth/__init__.py +45 -0
  28. mcli/ml/auth/models.py +2 -2
  29. mcli/ml/backtesting/__init__.py +39 -0
  30. mcli/ml/cli/__init__.py +5 -0
  31. mcli/ml/cli/main.py +1 -1
  32. mcli/ml/config/__init__.py +33 -0
  33. mcli/ml/configs/__init__.py +16 -0
  34. mcli/ml/dashboard/__init__.py +12 -0
  35. mcli/ml/dashboard/app.py +13 -13
  36. mcli/ml/dashboard/app_integrated.py +1309 -148
  37. mcli/ml/dashboard/app_supabase.py +46 -21
  38. mcli/ml/dashboard/app_training.py +14 -14
  39. mcli/ml/dashboard/components/__init__.py +7 -0
  40. mcli/ml/dashboard/components/charts.py +258 -0
  41. mcli/ml/dashboard/components/metrics.py +125 -0
  42. mcli/ml/dashboard/components/tables.py +228 -0
  43. mcli/ml/dashboard/pages/__init__.py +6 -0
  44. mcli/ml/dashboard/pages/cicd.py +382 -0
  45. mcli/ml/dashboard/pages/predictions_enhanced.py +834 -0
  46. mcli/ml/dashboard/pages/scrapers_and_logs.py +1060 -0
  47. mcli/ml/dashboard/pages/test_portfolio.py +373 -0
  48. mcli/ml/dashboard/pages/trading.py +714 -0
  49. mcli/ml/dashboard/pages/workflows.py +533 -0
  50. mcli/ml/dashboard/utils.py +154 -0
  51. mcli/ml/data_ingestion/__init__.py +39 -0
  52. mcli/ml/database/__init__.py +47 -0
  53. mcli/ml/experimentation/__init__.py +29 -0
  54. mcli/ml/features/__init__.py +39 -0
  55. mcli/ml/mlops/__init__.py +33 -0
  56. mcli/ml/models/__init__.py +94 -0
  57. mcli/ml/monitoring/__init__.py +25 -0
  58. mcli/ml/optimization/__init__.py +27 -0
  59. mcli/ml/predictions/__init__.py +5 -0
  60. mcli/ml/preprocessing/__init__.py +28 -0
  61. mcli/ml/scripts/__init__.py +1 -0
  62. mcli/ml/trading/__init__.py +60 -0
  63. mcli/ml/trading/alpaca_client.py +353 -0
  64. mcli/ml/trading/migrations.py +164 -0
  65. mcli/ml/trading/models.py +418 -0
  66. mcli/ml/trading/paper_trading.py +326 -0
  67. mcli/ml/trading/risk_management.py +370 -0
  68. mcli/ml/trading/trading_service.py +480 -0
  69. mcli/ml/training/__init__.py +10 -0
  70. mcli/ml/training/train_model.py +569 -0
  71. mcli/mygroup/__init__.py +3 -0
  72. mcli/public/__init__.py +1 -0
  73. mcli/public/commands/__init__.py +2 -0
  74. mcli/self/__init__.py +3 -0
  75. mcli/self/self_cmd.py +579 -91
  76. mcli/workflow/__init__.py +0 -0
  77. mcli/workflow/daemon/__init__.py +15 -0
  78. mcli/workflow/daemon/daemon.py +21 -3
  79. mcli/workflow/dashboard/__init__.py +5 -0
  80. mcli/workflow/docker/__init__.py +0 -0
  81. mcli/workflow/file/__init__.py +0 -0
  82. mcli/workflow/gcloud/__init__.py +1 -0
  83. mcli/workflow/git_commit/__init__.py +0 -0
  84. mcli/workflow/interview/__init__.py +0 -0
  85. mcli/workflow/politician_trading/__init__.py +4 -0
  86. mcli/workflow/politician_trading/data_sources.py +259 -1
  87. mcli/workflow/politician_trading/models.py +159 -1
  88. mcli/workflow/politician_trading/scrapers_corporate_registry.py +846 -0
  89. mcli/workflow/politician_trading/scrapers_free_sources.py +516 -0
  90. mcli/workflow/politician_trading/scrapers_third_party.py +391 -0
  91. mcli/workflow/politician_trading/seed_database.py +539 -0
  92. mcli/workflow/registry/__init__.py +0 -0
  93. mcli/workflow/repo/__init__.py +0 -0
  94. mcli/workflow/scheduler/__init__.py +25 -0
  95. mcli/workflow/search/__init__.py +0 -0
  96. mcli/workflow/sync/__init__.py +5 -0
  97. mcli/workflow/videos/__init__.py +1 -0
  98. mcli/workflow/wakatime/__init__.py +80 -0
  99. mcli/workflow/workflow.py +8 -27
  100. {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/METADATA +3 -1
  101. {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/RECORD +105 -29
  102. mcli/workflow/daemon/api_daemon.py +0 -800
  103. mcli/workflow/daemon/commands.py +0 -1196
  104. mcli/workflow/dashboard/dashboard_cmd.py +0 -120
  105. mcli/workflow/file/file.py +0 -100
  106. mcli/workflow/git_commit/commands.py +0 -430
  107. mcli/workflow/politician_trading/commands.py +0 -1939
  108. mcli/workflow/scheduler/commands.py +0 -493
  109. mcli/workflow/sync/sync_cmd.py +0 -437
  110. mcli/workflow/videos/videos.py +0 -242
  111. {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/WHEEL +0 -0
  112. {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/entry_points.txt +0 -0
  113. {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/licenses/LICENSE +0 -0
  114. {mcli_framework-7.1.3.dist-info → mcli_framework-7.3.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,353 @@
1
+ """Alpaca Trading API client for executing trades"""
2
+
3
+ import logging
4
+ from datetime import datetime, timedelta
5
+ from decimal import Decimal
6
+ from typing import Dict, List, Optional, Tuple, Union
7
+
8
+ import pandas as pd
9
+ from alpaca.trading.client import TradingClient
10
+ from alpaca.trading.requests import (
11
+ GetOrdersRequest,
12
+ MarketOrderRequest,
13
+ LimitOrderRequest,
14
+ GetPortfolioHistoryRequest,
15
+ )
16
+ from alpaca.trading.enums import OrderSide, TimeInForce, OrderStatus
17
+ from alpaca.data.historical import StockHistoricalDataClient
18
+ from alpaca.data.requests import StockBarsRequest
19
+ from alpaca.data.timeframe import TimeFrame
20
+ from pydantic import BaseModel, Field
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class TradingConfig(BaseModel):
26
+ """Configuration for Alpaca trading"""
27
+
28
+ api_key: str = Field(..., description="Alpaca API key")
29
+ secret_key: str = Field(..., description="Alpaca secret key")
30
+ base_url: str = Field(default="https://paper-api.alpaca.markets", description="Alpaca base URL")
31
+ data_url: str = Field(default="https://data.alpaca.markets", description="Alpaca data URL")
32
+ paper_trading: bool = Field(default=True, description="Use paper trading")
33
+
34
+
35
+ class Position(BaseModel):
36
+ """Represents a trading position"""
37
+
38
+ symbol: str
39
+ quantity: int
40
+ side: str # "long" or "short"
41
+ market_value: float
42
+ cost_basis: float
43
+ unrealized_pl: float
44
+ unrealized_plpc: float
45
+ current_price: float
46
+ qty_available: int
47
+
48
+
49
+ class Order(BaseModel):
50
+ """Represents a trading order"""
51
+
52
+ id: str
53
+ symbol: str
54
+ side: str # "buy" or "sell"
55
+ quantity: int
56
+ order_type: str
57
+ status: str
58
+ created_at: datetime
59
+ filled_at: Optional[datetime] = None
60
+ filled_price: Optional[float] = None
61
+ filled_quantity: Optional[int] = None
62
+
63
+
64
+ class Portfolio(BaseModel):
65
+ """Represents portfolio information"""
66
+
67
+ equity: float
68
+ cash: float
69
+ buying_power: float
70
+ portfolio_value: float
71
+ unrealized_pl: float
72
+ realized_pl: float
73
+ positions: List[Position] = []
74
+
75
+
76
+ class AlpacaTradingClient:
77
+ """Client for Alpaca Trading API operations"""
78
+
79
+ def __init__(self, config: TradingConfig):
80
+ self.config = config
81
+ self.trading_client = TradingClient(
82
+ api_key=config.api_key,
83
+ secret_key=config.secret_key,
84
+ paper=config.paper_trading,
85
+ base_url=config.base_url
86
+ )
87
+ self.data_client = StockHistoricalDataClient(
88
+ api_key=config.api_key,
89
+ secret_key=config.secret_key,
90
+ base_url=config.data_url
91
+ )
92
+
93
+ def get_account(self) -> Dict:
94
+ """Get account information"""
95
+ try:
96
+ account = self.trading_client.get_account()
97
+ return {
98
+ "account_id": account.id,
99
+ "equity": float(account.equity),
100
+ "cash": float(account.cash),
101
+ "buying_power": float(account.buying_power),
102
+ "portfolio_value": float(account.portfolio_value),
103
+ "unrealized_pl": float(account.unrealized_pl),
104
+ "realized_pl": float(account.realized_pl),
105
+ "currency": account.currency,
106
+ "status": account.status,
107
+ "trading_blocked": account.trading_blocked,
108
+ "pattern_day_trader": account.pattern_day_trader,
109
+ }
110
+ except Exception as e:
111
+ logger.error(f"Failed to get account info: {e}")
112
+ raise
113
+
114
+ def get_positions(self) -> List[Position]:
115
+ """Get current positions"""
116
+ try:
117
+ positions = self.trading_client.get_all_positions()
118
+ return [
119
+ Position(
120
+ symbol=pos.symbol,
121
+ quantity=int(pos.qty),
122
+ side="long" if int(pos.qty) > 0 else "short",
123
+ market_value=float(pos.market_value),
124
+ cost_basis=float(pos.cost_basis),
125
+ unrealized_pl=float(pos.unrealized_pl),
126
+ unrealized_plpc=float(pos.unrealized_plpc),
127
+ current_price=float(pos.current_price),
128
+ qty_available=int(pos.qty_available),
129
+ )
130
+ for pos in positions
131
+ ]
132
+ except Exception as e:
133
+ logger.error(f"Failed to get positions: {e}")
134
+ return []
135
+
136
+ def get_orders(self, status: Optional[str] = None, limit: int = 100) -> List[Order]:
137
+ """Get orders with optional status filter"""
138
+ try:
139
+ request = GetOrdersRequest(status=status, limit=limit)
140
+ orders = self.trading_client.get_orders(request)
141
+ return [
142
+ Order(
143
+ id=order.id,
144
+ symbol=order.symbol,
145
+ side=order.side.value,
146
+ quantity=int(order.qty),
147
+ order_type=order.order_type.value,
148
+ status=order.status.value,
149
+ created_at=order.created_at,
150
+ filled_at=order.filled_at,
151
+ filled_price=float(order.filled_avg_price) if order.filled_avg_price else None,
152
+ filled_quantity=int(order.filled_qty) if order.filled_qty else None,
153
+ )
154
+ for order in orders
155
+ ]
156
+ except Exception as e:
157
+ logger.error(f"Failed to get orders: {e}")
158
+ return []
159
+
160
+ def place_market_order(
161
+ self,
162
+ symbol: str,
163
+ quantity: int,
164
+ side: str,
165
+ time_in_force: str = "day"
166
+ ) -> Order:
167
+ """Place a market order"""
168
+ try:
169
+ order_side = OrderSide.BUY if side.lower() == "buy" else OrderSide.SELL
170
+ time_in_force_enum = TimeInForce.DAY if time_in_force.lower() == "day" else TimeInForce.GTC
171
+
172
+ order_request = MarketOrderRequest(
173
+ symbol=symbol,
174
+ qty=quantity,
175
+ side=order_side,
176
+ time_in_force=time_in_force_enum,
177
+ )
178
+
179
+ order = self.trading_client.submit_order(order_request)
180
+
181
+ return Order(
182
+ id=order.id,
183
+ symbol=order.symbol,
184
+ side=order.side.value,
185
+ quantity=int(order.qty),
186
+ order_type=order.order_type.value,
187
+ status=order.status.value,
188
+ created_at=order.created_at,
189
+ )
190
+ except Exception as e:
191
+ logger.error(f"Failed to place market order: {e}")
192
+ raise
193
+
194
+ def place_limit_order(
195
+ self,
196
+ symbol: str,
197
+ quantity: int,
198
+ side: str,
199
+ limit_price: float,
200
+ time_in_force: str = "day"
201
+ ) -> Order:
202
+ """Place a limit order"""
203
+ try:
204
+ order_side = OrderSide.BUY if side.lower() == "buy" else OrderSide.SELL
205
+ time_in_force_enum = TimeInForce.DAY if time_in_force.lower() == "day" else TimeInForce.GTC
206
+
207
+ order_request = LimitOrderRequest(
208
+ symbol=symbol,
209
+ qty=quantity,
210
+ side=order_side,
211
+ time_in_force=time_in_force_enum,
212
+ limit_price=limit_price,
213
+ )
214
+
215
+ order = self.trading_client.submit_order(order_request)
216
+
217
+ return Order(
218
+ id=order.id,
219
+ symbol=order.symbol,
220
+ side=order.side.value,
221
+ quantity=int(order.qty),
222
+ order_type=order.order_type.value,
223
+ status=order.status.value,
224
+ created_at=order.created_at,
225
+ )
226
+ except Exception as e:
227
+ logger.error(f"Failed to place limit order: {e}")
228
+ raise
229
+
230
+ def cancel_order(self, order_id: str) -> bool:
231
+ """Cancel an order"""
232
+ try:
233
+ self.trading_client.cancel_order_by_id(order_id)
234
+ return True
235
+ except Exception as e:
236
+ logger.error(f"Failed to cancel order {order_id}: {e}")
237
+ return False
238
+
239
+ def get_portfolio_history(
240
+ self,
241
+ period: str = "1M",
242
+ timeframe: str = "1Day"
243
+ ) -> pd.DataFrame:
244
+ """Get portfolio history"""
245
+ try:
246
+ # Convert period to start/end dates
247
+ end_date = datetime.now()
248
+ if period == "1D":
249
+ start_date = end_date - timedelta(days=1)
250
+ elif period == "1W":
251
+ start_date = end_date - timedelta(weeks=1)
252
+ elif period == "1M":
253
+ start_date = end_date - timedelta(days=30)
254
+ elif period == "3M":
255
+ start_date = end_date - timedelta(days=90)
256
+ elif period == "1Y":
257
+ start_date = end_date - timedelta(days=365)
258
+ else:
259
+ start_date = end_date - timedelta(days=30)
260
+
261
+ # Convert timeframe
262
+ tf = TimeFrame.Day if timeframe == "1Day" else TimeFrame.Hour
263
+
264
+ request = GetPortfolioHistoryRequest(
265
+ start=start_date,
266
+ end=end_date,
267
+ timeframe=tf,
268
+ )
269
+
270
+ history = self.trading_client.get_portfolio_history(request)
271
+
272
+ # Convert to DataFrame
273
+ data = []
274
+ for i, timestamp in enumerate(history.timestamp):
275
+ data.append({
276
+ "timestamp": timestamp,
277
+ "equity": float(history.equity[i]) if history.equity else 0,
278
+ "profit_loss": float(history.profit_loss[i]) if history.profit_loss else 0,
279
+ "profit_loss_pct": float(history.profit_loss_pct[i]) if history.profit_loss_pct else 0,
280
+ })
281
+
282
+ return pd.DataFrame(data)
283
+ except Exception as e:
284
+ logger.error(f"Failed to get portfolio history: {e}")
285
+ return pd.DataFrame()
286
+
287
+ def get_stock_data(
288
+ self,
289
+ symbols: List[str],
290
+ start_date: datetime,
291
+ end_date: datetime,
292
+ timeframe: str = "1Day"
293
+ ) -> pd.DataFrame:
294
+ """Get historical stock data"""
295
+ try:
296
+ tf = TimeFrame.Day if timeframe == "1Day" else TimeFrame.Hour
297
+
298
+ request = StockBarsRequest(
299
+ symbol_or_symbols=symbols,
300
+ timeframe=tf,
301
+ start=start_date,
302
+ end=end_date,
303
+ )
304
+
305
+ bars = self.data_client.get_stock_bars(request)
306
+
307
+ # Convert to DataFrame
308
+ data = []
309
+ for symbol, bar_list in bars.items():
310
+ for bar in bar_list:
311
+ data.append({
312
+ "symbol": symbol,
313
+ "timestamp": bar.timestamp,
314
+ "open": float(bar.open),
315
+ "high": float(bar.high),
316
+ "low": float(bar.low),
317
+ "close": float(bar.close),
318
+ "volume": int(bar.volume),
319
+ })
320
+
321
+ return pd.DataFrame(data)
322
+ except Exception as e:
323
+ logger.error(f"Failed to get stock data: {e}")
324
+ return pd.DataFrame()
325
+
326
+ def get_portfolio(self) -> Portfolio:
327
+ """Get complete portfolio information"""
328
+ try:
329
+ account = self.get_account()
330
+ positions = self.get_positions()
331
+
332
+ return Portfolio(
333
+ equity=account["equity"],
334
+ cash=account["cash"],
335
+ buying_power=account["buying_power"],
336
+ portfolio_value=account["portfolio_value"],
337
+ unrealized_pl=account["unrealized_pl"],
338
+ realized_pl=account["realized_pl"],
339
+ positions=positions,
340
+ )
341
+ except Exception as e:
342
+ logger.error(f"Failed to get portfolio: {e}")
343
+ raise
344
+
345
+
346
+ def create_trading_client(api_key: str, secret_key: str, paper_trading: bool = True) -> AlpacaTradingClient:
347
+ """Create a trading client with the given credentials"""
348
+ config = TradingConfig(
349
+ api_key=api_key,
350
+ secret_key=secret_key,
351
+ paper_trading=paper_trading
352
+ )
353
+ return AlpacaTradingClient(config)
@@ -0,0 +1,164 @@
1
+ """Database migrations for trading functionality"""
2
+
3
+ import logging
4
+ from sqlalchemy import create_engine, text
5
+ from sqlalchemy.orm import sessionmaker
6
+ from mcli.ml.trading.models import Base
7
+ from mcli.ml.config.settings import get_settings
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def create_trading_tables():
13
+ """Create trading-related tables in the database"""
14
+ try:
15
+ # Get database URL from settings
16
+ settings = get_settings()
17
+ engine = create_engine(settings.database.url)
18
+
19
+ # Create all tables
20
+ Base.metadata.create_all(engine)
21
+
22
+ logger.info("Trading tables created successfully")
23
+ return True
24
+
25
+ except Exception as e:
26
+ logger.error(f"Failed to create trading tables: {e}")
27
+ return False
28
+
29
+
30
+ def drop_trading_tables():
31
+ """Drop trading-related tables from the database"""
32
+ try:
33
+ # Get database URL from settings
34
+ settings = get_settings()
35
+ engine = create_engine(settings.database.url)
36
+
37
+ # Drop all tables
38
+ Base.metadata.drop_all(engine)
39
+
40
+ logger.info("Trading tables dropped successfully")
41
+ return True
42
+
43
+ except Exception as e:
44
+ logger.error(f"Failed to drop trading tables: {e}")
45
+ return False
46
+
47
+
48
+ def migrate_trading_data():
49
+ """Migrate existing data to new trading schema"""
50
+ try:
51
+ # Get database URL from settings
52
+ settings = get_settings()
53
+ engine = create_engine(settings.database.url)
54
+ Session = sessionmaker(bind=engine)
55
+ session = Session()
56
+
57
+ # Check if we need to migrate existing portfolio data
58
+ # This would depend on your existing schema
59
+
60
+ session.close()
61
+ logger.info("Trading data migration completed")
62
+ return True
63
+
64
+ except Exception as e:
65
+ logger.error(f"Failed to migrate trading data: {e}")
66
+ return False
67
+
68
+
69
+ def verify_trading_schema():
70
+ """Verify that trading schema is properly set up"""
71
+ try:
72
+ # Get database URL from settings
73
+ settings = get_settings()
74
+ engine = create_engine(settings.database.url)
75
+
76
+ # Check if tables exist
77
+ with engine.connect() as conn:
78
+ # Check for trading_accounts table
79
+ if "sqlite" in settings.database.url:
80
+ # SQLite syntax
81
+ result = conn.execute(text("""
82
+ SELECT name FROM sqlite_master
83
+ WHERE type='table' AND name='trading_accounts';
84
+ """))
85
+ accounts_exists = result.fetchone() is not None
86
+
87
+ result = conn.execute(text("""
88
+ SELECT name FROM sqlite_master
89
+ WHERE type='table' AND name='portfolios';
90
+ """))
91
+ portfolios_exists = result.fetchone() is not None
92
+
93
+ result = conn.execute(text("""
94
+ SELECT name FROM sqlite_master
95
+ WHERE type='table' AND name='positions';
96
+ """))
97
+ positions_exists = result.fetchone() is not None
98
+
99
+ result = conn.execute(text("""
100
+ SELECT name FROM sqlite_master
101
+ WHERE type='table' AND name='trading_orders';
102
+ """))
103
+ orders_exists = result.fetchone() is not None
104
+ else:
105
+ # PostgreSQL syntax
106
+ result = conn.execute(text("""
107
+ SELECT EXISTS (
108
+ SELECT FROM information_schema.tables
109
+ WHERE table_name = 'trading_accounts'
110
+ );
111
+ """))
112
+ accounts_exists = result.scalar()
113
+
114
+ result = conn.execute(text("""
115
+ SELECT EXISTS (
116
+ SELECT FROM information_schema.tables
117
+ WHERE table_name = 'portfolios'
118
+ );
119
+ """))
120
+ portfolios_exists = result.scalar()
121
+
122
+ result = conn.execute(text("""
123
+ SELECT EXISTS (
124
+ SELECT FROM information_schema.tables
125
+ WHERE table_name = 'positions'
126
+ );
127
+ """))
128
+ positions_exists = result.scalar()
129
+
130
+ result = conn.execute(text("""
131
+ SELECT EXISTS (
132
+ SELECT FROM information_schema.tables
133
+ WHERE table_name = 'trading_orders'
134
+ );
135
+ """))
136
+ orders_exists = result.scalar()
137
+
138
+ all_tables_exist = all([accounts_exists, portfolios_exists, positions_exists, orders_exists])
139
+
140
+ if all_tables_exist:
141
+ logger.info("Trading schema verification successful")
142
+ else:
143
+ logger.warning("Some trading tables are missing")
144
+
145
+ return all_tables_exist
146
+
147
+ except Exception as e:
148
+ logger.error(f"Failed to verify trading schema: {e}")
149
+ return False
150
+
151
+
152
+ if __name__ == "__main__":
153
+ # Run migrations
154
+ print("Creating trading tables...")
155
+ if create_trading_tables():
156
+ print("✅ Trading tables created successfully")
157
+ else:
158
+ print("❌ Failed to create trading tables")
159
+
160
+ print("Verifying schema...")
161
+ if verify_trading_schema():
162
+ print("✅ Trading schema verified successfully")
163
+ else:
164
+ print("❌ Trading schema verification failed")