AJAY KASU commited on
Commit
aa9ef27
·
1 Parent(s): e2d0a40

Feat: Add high-fidelity Plotly EDA dashboard

Browse files
Files changed (3) hide show
  1. main.py +4 -1
  2. requirements.txt +1 -0
  3. streamlit_app.py +70 -0
main.py CHANGED
@@ -171,7 +171,10 @@ class QuantScaleSystem:
171
  return {
172
  "optimization": opt_result,
173
  "attribution": attribution,
174
- "commentary": commentary
 
 
 
175
  }
176
 
177
  if __name__ == "__main__":
 
171
  return {
172
  "optimization": opt_result,
173
  "attribution": attribution,
174
+ "commentary": commentary,
175
+ "market_data": returns,
176
+ "benchmark_weights": benchmark_weights,
177
+ "sector_map": sector_map
178
  }
179
 
180
  if __name__ == "__main__":
requirements.txt CHANGED
@@ -14,3 +14,4 @@ scipy>=1.11.4
14
  huggingface_hub>=0.20.0
15
  streamlit>=1.30.0
16
  requests>=2.31.0
 
 
14
  huggingface_hub>=0.20.0
15
  streamlit>=1.30.0
16
  requests>=2.31.0
17
+ plotly>=5.18.0
streamlit_app.py CHANGED
@@ -105,6 +105,9 @@ def get_system():
105
  return QuantScaleSystem()
106
 
107
 
 
 
 
108
  # --- UI ---
109
  st.markdown('<div class="main-header">QuantScale AI</div>', unsafe_allow_html=True)
110
  st.markdown('<div class="sub-header">Direct Indexing & Attribution Engine</div>', unsafe_allow_html=True)
@@ -146,6 +149,9 @@ if run_btn and user_input:
146
 
147
  opt = result["optimization"]
148
  commentary = result["commentary"]
 
 
 
149
 
150
  # --- Metrics ---
151
  col1, col2, col3 = st.columns(3)
@@ -163,6 +169,70 @@ if run_btn and user_input:
163
 
164
  st.divider()
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  # --- AI Commentary ---
167
  st.markdown('<p class="section-title">AI Performance Attribution</p>', unsafe_allow_html=True)
168
  st.markdown(f'<div class="narrative-box">{commentary}</div>', unsafe_allow_html=True)
 
105
  return QuantScaleSystem()
106
 
107
 
108
+ import plotly.graph_objects as go
109
+ import plotly.express as px
110
+
111
  # --- UI ---
112
  st.markdown('<div class="main-header">QuantScale AI</div>', unsafe_allow_html=True)
113
  st.markdown('<div class="sub-header">Direct Indexing & Attribution Engine</div>', unsafe_allow_html=True)
 
149
 
150
  opt = result["optimization"]
151
  commentary = result["commentary"]
152
+ market_data = result["market_data"]
153
+ benchmark_weights = result["benchmark_weights"]
154
+ sector_map = result["sector_map"]
155
 
156
  # --- Metrics ---
157
  col1, col2, col3 = st.columns(3)
 
169
 
170
  st.divider()
171
 
172
+ # --- Advanced EDA Section ---
173
+ st.markdown('<p class="section-title">Market Context & Portfolio EDA</p>', unsafe_allow_html=True)
174
+
175
+ eda_col1, eda_col2 = st.columns([2, 1])
176
+
177
+ with eda_col1:
178
+ # 1. Performance Comparison (Trailing 30 Days)
179
+ last_30_returns = market_data.iloc[-21:]
180
+
181
+ # Portfolio vs Benchmark Cumulative Returns
182
+ bench_daily = (last_30_returns * benchmark_weights).sum(axis=1)
183
+ port_daily = (last_30_returns * pd.Series(opt.weights)).sum(axis=1)
184
+
185
+ cum_bench = (1 + bench_daily).cumprod() * 100
186
+ cum_port = (1 + port_daily).cumprod() * 100
187
+
188
+ fig_perf = go.Figure()
189
+ fig_perf.add_trace(go.Scatter(x=cum_bench.index, y=cum_bench, name="Benchmark (S&P 500 Proxy)", line=dict(color="#94a3b8", width=2, dash='dot')))
190
+ fig_perf.add_trace(go.Scatter(x=cum_port.index, y=cum_port, name="Optimized Portfolio", line=dict(color="#60a5fa", width=3)))
191
+
192
+ fig_perf.update_layout(
193
+ title="Growth of $100 (Trailing 30 Days)",
194
+ template="plotly_dark",
195
+ paper_bgcolor='rgba(0,0,0,0)',
196
+ plot_bgcolor='rgba(0,0,0,0)',
197
+ margin=dict(l=20, r=20, t=40, b=20),
198
+ legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1)
199
+ )
200
+ st.plotly_chart(fig_perf, use_container_width=True)
201
+
202
+ with eda_col2:
203
+ # 2. Sector Weight Comparison
204
+ # Aggregating sector weights
205
+ port_sector_weights = {}
206
+ bench_sector_weights = {}
207
+
208
+ for ticker, weight in opt.weights.items():
209
+ s = sector_map.get(ticker, "Unknown")
210
+ port_sector_weights[s] = port_sector_weights.get(s, 0) + weight
211
+
212
+ for ticker, weight in benchmark_weights.items():
213
+ s = sector_map.get(ticker, "Unknown")
214
+ bench_sector_weights[s] = bench_sector_weights.get(s, 0) + weight
215
+
216
+ all_sectors = sorted(list(set(list(port_sector_weights.keys()) + list(bench_sector_weights.keys()))))
217
+
218
+ fig_sector = go.Figure(data=[
219
+ go.Bar(name='Benchmark', x=all_sectors, y=[bench_sector_weights.get(s, 0)*100 for s in all_sectors], marker_color="#94a3b8"),
220
+ go.Bar(name='Portfolio', x=all_sectors, y=[port_sector_weights.get(s, 0)*100 for s in all_sectors], marker_color="#34d399")
221
+ ])
222
+
223
+ fig_sector.update_layout(
224
+ title="Sector Allocation (%)",
225
+ template="plotly_dark",
226
+ barmode='group',
227
+ paper_bgcolor='rgba(0,0,0,0)',
228
+ plot_bgcolor='rgba(0,0,0,0)',
229
+ margin=dict(l=20, r=20, t=40, b=20),
230
+ showlegend=False
231
+ )
232
+ st.plotly_chart(fig_sector, use_container_width=True)
233
+
234
+ st.divider()
235
+
236
  # --- AI Commentary ---
237
  st.markdown('<p class="section-title">AI Performance Attribution</p>', unsafe_allow_html=True)
238
  st.markdown(f'<div class="narrative-box">{commentary}</div>', unsafe_allow_html=True)