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,373 @@
1
+ """Test portfolio page for paper trading and backtesting"""
2
+
3
+ import logging
4
+ from datetime import datetime, timedelta
5
+ from typing import Dict, List, Optional
6
+ from uuid import UUID
7
+
8
+ import pandas as pd
9
+ import plotly.express as px
10
+ import plotly.graph_objects as go
11
+ from plotly.subplots import make_subplots
12
+ import streamlit as st
13
+ from sqlalchemy.orm import Session
14
+
15
+ from mcli.ml.trading.paper_trading import create_paper_trading_engine
16
+ from mcli.ml.trading.trading_service import TradingService
17
+ from mcli.ml.trading.models import OrderCreate, OrderType, OrderSide
18
+ from mcli.ml.database.session import get_session
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def show_test_portfolio():
24
+ """Test portfolio page for paper trading"""
25
+ st.title("🧪 Test Portfolio - Paper Trading")
26
+ st.markdown("Test your trading strategies with paper money before going live")
27
+
28
+ # Initialize session state
29
+ if "test_portfolio_id" not in st.session_state:
30
+ st.session_state.test_portfolio_id = None
31
+
32
+ try:
33
+ with get_session() as db:
34
+ trading_service = TradingService(db)
35
+ paper_engine = create_paper_trading_engine(db)
36
+
37
+ # Create test portfolio if none exists
38
+ if not st.session_state.test_portfolio_id:
39
+ if st.button("Create Test Portfolio", type="primary"):
40
+ with st.spinner("Creating test portfolio..."):
41
+ user_id = UUID("00000000-0000-0000-0000-000000000000") # Default user
42
+ portfolio = paper_engine.create_test_portfolio(
43
+ user_id=user_id,
44
+ name="My Test Portfolio",
45
+ initial_capital=100000.0
46
+ )
47
+ st.session_state.test_portfolio_id = portfolio.id
48
+ st.success("Test portfolio created successfully!")
49
+ st.rerun()
50
+ else:
51
+ # Get portfolio
52
+ portfolio = trading_service.get_portfolio(st.session_state.test_portfolio_id)
53
+ if not portfolio:
54
+ st.error("Test portfolio not found")
55
+ st.session_state.test_portfolio_id = None
56
+ st.rerun()
57
+ return
58
+
59
+ # Portfolio header
60
+ col1, col2, col3, col4 = st.columns(4)
61
+
62
+ with col1:
63
+ st.metric("Portfolio Value", f"${float(portfolio.current_value):,.2f}")
64
+ with col2:
65
+ st.metric("Cash Balance", f"${float(portfolio.cash_balance):,.2f}")
66
+ with col3:
67
+ st.metric("Total Return", f"{portfolio.total_return_pct:.2f}%")
68
+ with col4:
69
+ positions = trading_service.get_portfolio_positions(portfolio.id)
70
+ st.metric("Positions", len(positions))
71
+
72
+ # Trading interface
73
+ st.subheader("📈 Paper Trading Interface")
74
+
75
+ col1, col2 = st.columns([1, 1])
76
+
77
+ with col1:
78
+ st.markdown("#### Place Test Order")
79
+
80
+ with st.form("test_order"):
81
+ symbol = st.text_input("Symbol", placeholder="AAPL", help="Stock symbol to trade")
82
+ side = st.selectbox("Side", [OrderSide.BUY, OrderSide.SELL])
83
+ order_type = st.selectbox("Order Type", [OrderType.MARKET, OrderType.LIMIT])
84
+ quantity = st.number_input("Quantity", min_value=1, value=1)
85
+
86
+ limit_price = None
87
+ if order_type == OrderType.LIMIT:
88
+ limit_price = st.number_input("Limit Price", min_value=0.01, value=100.0, step=0.01)
89
+
90
+ if st.form_submit_button("Place Test Order", type="primary"):
91
+ if symbol:
92
+ try:
93
+ order_data = OrderCreate(
94
+ symbol=symbol.upper(),
95
+ side=side,
96
+ order_type=order_type,
97
+ quantity=quantity,
98
+ limit_price=limit_price,
99
+ time_in_force="day",
100
+ extended_hours=False
101
+ )
102
+
103
+ # Create order
104
+ order = trading_service.place_order(portfolio.id, order_data)
105
+
106
+ # Execute paper trade
107
+ success = paper_engine.execute_paper_order(order)
108
+
109
+ if success:
110
+ st.success(f"Test order executed: {symbol} {side.value} {quantity} shares")
111
+ st.rerun()
112
+ else:
113
+ st.error("Failed to execute test order")
114
+
115
+ except Exception as e:
116
+ st.error(f"Error placing order: {e}")
117
+ else:
118
+ st.error("Please enter a symbol")
119
+
120
+ with col2:
121
+ st.markdown("#### Current Positions")
122
+
123
+ positions = trading_service.get_portfolio_positions(portfolio.id)
124
+ if positions:
125
+ positions_data = []
126
+ for pos in positions:
127
+ positions_data.append({
128
+ "Symbol": pos.symbol,
129
+ "Quantity": pos.quantity,
130
+ "Side": pos.side.value,
131
+ "Avg Price": f"${pos.average_price:.2f}",
132
+ "Current Price": f"${pos.current_price:.2f}",
133
+ "Market Value": f"${pos.market_value:,.2f}",
134
+ "P&L": f"${pos.unrealized_pnl:,.2f}",
135
+ "P&L %": f"{pos.unrealized_pnl_pct:.2f}%",
136
+ "Weight": f"{pos.weight:.1%}",
137
+ })
138
+
139
+ positions_df = pd.DataFrame(positions_data)
140
+ st.dataframe(positions_df, use_container_width=True)
141
+ else:
142
+ st.info("No positions found")
143
+
144
+ # Market simulation
145
+ st.subheader("🎲 Market Simulation")
146
+
147
+ col1, col2, col3 = st.columns(3)
148
+
149
+ with col1:
150
+ if st.button("Simulate 1 Day", help="Simulate market movement for 1 day"):
151
+ with st.spinner("Simulating market movement..."):
152
+ paper_engine.simulate_market_movement(portfolio.id, days=1)
153
+ st.success("Market simulation completed!")
154
+ st.rerun()
155
+
156
+ with col2:
157
+ if st.button("Simulate 1 Week", help="Simulate market movement for 1 week"):
158
+ with st.spinner("Simulating market movement..."):
159
+ paper_engine.simulate_market_movement(portfolio.id, days=7)
160
+ st.success("Market simulation completed!")
161
+ st.rerun()
162
+
163
+ with col3:
164
+ if st.button("Reset Portfolio", help="Reset portfolio to initial state"):
165
+ if st.session_state.get("confirm_reset", False):
166
+ # Reset portfolio
167
+ portfolio.current_value = portfolio.initial_capital
168
+ portfolio.cash_balance = portfolio.initial_capital
169
+ portfolio.total_return = 0.0
170
+ portfolio.total_return_pct = 0.0
171
+
172
+ # Clear positions
173
+ trading_service.db.query(trading_service.db.query(Position).filter(
174
+ Position.portfolio_id == portfolio.id
175
+ ).first().__class__).filter(
176
+ Position.portfolio_id == portfolio.id
177
+ ).delete()
178
+
179
+ trading_service.db.commit()
180
+ st.success("Portfolio reset successfully!")
181
+ st.session_state.confirm_reset = False
182
+ st.rerun()
183
+ else:
184
+ st.session_state.confirm_reset = True
185
+ st.warning("Click again to confirm reset")
186
+
187
+ # Performance chart
188
+ st.subheader("📊 Performance Chart")
189
+
190
+ performance_df = trading_service.get_portfolio_performance(portfolio.id, days=30)
191
+
192
+ if not performance_df.empty:
193
+ fig = make_subplots(
194
+ rows=2, cols=1,
195
+ subplot_titles=("Portfolio Value", "Daily Returns"),
196
+ vertical_spacing=0.1
197
+ )
198
+
199
+ # Portfolio value
200
+ fig.add_trace(
201
+ go.Scatter(
202
+ x=performance_df["date"],
203
+ y=performance_df["portfolio_value"],
204
+ mode="lines",
205
+ name="Portfolio Value",
206
+ line=dict(color="blue")
207
+ ),
208
+ row=1, col=1
209
+ )
210
+
211
+ # Daily returns
212
+ fig.add_trace(
213
+ go.Bar(
214
+ x=performance_df["date"],
215
+ y=performance_df["daily_return_pct"],
216
+ name="Daily Return %",
217
+ marker_color=["green" if x >= 0 else "red" for x in performance_df["daily_return_pct"]]
218
+ ),
219
+ row=2, col=1
220
+ )
221
+
222
+ fig.update_layout(height=600, showlegend=True)
223
+ fig.update_xaxes(title_text="Date", row=2, col=1)
224
+ fig.update_yaxes(title_text="Portfolio Value ($)", row=1, col=1)
225
+ fig.update_yaxes(title_text="Daily Return (%)", row=2, col=1)
226
+
227
+ st.plotly_chart(fig, use_container_width=True)
228
+ else:
229
+ st.info("No performance data available yet. Start trading to see your performance!")
230
+
231
+ # Recent orders
232
+ st.subheader("📋 Recent Test Orders")
233
+
234
+ orders = trading_service.get_portfolio_orders(portfolio.id)
235
+ if orders:
236
+ orders_data = []
237
+ for order in orders[:10]: # Last 10 orders
238
+ orders_data.append({
239
+ "Symbol": order.symbol,
240
+ "Side": order.side.value,
241
+ "Type": order.order_type.value,
242
+ "Quantity": order.quantity,
243
+ "Status": order.status.value,
244
+ "Price": f"${order.average_fill_price:.2f}" if order.average_fill_price else "-",
245
+ "Created": order.created_at.strftime("%Y-%m-%d %H:%M"),
246
+ "Filled": order.filled_at.strftime("%Y-%m-%d %H:%M") if order.filled_at else "-",
247
+ })
248
+
249
+ orders_df = pd.DataFrame(orders_data)
250
+ st.dataframe(orders_df, use_container_width=True)
251
+ else:
252
+ st.info("No test orders found")
253
+
254
+ # Backtesting section
255
+ st.subheader("🔬 Backtesting")
256
+
257
+ with st.expander("Run Historical Backtest", expanded=False):
258
+ col1, col2 = st.columns(2)
259
+
260
+ with col1:
261
+ start_date = st.date_input(
262
+ "Start Date",
263
+ value=datetime.now() - timedelta(days=365),
264
+ max_value=datetime.now() - timedelta(days=1)
265
+ )
266
+
267
+ with col2:
268
+ end_date = st.date_input(
269
+ "End Date",
270
+ value=datetime.now() - timedelta(days=1),
271
+ max_value=datetime.now()
272
+ )
273
+
274
+ initial_capital = st.number_input(
275
+ "Initial Capital",
276
+ min_value=1000,
277
+ value=100000,
278
+ step=10000
279
+ )
280
+
281
+ if st.button("Run Backtest", type="primary"):
282
+ with st.spinner("Running backtest..."):
283
+ try:
284
+ results = paper_engine.run_backtest(
285
+ portfolio_id=portfolio.id,
286
+ start_date=datetime.combine(start_date, datetime.min.time()),
287
+ end_date=datetime.combine(end_date, datetime.min.time()),
288
+ initial_capital=initial_capital
289
+ )
290
+
291
+ st.success("Backtest completed!")
292
+
293
+ # Display results
294
+ col1, col2, col3, col4 = st.columns(4)
295
+
296
+ with col1:
297
+ st.metric("Initial Capital", f"${results['initial_capital']:,.2f}")
298
+ with col2:
299
+ st.metric("Final Value", f"${results['final_value']:,.2f}")
300
+ with col3:
301
+ st.metric("Total Return", f"${results['total_return']:,.2f}")
302
+ with col4:
303
+ st.metric("Total Return %", f"{results['total_return_pct']:.2f}%")
304
+
305
+ except Exception as e:
306
+ st.error(f"Backtest failed: {e}")
307
+
308
+ # Portfolio actions
309
+ st.subheader("⚙️ Portfolio Actions")
310
+
311
+ col1, col2, col3 = st.columns(3)
312
+
313
+ with col1:
314
+ if st.button("Sync with Market", help="Update all positions with current market prices"):
315
+ with st.spinner("Syncing with market..."):
316
+ paper_engine.simulate_market_movement(portfolio.id, days=0)
317
+ st.success("Portfolio synced with current market prices!")
318
+ st.rerun()
319
+
320
+ with col2:
321
+ if st.button("Export Data", help="Export portfolio data to CSV"):
322
+ # Export portfolio data
323
+ positions = trading_service.get_portfolio_positions(portfolio.id)
324
+ if positions:
325
+ positions_data = []
326
+ for pos in positions:
327
+ positions_data.append({
328
+ "Symbol": pos.symbol,
329
+ "Quantity": pos.quantity,
330
+ "Side": pos.side.value,
331
+ "Average Price": float(pos.average_price),
332
+ "Current Price": float(pos.current_price),
333
+ "Market Value": float(pos.market_value),
334
+ "Unrealized P&L": float(pos.unrealized_pnl),
335
+ "Unrealized P&L %": pos.unrealized_pnl_pct,
336
+ "Weight": pos.weight,
337
+ })
338
+
339
+ positions_df = pd.DataFrame(positions_data)
340
+ csv = positions_df.to_csv(index=False)
341
+ st.download_button(
342
+ label="Download Positions CSV",
343
+ data=csv,
344
+ file_name=f"test_portfolio_positions_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
345
+ mime="text/csv"
346
+ )
347
+ else:
348
+ st.info("No positions to export")
349
+
350
+ with col3:
351
+ if st.button("Delete Portfolio", help="Delete this test portfolio"):
352
+ if st.session_state.get("confirm_delete", False):
353
+ # Delete portfolio
354
+ trading_service.db.delete(portfolio)
355
+ trading_service.db.commit()
356
+ st.session_state.test_portfolio_id = None
357
+ st.session_state.confirm_delete = False
358
+ st.success("Test portfolio deleted!")
359
+ st.rerun()
360
+ else:
361
+ st.session_state.confirm_delete = True
362
+ st.warning("Click again to confirm deletion")
363
+
364
+ except Exception as e:
365
+ st.error(f"Error loading test portfolio: {e}")
366
+ logger.error(f"Test portfolio error: {e}")
367
+
368
+
369
+ # Import Position for the reset functionality
370
+ try:
371
+ from mcli.ml.trading.models import Position
372
+ except ImportError:
373
+ Position = None