import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import textwrap
# Custom Stylesheet Application helper (mirrors app.py custom style)
def apply_custom_style():
st.markdown("""
""", unsafe_allow_html=True)
# Helper function to get severity badge class
def get_severity_badge_class(cat):
c = str(cat).lower().strip()
if "critical" in c:
return "badge-critical"
elif "high" in c:
return "badge-high"
elif "medium" in c:
return "badge-medium"
else:
return "badge-low"
# Helper function to map risk level colors
def get_risk_level_color(risk):
r = str(risk).lower().strip()
if "high" in r:
return "#FF3D00"
elif "medium" in r or "moderate" in r:
return "#FFD600"
else:
return "#00B0FF"
# ----------------------------------------------------
# TECHNICAL REQUIREMENTS: load_data()
# ----------------------------------------------------
@st.cache_data
def load_data():
"""Loads and processes all anomaly intelligence datasets."""
intel_path = "data/processed/anomaly_intelligence.parquet"
summary_path = "data/processed/anomaly_summary.parquet"
master_path = "data/processed/investment_intelligence_master.parquet"
intel = pd.read_parquet(intel_path)
summary = pd.read_parquet(summary_path)
master = pd.read_parquet(master_path)
# Clean recommendation values globally
for df in [intel, summary, master]:
for col in ["Recommendation", "Recommendation_Final", "investment_signal", "historical_recommendation"]:
if col in df.columns:
df[col] = df[col].replace("Avoid", "Sell")
# Add Sector from master to summary and intel
sector_map = master.set_index("Symbol")["Sector"].to_dict()
summary["Sector"] = summary["Symbol"].map(sector_map).fillna("Unknown")
intel["Sector"] = intel["Symbol"].map(sector_map).fillna("Unknown")
# Extract total_records from master for frequency calculation
total_rec_map = master.set_index("Symbol")["total_records"].to_dict()
summary["total_records"] = summary["Symbol"].map(total_rec_map).fillna(1000)
# ----------------------------------------------------
# ADVANCED ANALYTICS SCORE CALCULATIONS
# ----------------------------------------------------
# 1. Anomaly Risk Score (0-100)
max_count = summary["anomaly_count"].max()
if max_count > 0:
summary["norm_count"] = (summary["anomaly_count"] / max_count) * 100
else:
summary["norm_count"] = 50.0
summary["Anomaly_Risk_Score"] = (
(summary["norm_count"] * 0.4) +
(summary["avg_severity"] * 0.4) +
(summary["max_severity"] * 0.2)
).clip(0.0, 100.0)
# 2. Sector Risk Score (0-100)
# Computed dynamically based on Sector averages
sector_grp = summary.groupby("Sector").agg({
"anomaly_count": "mean",
"avg_severity": "mean"
}).reset_index()
max_sec_count = sector_grp["anomaly_count"].max()
if max_sec_count > 0:
sector_grp["norm_sec_count"] = (sector_grp["anomaly_count"] / max_sec_count) * 100
else:
sector_grp["norm_sec_count"] = 50.0
sector_grp["Sector_Risk_Score"] = (
(sector_grp["norm_sec_count"] * 0.5) +
(sector_grp["avg_severity"] * 0.5)
).clip(0.0, 100.0)
sector_risk_map = sector_grp.set_index("Sector")["Sector_Risk_Score"].to_dict()
summary["Sector_Risk_Score"] = summary["Sector"].map(sector_risk_map).fillna(50.0)
intel["Sector_Risk_Score"] = intel["Sector"].map(sector_risk_map).fillna(50.0)
# 3. Event Frequency Score (0-100)
# Frequency = anomalies / total records, normalized
summary["raw_freq"] = summary["anomaly_count"] / summary["total_records"]
max_freq = summary["raw_freq"].max()
if max_freq > 0:
summary["Event_Frequency_Score"] = (summary["raw_freq"] / max_freq) * 100
else:
summary["Event_Frequency_Score"] = 50.0
summary["Event_Frequency_Score"] = summary["Event_Frequency_Score"].clip(0.0, 100.0)
# 4. Alert Priority Score (0-100) (calculated on individual events)
min_change = intel["risk_change"].min()
max_change = intel["risk_change"].max()
if max_change != min_change:
intel["norm_risk_change"] = (intel["risk_change"] - min_change) / (max_change - min_change) * 100
else:
intel["norm_risk_change"] = 50.0
intel["Alert_Priority_Score"] = (
(intel["severity_score"] * 0.7) +
(intel["norm_risk_change"].fillna(0.0) * 0.3)
).fillna(0.0).clip(0.0, 100.0)
return intel, summary, master, sector_grp
# ----------------------------------------------------
# TECHNICAL REQUIREMENTS: show_anomaly_overview()
# ----------------------------------------------------
def show_anomaly_overview(intel, summary):
"""Renders the dashboard metrics summary header."""
st.markdown('
', unsafe_allow_html=True)
total_anom = len(intel)
crit_events = len(intel[intel["severity_category"].str.lower().str.strip() == "critical"])
high_events = len(intel[intel["severity_category"].str.lower().str.strip() == "high"])
affected_stocks = intel["Symbol"].nunique()
avg_sev = intel["severity_score"].mean()
most_anom_row = summary.sort_values(by="anomaly_count", ascending=False).iloc[0]
most_anom_stock = most_anom_row["Symbol"]
most_anom_count = most_anom_row["anomaly_count"]
col1, col2, col3, col4, col5, col6 = st.columns(6)
with col1:
st.metric(label="Total Anomalies", value=f"{total_anom:,}")
with col2:
st.metric(label="Critical Events", value=f"{crit_events}")
with col3:
st.metric(label="High Severity Events", value=f"{high_events}")
with col4:
st.metric(label="Affected Stocks", value=f"{affected_stocks} / 49")
with col5:
st.metric(label="Average Severity", value=f"{avg_sev:.1f}")
with col6:
st.metric(label="Most Anomalous", value=most_anom_stock, delta=f"{most_anom_count} events")
# ----------------------------------------------------
# TECHNICAL REQUIREMENTS: show_event_feed()
# ----------------------------------------------------
def show_event_feed(intel):
"""Renders a real-time visual feed of latest anomalies using custom alert cards."""
st.markdown('', unsafe_allow_html=True)
# Sort by Date descending to get latest anomalies
latest_anoms = intel.sort_values(by="Date", ascending=False).head(20).reset_index(drop=True)
# Render first 6 cards directly to match the height of the anomaly history table (600px)
for idx, row in latest_anoms.head(6).iterrows():
b_class = get_severity_badge_class(row["severity_category"])
alert_pri = row['Alert_Priority_Score']
if pd.isna(alert_pri):
alert_pri = 0.0
card_html = f"""
{row['Symbol']}
({row['Sector']})
{row['severity_category']}
Event: {row['anomaly_type']}
Severity Score: {row['severity_score']:.1f}
Regime: {row['market_regime']}
Alert Priority: {alert_pri:.1f}
Alert: {row['anomaly_explanation']}
"""
st.markdown(card_html, unsafe_allow_html=True)
# Render the remaining items inside an expander
if len(latest_anoms) > 6:
with st.expander("βΌ Reveal Rest of the Alert Events"):
for idx, row in latest_anoms.iloc[6:].iterrows():
b_class = get_severity_badge_class(row["severity_category"])
alert_pri = row['Alert_Priority_Score']
if pd.isna(alert_pri):
alert_pri = 0.0
card_html = f"""
{row['Symbol']}
({row['Sector']})
{row['severity_category']}
Event: {row['anomaly_type']}
Severity Score: {row['severity_score']:.1f}
Regime: {row['market_regime']}
Alert Priority: {alert_pri:.1f}
Alert: {row['anomaly_explanation']}
"""
st.markdown(card_html, unsafe_allow_html=True)
# ----------------------------------------------------
# TECHNICAL REQUIREMENTS: show_stock_anomalies()
# ----------------------------------------------------
def show_stock_anomalies(selected_symbol, summary, intel):
"""Renders customized metrics and complete anomaly timeline history for a selected stock."""
st.markdown(f"### π Stock Anomaly Explorer: {selected_symbol}")
stock_sum = summary[summary["Symbol"] == selected_symbol].iloc[0]
stock_intel = intel[intel["Symbol"] == selected_symbol].sort_values(by="Date", ascending=False)
col1, col2, col3, col4, col5 = st.columns(5)
with col1:
st.metric(label="Total Anomalies", value=f"{stock_sum['anomaly_count']}")
with col2:
st.metric(label="Average Severity", value=f"{stock_sum['avg_severity']:.1f}")
with col3:
st.metric(label="Maximum Severity", value=f"{stock_sum['max_severity']:.1f}")
with col4:
st.markdown(f"""
Risk Level
{stock_sum['risk_level']}
""", unsafe_allow_html=True)
with col5:
st.metric(label="Anomaly Risk Score", value=f"{stock_sum['Anomaly_Risk_Score']:.1f}")
if not stock_intel.empty:
latest_event = stock_intel.iloc[0]
latest_date = latest_event["Date"].strftime("%d-%b-%Y")
st.info(f"π
**Latest Event Detected ({latest_date}):** {latest_event['anomaly_explanation']} (Severity: {latest_event['severity_score']:.1f})")
# Chronological layout table
st.markdown("**Complete Anomaly History**")
disp_cols = ["Date", "anomaly_type", "severity_score", "severity_category", "Alert_Priority_Score", "anomaly_explanation"]
col_config = {
"Date": st.column_config.DateColumn("Date", format="DD-MMM-YYYY"),
"anomaly_type": st.column_config.TextColumn("Anomaly Event Type"),
"severity_score": st.column_config.NumberColumn("Severity Score", format="%.1f"),
"severity_category": st.column_config.TextColumn("Category"),
"Alert_Priority_Score": st.column_config.ProgressColumn("Priority Score", format="%.1f", min_value=0.0, max_value=100.0),
"anomaly_explanation": st.column_config.TextColumn("Alert Explanation", width="large")
}
st.dataframe(
stock_intel[disp_cols],
column_config=col_config,
hide_index=True,
use_container_width=True,
height=600
)
else:
st.info("No anomalies recorded for this stock.")
# ----------------------------------------------------
# Severity Intelligence
# ----------------------------------------------------
def show_severity_intelligence(intel):
st.markdown('', unsafe_allow_html=True)
# Compute distribution counts
sev_counts = intel["severity_category"].value_counts().reset_index()
sev_counts.columns = ["Category", "Count"]
col_chart1, col_chart2 = st.columns(2)
with col_chart1:
# Severity Histogram
fig1 = px.histogram(
intel,
x="severity_score",
nbins=15,
title="Severity Score Distribution (All Anomalies)",
color_discrete_sequence=["#FF3D00"]
)
fig1.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
xaxis=dict(showgrid=False, title="Severity Score"),
yaxis=dict(showgrid=True, gridcolor="rgba(255,255,255,0.08)"),
bargap=0.05, height=280, margin=dict(l=10, r=10, t=50, b=10)
)
st.plotly_chart(fig1, use_container_width=True)
with col_chart2:
# Severity Category Pie Chart
fig2 = px.pie(
sev_counts,
values="Count",
names="Category",
title="Severity Category breakdown",
color="Category",
color_discrete_map={
"Critical": "#FF3D00",
"High": "#FF9100",
"Medium": "#FFD600",
"Low": "#00B0FF"
}
)
fig2.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)",
height=280, margin=dict(l=10, r=10, t=50, b=10)
)
st.plotly_chart(fig2, use_container_width=True)
# Severity Trend Over Time
# Aggregate average severity daily/weekly
intel_time = intel.copy()
intel_time["Month"] = intel_time["Date"].dt.to_period("M").astype(str)
monthly_avg = intel_time.groupby("Month")["severity_score"].mean().reset_index()
fig3 = px.line(
monthly_avg,
x="Month",
y="severity_score",
title="Monthly Average Severity Score Trend",
color_discrete_sequence=["#FF9100"]
)
fig3.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
xaxis=dict(showgrid=False),
yaxis=dict(showgrid=True, gridcolor="rgba(255,255,255,0.08)", title="Average Severity"),
height=280, margin=dict(l=10, r=10, t=50, b=10)
)
st.plotly_chart(fig3, use_container_width=True)
# ----------------------------------------------------
# Event Analytics Tabs
# ----------------------------------------------------
def show_event_analytics_tabs(intel):
st.markdown('', unsafe_allow_html=True)
tab1, tab2, tab3, tab4 = st.tabs([
"β‘ Volatility Spikes",
"π Drawdown Events",
"π Return Outliers",
"π‘οΈ Risk Shocks"
])
# Types: 'Volatility Spike' 'Drawdown Shock' 'Return Outlier' 'Risk Shock'
types_mapping = {
"Volatility Spike": "Volatility Spike",
"Drawdown Shock": "Drawdown Shock",
"Return Outlier": "Return Outlier",
"Risk Shock": "Risk Shock"
}
def render_tab_content(anom_type):
df_type = intel[intel["anomaly_type"] == anom_type]
count = len(df_type)
avg_sev = df_type["severity_score"].mean() if count > 0 else 0
# Most Affected Stocks
affected = df_type["Symbol"].value_counts().reset_index()
affected.columns = ["Symbol", "Event Count"]
col_c1, col_c2 = st.columns([1, 2])
with col_c1:
st.metric(label="Event Count", value=f"{count}")
st.metric(label="Average Severity", value=f"{avg_sev:.1f}")
with col_c2:
st.markdown(f"**Most Affected Assets ({anom_type})**")
st.dataframe(
affected.head(5),
hide_index=True,
use_container_width=True
)
with tab1:
render_tab_content("Volatility Spike")
with tab2:
render_tab_content("Drawdown Shock")
with tab3:
render_tab_content("Return Outlier")
with tab4:
render_tab_content("Risk Shock")
# ----------------------------------------------------
# TECHNICAL REQUIREMENTS: show_sector_analysis()
# ----------------------------------------------------
def show_sector_analysis(summary, sector_grp):
"""Renders sector anomaly counts, average severity and charts."""
st.markdown('', unsafe_allow_html=True)
most_volatile_row = sector_grp.sort_values(by="avg_severity", ascending=False).iloc[0]
safest_row = sector_grp.sort_values(by="avg_severity", ascending=True).iloc[0]
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric(label="Total Anomalies (Avg/Sector)", value=f"{sector_grp['anomaly_count'].mean():.1f}")
with col2:
st.metric(label="Average Sector Severity", value=f"{sector_grp['avg_severity'].mean():.1f}")
with col3:
st.metric(label="Most Volatile Sector", value=most_volatile_row["Sector"], delta=f"{most_volatile_row['avg_severity']:.1f} Avg Sev")
with col4:
st.metric(label="Safest Sector", value=safest_row["Sector"], delta=f"{safest_row['avg_severity']:.1f} Avg Sev", delta_color="inverse")
col_chart1, col_chart2 = st.columns(2)
with col_chart1:
# Anomalies by Sector
fig1 = px.bar(
sector_grp.sort_values("anomaly_count", ascending=True),
x="anomaly_count",
y="Sector",
orientation="h",
title="Total Anomalies by Sector",
color="anomaly_count",
color_continuous_scale="Reds"
)
fig1.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
xaxis=dict(showgrid=True, gridcolor="rgba(255,255,255,0.08)"),
yaxis=dict(showgrid=False, title=None),
coloraxis_showscale=False, height=280, margin=dict(l=10, r=10, t=50, b=10)
)
st.plotly_chart(fig1, use_container_width=True)
with col_chart2:
# Severity by Sector Heatmap
# Pivot table of sector average metrics
fig2 = px.density_heatmap(
sector_grp,
x="Sector",
y="avg_severity",
z="avg_severity",
title="Average Severity Heatmap by Sector",
color_continuous_scale="Reds"
)
fig2.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
height=280, margin=dict(l=10, r=10, t=50, b=10)
)
st.plotly_chart(fig2, use_container_width=True)
# ----------------------------------------------------
# Event Timeline
# ----------------------------------------------------
def show_event_timeline(intel):
st.markdown('', unsafe_allow_html=True)
# Interactive Timeline Scatter Chart
fig = px.scatter(
intel,
x="Date",
y="severity_score",
color="anomaly_type",
size="severity_score",
hover_name="Symbol",
title="Anomaly Timeline (Date vs Severity Score)",
color_discrete_map={
"Volatility Spike": "#FF3D00",
"Drawdown Shock": "#00B0FF",
"Return Outlier": "#FF9100",
"Risk Shock": "#FFD600"
}
)
fig.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
xaxis=dict(showgrid=False),
yaxis=dict(showgrid=True, gridcolor="rgba(255,255,255,0.08)", title="Severity Score"),
height=320, margin=dict(l=10, r=10, t=50, b=10)
)
st.plotly_chart(fig, use_container_width=True)
# Frequency aggregations
intel_freq = intel.copy()
intel_freq["Month"] = intel_freq["Date"].dt.to_period("M").astype(str)
intel_freq["Year"] = intel_freq["Date"].dt.year
monthly_cnt = intel_freq.groupby("Month").size().reset_index(name="Count")
yearly_cnt = intel_freq.groupby("Year").size().reset_index(name="Count")
col1, col2 = st.columns(2)
with col1:
fig_m = px.bar(
monthly_cnt, x="Month", y="Count",
title="Monthly Event Frequency Distribution",
color_discrete_sequence=["#00B0FF"]
)
fig_m.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
xaxis=dict(showgrid=False),
yaxis=dict(showgrid=True, gridcolor="rgba(255,255,255,0.08)"),
height=260, margin=dict(l=10, r=10, t=40, b=10)
)
st.plotly_chart(fig_m, use_container_width=True)
with col2:
fig_y = px.bar(
yearly_cnt, x="Year", y="Count",
title="Yearly Event Frequency Distribution",
color_discrete_sequence=["#FFD600"]
)
fig_y.update_layout(
template="plotly_dark", font_family="Outfit", paper_bgcolor="rgba(0,0,0,0)", plot_bgcolor="rgba(0,0,0,0)",
xaxis=dict(showgrid=False),
yaxis=dict(showgrid=True, gridcolor="rgba(255,255,255,0.08)"),
height=260, margin=dict(l=10, r=10, t=40, b=10)
)
st.plotly_chart(fig_y, use_container_width=True)
# ----------------------------------------------------
# TECHNICAL REQUIREMENTS: show_critical_alerts()
# ----------------------------------------------------
def show_critical_alerts(intel):
"""Renders the top 20 alerts ranked by severity."""
st.markdown('', unsafe_allow_html=True)
# Sort by severity score desc
critical_df = intel.sort_values(by="severity_score", ascending=False).head(20)
disp_cols = ["Date", "Symbol", "severity_score", "anomaly_type", "Alert_Priority_Score", "anomaly_explanation"]
col_config = {
"Date": st.column_config.DateColumn("Date", format="DD-MMM-YYYY"),
"Symbol": st.column_config.TextColumn("Stock"),
"severity_score": st.column_config.NumberColumn("Severity Score", format="%.1f"),
"anomaly_type": st.column_config.TextColumn("Anomaly Event"),
"Alert_Priority_Score": st.column_config.ProgressColumn("Priority Score", format="%.1f", min_value=0.0, max_value=100.0),
"anomaly_explanation": st.column_config.TextColumn("Advisory Alerts Detail", width="large")
}
st.dataframe(
critical_df[disp_cols],
column_config=col_config,
hide_index=True,
use_container_width=True
)
# ----------------------------------------------------
# TECHNICAL REQUIREMENTS: show_ai_event_report()
# ----------------------------------------------------
def show_ai_event_report(intel, summary, sector_grp):
"""Generates and displays AI executive strategizing briefs."""
st.markdown('', unsafe_allow_html=True)
total_anoms = len(intel)
unique_stocks = intel["Symbol"].nunique()
crit_cnt = len(intel[intel["severity_category"].str.lower().str.strip() == "critical"])
vol_cnt = len(intel[intel["anomaly_type"] == "Volatility Spike"])
outlier_cnt = len(intel[intel["anomaly_type"] == "Return Outlier"])
most_volatile_sector = sector_grp.sort_values(by="avg_severity", ascending=False).iloc[0]["Sector"]
safest_sector = sector_grp.sort_values(by="avg_severity", ascending=True).iloc[0]["Sector"]
st.markdown(f"""
π§ Premium Market Surveillance & Event Assessment
The surveillance engines have processed {total_anoms:,} anomalies across {unique_stocks} active assets. A total of {crit_cnt} critical alerts are currently logged, indicating localized market volatility regimes.
""", unsafe_allow_html=True)
c1, c2 = st.columns(2)
with c1:
st.markdown(f"""
π Market Event Summary & Risk Commentary
The bulk of identified anomalies originated from Volatility Spikes ({vol_cnt}) and Return Outliers ({outlier_cnt}). This points toward dynamic shifts in volatility levels rather than structural breakdowns in downside support.
π‘ Opportunity Commentary
Several high-severity return outliers may represent temporary price dislocations. Growth managers should track high-severity events on blue chips as entry windows rather than risk flags.
""", unsafe_allow_html=True)
with c2:
st.markdown(f"""
π Sector Vulnerability Commentary
Vulnerabilities are currently concentrated within the {most_volatile_sector} sector, yielding the highest average severity score. Defensive reallocations should favor the {safest_sector} cohort which remains extremely resilient.
π‘οΈ Defensive Signals
Risk profiles show sideways volatility regime shifts inside consumer defense clusters, indicating robust protection flags.
""", unsafe_allow_html=True)
# ----------------------------------------------------
# Export Features
# ----------------------------------------------------
def show_export_features(selected_symbol, intel):
st.markdown('', unsafe_allow_html=True)
col_e1, col_e2, col_e3 = st.columns(3)
with col_e1:
feed_csv = intel.sort_values(by="Date", ascending=False).head(100)[["Date", "Symbol", "anomaly_type", "severity_score", "severity_category", "anomaly_explanation"]].to_csv(index=False).encode("utf-8")
st.download_button(
label="Download Anomaly Feed",
data=feed_csv,
file_name="investiq_anomaly_feed.csv",
mime="text/csv",
key="dl-anom-feed",
use_container_width=True
)
with col_e2:
crit_csv = intel.sort_values(by="severity_score", ascending=False).head(50)[["Date", "Symbol", "severity_score", "anomaly_type", "Alert_Priority_Score", "anomaly_explanation"]].to_csv(index=False).encode("utf-8")
st.download_button(
label="Download Critical Alerts",
data=crit_csv,
file_name="investiq_critical_alerts.csv",
mime="text/csv",
key="dl-crit-alerts",
use_container_width=True
)
with col_e3:
stock_csv = intel[intel["Symbol"] == selected_symbol].sort_values(by="Date", ascending=False)[["Date", "anomaly_type", "severity_score", "severity_category", "anomaly_explanation"]].to_csv(index=False).encode("utf-8")
st.download_button(
label="Download Stock Events",
data=stock_csv,
file_name=f"investiq_{selected_symbol}_anomaly_events.csv",
mime="text/csv",
key="dl-stock-events",
use_container_width=True
)
# ----------------------------------------------------
# MAIN CALLABLE: show_anomaly_intelligence_workstation()
# ----------------------------------------------------
def show_anomaly_intelligence_workstation():
"""Main Streamlit execution point for Section 8 Page."""
apply_custom_style()
st.title("π¨ Anomaly Intelligence Workstation")
st.markdown("Monitor unusual market events, risk shocks, and abnormal price/volatility behaviors before they propagate.
", unsafe_allow_html=True)
# Load cached datasets
try:
intel, summary, master, sector_grp = load_data()
except Exception as e:
st.error(f"Error loading anomaly datasets: {e}")
st.stop()
# 1. Anomaly Overview
show_anomaly_overview(intel, summary)
st.markdown("---")
# 2. Event Feed & Explorer Split (Layout split: 40% Explorer, 60% Feed)
col_explorer, col_feed = st.columns([4, 6])
with col_explorer:
st.markdown('', unsafe_allow_html=True)
selected_symbol = st.selectbox("Select Asset to Audit Anomalies", summary["Symbol"].unique())
show_stock_anomalies(selected_symbol, summary, intel)
with col_feed:
show_event_feed(intel)
st.markdown("---")
# 4. Severity Intelligence
show_severity_intelligence(intel)
st.markdown("---")
# 5. Event Analytics Workspace
show_event_analytics_tabs(intel)
st.markdown("---")
# 6. Sector Anomaly Analysis
show_sector_analysis(summary, sector_grp)
st.markdown("---")
# 7. Event Timeline & Frequency
show_event_timeline(intel)
st.markdown("---")
# 8. Critical Alerts Center
show_critical_alerts(intel)
st.markdown("---")
# 9. AI Event Strategist
show_ai_event_report(intel, summary, sector_grp)
st.markdown("---")
# 10. Export Features
show_export_features(selected_symbol, intel)