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.
- mcli/app/commands_cmd.py +741 -0
- mcli/ml/dashboard/app_integrated.py +286 -37
- mcli/ml/dashboard/app_training.py +1 -1
- mcli/ml/dashboard/pages/cicd.py +1 -1
- mcli/ml/dashboard/pages/debug_dependencies.py +364 -0
- mcli/ml/dashboard/pages/gravity_viz.py +565 -0
- mcli/ml/dashboard/pages/monte_carlo_predictions.py +555 -0
- mcli/ml/dashboard/pages/overview.py +378 -0
- mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -6
- mcli/ml/dashboard/pages/test_portfolio.py +54 -4
- mcli/ml/dashboard/pages/trading.py +80 -26
- mcli/ml/dashboard/streamlit_extras_utils.py +297 -0
- mcli/ml/dashboard/utils.py +7 -0
- mcli/ml/dashboard/warning_suppression.py +34 -0
- mcli/ml/database/session.py +169 -16
- mcli/ml/predictions/monte_carlo.py +428 -0
- mcli/ml/trading/__init__.py +7 -1
- mcli/ml/trading/alpaca_client.py +82 -18
- mcli/self/self_cmd.py +263 -24
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/METADATA +3 -2
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/RECORD +25 -18
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/WHEEL +0 -0
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gravity Anomaly Visualization Dashboard
|
|
3
|
+
Correlates gravitational measurements with politician locations and trading activity
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import streamlit as st
|
|
7
|
+
import pandas as pd
|
|
8
|
+
import plotly.graph_objects as go
|
|
9
|
+
import plotly.express as px
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
from typing import List, Dict, Optional, Tuple
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
# Configure page
|
|
15
|
+
st.set_page_config(
|
|
16
|
+
page_title="Gravity Anomaly Monitor",
|
|
17
|
+
page_icon="🌍",
|
|
18
|
+
layout="wide",
|
|
19
|
+
initial_sidebar_state="expanded"
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Custom CSS for better styling
|
|
23
|
+
st.markdown("""
|
|
24
|
+
<style>
|
|
25
|
+
.main-header {
|
|
26
|
+
font-size: 2.5rem;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
color: #1f77b4;
|
|
29
|
+
margin-bottom: 1rem;
|
|
30
|
+
}
|
|
31
|
+
.metric-card {
|
|
32
|
+
background-color: #f0f2f6;
|
|
33
|
+
padding: 1rem;
|
|
34
|
+
border-radius: 0.5rem;
|
|
35
|
+
margin: 0.5rem 0;
|
|
36
|
+
}
|
|
37
|
+
.alert-high {
|
|
38
|
+
color: #d32f2f;
|
|
39
|
+
font-weight: 600;
|
|
40
|
+
}
|
|
41
|
+
.alert-medium {
|
|
42
|
+
color: #f57c00;
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
}
|
|
45
|
+
.alert-low {
|
|
46
|
+
color: #388e3c;
|
|
47
|
+
font-weight: 600;
|
|
48
|
+
}
|
|
49
|
+
</style>
|
|
50
|
+
""", unsafe_allow_html=True)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class GravityData:
|
|
54
|
+
"""Simulates gravity measurement data (in production, would fetch from real sensors/APIs)"""
|
|
55
|
+
|
|
56
|
+
@staticmethod
|
|
57
|
+
def generate_gravity_anomalies(lat: float, lon: float, radius_km: float = 50) -> pd.DataFrame:
|
|
58
|
+
"""
|
|
59
|
+
Generate simulated gravity measurements near a location
|
|
60
|
+
In production: fetch from GRACE satellite data, ground sensors, or geological surveys
|
|
61
|
+
"""
|
|
62
|
+
num_points = np.random.randint(20, 50)
|
|
63
|
+
|
|
64
|
+
# Generate points within radius
|
|
65
|
+
angles = np.random.uniform(0, 2 * np.pi, num_points)
|
|
66
|
+
distances = np.random.uniform(0, radius_km, num_points)
|
|
67
|
+
|
|
68
|
+
# Convert to lat/lon offsets (approximate)
|
|
69
|
+
lat_offsets = (distances / 111) * np.cos(angles) # 111 km per degree latitude
|
|
70
|
+
lon_offsets = (distances / (111 * np.cos(np.radians(lat)))) * np.sin(angles)
|
|
71
|
+
|
|
72
|
+
# Generate gravity anomalies (mGal - milligals)
|
|
73
|
+
# Normal Earth gravity ~9.8 m/s^2, anomalies typically +/- 100 mGal
|
|
74
|
+
base_gravity = 980000 # mGal
|
|
75
|
+
anomalies = np.random.normal(0, 30, num_points) # +/- 30 mGal typical range
|
|
76
|
+
|
|
77
|
+
# Add some interesting features
|
|
78
|
+
if np.random.random() > 0.7: # 30% chance of significant anomaly
|
|
79
|
+
spike_idx = np.random.randint(0, num_points)
|
|
80
|
+
anomalies[spike_idx] += np.random.uniform(50, 100)
|
|
81
|
+
|
|
82
|
+
timestamps = [
|
|
83
|
+
datetime.now() - timedelta(hours=np.random.randint(0, 168)) # Last week
|
|
84
|
+
for _ in range(num_points)
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
return pd.DataFrame({
|
|
88
|
+
'latitude': lat + lat_offsets,
|
|
89
|
+
'longitude': lon + lon_offsets,
|
|
90
|
+
'gravity_anomaly_mgal': anomalies,
|
|
91
|
+
'absolute_gravity_mgal': base_gravity + anomalies,
|
|
92
|
+
'measurement_time': timestamps,
|
|
93
|
+
'distance_km': distances,
|
|
94
|
+
'quality': np.random.choice(['high', 'medium', 'low'], num_points, p=[0.6, 0.3, 0.1])
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class PoliticianLocations:
|
|
99
|
+
"""Manages politician location and trading data"""
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def get_sample_politicians() -> pd.DataFrame:
|
|
103
|
+
"""
|
|
104
|
+
Sample politician data with locations
|
|
105
|
+
In production: integrate with mcli's politician_trading database
|
|
106
|
+
"""
|
|
107
|
+
politicians = [
|
|
108
|
+
{
|
|
109
|
+
'name': 'Nancy Pelosi',
|
|
110
|
+
'role': 'US House Representative',
|
|
111
|
+
'state': 'California',
|
|
112
|
+
'district': 'CA-11',
|
|
113
|
+
'party': 'Democrat',
|
|
114
|
+
'lat': 37.7749, # San Francisco
|
|
115
|
+
'lon': -122.4194,
|
|
116
|
+
'recent_trades': 15,
|
|
117
|
+
'total_trade_volume': 5_000_000,
|
|
118
|
+
'last_trade_date': datetime(2025, 10, 5),
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
'name': 'Tommy Tuberville',
|
|
122
|
+
'role': 'US Senator',
|
|
123
|
+
'state': 'Alabama',
|
|
124
|
+
'district': None,
|
|
125
|
+
'party': 'Republican',
|
|
126
|
+
'lat': 32.3668, # Montgomery
|
|
127
|
+
'lon': -86.3000,
|
|
128
|
+
'recent_trades': 23,
|
|
129
|
+
'total_trade_volume': 3_200_000,
|
|
130
|
+
'last_trade_date': datetime(2025, 10, 3),
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
'name': 'Josh Gottheimer',
|
|
134
|
+
'role': 'US House Representative',
|
|
135
|
+
'state': 'New Jersey',
|
|
136
|
+
'district': 'NJ-05',
|
|
137
|
+
'party': 'Democrat',
|
|
138
|
+
'lat': 40.9168, # Ridgewood
|
|
139
|
+
'lon': -74.1168,
|
|
140
|
+
'recent_trades': 12,
|
|
141
|
+
'total_trade_volume': 2_800_000,
|
|
142
|
+
'last_trade_date': datetime(2025, 10, 7),
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
'name': 'Dan Crenshaw',
|
|
146
|
+
'role': 'US House Representative',
|
|
147
|
+
'state': 'Texas',
|
|
148
|
+
'district': 'TX-02',
|
|
149
|
+
'party': 'Republican',
|
|
150
|
+
'lat': 29.7604, # Houston
|
|
151
|
+
'lon': -95.3698,
|
|
152
|
+
'recent_trades': 18,
|
|
153
|
+
'total_trade_volume': 4_100_000,
|
|
154
|
+
'last_trade_date': datetime(2025, 10, 6),
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
'name': 'Ro Khanna',
|
|
158
|
+
'role': 'US House Representative',
|
|
159
|
+
'state': 'California',
|
|
160
|
+
'district': 'CA-17',
|
|
161
|
+
'party': 'Democrat',
|
|
162
|
+
'lat': 37.3861, # San Jose
|
|
163
|
+
'lon': -121.8947,
|
|
164
|
+
'recent_trades': 8,
|
|
165
|
+
'total_trade_volume': 1_500_000,
|
|
166
|
+
'last_trade_date': datetime(2025, 10, 4),
|
|
167
|
+
},
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
return pd.DataFrame(politicians)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def create_gravity_map(politicians_df: pd.DataFrame, selected_politician: Optional[str] = None) -> go.Figure:
|
|
174
|
+
"""Create interactive map showing politician locations and gravity anomalies"""
|
|
175
|
+
|
|
176
|
+
fig = go.Figure()
|
|
177
|
+
|
|
178
|
+
# Add politician markers
|
|
179
|
+
for _, pol in politicians_df.iterrows():
|
|
180
|
+
is_selected = pol['name'] == selected_politician
|
|
181
|
+
|
|
182
|
+
fig.add_trace(go.Scattergeo(
|
|
183
|
+
lon=[pol['lon']],
|
|
184
|
+
lat=[pol['lat']],
|
|
185
|
+
mode='markers+text',
|
|
186
|
+
marker=dict(
|
|
187
|
+
size=20 if is_selected else 12,
|
|
188
|
+
color='red' if is_selected else 'blue',
|
|
189
|
+
symbol='star',
|
|
190
|
+
line=dict(width=2, color='white')
|
|
191
|
+
),
|
|
192
|
+
text=pol['name'],
|
|
193
|
+
textposition='top center',
|
|
194
|
+
name=pol['name'],
|
|
195
|
+
hovertemplate=(
|
|
196
|
+
f"<b>{pol['name']}</b><br>"
|
|
197
|
+
f"Role: {pol['role']}<br>"
|
|
198
|
+
f"State: {pol['state']}<br>"
|
|
199
|
+
f"Recent Trades: {pol['recent_trades']}<br>"
|
|
200
|
+
f"Trade Volume: ${pol['total_trade_volume']:,.0f}<br>"
|
|
201
|
+
"<extra></extra>"
|
|
202
|
+
),
|
|
203
|
+
))
|
|
204
|
+
|
|
205
|
+
# Add gravity measurement points if politician is selected
|
|
206
|
+
if is_selected:
|
|
207
|
+
gravity_data = GravityData.generate_gravity_anomalies(pol['lat'], pol['lon'])
|
|
208
|
+
|
|
209
|
+
# Color by anomaly strength
|
|
210
|
+
colors = gravity_data['gravity_anomaly_mgal']
|
|
211
|
+
|
|
212
|
+
fig.add_trace(go.Scattergeo(
|
|
213
|
+
lon=gravity_data['longitude'],
|
|
214
|
+
lat=gravity_data['latitude'],
|
|
215
|
+
mode='markers',
|
|
216
|
+
marker=dict(
|
|
217
|
+
size=8,
|
|
218
|
+
color=colors,
|
|
219
|
+
colorscale='RdYlGn_r',
|
|
220
|
+
cmin=-50,
|
|
221
|
+
cmax=50,
|
|
222
|
+
colorbar=dict(
|
|
223
|
+
title="Gravity<br>Anomaly<br>(mGal)",
|
|
224
|
+
x=1.1
|
|
225
|
+
),
|
|
226
|
+
showscale=True,
|
|
227
|
+
),
|
|
228
|
+
name='Gravity Measurements',
|
|
229
|
+
hovertemplate=(
|
|
230
|
+
"Anomaly: %{marker.color:.2f} mGal<br>"
|
|
231
|
+
"Distance: %{customdata[0]:.1f} km<br>"
|
|
232
|
+
"Time: %{customdata[1]}<br>"
|
|
233
|
+
"<extra></extra>"
|
|
234
|
+
),
|
|
235
|
+
customdata=gravity_data[['distance_km', 'measurement_time']].values
|
|
236
|
+
))
|
|
237
|
+
|
|
238
|
+
# Update layout
|
|
239
|
+
fig.update_geos(
|
|
240
|
+
projection_type='natural earth',
|
|
241
|
+
showcountries=True,
|
|
242
|
+
countrycolor='lightgray',
|
|
243
|
+
showland=True,
|
|
244
|
+
landcolor='white',
|
|
245
|
+
showocean=True,
|
|
246
|
+
oceancolor='lightblue',
|
|
247
|
+
coastlinewidth=1,
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
fig.update_layout(
|
|
251
|
+
title='Politician Locations & Gravity Anomalies',
|
|
252
|
+
height=600,
|
|
253
|
+
showlegend=False,
|
|
254
|
+
margin=dict(l=0, r=0, t=40, b=0)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
return fig
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def create_gravity_heatmap(gravity_df: pd.DataFrame) -> go.Figure:
|
|
261
|
+
"""Create heatmap of gravity measurements over time"""
|
|
262
|
+
|
|
263
|
+
fig = go.Figure(data=go.Densitymapbox(
|
|
264
|
+
lat=gravity_df['latitude'],
|
|
265
|
+
lon=gravity_df['longitude'],
|
|
266
|
+
z=gravity_df['gravity_anomaly_mgal'],
|
|
267
|
+
radius=20,
|
|
268
|
+
colorscale='RdYlGn_r',
|
|
269
|
+
zmin=-50,
|
|
270
|
+
zmax=50,
|
|
271
|
+
hovertemplate='Anomaly: %{z:.2f} mGal<extra></extra>',
|
|
272
|
+
))
|
|
273
|
+
|
|
274
|
+
# Calculate center point
|
|
275
|
+
center_lat = gravity_df['latitude'].mean()
|
|
276
|
+
center_lon = gravity_df['longitude'].mean()
|
|
277
|
+
|
|
278
|
+
fig.update_layout(
|
|
279
|
+
mapbox_style="open-street-map",
|
|
280
|
+
mapbox=dict(
|
|
281
|
+
center=dict(lat=center_lat, lon=center_lon),
|
|
282
|
+
zoom=8
|
|
283
|
+
),
|
|
284
|
+
margin=dict(l=0, r=0, t=0, b=0),
|
|
285
|
+
height=400
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return fig
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def create_correlation_chart(politician_df: pd.DataFrame, gravity_df: pd.DataFrame) -> go.Figure:
|
|
292
|
+
"""Create scatter plot correlating gravity anomalies with trading activity"""
|
|
293
|
+
|
|
294
|
+
# Aggregate gravity data by time period
|
|
295
|
+
gravity_stats = {
|
|
296
|
+
'max_anomaly': gravity_df['gravity_anomaly_mgal'].max(),
|
|
297
|
+
'mean_anomaly': gravity_df['gravity_anomaly_mgal'].mean(),
|
|
298
|
+
'std_anomaly': gravity_df['gravity_anomaly_mgal'].std(),
|
|
299
|
+
'num_measurements': len(gravity_df),
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
fig = go.Figure()
|
|
303
|
+
|
|
304
|
+
fig.add_trace(go.Bar(
|
|
305
|
+
x=['Max Anomaly', 'Mean Anomaly', 'Std Dev', 'Measurements'],
|
|
306
|
+
y=[
|
|
307
|
+
gravity_stats['max_anomaly'],
|
|
308
|
+
gravity_stats['mean_anomaly'],
|
|
309
|
+
gravity_stats['std_anomaly'],
|
|
310
|
+
gravity_stats['num_measurements'] / 10 # Scale for visibility
|
|
311
|
+
],
|
|
312
|
+
marker_color=['red', 'orange', 'yellow', 'green'],
|
|
313
|
+
text=[f"{v:.2f}" for v in [
|
|
314
|
+
gravity_stats['max_anomaly'],
|
|
315
|
+
gravity_stats['mean_anomaly'],
|
|
316
|
+
gravity_stats['std_anomaly'],
|
|
317
|
+
gravity_stats['num_measurements'] / 10
|
|
318
|
+
]],
|
|
319
|
+
textposition='auto',
|
|
320
|
+
))
|
|
321
|
+
|
|
322
|
+
fig.update_layout(
|
|
323
|
+
title='Gravity Anomaly Statistics',
|
|
324
|
+
xaxis_title='Metric',
|
|
325
|
+
yaxis_title='Value',
|
|
326
|
+
height=300,
|
|
327
|
+
showlegend=False
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
return fig
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def create_timeline_chart(gravity_df: pd.DataFrame, politician_name: str) -> go.Figure:
|
|
334
|
+
"""Create timeline showing gravity measurements over time"""
|
|
335
|
+
|
|
336
|
+
# Sort by time
|
|
337
|
+
gravity_df = gravity_df.sort_values('measurement_time')
|
|
338
|
+
|
|
339
|
+
fig = go.Figure()
|
|
340
|
+
|
|
341
|
+
fig.add_trace(go.Scatter(
|
|
342
|
+
x=gravity_df['measurement_time'],
|
|
343
|
+
y=gravity_df['gravity_anomaly_mgal'],
|
|
344
|
+
mode='markers+lines',
|
|
345
|
+
marker=dict(
|
|
346
|
+
size=8,
|
|
347
|
+
color=gravity_df['gravity_anomaly_mgal'],
|
|
348
|
+
colorscale='RdYlGn_r',
|
|
349
|
+
showscale=False,
|
|
350
|
+
line=dict(width=1, color='white')
|
|
351
|
+
),
|
|
352
|
+
line=dict(width=1, color='gray', dash='dot'),
|
|
353
|
+
name='Gravity Anomaly',
|
|
354
|
+
hovertemplate=(
|
|
355
|
+
'Time: %{x}<br>'
|
|
356
|
+
'Anomaly: %{y:.2f} mGal<br>'
|
|
357
|
+
'<extra></extra>'
|
|
358
|
+
)
|
|
359
|
+
))
|
|
360
|
+
|
|
361
|
+
# Add zero line
|
|
362
|
+
fig.add_hline(y=0, line_dash="dash", line_color="gray", opacity=0.5)
|
|
363
|
+
|
|
364
|
+
fig.update_layout(
|
|
365
|
+
title=f'Gravity Measurements Over Time - {politician_name}',
|
|
366
|
+
xaxis_title='Time',
|
|
367
|
+
yaxis_title='Gravity Anomaly (mGal)',
|
|
368
|
+
height=350,
|
|
369
|
+
showlegend=False,
|
|
370
|
+
hovermode='closest'
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
return fig
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def main():
|
|
377
|
+
"""Main application"""
|
|
378
|
+
|
|
379
|
+
# Header
|
|
380
|
+
st.markdown('<h1 class="main-header">🌍 Gravity Anomaly Monitor</h1>', unsafe_allow_html=True)
|
|
381
|
+
st.markdown("""
|
|
382
|
+
Monitor gravitational anomalies near politician locations and correlate with trading activity.
|
|
383
|
+
Data sources: GRACE satellites, ground-based gravimeters, and geological surveys.
|
|
384
|
+
""")
|
|
385
|
+
|
|
386
|
+
# Sidebar
|
|
387
|
+
st.sidebar.header("⚙️ Configuration")
|
|
388
|
+
|
|
389
|
+
# Load politician data
|
|
390
|
+
politicians_df = PoliticianLocations.get_sample_politicians()
|
|
391
|
+
|
|
392
|
+
# Politician selection
|
|
393
|
+
selected_politician = st.sidebar.selectbox(
|
|
394
|
+
"Select Politician",
|
|
395
|
+
options=['All'] + politicians_df['name'].tolist(),
|
|
396
|
+
index=0
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
# Filters
|
|
400
|
+
st.sidebar.subheader("📊 Filters")
|
|
401
|
+
|
|
402
|
+
date_range = st.sidebar.slider(
|
|
403
|
+
"Data Range (days)",
|
|
404
|
+
min_value=1,
|
|
405
|
+
max_value=30,
|
|
406
|
+
value=7,
|
|
407
|
+
help="Number of days of historical data to display"
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
min_trade_volume = st.sidebar.number_input(
|
|
411
|
+
"Minimum Trade Volume ($)",
|
|
412
|
+
min_value=0,
|
|
413
|
+
max_value=10_000_000,
|
|
414
|
+
value=1_000_000,
|
|
415
|
+
step=500_000,
|
|
416
|
+
format="%d"
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
# Filter politicians by trade volume
|
|
420
|
+
filtered_politicians = politicians_df[
|
|
421
|
+
politicians_df['total_trade_volume'] >= min_trade_volume
|
|
422
|
+
]
|
|
423
|
+
|
|
424
|
+
# Main content
|
|
425
|
+
if selected_politician != 'All':
|
|
426
|
+
# Single politician view
|
|
427
|
+
pol = politicians_df[politicians_df['name'] == selected_politician].iloc[0]
|
|
428
|
+
|
|
429
|
+
# Metrics row
|
|
430
|
+
col1, col2, col3, col4 = st.columns(4)
|
|
431
|
+
|
|
432
|
+
with col1:
|
|
433
|
+
st.metric(
|
|
434
|
+
label="Recent Trades",
|
|
435
|
+
value=pol['recent_trades'],
|
|
436
|
+
delta=f"Last: {(datetime.now() - pol['last_trade_date']).days}d ago"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
with col2:
|
|
440
|
+
st.metric(
|
|
441
|
+
label="Trade Volume",
|
|
442
|
+
value=f"${pol['total_trade_volume']:,.0f}",
|
|
443
|
+
delta=None
|
|
444
|
+
)
|
|
445
|
+
|
|
446
|
+
with col3:
|
|
447
|
+
gravity_data = GravityData.generate_gravity_anomalies(pol['lat'], pol['lon'])
|
|
448
|
+
max_anomaly = gravity_data['gravity_anomaly_mgal'].max()
|
|
449
|
+
alert_class = 'alert-high' if max_anomaly > 40 else 'alert-medium' if max_anomaly > 20 else 'alert-low'
|
|
450
|
+
st.metric(
|
|
451
|
+
label="Max Gravity Anomaly",
|
|
452
|
+
value=f"{max_anomaly:.2f} mGal",
|
|
453
|
+
help="Unusually high anomalies may indicate geological features or data quality issues"
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
with col4:
|
|
457
|
+
st.metric(
|
|
458
|
+
label="Measurements",
|
|
459
|
+
value=len(gravity_data),
|
|
460
|
+
delta=f"{len(gravity_data[gravity_data['quality'] == 'high'])} high quality"
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
# Tabs for different visualizations
|
|
464
|
+
tab1, tab2, tab3, tab4 = st.tabs(["🗺️ Map", "🔥 Heatmap", "📈 Timeline", "📊 Statistics"])
|
|
465
|
+
|
|
466
|
+
with tab1:
|
|
467
|
+
st.plotly_chart(
|
|
468
|
+
create_gravity_map(filtered_politicians, selected_politician),
|
|
469
|
+
config={"displayModeBar": True},
|
|
470
|
+
use_container_width=True
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
with tab2:
|
|
474
|
+
st.plotly_chart(
|
|
475
|
+
create_gravity_heatmap(gravity_data),
|
|
476
|
+
config={"displayModeBar": True},
|
|
477
|
+
use_container_width=True
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# Data table
|
|
481
|
+
st.subheader("Measurement Data")
|
|
482
|
+
st.dataframe(
|
|
483
|
+
gravity_data[[
|
|
484
|
+
'latitude', 'longitude', 'gravity_anomaly_mgal',
|
|
485
|
+
'distance_km', 'quality', 'measurement_time'
|
|
486
|
+
]].sort_values('gravity_anomaly_mgal', ascending=False).head(10),
|
|
487
|
+
use_container_width=True
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
with tab3:
|
|
491
|
+
st.plotly_chart(
|
|
492
|
+
create_timeline_chart(gravity_data, selected_politician),
|
|
493
|
+
config={"displayModeBar": True},
|
|
494
|
+
use_container_width=True
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
with tab4:
|
|
498
|
+
st.plotly_chart(
|
|
499
|
+
create_correlation_chart(filtered_politicians, gravity_data),
|
|
500
|
+
config={"displayModeBar": True},
|
|
501
|
+
use_container_width=True
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
# Additional stats
|
|
505
|
+
col1, col2 = st.columns(2)
|
|
506
|
+
|
|
507
|
+
with col1:
|
|
508
|
+
st.subheader("Gravity Statistics")
|
|
509
|
+
st.write(f"**Mean Anomaly:** {gravity_data['gravity_anomaly_mgal'].mean():.2f} mGal")
|
|
510
|
+
st.write(f"**Std Dev:** {gravity_data['gravity_anomaly_mgal'].std():.2f} mGal")
|
|
511
|
+
st.write(f"**Min:** {gravity_data['gravity_anomaly_mgal'].min():.2f} mGal")
|
|
512
|
+
st.write(f"**Max:** {gravity_data['gravity_anomaly_mgal'].max():.2f} mGal")
|
|
513
|
+
|
|
514
|
+
with col2:
|
|
515
|
+
st.subheader("Data Quality")
|
|
516
|
+
quality_counts = gravity_data['quality'].value_counts()
|
|
517
|
+
st.bar_chart(quality_counts)
|
|
518
|
+
|
|
519
|
+
else:
|
|
520
|
+
# Overview of all politicians
|
|
521
|
+
st.subheader("📍 All Politicians Overview")
|
|
522
|
+
|
|
523
|
+
st.plotly_chart(
|
|
524
|
+
create_gravity_map(filtered_politicians),
|
|
525
|
+
config={"displayModeBar": True},
|
|
526
|
+
use_container_width=True
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
# Summary table
|
|
530
|
+
st.subheader("Trading Activity Summary")
|
|
531
|
+
summary_df = filtered_politicians[[
|
|
532
|
+
'name', 'role', 'state', 'party',
|
|
533
|
+
'recent_trades', 'total_trade_volume', 'last_trade_date'
|
|
534
|
+
]].sort_values('total_trade_volume', ascending=False)
|
|
535
|
+
|
|
536
|
+
st.dataframe(summary_df, use_container_width=True)
|
|
537
|
+
|
|
538
|
+
# Trading volume chart
|
|
539
|
+
st.subheader("Trade Volume Comparison")
|
|
540
|
+
fig = px.bar(
|
|
541
|
+
filtered_politicians.sort_values('total_trade_volume', ascending=True),
|
|
542
|
+
x='total_trade_volume',
|
|
543
|
+
y='name',
|
|
544
|
+
orientation='h',
|
|
545
|
+
color='party',
|
|
546
|
+
color_discrete_map={'Democrat': 'blue', 'Republican': 'red'},
|
|
547
|
+
labels={'total_trade_volume': 'Total Trade Volume ($)', 'name': 'Politician'},
|
|
548
|
+
title='Trade Volume by Politician'
|
|
549
|
+
)
|
|
550
|
+
st.plotly_chart(fig, config={"displayModeBar": True}, use_container_width=True)
|
|
551
|
+
|
|
552
|
+
# Footer
|
|
553
|
+
st.markdown("---")
|
|
554
|
+
st.markdown("""
|
|
555
|
+
**Data Sources:**
|
|
556
|
+
- Gravity: GRACE satellites, ground gravimeters, geological surveys
|
|
557
|
+
- Trading: mcli politician trading database
|
|
558
|
+
- Locations: Official government records
|
|
559
|
+
|
|
560
|
+
**Note:** This is a demonstration. In production, integrate with real-time data sources.
|
|
561
|
+
""")
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
if __name__ == "__main__":
|
|
565
|
+
main()
|