mcli-framework 7.3.1__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.

@@ -1,6 +1,7 @@
1
1
  """Trading dashboard page for portfolio management and trade execution"""
2
2
 
3
3
  import logging
4
+ import warnings
4
5
  from datetime import datetime, timedelta
5
6
  from typing import Dict, List, Optional
6
7
  from uuid import UUID
@@ -11,6 +12,12 @@ import plotly.graph_objects as go
11
12
  from plotly.subplots import make_subplots
12
13
  import streamlit as st
13
14
 
15
+ # Suppress Streamlit warnings when used outside runtime context
16
+ warnings.filterwarnings("ignore", message=".*missing ScriptRunContext.*")
17
+ warnings.filterwarnings("ignore", message=".*No runtime found.*")
18
+ warnings.filterwarnings("ignore", message=".*Session state does not function.*")
19
+ warnings.filterwarnings("ignore", message=".*to view this Streamlit app.*")
20
+
14
21
  # Try to import trading dependencies with fallbacks
15
22
  try:
16
23
  from sqlalchemy.orm import Session
@@ -64,6 +71,9 @@ def show_trading_dashboard():
64
71
  """Main trading dashboard page"""
65
72
  st.title("📈 Trading Dashboard")
66
73
  st.markdown("Manage your portfolios and execute trades based on politician trading insights")
74
+
75
+ # Add a simple test to ensure the page is rendering
76
+ st.info("📋 Page loaded successfully - Trading Dashboard functionality is available")
67
77
 
68
78
  # Check if trading dependencies are available
69
79
  if not HAS_TRADING_DEPS:
@@ -215,7 +225,7 @@ def show_trading_overview():
215
225
  labels={"total_return_pct": "Total Return (%)", "date": "Date"}
216
226
  )
217
227
  fig.update_layout(height=400)
218
- st.plotly_chart(fig, use_container_width=True)
228
+ st.plotly_chart(fig, config={"displayModeBar": True}, use_container_width=True)
219
229
  else:
220
230
  st.info("No performance data available yet. Start trading to see your performance!")
221
231
 
@@ -570,7 +580,7 @@ def show_performance_page():
570
580
  fig.update_yaxes(title_text="Portfolio Value ($)", row=1, col=1)
571
581
  fig.update_yaxes(title_text="Daily Return (%)", row=2, col=1)
572
582
 
573
- st.plotly_chart(fig, use_container_width=True)
583
+ st.plotly_chart(fig, config={"displayModeBar": True}, use_container_width=True)
574
584
  else:
575
585
  st.info("No performance data available yet")
576
586
 
@@ -667,38 +677,77 @@ def show_signals_page():
667
677
 
668
678
  def show_settings_page():
669
679
  """Show trading settings page"""
680
+ import os
681
+
670
682
  st.header("⚙️ Trading Settings")
671
-
683
+
672
684
  st.subheader("Alpaca API Configuration")
673
-
674
- with st.form("alpaca_config"):
675
- col1, col2 = st.columns(2)
676
-
677
- with col1:
678
- api_key = st.text_input("API Key", type="password", help="Your Alpaca API key")
679
- base_url = st.selectbox("Environment", ["Paper Trading", "Live Trading"])
680
-
681
- with col2:
682
- secret_key = st.text_input("Secret Key", type="password", help="Your Alpaca secret key")
683
- risk_level = st.selectbox("Risk Level", [RiskLevel.CONSERVATIVE, RiskLevel.MODERATE, RiskLevel.AGGRESSIVE])
684
-
685
- max_position_size = st.slider("Max Position Size (%)", 1, 50, 10, help="Maximum percentage of portfolio per position")
686
- max_portfolio_risk = st.slider("Max Portfolio Risk (%)", 5, 100, 20, help="Maximum portfolio risk percentage")
687
-
688
- if st.form_submit_button("Save Settings", type="primary"):
689
- st.success("Settings saved successfully!")
690
-
685
+
686
+ # Check current configuration from environment
687
+ api_key_configured = bool(os.getenv("ALPACA_API_KEY"))
688
+ secret_key_configured = bool(os.getenv("ALPACA_SECRET_KEY"))
689
+ base_url = os.getenv("ALPACA_BASE_URL", "https://paper-api.alpaca.markets")
690
+ is_paper = "paper" in base_url.lower()
691
+
692
+ # Show current configuration status
693
+ st.info("📝 **Configuration Status**")
694
+ col1, col2, col3 = st.columns(3)
695
+
696
+ with col1:
697
+ if api_key_configured:
698
+ st.success(" API Key Configured")
699
+ # Show masked version
700
+ api_key_value = os.getenv("ALPACA_API_KEY", "")
701
+ if len(api_key_value) > 8:
702
+ masked_key = api_key_value[:4] + "..." + api_key_value[-4:]
703
+ st.code(masked_key)
704
+ else:
705
+ st.error("❌ API Key Not Set")
706
+
707
+ with col2:
708
+ if secret_key_configured:
709
+ st.success("✅ Secret Key Configured")
710
+ else:
711
+ st.error("❌ Secret Key Not Set")
712
+
713
+ with col3:
714
+ st.metric("Environment", "Paper Trading" if is_paper else "Live Trading")
715
+
716
+ st.markdown("---")
717
+
718
+ # Configuration instructions
719
+ with st.expander("🔧 How to Configure Alpaca API Keys"):
720
+ st.markdown("""
721
+ ### Setting up Alpaca API Credentials
722
+
723
+ 1. **Get your API keys from Alpaca:**
724
+ - Visit [Alpaca Dashboard](https://app.alpaca.markets/paper/dashboard/overview)
725
+ - Go to "Your API Keys" section
726
+ - Generate new API keys if needed
727
+
728
+ 2. **Add keys to your `.env` file:**
729
+ ```bash
730
+ ALPACA_API_KEY=your_api_key_here
731
+ ALPACA_SECRET_KEY=your_secret_key_here
732
+ ALPACA_BASE_URL=https://paper-api.alpaca.markets # For paper trading
733
+ ```
734
+
735
+ 3. **Restart the Streamlit app** to load the new configuration
736
+
737
+ **Current Configuration File:** `/Users/lefv/repos/mcli/.env`
738
+ """)
739
+
691
740
  st.subheader("Risk Management")
