import streamlit as st
import numpy as np
import pandas as pd
import [Link] as plt
import io, math, datetime as dt, re
import openai
from openpyxl import Workbook
from [Link] import Font, PatternFill, Alignment, Border, Side
from [Link] import get_column_letter
# ====== PDF imports (reportlab) ======
from [Link] import A4, landscape
from [Link] import (
SimpleDocTemplate, LongTable, TableStyle, Paragraph, Spacer,
PageBreak, Image as RLImage
from [Link] import colors
from [Link] import getSampleStyleSheet, ParagraphStyle
from [Link] import TA_LEFT, TA_CENTER, TA_RIGHT
#
============================================
=========
# 1. PAGE SETUP & DATA LOADING
#
============================================
=========
st.set_page_config(page_title="Jarir Quant Analyzer", layout="wide")
[Link]("Jarir Quant Analysis with AI assistance")
uploaded_file = st.file_uploader("Please upload the raw quant file to
begin.", type=["csv", "xlsx"])
if uploaded_file is None:
[Link]("Please upload the raw quant file to begin.")
[Link]()
@st.cache_data
def load_and_clean_base(file):
df = pd.read_csv(file) if [Link](".csv") else
pd.read_excel(file)
[Link] = [Link]()
if "Data" in [Link]: [Link](columns={"Data": "Date"},
inplace=True)
df["Date"] = pd.to_datetime(df["Date"], format='%b-%y',
errors="coerce")
if "Year" in [Link]:
df['Date'] = [Link](lambda x: x['Date'].replace(year=int(x['Year']))
if [Link](x['Date']) else x['Date'], axis=1)
mgr_cols = [c for c in [Link] if c not in ["Date", "Year"]]
last_idx = df[mgr_cols].dropna(how='all').[Link]()
return [Link][:last_idx + 1].sort_values("Date").reset_index(drop=True)
df_raw = load_and_clean_base(uploaded_file)
#
============================================
=========
# 2. SIDEBAR SETTINGS
#
============================================
=========
[Link]("Analysis Settings")
available_dates = df_raw["Date"].dropna().sort_values().unique()
date_labels = [[Link]('%b-%Y') for d in available_dates]
col_s1, col_s2 = [Link](2)
with col_s1:
start_label = [Link]("Start Month", options=date_labels, index=0)
with col_s2:
end_label = [Link]("End Month", options=date_labels,
index=len(date_labels)-1)
start_dt = pd.to_datetime(start_label, format='%b-%Y')
end_dt = pd.to_datetime(end_label, format='%b-%Y')
df_filtered = df_raw[(df_raw["Date"] >= start_dt) & (df_raw["Date"] <=
end_dt)].reset_index(drop=True)
rfr_target = "13 Wk US Treasury Bills"
default_rfr_idx = df_filtered.columns.get_loc(rfr_target) if rfr_target in
df_filtered.columns else 0
rfr_col = [Link]("Risk-Free Rate Column",
options=df_filtered.columns, index=default_rfr_idx)
all_mgrs = [c for c in df_filtered.columns if c not in ["Date", "Year", rfr_col]]
manager_cols = [Link]("Select Managers",
options=all_mgrs, default=all_mgrs[:5])
if not manager_cols:
[Link]("Please select managers.")
[Link]()
#
============================================
=========
# 3. DATA CLEANING
#
============================================
=========
cleaned_df = df_filtered.copy()
for col in manager_cols + [rfr_col]:
s = cleaned_df[col].astype(str).[Link]()
is_pct = [Link]("%", regex=False)
s = [Link](["", "nan", "-", "–", " - "], [Link])\
.[Link]("%", "", regex=False)\
.[Link](r"\((.*?)\)", r"-\1", regex=True)\
.astype(float)
[Link][is_pct] = [Link][is_pct] / 100
cleaned_df[col] = s
#
============================================
=========
# 4. CALCULATION HELPERS
#
============================================
=========
def get_cap(s_m, s_b):
combined = [Link]({'m': s_m, 'b': s_b}).dropna()
up = combined[combined['b'] > 0]; dn = combined[combined['b'] < 0]
u_cap = ([Link](1+up['m'])/[Link](1+up['b']))*100 if not [Link]
else [Link]
d_cap = ([Link](1+dn['m'])/[Link](1+dn['b']))*100 if not [Link]
else [Link]
return u_cap, d_cap
def slice_series(series, years=None):
s = [Link]()
if years is None: return s
months = int(years * 12)
return [Link](months) if len(s) >= months else None
def get_ann_ret(s):
if s is None or len(s) < 12: return [Link]
return ([Link](1 + s)) ** (1 / (len(s) / 12)) - 1
def get_ann_vol(s):
if s is None or len(s) < 2: return [Link]
return [Link](ddof=1) * [Link](12)
def get_downside_dev(s):
if s is None or len(s) < 12: return [Link]
return [Link]([Link]([Link](s, 0) ** 2)) * [Link](12)
def get_upside_dev(s):
if s is None or len(s) < 12: return [Link]
return [Link]([Link]([Link](s, 0) ** 2)) * [Link](12)
def get_max_drawdown(s):
if s is None or len(s) == 0: return [Link]
cum = (1 + s).cumprod()
return ((cum - [Link]()) / [Link]()).min()
#
============================================
=========
# 5. ANALYTICS ENGINE (extended horizons: 1–10, 15, 20)
#
============================================
=========
horizons = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20]
h_labels = [f"{y} Year" for y in horizons] + ["Since Inception"]
metrics = ["Annualized Return (%)", "Annualized Volatility (%)",
"Upward Deviation (%)", "Downward Deviation (%)",
"Sharpe Ratio", "Sortino Ratio", "Max Drawdown (%)"]
results = {m: [Link](index=h_labels, columns=manager_cols)
for m in metrics}
for yrs, lbl in zip(horizons + [None], h_labels):
for mgr in manager_cols:
s_m = slice_series(cleaned_df[mgr], yrs)
s_rf = slice_series(cleaned_df[rfr_col], yrs)
if s_m is None or s_rf is None: continue
ann_ret = get_ann_ret(s_m)
ann_rfr = get_ann_ret(s_rf)
ann_vol = get_ann_vol(s_m)
ann_ddev = get_downside_dev(s_m)
ann_udev = get_upside_dev(s_m)
exc_ret = ann_ret - ann_rfr
results["Annualized Return (%)"].at[lbl, mgr] = ann_ret * 100 if not
[Link](ann_ret) else [Link]
results["Annualized Volatility (%)"].at[lbl, mgr] = ann_vol * 100 if not
[Link](ann_vol) else [Link]
results["Upward Deviation (%)"].at[lbl, mgr] = ann_udev * 100 if
not [Link](ann_udev) else [Link]
results["Downward Deviation (%)"].at[lbl, mgr] = ann_ddev * 100 if
not [Link](ann_ddev) else [Link]
results["Sharpe Ratio"].at[lbl, mgr] = exc_ret / ann_vol if ann_vol >
0 else [Link]
results["Sortino Ratio"].at[lbl, mgr] = exc_ret / ann_ddev if ann_ddev
> 0 else [Link]
results["Max Drawdown (%)"].at[lbl, mgr] = get_max_drawdown(s_m)
* 100
#
============================================
=========
# 6. PERFORMANCE SNAPSHOT + KPI BANNER
#
============================================
=========
# ── KPI Banner Cards with selectable horizon ──
[Link]("### 📊 Performance Summary")
banner_horizon = [Link]("Select Horizon for Summary Cards",
options=h_labels, index=h_labels.index("Since Inception"),
key="banner_h")
si_ret = results["Annualized Return
(%)"].loc[banner_horizon].apply(pd.to_numeric, errors='coerce')
si_vol = results["Annualized Volatility
(%)"].loc[banner_horizon].apply(pd.to_numeric, errors='coerce')
si_sharpe = results["Sharpe
Ratio"].loc[banner_horizon].apply(pd.to_numeric, errors='coerce')
si_sort = results["Sortino
Ratio"].loc[banner_horizon].apply(pd.to_numeric, errors='coerce')
si_dd = results["Max Drawdown
(%)"].loc[banner_horizon].apply(pd.to_numeric, errors='coerce')
# All cards use same clean dark navy — no vague colour variety
CARD_BG = "#1C2B4A"
banner_cards = [
("🏆 Best Sharpe", si_sharpe.idxmax(), f"{si_sharpe.max():.2f}"),
("📈 Highest Ann. Return", si_ret.idxmax(), f"{si_ret.max():.2f}%"),
("📉 Lowest Ann. Return", si_ret.idxmin(), f"{si_ret.min():.2f}%"),
("💪 Lowest Drawdown", si_dd.idxmax(), f"{si_dd.max():.2f}%"),
("⚠️Highest Drawdown", si_dd.idxmin(), f"{si_dd.min():.2f}%"),
("📊 Highest Volatility", si_vol.idxmax(), f"{si_vol.max():.2f}%"),
("🎯 Lowest Volatility", si_vol.idxmin(), f"{si_vol.min():.2f}%"),
("⚡ Best Sortino", si_sort.idxmax(), f"{si_sort.max():.2f}"),
[Link]("""
<style>
.banner-grid{display:flex;flex-wrap:wrap;gap:10px;margin-bottom:20px}
.banner-card{
flex:1 1 160px;border-radius:10px;padding:14px 12px;text-align:center;
box-shadow:0 2px 8px rgba(0,0,0,.20);
.banner-card .label{font-size:11px;font-weight:600;opacity:.80;margin-
bottom:4px;letter-spacing:.4px;text-transform:uppercase}
.banner-card .manager{font-size:13px;font-weight:700;margin-
bottom:4px}
.banner-card .value{font-size:20px;font-weight:800;letter-spacing:-.5px}
</style>""", unsafe_allow_html=True)
cards_html = '<div class="banner-grid">'
for lbl_b, mgr_b, val_b in banner_cards:
cards_html += (
f'<div class="banner-card" style="background:
{CARD_BG};color:white;">'
f'<div class="label">{lbl_b}</div>'
f'<div class="manager">{mgr_b}</div>'
f'<div class="value">{val_b}</div>'
f'</div>'
cards_html += '</div>'
[Link](cards_html, unsafe_allow_html=True)
[Link]()
#
============================================
=========
# 7. STYLE HELPER
#
============================================
=========
def style_df(df_in, pct=True):
fmt = "{:.2f}%" if pct else "{:.2f}"
return df_in.apply(pd.to_numeric).style \
.map(lambda x: 'color:red;text-align:center;font-weight:normal'
if ([Link](x) and x < 0)
else ('text-align:center;color:black;font-weight:normal' if
[Link](x) else '')) \
.format(fmt, na_rep="") \
.set_properties(**{'text-align': 'center'}) \
.set_table_styles([
{'selector': 'th.row_heading', 'props': [('font-weight','bold'),
('color','black'),('text-align','left'),('white-space','nowrap')]},
{'selector': 'th.col_heading', 'props': [('font-weight','bold'),
('color','black'),('text-align','center')]},
{'selector': '[Link]', 'props': [('text-align','center')]},
])
#
============================================
=========
# 8. METRIC TABLES
#
============================================
=========
for m in metrics:
is_p = "Ratio" not in m
with [Link](f"View: {m}", expanded=True):
[Link](style_df(results[m], pct=is_p), width="stretch",
height=(len(h_labels)+1)*35+15)
#
============================================
=========
# 9. RISK–RETURN + UPSIDE–DOWNSIDE PLOTS
#
============================================
=========
[Link]()
viz_h = [Link]("Select Horizon for Plots", options=h_labels,
index=len(h_labels)-2)
col_v1, col_v2 = [Link](2)
def smart_scatter(ax, x_vals, y_vals, labels, dot_color, xlabel, ylabel,
reflines=None):
"""Scatter with repulsion-based label placement — no overlaps."""
import numpy as np
[Link](x_vals, y_vals, color=dot_color, s=90, zorder=5)
if not labels:
ax.set_xlabel(xlabel, fontsize=9)
ax.set_ylabel(ylabel, fontsize=9)
[Link](True, ls=':', alpha=0.6)
return
x_arr = [Link](x_vals, dtype=float)
y_arr = [Link](y_vals, dtype=float)
n = len(labels)
x_range = max(x_arr.max() - x_arr.min(), 1e-6)
y_range = max(y_arr.max() - y_arr.min(), 1e-6)
# Start label positions offset from dots
lx = x_arr + x_range * 0.04
ly = y_arr + y_range * 0.04
# Iterative repulsion between labels
for _ in range(300):
for i in range(n):
fx, fy = 0.0, 0.0
for j in range(n):
if i == j: continue
dx = (lx[i] - lx[j]) / x_range
dy = (ly[i] - ly[j]) / y_range
dist2 = dx*dx + dy*dy + 1e-9
if dist2 < 0.08:
fx += dx / dist2
fy += dy / dist2
# Also repel from original dot positions
for j in range(n):
dx = (lx[i] - x_arr[j]) / x_range
dy = (ly[i] - y_arr[j]) / y_range
dist2 = dx*dx + dy*dy + 1e-9
if dist2 < 0.06:
fx += 0.5 * dx / dist2
fy += 0.5 * dy / dist2
step = 0.0008
lx[i] += fx * step * x_range
ly[i] += fy * step * y_range
for i, lbl in enumerate(labels):
[Link](
lbl,
xy=(x_arr[i], y_arr[i]),
xytext=(lx[i], ly[i]),
fontsize=8.5, fontweight='bold', color='#1a1a2e',
bbox=dict(boxstyle='round,pad=0.3', fc='white', ec='#cccccc',
alpha=0.85, lw=0.6),
arrowprops=dict(arrowstyle='-', color='#999999', lw=0.8),
zorder=10
ax.set_xlabel(xlabel, fontsize=9)
ax.set_ylabel(ylabel, fontsize=9)
[Link](True, ls=':', alpha=0.6)
if reflines:
for axis, val, lw in reflines:
if axis == 'h': [Link](val, color='black', lw=lw)
if axis == 'v': [Link](val, color='black', lw=lw)
with col_v1:
[Link](f"**Risk-Return Plot ({viz_h})**")
rr_ui = [Link]({
"Return (%)": results["Annualized Return (%)"].loc[viz_h],
"Volatility (%)": results["Annualized Volatility (%)"].loc[viz_h]
}).apply(pd.to_numeric).dropna()
fig_rr, ax_rr = [Link](figsize=(8, 5))
smart_scatter(ax_rr, rr_ui["Volatility (%)"].tolist(), rr_ui["Return
(%)"].tolist(),
rr_ui.[Link](), '#0d6e8a', "Volatility (%)", "Return (%)")
plt.tight_layout(); [Link](fig_rr)
with col_v2:
[Link](f"**Upside–Downside Plot ({viz_h})**")
ud_ui = [Link]({
"Return (%)": results["Annualized Return (%)"].loc[viz_h],
"Downside Dev (%)": results["Downward Deviation (%)"].loc[viz_h]
}).apply(pd.to_numeric).dropna()
fig_ud, ax_ud = [Link](figsize=(8, 5))
smart_scatter(ax_ud, ud_ui["Downside Dev (%)"].tolist(), ud_ui["Return
(%)"].tolist(),
ud_ui.[Link](), '#c0392b',
"Downside Deviation (%) — Bad Volatility",
"Annualized Return (%) — Reward",
reflines=[('h', 0, 0.8), ('v', 0, 0.8)])
plt.tight_layout(); [Link](fig_ud)
# Capture scatter
[Link]()
col_v3, col_v4 = [Link](2)
with col_v3:
b_cap = [Link]("Benchmark for Capture Scatter",
options=manager_cols, index=len(manager_cols)-1)
[Link](f"**Capture Matrix ({viz_h}) — Benchmark: {b_cap}**")
caps_ui = []
for m in manager_cols:
y_c = int(viz_h.split()[0]) if "Year" in viz_h else None
s_m_ = cleaned_df[m].tail(y_c*12) if y_c else cleaned_df[m]
s_b_ = cleaned_df[b_cap].tail(y_c*12) if y_c else cleaned_df[b_cap]
u, d = get_cap(s_m_.dropna(), s_b_.dropna())
caps_ui.append({"Manager": m, "Upside": u, "Downside": d})
cap_ui_df = [Link](caps_ui).set_index("Manager").dropna()
fig_cap, ax_cap = [Link](figsize=(8, 5))
smart_scatter(ax_cap, cap_ui_df["Downside"].tolist(),
cap_ui_df["Upside"].tolist(),
cap_ui_df.[Link](), '#8e1a0e',
"Downside Capture (%)", "Upside Capture (%)",
reflines=[('h', 100, 1), ('v', 100, 1)])
plt.tight_layout(); [Link](fig_cap)
#
============================================
=========
# 10. CALENDAR RETURNS & ALPHA
#
============================================
=========
[Link]()
cal_base = cleaned_df.set_index('Date')[manager_cols]
cal_ret = cal_base.groupby(cal_base.[Link]).apply(
lambda x: ([Link](x + 1, axis=0) - 1) *
100).sort_index(ascending=False)
[Link]("**Calendar Returns (%)**")
[Link](style_df(cal_ret), width="stretch")
bench_diff = [Link]("Select Alpha Benchmark",
options=manager_cols, index=len(manager_cols)-1)
cal_diff = cal_ret.subtract(cal_ret[bench_diff], axis=0)
[Link](f"**Calendar Difference (Alpha vs {bench_diff}) %**")
[Link](style_df(cal_diff), width="stretch")
[Link]()
alpha_fund = [Link]("Select Fund for Alpha Matrix",
options=manager_cols, index=0)
alpha_bench = [Link]("Select Benchmark for Alpha Matrix",
options=manager_cols, index=len(manager_cols)-1)
[Link](f"**Alpha Over Benchmark (Yearly View): {alpha_fund} vs
{alpha_bench} (%)**")
alpha_matrix = [Link](index=cleaned_df.index)
for y in range(1, 21):
f_r = cleaned_df[alpha_fund].rolling(window=y*12).apply(
lambda x: ([Link](x+1)**(12/len(x))-1)*100)
b_r = cleaned_df[alpha_bench].rolling(window=y*12).apply(
lambda x: ([Link](x+1)**(12/len(x))-1)*100)
alpha_matrix[f"{y}Y"] = f_r - b_r
alpha_matrix['Date'] = cleaned_df['Date']
alpha_disp_year =
alpha_matrix.groupby(alpha_matrix['Date'].[Link]).tail(1).copy()
alpha_disp_year.set_index(alpha_disp_year['Date'].[Link], inplace=True)
alpha_disp_year =
alpha_disp_year.drop(columns=['Date']).sort_index(ascending=False)
[Link](style_df(alpha_disp_year), width="stretch", height=600)
[Link]()
[Link]("36-Monthly Returns of the selected funds")
m_36_ui = cleaned_df.set_index('Date')[manager_cols].tail(36).iloc[::-1]
m_36_ui.index = m_36_ui.[Link]('%b-%Y')
[Link](style_df(m_36_ui * 100), width="stretch", height=1300)
#
============================================
=========
# 11. CAPTURE TABLE
#
============================================
=========
[Link]()
[Link]("📋 Upside / Downside Capture Table")
cap_bench = [Link]("Benchmark for Capture Table",
options=manager_cols,
index=len(manager_cols)-1, key="cap_tbl_bench")
cap_horizons_lbl = [f"{y} Year" for y in [1, 3, 5, 8, 10]] + ["Since
Inception"]
cap_rows = []
for mgr in manager_cols:
row = {"Manager": mgr}
for lbl_c in cap_horizons_lbl:
yrs_c = int(lbl_c.split()[0]) if "Year" in lbl_c else None
sm = cleaned_df[mgr].dropna(); sb =
cleaned_df[cap_bench].dropna()
if yrs_c:
if len(sm) < yrs_c*12:
row[f"{lbl_c} Up"] = [Link]; row[f"{lbl_c} Dn"] = [Link];
continue
sm = [Link](yrs_c*12); sb = [Link](yrs_c*12)
u, d = get_cap(sm, sb)
row[f"{lbl_c} Up"] = round(u, 2) if u is not None else [Link]
row[f"{lbl_c} Dn"] = round(d, 2) if d is not None else [Link]
cap_rows.append(row)
cap_tbl = [Link](cap_rows).set_index("Manager")
def style_capture(df_in):
def color_cell(val):
if [Link](val): return ''
return 'color:green;font-weight:bold;text-align:center' if val >= 100
else 'color:red;text-align:center'
return df_in.[Link](color_cell).format("{:.1f}",
na_rep="").set_table_styles([
{'selector': 'th.row_heading', 'props': [('font-weight','bold'),
('color','black'),('text-align','left')]},
{'selector': 'th.col_heading', 'props': [('font-weight','bold'),
('color','black'),('text-align','center'),('font-size','11px')]},
])
[Link](style_capture(cap_tbl), width="stretch")
#
============================================
=========
# 12. WIN RATE VS BENCHMARK
#
============================================
=========
[Link]()
[Link]("🏅 Win Rate vs Benchmark (% of months outperformed)")
wr_bench = [Link]("Select Benchmark for Win Rate",
options=manager_cols,
index=len(manager_cols)-1, key="wr_bench")
wr_horizons = [1, 3, 5, 8, 10]
wr_rows = []
for mgr in [m for m in manager_cols if m != wr_bench]:
row = {"Manager": mgr}
for yrs_w in wr_horizons:
lbl_w = f"{yrs_w}Y"
sm = cleaned_df[mgr].dropna(); sb = cleaned_df[wr_bench].dropna()
if len(sm) < yrs_w*12: row[lbl_w] = [Link]; continue
sm = [Link](yrs_w*12); sb = [Link](yrs_w*12)
combined = [Link]({'m': [Link], 'b': [Link]}).dropna()
row[lbl_w] = round((combined['m'] > combined['b']).mean()*100, 1)
wr_rows.append(row)
wr_df = [Link](wr_rows).set_index("Manager")
def style_winrate(df_in):
def color_wr(val):
if [Link](val): return ''
if val >= 55: return 'color:green;font-weight:bold;text-align:center'
if val <= 45: return 'color:red;text-align:center'
return 'color:orange;text-align:center'
return df_in.[Link](color_wr).format("{:.1f}%",
na_rep="").set_table_styles([
{'selector': 'th.row_heading', 'props': [('font-weight','bold'),
('color','black'),('text-align','left')]},
{'selector': 'th.col_heading', 'props': [('font-weight','bold'),
('color','black'),('text-align','center')]},
])
[Link](style_winrate(wr_df), width="stretch")
[Link]("🟢 ≥55% consistent outperformance | 🟠 45–55% neutral | 🔴
≤45% underperformance")
#
============================================
=========
# 13. MANAGER COMPARISON CARD
#
============================================
=========
[Link]()
[Link]("🔍 Manager Comparison Card")
col_c1, col_c2 = [Link](2)
with col_c1: mgr_a = [Link]("Manager A", options=manager_cols,
index=0, key="cmp_a")
with col_c2: mgr_b = [Link]("Manager B", options=manager_cols,
index=min(1,len(manager_cols)-1), key="cmp_b")
cmp_horizon = [Link]("Comparison Horizon", options=h_labels,
index=len(h_labels)-1, key="cmp_h")
cmp_metrics = {
"Annualized Return (%)": ("{:.2f}%", True),
"Annualized Volatility (%)": ("{:.2f}%", False),
"Sharpe Ratio": ("{:.2f}", True),
"Sortino Ratio": ("{:.2f}", True),
"Max Drawdown (%)": ("{:.2f}%", False),
"Upward Deviation (%)": ("{:.2f}%", True),
"Downward Deviation (%)": ("{:.2f}%", False),
[Link]("""
<style>
.cmp-wrap{display:flex;gap:12px;margin-top:10px}
.cmp-box{flex:1;border-
radius:10px;padding:18px;background:#f8f9fa;border:1px solid #dee2e6}
.cmp-title{font-size:18px;font-weight:800;color:#1a1a2e;margin-
bottom:14px;border-bottom:3px solid #1a5276;padding-bottom:6px}
.cmp-row{display:flex;justify-content:space-between;padding:6px
0;border-bottom:1px solid #eee;font-size:13px}
.cmp-label{color:#555;font-weight:500}
.cmp-val-good{color:#1e8449;font-weight:700}
.cmp-val-bad{color:#922b21;font-weight:700}
.cmp-val-neu{color:#333;font-weight:700}
.cmp-winner{background:#eafaf1;border:2px solid #1e8449}
</style>""", unsafe_allow_html=True)
def get_cmp_val(mgr, metric):
try: return float(results[metric].at[cmp_horizon, mgr])
except: return [Link]
def render_cmp_box(mgr, other, is_winner):
box_class = "cmp-box cmp-winner" if is_winner else "cmp-box"
rows_html = ""
for metric, (fmt, higher_better) in cmp_metrics.items():
v = get_cmp_val(mgr, metric); o = get_cmp_val(other, metric)
if [Link](v): val_str, cls = "N/A", "cmp-val-neu"
else:
val_str = [Link](v)
if [Link](o): cls = "cmp-val-neu"
elif higher_better: cls = "cmp-val-good" if v > o else ("cmp-val-
bad" if v < o else "cmp-val-neu")
else: cls = "cmp-val-good" if v < o else ("cmp-val-bad" if v
> o else "cmp-val-neu")
rows_html += f'<div class="cmp-row"><span class="cmp-
label">{metric}</span><span class="{cls}">{val_str}</span></div>'
badge = " 🏆" if is_winner else ""
return f'<div class="{box_class}"><div class="cmp-title">{mgr}
{badge}</div>{rows_html}</div>'
sharpe_a = get_cmp_val(mgr_a, "Sharpe Ratio"); sharpe_b =
get_cmp_val(mgr_b, "Sharpe Ratio")
a_wins = (not [Link](sharpe_a)) and ([Link](sharpe_b) or sharpe_a >=
sharpe_b)
[Link](f'<div class="cmp-
wrap">{render_cmp_box(mgr_a,mgr_b,a_wins)}
{render_cmp_box(mgr_b,mgr_a,not a_wins)}</div>',
unsafe_allow_html=True)
[Link]("🟢 = better value for that metric | 🏆 = overall winner by
Sharpe Ratio")
#
============================================
=========
# 14. ROLLING SHARPE & VOLATILITY
#
============================================
=========
[Link]()
[Link]("📈 Rolling Sharpe Ratio & Volatility")
roll_col1, roll_col2 = [Link](2)
with roll_col1: roll_window = [Link]("Rolling Window", options=[12,
24, 36], index=0, key="roll_win")
with roll_col2: roll_mgrs = [Link]("Managers to Plot",
options=manager_cols, default=manager_cols[:3], key="roll_mgrs")
if roll_mgrs:
fig_roll, (ax_rs, ax_rv) = [Link](2, 1, figsize=(12, 8), sharex=True)
colors_list = [Link]
rfr_series = cleaned_df[rfr_col].fillna(method='ffill')
for i, mgr in enumerate(roll_mgrs):
s = cleaned_df[mgr]; dates = cleaned_df['Date']
roll_sharpe = [Link](roll_window).apply(
lambda x: (([Link](x+1)**(12/len(x))-1) -
([Link](rfr_series.iloc[[Link][-roll_window:] if
len([Link])>=roll_window else [Link]]+1)**(12/len(x))-1))
/ ([Link](ddof=1)*[Link](12)) if [Link]() > 0 else [Link])
roll_vol = [Link](roll_window).std(ddof=1) * [Link](12) * 100
c = colors_list[i % len(colors_list)]
ax_rs.plot(dates, roll_sharpe, label=mgr, color=c, lw=1.5)
ax_rv.plot(dates, roll_vol, label=mgr, color=c, lw=1.5)
ax_rs.axhline(0, color='black', lw=0.8, ls='--')
ax_rs.set_ylabel("Rolling Sharpe"); ax_rs.legend(fontsize=8);
ax_rs.grid(True, ls=':')
ax_rs.set_title(f"{roll_window}-Month Rolling Sharpe Ratio")
ax_rv.set_ylabel("Rolling Volatility (%)"); ax_rv.legend(fontsize=8);
ax_rv.grid(True, ls=':')
ax_rv.set_title(f"{roll_window}-Month Rolling Annualized Volatility")
plt.tight_layout(); [Link](fig_roll)
#
============================================
=========
# 15. DRAWDOWN TIMELINE
#
============================================
=========
[Link]()
[Link]("🌊 Drawdown Timeline (Underwater Chart)")
dd_mgrs = [Link]("Managers for Drawdown Chart",
options=manager_cols,
default=manager_cols[:4], key="dd_mgrs")
if dd_mgrs:
n_mgrs = len(dd_mgrs)
fig_dd, axes = [Link](n_mgrs, 1, figsize=(12, 3*n_mgrs),
sharex=True)
if n_mgrs == 1: axes = [axes]
colors_dd = [Link]
for i, mgr in enumerate(dd_mgrs):
s = cleaned_df[mgr].fillna(0)
cum = (1+s).cumprod(); roll_max = [Link]()
dd_vals = (cum/roll_max - 1)*100
ax = axes[i]
ax.fill_between(cleaned_df['Date'], dd_vals, 0, color=colors_dd[i
%len(colors_dd)], alpha=0.5)
[Link](cleaned_df['Date'], dd_vals, color=colors_dd[i
%len(colors_dd)], lw=1)
[Link](0, color='black', lw=0.8); ax.set_ylabel("Drawdown (%)")
ax.set_title(mgr, fontweight='bold', fontsize=10); [Link](True, ls=':',
alpha=0.5)
max_dd_val = dd_vals.min()
max_dd_date = cleaned_df['Date'].iloc[dd_vals.argmin()]
[Link](f"{max_dd_val:.1f}%", xy=(max_dd_date, max_dd_val),
xytext=(max_dd_date, max_dd_val-2), fontsize=8,
color='red', fontweight='bold')
plt.tight_layout(); [Link](fig_dd)
#
============================================
=========
# 16. AI STRATEGIC CONVERSATION
#
============================================
=========
[Link]()
[Link]("Jarir AI Strategic Advisor")
full_results_text = ""
for metric_name, df_res in [Link]():
full_results_text += f"\n--- {metric_name} ---\n{df_res.to_string()}\n"
chat_context = f"""
Portfolio Window: {start_label} to {end_label}
Managers: {', '.join(manager_cols)}
Alpha Check: {alpha_fund} vs {alpha_bench}
[DATASET]\n{full_results_text}\n{alpha_disp_year.to_string()}
"""
with [Link]("Activate AI Conversation", expanded=False):
key_input = st.text_input("Enter OpenAI API Key", type="password")
if [Link](" Generate insights"):
if not key_input:
[Link]("Please provide an API key.")
else:
with [Link]("Generating Insights..."):
try:
client = [Link](api_key=key_input)
sys_role = "You are acting as a senior investment analyst
performing a comprehensive evaluation of fund manager performance for
institutional portfolio decisions. I will provide manager-level performance
data including returns, volatility, upside and downside deviations, Sharpe
and Sortino ratios, drawdowns, capture ratios, calendar returns, alpha
metrics, and risk-return visualizations. Your objective is to interpret the
data the way an experienced investment analyst would—by identifying
what is really driving performance, when it occurred, and whether it is
repeatable. Examine whether returns are generated through genuine
manager skill or through exposure to favorable market regimes, elevated
risk-taking, or beta concentration. Assess return consistency across time
by identifying periods of performance concentration, regime dependence,
and month-year-specific inflection points such as market stress,
drawdowns, recoveries, or rallies. Evaluate downside risk by analyzing
drawdowns, downside deviation, recovery speed, and Sortino behavior to
determine capital preservation capability. Analyze upside versus downside
capture to understand payoff asymmetry and to distinguish convex return
profiles from leveraged or directional exposure. Use the risk-return
positioning to identify efficiency, dominance, and risk-adjusted
attractiveness relative to peers. Leverage alpha and relative metrics to
test persistence, robustness, and benchmark independence, flagging
statistically fragile or benchmark-hugging strategies. Synthesize these
findings into actionable conclusions by classifying managers as core,
satellite, tactical, or unsuitable; identifying complementary pairings based
on risk and asymmetry; highlighting red flags and monitoring triggers; and
presenting insights in clear, decision-focused language suitable for
investment committee review, explicitly referencing relevant time periods
and market contexts rather than relying on generic performance
summaries."
user_prompt = f"Using this EXACT DATA: {chat_context}\n\
nTask: {sys_role}"
response = [Link](
model="gpt-4-turbo",
messages=[{"role":"system","content":sys_role},
{"role":"user","content":user_prompt}])
st.session_state.insights =
[Link][0].[Link]
st.session_state.chat_history =
[{"role":"assistant","content":st.session_state.insights}]
except Exception as e: [Link](f"AI Error: {e}")
if 'insights' in st.session_state:
[Link](st.session_state.insights)
[Link]()
[Link]("**Chat with Jarir AI to get more insights:**")
if "chat_history" not in st.session_state: st.session_state.chat_history
= []
for msg in st.session_state.chat_history:
with st.chat_message(msg["role"]): [Link](msg["content"])
if user_query := st.chat_input("Ask about the data..."):
st.session_state.chat_history.append({"role":"user","content":user_query}
)
with st.chat_message("user"): [Link](user_query)
with st.chat_message("assistant"):
placeholder = [Link](); [Link]("🤓 Investment
Analyst is thinking...")
try:
client = [Link](api_key=key_input)
chat_resp = [Link](
model="gpt-4-turbo",
messages=[{"role":"system","content":f"Senior Analyst.
Context: {chat_context}"},
*st.session_state.chat_history])
reply = chat_resp.choices[0].[Link]
[Link](); [Link](reply)
st.session_state.chat_history.append({"role":"assistant","content":reply})
except Exception as e: [Link](f"Chat Error: {e}")
#
============================================
=========
# 17. EXCEL EXPORT
#
============================================
=========
def generate_excel(res, mgrs, cal_ret_df, cal_diff_df, alpha_df, cap_tbl_df,
wr_df_ex):
wb = Workbook(); [Link]([Link])
hdr_font = Font(name='Arial', bold=True, color='FFFFFF', size=10)
hdr_fill = PatternFill('solid', start_color='1A5276')
idx_font = Font(name='Arial', bold=True, size=10)
num_font = Font(name='Arial', size=10)
red_font = Font(name='Arial', size=10, color='FF0000')
center = Alignment(horizontal='center', vertical='center')
left_al = Alignment(horizontal='left', vertical='center')
thin = Side(style='thin', color='CCCCCC')
bdr = Border(left=thin, right=thin, top=thin, bottom=thin)
alt_fill = PatternFill('solid', start_color='EBF5FB')
def write_sheet(sheet_name, df, fmt="{:.2f}%"):
ws = wb.create_sheet(title=sheet_name[:31]); ws.freeze_panes =
'B2'
[Link](1,1,"Horizon").font = hdr_font; [Link](1,1).fill = hdr_fill
[Link](1,1).alignment = center; [Link](1,1).border = bdr
for ci, col in enumerate([Link], 2):
c = [Link](1,ci,str(col))
[Link] = hdr_font; [Link] = hdr_fill; [Link] = center; [Link] =
bdr
ws.column_dimensions[get_column_letter(ci)].width = max(14,
len(str(col))+4)
ws.column_dimensions['A'].width = 18; ws.row_dimensions[1].height
= 22
for ri, (idx, row) in enumerate([Link](), 2):
ic = [Link](ri,1,str(idx)); [Link] = idx_font; [Link] = left_al;
[Link] = bdr
if ri%2==0: [Link] = alt_fill
for ci, val in enumerate(row, 2):
cell = [Link](ri,ci); [Link] = bdr; [Link] = center
if ri%2==0: [Link] = alt_fill
try:
v = float(val)
[Link] = v/100 if "%" in fmt else v
cell.number_format = '0.00%' if "%" in fmt else '0.00'
[Link] = red_font if v < 0 else num_font
except: [Link] = ""; [Link] = num_font
for metric in res:
fmt = "{:.2f}%" if "Ratio" not in metric else "{:.2f}"
write_sheet(metric[:31], res[metric].apply(pd.to_numeric,
errors='coerce'), fmt)
write_sheet("Calendar Returns", cal_ret_df.apply(pd.to_numeric,
errors='coerce'))
write_sheet("Calendar Difference", cal_diff_df.apply(pd.to_numeric,
errors='coerce'))
write_sheet("Alpha Matrix", alpha_df.apply(pd.to_numeric,
errors='coerce'))
# Capture table
ws_cap = wb.create_sheet("Capture Table"); ws_cap.freeze_panes =
'B2'
ws_cap.cell(1,1,"Manager").font = hdr_font; ws_cap.cell(1,1).fill =
hdr_fill
ws_cap.cell(1,1).alignment = center; ws_cap.cell(1,1).border = bdr
ws_cap.column_dimensions['A'].width = 22
for ci, col in enumerate(cap_tbl_df.columns, 2):
c = ws_cap.cell(1,ci,str(col)); [Link] = hdr_font; [Link] = hdr_fill
[Link] = center; [Link] = bdr
ws_cap.column_dimensions[get_column_letter(ci)].width = 12
for ri, (idx, row) in enumerate(cap_tbl_df.iterrows(), 2):
ws_cap.cell(ri,1,str(idx)).font = idx_font; ws_cap.cell(ri,1).alignment
= left_al; ws_cap.cell(ri,1).border = bdr
for ci, val in enumerate(row, 2):
cell = ws_cap.cell(ri,ci); [Link] = bdr; [Link] = center
try:
v = float(val); [Link] = v; cell.number_format = '0.0'
[Link] = Font(name='Arial',size=10,color='1E8449' if v>=100
else 'FF0000',bold=(v>=100))
except: [Link] = ""; [Link] = num_font
# Win Rate
ws_wr = wb.create_sheet("Win Rate vs Benchmark");
ws_wr.freeze_panes = 'B2'
ws_wr.cell(1,1,"Manager").font = hdr_font; ws_wr.cell(1,1).fill = hdr_fill
ws_wr.cell(1,1).alignment = center; ws_wr.cell(1,1).border = bdr
ws_wr.column_dimensions['A'].width = 22
for ci, col in enumerate(wr_df_ex.columns, 2):
c = ws_wr.cell(1,ci,str(col)); [Link] = hdr_font; [Link] = hdr_fill
[Link] = center; [Link] = bdr
ws_wr.column_dimensions[get_column_letter(ci)].width = 10
for ri, (idx, row) in enumerate(wr_df_ex.iterrows(), 2):
ws_wr.cell(ri,1,str(idx)).font = idx_font; ws_wr.cell(ri,1).alignment =
left_al; ws_wr.cell(ri,1).border = bdr
for ci, val in enumerate(row, 2):
cell = ws_wr.cell(ri,ci); [Link] = bdr; [Link] = center
try:
v = float(val); [Link] = v/100; cell.number_format = '0.0%'
if v>=55: [Link] =
Font(name='Arial',size=10,color='1E8449',bold=True)
elif v<=45: [Link] =
Font(name='Arial',size=10,color='FF0000')
else: [Link] = Font(name='Arial',size=10,color='E67E22')
except: [Link] = ""; [Link] = num_font
buf = [Link](); [Link](buf); [Link](0); return buf
#
============================================
=========
# 18. PRETTY PDF EXPORT
#
============================================
=========
def _fmt_is_pct(metric_name): return metric_name.endswith("(%)")
def _format_val(v, is_pct=False):
if [Link](v) or v == "": return ""
try:
f = float(v)
return f"{f:,.2f}" + ("%" if is_pct else "")
except: return str(v)
def _is_negative(v):
try: return float(v) < 0
except: return False
def _wrap_label(name, max_word_len=14):
name = [Link](r"[/\-]", " / ", name)
def chunk(tok): return tok if len(tok)<=max_word_len else "\
u200b".join([tok[i:i+max_word_len] for i in
range(0,len(tok),max_word_len)])
return " ".join([chunk(t) for t in [Link]()])
def _fig_to_rl_image(fig, max_w, max_h, dpi=160):
buf = [Link](); [Link](buf, format="png", dpi=dpi,
bbox_inches="tight"); [Link](0)
img = RLImage(buf); w, h = [Link], [Link]
scale = min(max_w/float(w), max_h/float(h), 1.0)
[Link] = w*scale; [Link] = h*scale; return img
def generate_pdf_pretty(results_dict, mgrs, rfr_name, start_l, end_l,
metrics_order, fig_rr_=None, fig_ud_=None, viz_h_="",
max_cols_per_page=7):
buffer = [Link]()
doc = SimpleDocTemplate(buffer, pagesize=landscape(A4),
leftMargin=24, rightMargin=24, topMargin=36,
bottomMargin=36)
styles = getSampleStyleSheet()
page_w, page_h = landscape(A4)
avail_w = page_w - [Link] - [Link]
avail_h = page_h - [Link] - [Link]
elems = []
hdr_style = ParagraphStyle("hdr", parent=styles["Normal"],
fontName="Helvetica-Bold",
fontSize=8, leading=9.2, alignment=TA_CENTER)
hor_style = ParagraphStyle("hor", parent=styles["Normal"],
fontName="Helvetica",
fontSize=8, leading=9.2, alignment=TA_LEFT)
header_bg = [Link]("#ECF0F4")
grid_col = [Link]("#D1D7DF")
row_bg2 = [Link]("#FAFBFD")
# Cover
now_str = [Link]().strftime("%Y-%m-%d %H:%M")
[Link](Paragraph(f"Jarir Investments — Quant Performance
Report: {start_l} to {end_l}", styles["Title"]))
[Link](Paragraph(f"Generated: {now_str} | Risk-Free Rate:
<b>{rfr_name}</b>", styles["Normal"]))
[Link](Paragraph(f"Managers ({len(mgrs)}): {',
'.join(mgrs[:10])}{'…' if len(mgrs)>10 else ''}", styles["Normal"]))
[Link](Spacer(1,10))
# Charts
if fig_rr_ is not None:
[Link](PageBreak())
[Link](Paragraph(f"Risk–Return (Volatility vs Return) —
{viz_h_}", styles["Heading2"]))
[Link](Spacer(1,6))
[Link](_fig_to_rl_image(fig_rr_, avail_w, avail_h-40))
if fig_ud_ is not None:
[Link](PageBreak())
[Link](Paragraph(f"Upside–Downside (Downside Dev vs
Return) — {viz_h_}", styles["Heading2"]))
[Link](Spacer(1,6))
[Link](_fig_to_rl_image(fig_ud_, avail_w, avail_h-40))
if fig_rr_ is not None or fig_ud_ is not None:
[Link](PageBreak())
# Metric tables (chunked, pretty)
for mi, metric in enumerate(metrics_order):
dfm = results_dict[metric].apply(pd.to_numeric,
errors='coerce').round(2)
ncols = [Link][1]
chunks = max(1, [Link](ncols / max_cols_per_page))
for ci in range(chunks):
start_c = ci*max_cols_per_page; end_c =
min((ci+1)*max_cols_per_page, ncols)
sub = [Link][:, start_c:end_c].replace({[Link]: ""})
chunk_mgrs = ", ".join([_wrap_label(c) for c in [Link]])
[Link](Paragraph(metric, styles["Heading2"]))
[Link](Paragraph(f"<font size=9><b>Managers:</b>
{chunk_mgrs}</font>", styles["Normal"]))
[Link](Spacer(1,4))
is_pct = _fmt_is_pct(metric)
hdr_row = [Paragraph("Horizon", hdr_style)] +
[Paragraph(_wrap_label(str(c)), hdr_style) for c in [Link]]
data = [hdr_row]
for idx_, row_ in [Link]():
r = [Paragraph(str(idx_), hor_style)]
r += [_format_val(v, is_pct) for v in row_.values]
[Link](r)
ncols_t = len(data[0])
rest_ = ncols_t - 1
horizon_w = 120; avail_rest = max(120, avail_w - horizon_w)
w_each = min(max(avail_rest/rest_, 70), 115) if rest_ > 0 else
avail_rest
col_widths = [horizon_w] + [w_each]*rest_
tbl = LongTable(data, repeatRows=1, colWidths=col_widths,
hAlign="LEFT")
ts = TableStyle([
("BACKGROUND",(0,0),(-1,0), header_bg),
("TEXTCOLOR",(0,0),(-1,0), [Link]("#1F2328")),
("FONTNAME",(0,0),(-1,0), "Helvetica-Bold"),
("FONTSIZE",(0,0),(-1,0), 8.5),
("FONTSIZE",(0,1),(-1,-1), 8),
("VALIGN",(0,0),(-1,-1), "MIDDLE"),
("ALIGN",(0,0),(0,-1), "LEFT"),
("ALIGN",(1,0),(-1,-1), "RIGHT"),
("ROWBACKGROUNDS",(0,1),(-1,-1), [[Link], row_bg2]),
("GRID",(0,0),(-1,-1),0.25, grid_col),
("LEFTPADDING",(0,0),(-1,-1), 5),
("RIGHTPADDING",(0,0),(-1,-1), 5),
("TOPPADDING",(0,0),(-1,-1), 3),
("BOTTOMPADDING",(0,0),(-1,-1),3),
])
for r_idx in range(1, len(data)):
for c_idx in range(1, len(data[0])):
if _is_negative([Link][r_idx-1, c_idx-1]):
[Link]("TEXTCOLOR",(c_idx,r_idx),(c_idx,r_idx),
[Link]("#C0342B"))
[Link](ts); [Link](tbl)
[Link](Spacer(1,8) if ci < chunks-1 else (PageBreak() if mi
< len(metrics_order)-1 else Spacer(1,6)))
# Calendar, Alpha, 36M
def add_simple_table(df_p, title, is_pct=True):
[Link](Paragraph(title, styles["Heading2"]))
num_cols = len(df_p.columns)+1
font_size = 7 if num_cols<=10 else 5.5
index_w = 80 if num_cols<=10 else 60
col_widths = [index_w] + [(avail_w-index_w)/(num_cols-
1)]*(num_cols-1) if num_cols > 1 else [avail_w]
data = [[""] + list(df_p.columns)]
style_ = [('BACKGROUND',(0,0),(-1,0),[Link]("#4F6228")),
('TEXTCOLOR',(0,0),(-1,0),[Link]),
('GRID',(0,0),(-1,-1),0.5,[Link]),
('FONTSIZE',(0,0),(-1,-1),font_size),
('ALIGN',(0,0),(-1,-1),'CENTER'),
('FONTNAME',(0,0),(0,-1),'Helvetica-Bold'),
('TEXTCOLOR',(0,1),(0,-1),[Link])]
for r_i,(idx_,row_) in enumerate(df_p.iterrows(),1):
row_vals = [str(idx_)]
for c_i,v in enumerate(row_,1):
if [Link](v): row_vals.append("")
else:
row_vals.append(f"{v:.2f}%" if is_pct else f"{v:.2f}")
if v<0: style_.append(('TEXTCOLOR',(c_i,r_i),
(c_i,r_i),[Link]))
[Link](row_vals)
t = LongTable(data, repeatRows=1, colWidths=col_widths)
[Link](TableStyle(style_)); [Link](t);
[Link](Spacer(1,15))
[Link](PageBreak())
add_simple_table(cal_ret.apply(pd.to_numeric,errors='coerce').round(2),
"Calendar Year Returns (%)")
add_simple_table(cal_diff.apply(pd.to_numeric,errors='coerce').round(2),
f"Calendar Difference vs {bench_diff} (%)")
[Link](PageBreak())
add_simple_table(alpha_disp_year.apply(pd.to_numeric,errors='coerce').ro
und(2),
f"Alpha Matrix: {alpha_fund} vs {alpha_bench} (%)")
[Link](PageBreak())
m_36_p = cleaned_df.set_index('Date')[mgrs].tail(36).iloc[::-1]
m_36_p.index = m_36_p.[Link]('%b-%Y')
add_simple_table((m_36_p*100).apply(pd.to_numeric,errors='coerce').rou
nd(2), "Trailing 36-Month Returns (%)")
# Page numbers
def add_footer(canvas, doc_):
[Link]()
[Link]("Helvetica", 8)
[Link](doc_.pagesize[0]-24, 20, f"Page
{doc_.page}")
[Link]()
[Link](elems, onFirstPage=add_footer, onLaterPages=add_footer)
[Link](0); return [Link]()
#
============================================
=========
# 19. SIDEBAR EXPORT BUTTONS
#
============================================
=========
[Link]()
# CSV
csv_data = [Link](results, axis=0).to_csv().encode("utf-8")
[Link].download_button("📄 Download CSV", data=csv_data,
file_name=f"{[Link]()}_jarir_results.csv",
mime="text/csv")
[Link]()
if [Link]("📥 Generate Excel Report"):
with [Link]("Building Excel workbook..."):
xl_buf = generate_excel(
{m: results[m].apply(pd.to_numeric, errors='coerce') for m in
metrics},
manager_cols, cal_ret, cal_diff, alpha_disp_year, cap_tbl, wr_df)
[Link].download_button(
"⬇️Download Excel", data=xl_buf,
file_name=f"{[Link]()}_jarir_quant_analysis.xlsx",
mime="application/[Link]-
[Link]")
[Link]()
if [Link]("📑 Generate Master PDF"):
with [Link]("Building professional PDF report..."):
pdf_bytes = generate_pdf_pretty(
results_dict=results, mgrs=manager_cols, rfr_name=rfr_col,
start_l=start_label, end_l=end_label, metrics_order=metrics,
fig_rr_=fig_rr, fig_ud_=fig_ud, viz_h_=viz_h)
[Link].download_button(
"⬇️Download PDF", data=pdf_bytes,
file_name=f"{[Link]()}_jarir_quant_analysis.pdf",
mime="application/pdf")