Spaces:
Running
Running
AJAY KASU commited on
Commit ·
aa9ef27
1
Parent(s): e2d0a40
Feat: Add high-fidelity Plotly EDA dashboard
Browse files- main.py +4 -1
- requirements.txt +1 -0
- 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)
|