692
-
741
+
693
742
  col1, col2 = st.columns(2)
694
-
743
+
695
744
  with col1:
696
745
  st.metric("Current Max Position Size", "10%")
697
746
  st.metric("Current Max Portfolio Risk", "20%")
698
-
747
+
699
748
  with col2:
700
749
  st.metric("Active Risk Level", "Moderate")
701
- st.metric("Paper Trading", "Enabled")
750
+ st.metric("Paper Trading", "Enabled" if is_paper else "Disabled")
702
751
 
703
752
  st.subheader("Portfolio Alerts")
704
753
 
@@ -711,4 +760,9 @@ def show_settings_page():
711
760
  ]
712
761
 
713
762
  for alert_type in alert_types:
714
- st.checkbox(alert_type, value=True)
763
+ st.checkbox(alert_type, value=True)
764
+
765
+
766
+ # Module-level execution only when run directly (not when imported)
767
+ if __name__ == "__main__":
768
+ show_trading_dashboard()
@@ -0,0 +1,297 @@
1
+ """Utility functions using streamlit-extras for enhanced dashboard UI"""
2
+
3
+ import streamlit as st
4
+
5
+ # Try to import streamlit-extras components
6
+ HAS_EXTRAS = True
7
+ try:
8
+ from streamlit_extras.metric_cards import style_metric_cards
9
+ from streamlit_extras.badges import badge
10
+ from streamlit_extras.colored_header import colored_header
11
+ from streamlit_extras.card import card
12
+ from streamlit_extras.stoggle import stoggle
13
+ from streamlit_extras.grid import grid
14
+ from streamlit_extras.add_vertical_space import add_vertical_space
15
+ from streamlit_extras.stylable_container import stylable_container
16
+ except ImportError:
17
+ HAS_EXTRAS = False
18
+ style_metric_cards = None
19
+ badge = None
20
+ colored_header = None
21
+ card = None
22
+ stoggle = None
23
+ grid = None
24
+ add_vertical_space = None
25
+ stylable_container = None
26
+
27
+
28
+ def enhanced_metrics(metrics_data: list, use_container_width: bool = True):
29
+ """
30
+ Display enhanced metric cards with styling from streamlit-extras
31
+
32
+ Args:
33
+ metrics_data: List of dicts with keys: label, value, delta (optional)
34
+ use_container_width: Whether to use full container width
35
+
36
+ Example:
37
+ enhanced_metrics([
38
+ {"label": "Total Transactions", "value": "1,234", "delta": "+12%"},
39
+ {"label": "Portfolio Value", "value": "$50,000", "delta": "-2.3%"},
40
+ ])
41
+ """
42
+ if not HAS_EXTRAS:
43
+ # Fallback to standard metrics
44
+ cols = st.columns(len(metrics_data))
45
+ for i, metric in enumerate(metrics_data):
46
+ with cols[i]:
47
+ st.metric(
48
+ label=metric["label"],
49
+ value=metric["value"],
50
+ delta=metric.get("delta")
51
+ )
52
+ return
53
+
54
+ # Use streamlit-extras styled metrics
55
+ cols = st.columns(len(metrics_data))
56
+ for i, metric in enumerate(metrics_data):
57
+ with cols[i]:
58
+ st.metric(
59
+ label=metric["label"],
60
+ value=metric["value"],
61
+ delta=metric.get("delta")
62
+ )
63
+ style_metric_cards()
64
+
65
+
66
+ def status_badge(label: str, url: str = None):
67
+ """
68
+ Display a status badge
69
+
70
+ Args:
71
+ label: Badge text (e.g., "Live", "Production", "Beta")
72
+ url: Optional URL to link to
73
+ """
74
+ if not HAS_EXTRAS:
75
+ st.markdown(f"**{label}**")
76
+ return
77
+
78
+ badge(type="success", name=label, url=url)
79
+
80
+
81
+ def section_header(label: str, description: str = None, divider: str = "rainbow"):
82
+ """
83
+ Display a colored section header with optional description
84
+
85
+ Args:
86
+ label: Header text
87
+ description: Optional description text
88
+ divider: Color of divider line
89
+ """
90
+ if not HAS_EXTRAS:
91
+ st.header(label)
92
+ if description:
93
+ st.markdown(description)
94
+ st.divider()
95
+ return
96
+
97
+ colored_header(
98
+ label=label,
99
+ description=description or "",
100
+ color_name=divider
101
+ )
102
+
103
+
104
+ def info_card(title: str, text: str, image: str = None, url: str = None,
105
+ has_button: bool = False, button_text: str = "Learn More"):
106
+ """
107
+ Display an information card
108
+
109
+ Args:
110
+ title: Card title
111
+ text: Card content
112
+ image: Optional image URL
113
+ url: Optional URL to link to
114
+ has_button: Whether to show a button
115
+ button_text: Button text if has_button is True
116
+ """
117
+ if not HAS_EXTRAS:
118
+ with st.container():
119
+ st.subheader(title)
120
+ if image:
121
+ st.image(image)
122
+ st.markdown(text)
123
+ if url and has_button:
124
+ st.link_button(button_text, url)
125
+ return
126
+
127
+ kwargs = {
128
+ "title": title,
129
+ "text": text,
130
+ }
131
+ if image:
132
+ kwargs["image"] = image
133
+ if url:
134
+ kwargs["url"] = url
135
+
136
+ card(**kwargs)
137
+
138
+
139
+ def collapsible_section(label: str, content_fn, default_open: bool = False):
140
+ """
141
+ Create a collapsible toggle section
142
+
143
+ Args:
144
+ label: Section label
145
+ content_fn: Function to call to render content
146
+ default_open: Whether section starts open
147
+ """
148
+ if not HAS_EXTRAS:
149
+ with st.expander(label, expanded=default_open):
150
+ content_fn()
151
+ return
152
+
153
+ stoggle(label, content_fn)
154
+
155
+
156
+ def dashboard_grid(num_cols: int = 3, gap: str = "medium"):
157
+ """
158
+ Create a responsive grid layout
159
+
160
+ Args:
161
+ num_cols: Number of columns
162
+ gap: Gap size between columns
163
+
164
+ Returns:
165
+ Grid object for use in with statement
166
+ """
167
+ if not HAS_EXTRAS:
168
+ return st.columns(num_cols)
169
+
170
+ return grid(num_cols, gap=gap)
171
+
172
+
173
+ def vertical_space(lines: int = 1):
174
+ """
175
+ Add vertical spacing
176
+
177
+ Args:
178
+ lines: Number of lines to add
179
+ """
180
+ if not HAS_EXTRAS:
181
+ for _ in range(lines):
182
+ st.write("")
183
+ return
184
+
185
+ add_vertical_space(lines)
186
+
187
+
188
+ def styled_container(key: str, css_styles: str):
189
+ """
190
+ Create a container with custom CSS styling
191
+
192
+ Args:
193
+ key: Unique key for the container
194
+ css_styles: CSS styles to apply
195
+
196
+ Returns:
197
+ Context manager for styled container
198
+ """
199
+ if not HAS_EXTRAS:
200
+ return st.container()
201
+
202
+ return stylable_container(
203
+ key=key,
204
+ css_styles=css_styles
205
+ )
206
+
207
+
208
+ def trading_status_card(status: str, portfolio_value: float, daily_pnl: float,
209
+ positions: int, cash: float):
210
+ """
211
+ Display a trading status summary card
212
+
213
+ Args:
214
+ status: Trading status (e.g., "Active", "Paused")
215
+ portfolio_value: Current portfolio value
216
+ daily_pnl: Daily profit/loss
217
+ positions: Number of open positions
218
+ cash: Available cash
219
+ """
220
+ status_color = "🟢" if status == "Active" else "🔴"
221
+ pnl_emoji = "📈" if daily_pnl >= 0 else "📉"
222
+ pnl_sign = "+" if daily_pnl >= 0 else ""
223
+
224
+ section_header(
225
+ f"{status_color} Trading Status: {status}",
226
+ f"Real-time portfolio monitoring and execution",
227
+ divider="blue"
228
+ )
229
+
230
+ enhanced_metrics([
231
+ {
232
+ "label": "Portfolio Value",
233
+ "value": f"${portfolio_value:,.2f}",
234
+ "delta": f"{pnl_sign}${daily_pnl:,.2f}"
235
+ },
236
+ {
237
+ "label": "Open Positions",
238
+ "value": str(positions),
239
+ },
240
+ {
241
+ "label": "Available Cash",
242
+ "value": f"${cash:,.2f}",
243
+ },
244
+ ])
245
+
246
+
247
+ def data_quality_indicators(total_records: int, clean_records: int,
248
+ errors: int, last_update: str):
249
+ """
250
+ Display data quality indicators
251
+
252
+ Args:
253
+ total_records: Total number of records
254
+ clean_records: Number of clean records
255
+ errors: Number of errors
256
+ last_update: Last update timestamp
257
+ """
258
+ quality_pct = (clean_records / total_records * 100) if total_records > 0 else 0
259
+
260
+ section_header(
261
+ "📊 Data Quality Metrics",
262
+ f"Last updated: {last_update}",
263
+ divider="green"
264
+ )
265
+
266
+ enhanced_metrics([
267
+ {
268
+ "label": "Total Records",
269
+ "value": f"{total_records:,}",
270
+ },
271
+ {
272
+ "label": "Data Quality",
273
+ "value": f"{quality_pct:.1f}%",
274
+ "delta": f"{clean_records:,} clean"
275
+ },
276
+ {
277
+ "label": "Errors",
278
+ "value": str(errors),
279
+ "delta": f"{(errors/total_records*100):.2f}%" if total_records > 0 else "0%"
280
+ },
281
+ ])
282
+
283
+
284
+ # Export available components
285
+ __all__ = [
286
+ 'HAS_EXTRAS',
287
+ 'enhanced_metrics',
288
+ 'status_badge',
289
+ 'section_header',
290
+ 'info_card',
291
+ 'collapsible_section',
292
+ 'dashboard_grid',
293
+ 'vertical_space',
294
+ 'styled_container',
295
+ 'trading_status_card',
296
+ 'data_quality_indicators',
297
+ ]
@@ -2,11 +2,18 @@
2
2
 
