import pandas as pd import numpy as np def israelsen_sharpe(excess_return, vol): """ Modified Sharpe Ratio by Craig Israelsen. Standard Sharpe penalizes higher volatility even when returns are negative. This adjustment correctly rewards lower volatility when returns are negative. """ if excess_return == 0 or vol == 0: return 0.0 exponent = excess_return / abs(excess_return) return excess_return / (vol ** exponent) def portfolio_gross_metrics(weights: pd.Series, current_yields: pd.Series = None, durations: pd.Series = None) -> dict: """ Calculates gross leverage, total long exposure, and total short exposure. Optionally computes portfolio-level weighted yield and duration if data is provided. Returns: dict: Containing 'gross_lev', 'long_e', 'short_e', 'port_yield', 'port_duration' """ w = weights.drop(labels=['CASH'], errors='ignore') long_e = w[w > 0].sum() short_e = w[w < 0].sum() gross_lev = abs(long_e) + abs(short_e) port_yield = None port_duration = None if current_yields is not None: port_yield = sum(w.get(t, 0) * current_yields.get(t, 0) for t in w.index) if durations is not None: port_duration = sum(w.get(t, 0) * durations.get(t, 0) for t in w.index) return { "gross_lev": float(gross_lev), "long_e": float(long_e), "short_e": float(short_e), "port_yield": float(port_yield) if port_yield is not None else None, "port_duration": float(port_duration) if port_duration is not None else None } def liquidity_score(weights, spread_map): """Calculates the weighted average bid-ask spread of the portfolio.""" w = weights.drop(labels=['CASH'], errors='ignore') if w.abs().sum() == 0: return 0.0 w_norm = w.abs() / w.abs().sum() score = sum(w_norm.get(t, 0) * spread_map.get(t, 0.0008) for t in w.index) return score def annual_returns(daily_returns_series): """Aggregates daily returns into calendar year returns.""" if daily_returns_series.empty: return {} s = daily_returns_series.copy() s.index = pd.to_datetime(s.index) ann = s.groupby(s.index.year).apply(lambda x: (1 + x).prod() - 1) return {int(year): float(v) for year, v in ann.items() if not pd.isna(v)}