@@ -46,7 +46,9 @@ def _calculate_risk_metrics(self) -> Dict[str, float]:
4646 vol = float (self .returns .std (ddof = 1 ) * np .sqrt (252 ))
4747 downside_returns = self .returns [self .returns < 0 ]
4848 downside_vol = (
49- float (downside_returns .std (ddof = 1 ) * np .sqrt (252 )) if len (downside_returns ) > 0 else 0.0
49+ float (downside_returns .std (ddof = 1 ) * np .sqrt (252 ))
50+ if len (downside_returns ) > 0
51+ else 0.0
5052 )
5153
5254 max_drawdown , drawdown_duration = self ._calculate_drawdown ()
@@ -57,7 +59,9 @@ def _calculate_risk_metrics(self) -> Dict[str, float]:
5759 "max_drawdown" : max_drawdown ,
5860 "drawdown_duration" : drawdown_duration ,
5961 "var_95" : float (self .returns .quantile (0.05 )),
60- "cvar_95" : float (self .returns [self .returns <= self .returns .quantile (0.05 )].mean ()),
62+ "cvar_95" : float (
63+ self .returns [self .returns <= self .returns .quantile (0.05 )].mean ()
64+ ),
6165 }
6266
6367 def _calculate_ratio_metrics (self ) -> Dict [str , float ]:
@@ -91,7 +95,9 @@ def _calculate_benchmark_metrics(self) -> Dict[str, float]:
9195 return {}
9296
9397 # Ensure identical index after dropna
94- strategy_returns , benchmark_returns = strategy_returns .align (benchmark_returns , join = "inner" )
98+ strategy_returns , benchmark_returns = strategy_returns .align (
99+ benchmark_returns , join = "inner"
100+ )
95101
96102 x = benchmark_returns .values .astype (float )
97103 y = strategy_returns .values .astype (float )
@@ -114,10 +120,18 @@ def _calculate_benchmark_metrics(self) -> Dict[str, float]:
114120
115121 # Tracking error (annualized std of active returns)
116122 active_returns = (strategy_returns - benchmark_returns ).dropna ()
117- tracking_error = float (active_returns .std (ddof = 1 ) * np .sqrt (252 )) if len (active_returns ) > 1 else 0.0
123+ tracking_error = (
124+ float (active_returns .std (ddof = 1 ) * np .sqrt (252 ))
125+ if len (active_returns ) > 1
126+ else 0.0
127+ )
118128
119129 # Information ratio
120- info_ratio = float ((strategy_cagr - benchmark_cagr ) / tracking_error ) if tracking_error > 0 else 0.0
130+ info_ratio = (
131+ float ((strategy_cagr - benchmark_cagr ) / tracking_error )
132+ if tracking_error > 0
133+ else 0.0
134+ )
121135
122136 return {
123137 "alpha" : alpha ,
@@ -144,7 +158,11 @@ def _calculate_cagr_from_returns(self, returns: pd.Series) -> float:
144158 def _calculate_downside_vol (self ) -> float :
145159 """Calculate downside volatility (for Sortino ratio)."""
146160 downside_returns = self .returns [self .returns < 0 ]
147- return float (downside_returns .std (ddof = 1 ) * np .sqrt (252 )) if len (downside_returns ) > 0 else 0.0
161+ return (
162+ float (downside_returns .std (ddof = 1 ) * np .sqrt (252 ))
163+ if len (downside_returns ) > 0
164+ else 0.0
165+ )
148166
149167 def _calculate_drawdown (self ) -> Tuple [float , int ]:
150168 """Calculate maximum drawdown and duration."""
@@ -168,8 +186,14 @@ def _calculate_drawdown(self) -> Tuple[float, int]:
168186 try :
169187 prior_max_mask = running_max [running_max .index <= max_dd_period ]
170188 drawdown_start_val = prior_max_mask .max ()
171- start_candidates = prior_max_mask [prior_max_mask == drawdown_start_val ].index
172- drawdown_start = start_candidates [- 1 ] if len (start_candidates ) > 0 else running_max .index [0 ]
189+ start_candidates = prior_max_mask [
190+ prior_max_mask == drawdown_start_val
191+ ].index
192+ drawdown_start = (
193+ start_candidates [- 1 ]
194+ if len (start_candidates ) > 0
195+ else running_max .index [0 ]
196+ )
173197 drawdown_duration = int ((max_dd_period - drawdown_start ).days )
174198 except Exception :
175199 drawdown_duration = 0
0 commit comments