mcli-framework 7.2.0__py3-none-any.whl → 7.4.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 (97) hide show
  1. mcli/__init__.py +160 -0
  2. mcli/__main__.py +14 -0
  3. mcli/app/__init__.py +23 -0
  4. mcli/app/commands_cmd.py +741 -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/erd/__init__.py +25 -0
  13. mcli/lib/files/__init__.py +0 -0
  14. mcli/lib/fs/__init__.py +1 -0
  15. mcli/lib/logger/__init__.py +3 -0
  16. mcli/lib/performance/__init__.py +17 -0
  17. mcli/lib/pickles/__init__.py +1 -0
  18. mcli/lib/shell/__init__.py +0 -0
  19. mcli/lib/toml/__init__.py +1 -0
  20. mcli/lib/watcher/__init__.py +0 -0
  21. mcli/ml/__init__.py +16 -0
  22. mcli/ml/api/__init__.py +30 -0
  23. mcli/ml/api/routers/__init__.py +27 -0
  24. mcli/ml/api/schemas.py +2 -2
  25. mcli/ml/auth/__init__.py +45 -0
  26. mcli/ml/auth/models.py +2 -2
  27. mcli/ml/backtesting/__init__.py +39 -0
  28. mcli/ml/cli/__init__.py +5 -0
  29. mcli/ml/cli/main.py +1 -1
  30. mcli/ml/config/__init__.py +33 -0
  31. mcli/ml/configs/__init__.py +16 -0
  32. mcli/ml/dashboard/__init__.py +12 -0
  33. mcli/ml/dashboard/app_integrated.py +296 -30
  34. mcli/ml/dashboard/app_training.py +1 -1
  35. mcli/ml/dashboard/components/__init__.py +7 -0
  36. mcli/ml/dashboard/pages/__init__.py +6 -0
  37. mcli/ml/dashboard/pages/cicd.py +1 -1
  38. mcli/ml/dashboard/pages/debug_dependencies.py +364 -0
  39. mcli/ml/dashboard/pages/gravity_viz.py +565 -0
  40. mcli/ml/dashboard/pages/monte_carlo_predictions.py +555 -0
  41. mcli/ml/dashboard/pages/overview.py +378 -0
  42. mcli/ml/dashboard/pages/predictions_enhanced.py +20 -6
  43. mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -6
  44. mcli/ml/dashboard/pages/test_portfolio.py +423 -0
  45. mcli/ml/dashboard/pages/trading.py +768 -0
  46. mcli/ml/dashboard/streamlit_extras_utils.py +297 -0
  47. mcli/ml/dashboard/utils.py +161 -0
  48. mcli/ml/dashboard/warning_suppression.py +34 -0
  49. mcli/ml/data_ingestion/__init__.py +39 -0
  50. mcli/ml/database/__init__.py +47 -0
  51. mcli/ml/database/session.py +169 -16
  52. mcli/ml/experimentation/__init__.py +29 -0
  53. mcli/ml/features/__init__.py +39 -0
  54. mcli/ml/mlops/__init__.py +33 -0
  55. mcli/ml/models/__init__.py +94 -0
  56. mcli/ml/monitoring/__init__.py +25 -0
  57. mcli/ml/optimization/__init__.py +27 -0
  58. mcli/ml/predictions/__init__.py +5 -0
  59. mcli/ml/predictions/monte_carlo.py +428 -0
  60. mcli/ml/preprocessing/__init__.py +28 -0
  61. mcli/ml/scripts/__init__.py +1 -0
  62. mcli/ml/trading/__init__.py +66 -0
  63. mcli/ml/trading/alpaca_client.py +417 -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/mygroup/__init__.py +3 -0
  71. mcli/public/__init__.py +1 -0
  72. mcli/public/commands/__init__.py +2 -0
  73. mcli/self/__init__.py +3 -0
  74. mcli/self/self_cmd.py +514 -15
  75. mcli/workflow/__init__.py +0 -0
  76. mcli/workflow/daemon/__init__.py +15 -0
  77. mcli/workflow/daemon/daemon.py +21 -3
  78. mcli/workflow/dashboard/__init__.py +5 -0
  79. mcli/workflow/docker/__init__.py +0 -0
  80. mcli/workflow/file/__init__.py +0 -0
  81. mcli/workflow/gcloud/__init__.py +1 -0
  82. mcli/workflow/git_commit/__init__.py +0 -0
  83. mcli/workflow/interview/__init__.py +0 -0
  84. mcli/workflow/politician_trading/__init__.py +4 -0
  85. mcli/workflow/registry/__init__.py +0 -0
  86. mcli/workflow/repo/__init__.py +0 -0
  87. mcli/workflow/scheduler/__init__.py +25 -0
  88. mcli/workflow/search/__init__.py +0 -0
  89. mcli/workflow/sync/__init__.py +5 -0
  90. mcli/workflow/videos/__init__.py +1 -0
  91. mcli/workflow/wakatime/__init__.py +80 -0
  92. {mcli_framework-7.2.0.dist-info → mcli_framework-7.4.0.dist-info}/METADATA +4 -1
  93. {mcli_framework-7.2.0.dist-info → mcli_framework-7.4.0.dist-info}/RECORD +97 -18
  94. {mcli_framework-7.2.0.dist-info → mcli_framework-7.4.0.dist-info}/WHEEL +0 -0
  95. {mcli_framework-7.2.0.dist-info → mcli_framework-7.4.0.dist-info}/entry_points.txt +0 -0
  96. {mcli_framework-7.2.0.dist-info → mcli_framework-7.4.0.dist-info}/licenses/LICENSE +0 -0
  97. {mcli_framework-7.2.0.dist-info → mcli_framework-7.4.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,417 @@
1
+ """Alpaca Trading API client for executing trades"""
2
+
3
+ import logging
4
+ import os
5
+ from datetime import datetime, timedelta
6
+ from decimal import Decimal
7
+ from typing import Dict, List, Optional, Tuple, Union
8
+
9
+ import pandas as pd
10
+ from alpaca.trading.client import TradingClient
11
+ from alpaca.trading.requests import (
12
+ GetOrdersRequest,
13
+ MarketOrderRequest,
14
+ LimitOrderRequest,
15
+ GetPortfolioHistoryRequest,
16
+ )
17
+ from alpaca.trading.enums import OrderSide, TimeInForce, OrderStatus
18
+ from alpaca.data.historical import StockHistoricalDataClient
19
+ from alpaca.data.requests import StockBarsRequest
20
+ from alpaca.data.timeframe import TimeFrame
21
+ from pydantic import BaseModel, Field
22
+ from dotenv import load_dotenv
23
+
24
+ # Load environment variables
25
+ load_dotenv()
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class TradingConfig(BaseModel):
31
+ """Configuration for Alpaca trading"""
32
+
33
+ api_key: str = Field(..., description="Alpaca API key")
34
+ secret_key: str = Field(..., description="Alpaca secret key")
35
+ base_url: str = Field(default="https://paper-api.alpaca.markets", description="Alpaca base URL")
36
+ data_url: str = Field(default="https://data.alpaca.markets", description="Alpaca data URL")
37
+ paper_trading: bool = Field(default=True, description="Use paper trading")
38
+
39
+
40
+ class Position(BaseModel):
41
+ """Represents a trading position"""
42
+
43
+ symbol: str
44
+ quantity: int
45
+ side: str # "long" or "short"
46
+ market_value: float
47
+ cost_basis: float
48
+ unrealized_pl: float
49
+ unrealized_plpc: float
50
+ current_price: float
51
+ qty_available: int
52
+
53
+
54
+ class Order(BaseModel):
55
+ """Represents a trading order"""
56
+
57
+ id: str
58
+ symbol: str
59
+ side: str # "buy" or "sell"
60
+ quantity: int
61
+ order_type: str
62
+ status: str
63
+ created_at: datetime
64
+ filled_at: Optional[datetime] = None
65
+ filled_price: Optional[float] = None
66
+ filled_quantity: Optional[int] = None
67
+
68
+
69
+ class Portfolio(BaseModel):
70
+ """Represents portfolio information"""
71
+
72
+ equity: float
73
+ cash: float
74
+ buying_power: float
75
+ portfolio_value: float
76
+ unrealized_pl: float
77
+ realized_pl: float
78
+ positions: List[Position] = []
79
+
80
+
81
+ class AlpacaTradingClient:
82
+ """Client for Alpaca Trading API operations"""
83
+
84
+ def __init__(self, config: TradingConfig):
85
+ self.config = config
86
+ self.trading_client = TradingClient(
87
+ api_key=config.api_key,
88
+ secret_key=config.secret_key,
89
+ paper=config.paper_trading
90
+ )
91
+ self.data_client = StockHistoricalDataClient(
92
+ api_key=config.api_key,
93
+ secret_key=config.secret_key
94
+ )
95
+
96
+ def get_account(self) -> Dict:
97
+ """Get account information"""
98
+ try:
99
+ account = self.trading_client.get_account()
100
+
101
+ # Build response with safe attribute access
102
+ response = {
103
+ "account_id": account.id,
104
+ "equity": float(account.equity) if hasattr(account, 'equity') else 0.0,
105
+ "cash": float(account.cash) if hasattr(account, 'cash') else 0.0,
106
+ "buying_power": float(account.buying_power) if hasattr(account, 'buying_power') else 0.0,
107
+ "currency": account.currency if hasattr(account, 'currency') else "USD",
108
+ "status": account.status.value if hasattr(account.status, 'value') else str(account.status),
109
+ "trading_blocked": account.trading_blocked if hasattr(account, 'trading_blocked') else False,
110
+ "pattern_day_trader": account.pattern_day_trader if hasattr(account, 'pattern_day_trader') else False,
111
+ }
112
+
113
+ # Add optional fields that may not exist in all account types
114
+ if hasattr(account, 'portfolio_value'):
115
+ response["portfolio_value"] = float(account.portfolio_value)
116
+ else:
117
+ response["portfolio_value"] = response["equity"]
118
+
119
+ if hasattr(account, 'long_market_value'):
120
+ response["unrealized_pl"] = float(account.long_market_value) - float(account.cash)
121
+ else:
122
+ response["unrealized_pl"] = 0.0
123
+
124
+ response["realized_pl"] = 0.0 # Not always available in paper accounts
125
+
126
+ return response
127
+ except Exception as e:
128
+ logger.error(f"Failed to get account info: {e}")
129
+ raise
130
+
131
+ def get_positions(self) -> List[Position]:
132
+ """Get current positions"""
133
+ try:
134
+ positions = self.trading_client.get_all_positions()
135
+ return [
136
+ Position(
137
+ symbol=pos.symbol,
138
+ quantity=int(pos.qty),
139
+ side="long" if int(pos.qty) > 0 else "short",
140
+ market_value=float(pos.market_value),
141
+ cost_basis=float(pos.cost_basis),
142
+ unrealized_pl=float(pos.unrealized_pl),
143
+ unrealized_plpc=float(pos.unrealized_plpc),
144
+ current_price=float(pos.current_price),
145
+ qty_available=int(pos.qty_available),
146
+ )
147
+ for pos in positions
148
+ ]
149
+ except Exception as e:
150
+ logger.error(f"Failed to get positions: {e}")
151
+ return []
152
+
153
+ def get_orders(self, status: Optional[str] = None, limit: int = 100) -> List[Order]:
154
+ """Get orders with optional status filter"""
155
+ try:
156
+ request = GetOrdersRequest(status=status, limit=limit)
157
+ orders = self.trading_client.get_orders(request)
158
+ return [
159
+ Order(
160
+ id=order.id,
161
+ symbol=order.symbol,
162
+ side=order.side.value,
163
+ quantity=int(order.qty),
164
+ order_type=order.order_type.value,
165
+ status=order.status.value,
166
+ created_at=order.created_at,
167
+ filled_at=order.filled_at,
168
+ filled_price=float(order.filled_avg_price) if order.filled_avg_price else None,
169
+ filled_quantity=int(order.filled_qty) if order.filled_qty else None,
170
+ )
171
+ for order in orders
172
+ ]
173
+ except Exception as e:
174
+ logger.error(f"Failed to get orders: {e}")
175
+ return []
176
+
177
+ def place_market_order(
178
+ self,
179
+ symbol: str,
180
+ quantity: int,
181
+ side: str,
182
+ time_in_force: str = "day"
183
+ ) -> Order:
184
+ """Place a market order"""
185
+ try:
186
+ order_side = OrderSide.BUY if side.lower() == "buy" else OrderSide.SELL
187
+ time_in_force_enum = TimeInForce.DAY if time_in_force.lower() == "day" else TimeInForce.GTC
188
+
189
+ order_request = MarketOrderRequest(
190
+ symbol=symbol,
191
+ qty=quantity,
192
+ side=order_side,
193
+ time_in_force=time_in_force_enum,
194
+ )
195
+
196
+ order = self.trading_client.submit_order(order_request)
197
+
198
+ return Order(
199
+ id=order.id,
200
+ symbol=order.symbol,
201
+ side=order.side.value,
202
+ quantity=int(order.qty),
203
+ order_type=order.order_type.value,
204
+ status=order.status.value,
205
+ created_at=order.created_at,
206
+ )
207
+ except Exception as e:
208
+ logger.error(f"Failed to place market order: {e}")
209
+ raise
210
+
211
+ def place_limit_order(
212
+ self,
213
+ symbol: str,
214
+ quantity: int,
215
+ side: str,
216
+ limit_price: float,
217
+ time_in_force: str = "day"
218
+ ) -> Order:
219
+ """Place a limit order"""
220
+ try:
221
+ order_side = OrderSide.BUY if side.lower() == "buy" else OrderSide.SELL
222
+ time_in_force_enum = TimeInForce.DAY if time_in_force.lower() == "day" else TimeInForce.GTC
223
+
224
+ order_request = LimitOrderRequest(
225
+ symbol=symbol,
226
+ qty=quantity,
227
+ side=order_side,
228
+ time_in_force=time_in_force_enum,
229
+ limit_price=limit_price,
230
+ )
231
+
232
+ order = self.trading_client.submit_order(order_request)
233
+
234
+ return Order(
235
+ id=order.id,
236
+ symbol=order.symbol,
237
+ side=order.side.value,
238
+ quantity=int(order.qty),
239
+ order_type=order.order_type.value,
240
+ status=order.status.value,
241
+ created_at=order.created_at,
242
+ )
243
+ except Exception as e:
244
+ logger.error(f"Failed to place limit order: {e}")
245
+ raise
246
+
247
+ def cancel_order(self, order_id: str) -> bool:
248
+ """Cancel an order"""
249
+ try:
250
+ self.trading_client.cancel_order_by_id(order_id)
251
+ return True
252
+ except Exception as e:
253
+ logger.error(f"Failed to cancel order {order_id}: {e}")
254
+ return False
255
+
256
+ def get_portfolio_history(
257
+ self,
258
+ period: str = "1M",
259
+ timeframe: str = "1Day"
260
+ ) -> pd.DataFrame:
261
+ """Get portfolio history"""
262
+ try:
263
+ # Convert period to start/end dates
264
+ end_date = datetime.now()
265
+ if period == "1D":
266
+ start_date = end_date - timedelta(days=1)
267
+ elif period == "1W":
268
+ start_date = end_date - timedelta(weeks=1)
269
+ elif period == "1M":
270
+ start_date = end_date - timedelta(days=30)
271
+ elif period == "3M":
272
+ start_date = end_date - timedelta(days=90)
273
+ elif period == "1Y":
274
+ start_date = end_date - timedelta(days=365)
275
+ else:
276
+ start_date = end_date - timedelta(days=30)
277
+
278
+ # Convert timeframe
279
+ tf = TimeFrame.Day if timeframe == "1Day" else TimeFrame.Hour
280
+
281
+ request = GetPortfolioHistoryRequest(
282
+ start=start_date,
283
+ end=end_date,
284
+ timeframe=tf,
285
+ )
286
+
287
+ history = self.trading_client.get_portfolio_history(request)
288
+
289
+ # Convert to DataFrame
290
+ data = []
291
+ for i, timestamp in enumerate(history.timestamp):
292
+ data.append({
293
+ "timestamp": timestamp,
294
+ "equity": float(history.equity[i]) if history.equity else 0,
295
+ "profit_loss": float(history.profit_loss[i]) if history.profit_loss else 0,
296
+ "profit_loss_pct": float(history.profit_loss_pct[i]) if history.profit_loss_pct else 0,
297
+ })
298
+
299
+ return pd.DataFrame(data)
300
+ except Exception as e:
301
+ logger.error(f"Failed to get portfolio history: {e}")
302
+ return pd.DataFrame()
303
+
304
+ def get_stock_data(
305
+ self,
306
+ symbols: List[str],
307
+ start_date: datetime,
308
+ end_date: datetime,
309
+ timeframe: str = "1Day"
310
+ ) -> pd.DataFrame:
311
+ """Get historical stock data"""
312
+ try:
313
+ tf = TimeFrame.Day if timeframe == "1Day" else TimeFrame.Hour
314
+
315
+ request = StockBarsRequest(
316
+ symbol_or_symbols=symbols,
317
+ timeframe=tf,
318
+ start=start_date,
319
+ end=end_date,
320
+ )
321
+
322
+ bars = self.data_client.get_stock_bars(request)
323
+
324
+ # Convert to DataFrame
325
+ data = []
326
+ for symbol, bar_list in bars.items():
327
+ for bar in bar_list:
328
+ data.append({
329
+ "symbol": symbol,
330
+ "timestamp": bar.timestamp,
331
+ "open": float(bar.open),
332
+ "high": float(bar.high),
333
+ "low": float(bar.low),
334
+ "close": float(bar.close),
335
+ "volume": int(bar.volume),
336
+ })
337
+
338
+ return pd.DataFrame(data)
339
+ except Exception as e:
340
+ logger.error(f"Failed to get stock data: {e}")
341
+ return pd.DataFrame()
342
+
343
+ def get_portfolio(self) -> Portfolio:
344
+ """Get complete portfolio information"""
345
+ try:
346
+ account = self.get_account()
347
+ positions = self.get_positions()
348
+
349
+ return Portfolio(
350
+ equity=account["equity"],
351
+ cash=account["cash"],
352
+ buying_power=account["buying_power"],
353
+ portfolio_value=account["portfolio_value"],
354
+ unrealized_pl=account["unrealized_pl"],
355
+ realized_pl=account["realized_pl"],
356
+ positions=positions,
357
+ )
358
+ except Exception as e:
359
+ logger.error(f"Failed to get portfolio: {e}")
360
+ raise
361
+
362
+
363
+ def create_trading_client(api_key: str = None, secret_key: str = None, paper_trading: bool = True) -> AlpacaTradingClient:
364
+ """
365
+ Create a trading client with the given credentials or from environment variables
366
+
367
+ Args:
368
+ api_key: Alpaca API key (if None, loads from ALPACA_API_KEY env var)
369
+ secret_key: Alpaca secret key (if None, loads from ALPACA_SECRET_KEY env var)
370
+ paper_trading: Whether to use paper trading (default: True)
371
+
372
+ Returns:
373
+ AlpacaTradingClient instance
374
+ """
375
+ # Load from environment if not provided
376
+ if api_key is None:
377
+ api_key = os.getenv("ALPACA_API_KEY")
378
+ if secret_key is None:
379
+ secret_key = os.getenv("ALPACA_SECRET_KEY")
380
+
381
+ if not api_key or not secret_key:
382
+ raise ValueError(
383
+ "Alpaca API credentials not found. "
384
+ "Please provide api_key and secret_key, or set ALPACA_API_KEY and ALPACA_SECRET_KEY environment variables."
385
+ )
386
+
387
+ base_url = os.getenv("ALPACA_BASE_URL", "https://paper-api.alpaca.markets")
388
+
389
+ config = TradingConfig(
390
+ api_key=api_key,
391
+ secret_key=secret_key,
392
+ base_url=base_url,
393
+ paper_trading=paper_trading
394
+ )
395
+ return AlpacaTradingClient(config)
396
+
397
+
398
+ def get_alpaca_config_from_env() -> Optional[Dict[str, str]]:
399
+ """
400
+ Get Alpaca configuration from environment variables
401
+
402
+ Returns:
403
+ Dictionary with API configuration or None if not configured
404
+ """
405
+ api_key = os.getenv("ALPACA_API_KEY")
406
+ secret_key = os.getenv("ALPACA_SECRET_KEY")
407
+ base_url = os.getenv("ALPACA_BASE_URL", "https://paper-api.alpaca.markets")
408
+
409
+ if not api_key or not secret_key:
410
+ return None
411
+
412
+ return {
413
+ "api_key": api_key,
414
+ "secret_key": secret_key,
415
+ "base_url": base_url,
416
+ "is_paper": "paper" in base_url.lower()
417
+ }
@@ -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")