3
3
  import os
4
4
  import logging
5
+ import warnings
5
6
  from typing import List, Optional
6
7
  import pandas as pd
7
8
  import streamlit as st
8
9
  from supabase import Client, create_client
9
10
 
11
+ # Suppress Streamlit warnings when used outside runtime context
12
+ warnings.filterwarnings("ignore", message=".*missing ScriptRunContext.*")
13
+ warnings.filterwarnings("ignore", message=".*No runtime found.*")
14
+ warnings.filterwarnings("ignore", message=".*Session state does not function.*")
15
+ warnings.filterwarnings("ignore", message=".*to view this Streamlit app.*")
16
+
10
17
  logger = logging.getLogger(__name__)
11
18
 
12
19
 
@@ -0,0 +1,34 @@
1
+ """Warning suppression utilities for Streamlit components used outside runtime context"""
2
+
3
+ import warnings
4
+ import logging
5
+ from contextlib import contextmanager
6
+
7
+
8
+ @contextmanager
9
+ def suppress_streamlit_warnings():
10
+ """Context manager to suppress Streamlit warnings when used outside runtime context"""
11
+ # Suppress specific Streamlit warnings
12
+ with warnings.catch_warnings():
13
+ warnings.filterwarnings("ignore", message=".*missing ScriptRunContext.*")
14
+ warnings.filterwarnings("ignore", message=".*No runtime found.*")
15
+ warnings.filterwarnings("ignore", message=".*Session state does not function.*")
16
+ warnings.filterwarnings("ignore", message=".*to view this Streamlit app.*")
17
+
18
+ # Also suppress logging warnings from Streamlit
19
+ streamlit_logger = logging.getLogger("streamlit")
20
+ original_level = streamlit_logger.level
21
+ streamlit_logger.setLevel(logging.ERROR)
22
+
23
+ try:
24
+ yield
25
+ finally:
26
+ streamlit_logger.setLevel(original_level)
27
+
28
+
29
+ def suppress_streamlit_warnings_decorator(func):
30
+ """Decorator to suppress Streamlit warnings for a function"""
31
+ def wrapper(*args, **kwargs):
32
+ with suppress_streamlit_warnings():
33
+ return func(*args, **kwargs)
34
+ return wrapper