Spaces:
Running
on
Zero
Running
on
Zero
attempt more advanced predictions using model ensemble
Browse files
app.py
CHANGED
@@ -1128,81 +1128,126 @@ def calculate_advanced_risk_metrics(df: pd.DataFrame, market_returns: pd.Series
|
|
1128 |
Returns:
|
1129 |
Dict: Advanced risk metrics
|
1130 |
"""
|
1131 |
-
|
1132 |
-
|
1133 |
-
|
1134 |
-
|
1135 |
-
|
1136 |
-
|
1137 |
-
|
1138 |
-
|
1139 |
-
|
1140 |
-
|
1141 |
-
|
1142 |
-
# Align dates
|
1143 |
-
aligned_returns = returns.reindex(market_returns.index).dropna()
|
1144 |
-
aligned_market = market_returns.reindex(aligned_returns.index).dropna()
|
1145 |
-
|
1146 |
-
if len(aligned_returns) > 10:
|
1147 |
-
beta = np.cov(aligned_returns, aligned_market)[0,1] / np.var(aligned_market)
|
1148 |
-
alpha = aligned_returns.mean() - beta * aligned_market.mean()
|
1149 |
-
correlation = np.corrcoef(aligned_returns, aligned_market)[0,1]
|
1150 |
-
else:
|
1151 |
-
beta = 1.0
|
1152 |
-
alpha = 0.0
|
1153 |
-
correlation = 0.0
|
1154 |
-
else:
|
1155 |
beta = 1.0
|
1156 |
alpha = 0.0
|
1157 |
correlation = 0.0
|
1158 |
-
|
1159 |
-
|
1160 |
-
|
1161 |
-
|
1162 |
-
|
1163 |
-
|
1164 |
-
|
1165 |
-
|
1166 |
-
|
1167 |
-
|
1168 |
-
|
1169 |
-
|
1170 |
-
|
1171 |
-
|
1172 |
-
|
1173 |
-
|
1174 |
-
|
1175 |
-
|
1176 |
-
|
1177 |
-
|
1178 |
-
|
1179 |
-
|
1180 |
-
|
1181 |
-
|
1182 |
-
|
1183 |
-
|
1184 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1185 |
information_ratio = 0
|
1186 |
-
|
1187 |
-
|
1188 |
-
|
1189 |
-
|
1190 |
-
|
1191 |
-
|
1192 |
-
|
1193 |
-
|
1194 |
-
|
1195 |
-
|
1196 |
-
|
1197 |
-
|
1198 |
-
|
1199 |
-
|
1200 |
-
|
1201 |
-
|
1202 |
-
|
1203 |
-
|
1204 |
-
|
1205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1206 |
|
1207 |
def create_ensemble_prediction(df: pd.DataFrame, prediction_days: int,
|
1208 |
ensemble_weights: Dict = None) -> Tuple[np.ndarray, np.ndarray]:
|
|
|
1128 |
Returns:
|
1129 |
Dict: Advanced risk metrics
|
1130 |
"""
|
1131 |
+
try:
|
1132 |
+
returns = df['Returns'].dropna()
|
1133 |
+
|
1134 |
+
if len(returns) < 30:
|
1135 |
+
return {"error": "Insufficient data for risk calculation"}
|
1136 |
+
|
1137 |
+
# Basic metrics
|
1138 |
+
annual_return = returns.mean() * 252
|
1139 |
+
annual_vol = returns.std() * np.sqrt(252)
|
1140 |
+
|
1141 |
+
# Market-adjusted metrics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1142 |
beta = 1.0
|
1143 |
alpha = 0.0
|
1144 |
correlation = 0.0
|
1145 |
+
aligned_returns = None
|
1146 |
+
aligned_market = None
|
1147 |
+
|
1148 |
+
if market_returns is not None and len(market_returns) > 0:
|
1149 |
+
try:
|
1150 |
+
# Align dates
|
1151 |
+
aligned_returns = returns.reindex(market_returns.index).dropna()
|
1152 |
+
aligned_market = market_returns.reindex(aligned_returns.index).dropna()
|
1153 |
+
|
1154 |
+
# Ensure both arrays have the same length
|
1155 |
+
if len(aligned_returns) > 10 and len(aligned_market) > 10:
|
1156 |
+
# Find the common length
|
1157 |
+
min_length = min(len(aligned_returns), len(aligned_market))
|
1158 |
+
aligned_returns = aligned_returns.iloc[-min_length:]
|
1159 |
+
aligned_market = aligned_market.iloc[-min_length:]
|
1160 |
+
|
1161 |
+
# Ensure they have the same length
|
1162 |
+
if len(aligned_returns) == len(aligned_market) and len(aligned_returns) > 10:
|
1163 |
+
try:
|
1164 |
+
beta = np.cov(aligned_returns, aligned_market)[0,1] / np.var(aligned_market)
|
1165 |
+
alpha = aligned_returns.mean() - beta * aligned_market.mean()
|
1166 |
+
correlation = np.corrcoef(aligned_returns, aligned_market)[0,1]
|
1167 |
+
except Exception as e:
|
1168 |
+
print(f"Market correlation calculation error: {str(e)}")
|
1169 |
+
beta = 1.0
|
1170 |
+
alpha = 0.0
|
1171 |
+
correlation = 0.0
|
1172 |
+
else:
|
1173 |
+
beta = 1.0
|
1174 |
+
alpha = 0.0
|
1175 |
+
correlation = 0.0
|
1176 |
+
else:
|
1177 |
+
beta = 1.0
|
1178 |
+
alpha = 0.0
|
1179 |
+
correlation = 0.0
|
1180 |
+
except Exception as e:
|
1181 |
+
print(f"Market data alignment error: {str(e)}")
|
1182 |
+
beta = 1.0
|
1183 |
+
alpha = 0.0
|
1184 |
+
correlation = 0.0
|
1185 |
+
aligned_returns = None
|
1186 |
+
aligned_market = None
|
1187 |
+
|
1188 |
+
# Tail risk metrics
|
1189 |
+
var_95 = np.percentile(returns, 5)
|
1190 |
+
var_99 = np.percentile(returns, 1)
|
1191 |
+
cvar_95 = returns[returns <= var_95].mean()
|
1192 |
+
cvar_99 = returns[returns <= var_99].mean()
|
1193 |
+
|
1194 |
+
# Maximum drawdown
|
1195 |
+
cumulative_returns = (1 + returns).cumprod()
|
1196 |
+
rolling_max = cumulative_returns.expanding().max()
|
1197 |
+
drawdown = (cumulative_returns - rolling_max) / rolling_max
|
1198 |
+
max_drawdown = drawdown.min()
|
1199 |
+
|
1200 |
+
# Skewness and kurtosis
|
1201 |
+
skewness = stats.skew(returns)
|
1202 |
+
kurtosis = stats.kurtosis(returns)
|
1203 |
+
|
1204 |
+
# Risk-adjusted returns
|
1205 |
+
sharpe_ratio = (annual_return - risk_free_rate) / annual_vol if annual_vol > 0 else 0
|
1206 |
+
sortino_ratio = (annual_return - risk_free_rate) / (returns[returns < 0].std() * np.sqrt(252)) if returns[returns < 0].std() > 0 else 0
|
1207 |
+
calmar_ratio = annual_return / abs(max_drawdown) if max_drawdown != 0 else 0
|
1208 |
+
|
1209 |
+
# Information ratio (if market data available)
|
1210 |
information_ratio = 0
|
1211 |
+
if aligned_returns is not None and aligned_market is not None:
|
1212 |
+
try:
|
1213 |
+
if len(aligned_returns) > 10 and len(aligned_market) > 10:
|
1214 |
+
min_length = min(len(aligned_returns), len(aligned_market))
|
1215 |
+
aligned_returns_for_ir = aligned_returns.iloc[-min_length:]
|
1216 |
+
aligned_market_for_ir = aligned_market.iloc[-min_length:]
|
1217 |
+
|
1218 |
+
if len(aligned_returns_for_ir) == len(aligned_market_for_ir):
|
1219 |
+
excess_returns = aligned_returns_for_ir - aligned_market_for_ir
|
1220 |
+
information_ratio = excess_returns.mean() / excess_returns.std() if excess_returns.std() > 0 else 0
|
1221 |
+
else:
|
1222 |
+
information_ratio = 0
|
1223 |
+
else:
|
1224 |
+
information_ratio = 0
|
1225 |
+
except Exception as e:
|
1226 |
+
print(f"Information ratio calculation error: {str(e)}")
|
1227 |
+
information_ratio = 0
|
1228 |
+
|
1229 |
+
return {
|
1230 |
+
"Annual_Return": annual_return,
|
1231 |
+
"Annual_Volatility": annual_vol,
|
1232 |
+
"Sharpe_Ratio": sharpe_ratio,
|
1233 |
+
"Sortino_Ratio": sortino_ratio,
|
1234 |
+
"Calmar_Ratio": calmar_ratio,
|
1235 |
+
"Information_Ratio": information_ratio,
|
1236 |
+
"Beta": beta,
|
1237 |
+
"Alpha": alpha * 252,
|
1238 |
+
"Correlation_with_Market": correlation,
|
1239 |
+
"VaR_95": var_95,
|
1240 |
+
"VaR_99": var_99,
|
1241 |
+
"CVaR_95": cvar_95,
|
1242 |
+
"CVaR_99": cvar_99,
|
1243 |
+
"Max_Drawdown": max_drawdown,
|
1244 |
+
"Skewness": skewness,
|
1245 |
+
"Kurtosis": kurtosis,
|
1246 |
+
"Risk_Free_Rate": risk_free_rate
|
1247 |
+
}
|
1248 |
+
except Exception as e:
|
1249 |
+
print(f"Advanced risk metrics calculation error: {str(e)}")
|
1250 |
+
return {"error": f"Risk calculation failed: {str(e)}"}
|
1251 |
|
1252 |
def create_ensemble_prediction(df: pd.DataFrame, prediction_days: int,
|
1253 |
ensemble_weights: Dict = None) -> Tuple[np.ndarray, np.ndarray]:
|